import { expect, tap } from '@git.zone/tstest/tapbundle'; import { EInvoice } from '../../../ts/index.js'; tap.test('ERR-06: Concurrent Errors - should handle concurrent processing errors', async () => { console.log('Testing concurrent processing error handling...\n'); // Test 1: Concurrent processing of different invoices const testConcurrentInvoiceProcessing = async () => { console.log('Test 1 - Concurrent processing of different invoices:'); let allProcessed = true; let errorsCaught = 0; const invoiceCount = 5; try { const promises = []; for (let i = 0; i < invoiceCount; i++) { const promise = (async () => { try { const einvoice = new EInvoice(); einvoice.issueDate = new Date(2024, 0, 1); einvoice.invoiceId = `CONCURRENT-${i + 1}`; einvoice.from = { type: 'company', name: `Company ${i + 1}`, description: 'Testing concurrent processing', address: { streetName: 'Test Street', houseNumber: String(i + 1), postalCode: '12345', city: 'Test City', country: 'DE' }, status: 'active', foundedDate: { year: 2020, month: 1, day: 1 }, registrationDetails: { vatId: 'DE123456789', registrationId: 'HRB 12345', registrationName: 'Commercial Register' } }; einvoice.to = { type: 'person', name: 'Test', surname: 'Customer', salutation: 'Mr' as const, sex: 'male' as const, title: 'Doctor' as const, description: 'Test customer', address: { streetName: 'Customer Street', houseNumber: '2', postalCode: '54321', city: 'Customer City', country: 'DE' } }; einvoice.items = [{ position: 1, name: `Item for Invoice ${i + 1}`, articleNumber: `ART-${i + 1}`, unitType: 'EA', unitQuantity: 1, unitNetPrice: 100 + i, vatPercentage: 19 }]; const xml = await einvoice.toXmlString('ubl'); return { success: true, invoiceId: `CONCURRENT-${i + 1}`, xml }; } catch (error) { return { success: false, error: error.message, invoiceId: `CONCURRENT-${i + 1}` }; } })(); promises.push(promise); } const results = await Promise.all(promises); const successful = results.filter(r => r.success); const failed = results.filter(r => !r.success); allProcessed = successful.length === invoiceCount; errorsCaught = failed.length; console.log(` Successful: ${successful.length}/${invoiceCount}`); console.log(` Failed: ${failed.length}/${invoiceCount}`); if (failed.length > 0) { console.log(` Errors: ${failed.map(f => f.error).join(', ')}`); } } catch (error) { console.log(` Concurrent processing failed: ${error.message}`); allProcessed = false; } return { allProcessed, errorsCaught }; }; // Test 2: Mixed valid and invalid concurrent operations const testMixedConcurrentOperations = async () => { console.log('\nTest 2 - Mixed valid and invalid concurrent operations:'); let validProcessed = 0; let invalidHandled = 0; let totalOperations = 0; try { const operations = [ // Valid operations async () => { const einvoice = new EInvoice(); einvoice.issueDate = new Date(2024, 0, 1); einvoice.invoiceId = 'VALID-001'; einvoice.from = { type: 'company', name: 'Valid Company', description: 'Valid invoice', address: { streetName: 'Valid Street', houseNumber: '1', postalCode: '12345', city: 'Valid City', country: 'DE' }, status: 'active', foundedDate: { year: 2020, month: 1, day: 1 }, registrationDetails: { vatId: 'DE123456789', registrationId: 'HRB 12345', registrationName: 'Commercial Register' } }; einvoice.to = { type: 'person', name: 'Valid', surname: 'Customer', salutation: 'Mr' as const, sex: 'male' as const, title: 'Doctor' as const, description: 'Valid customer', address: { streetName: 'Customer Street', houseNumber: '2', postalCode: '54321', city: 'Customer City', country: 'DE' } }; einvoice.items = [{ position: 1, name: 'Valid Item', articleNumber: 'VALID-001', unitType: 'EA', unitQuantity: 1, unitNetPrice: 100, vatPercentage: 19 }]; await einvoice.toXmlString('ubl'); return { type: 'valid', success: true }; }, // Invalid XML parsing async () => { const einvoice = new EInvoice(); await einvoice.fromXmlString('broken'); return { type: 'invalid', success: false }; }, // Invalid validation (missing required fields) async () => { const einvoice = new EInvoice(); await einvoice.toXmlString('ubl'); // Missing required fields return { type: 'invalid', success: false }; }, // Another valid operation async () => { const einvoice = new EInvoice(); einvoice.issueDate = new Date(2024, 0, 1); einvoice.invoiceId = 'VALID-002'; einvoice.from = { type: 'company', name: 'Another Valid Company', description: 'Another valid invoice', address: { streetName: 'Another Valid Street', houseNumber: '2', postalCode: '12345', city: 'Valid City', country: 'DE' }, status: 'active', foundedDate: { year: 2020, month: 1, day: 1 }, registrationDetails: { vatId: 'DE123456789', registrationId: 'HRB 12345', registrationName: 'Commercial Register' } }; einvoice.to = { type: 'person', name: 'Another', surname: 'Customer', salutation: 'Ms' as const, sex: 'female' as const, title: 'Doctor' as const, description: 'Another customer', address: { streetName: 'Another Customer Street', houseNumber: '3', postalCode: '54321', city: 'Customer City', country: 'DE' } }; einvoice.items = [{ position: 1, name: 'Another Valid Item', articleNumber: 'VALID-002', unitType: 'EA', unitQuantity: 2, unitNetPrice: 200, vatPercentage: 19 }]; await einvoice.toXmlString('cii'); return { type: 'valid', success: true }; } ]; totalOperations = operations.length; const results = await Promise.allSettled(operations.map(op => op())); for (const result of results) { if (result.status === 'fulfilled') { if (result.value.type === 'valid' && result.value.success) { validProcessed++; } } else { // Rejected (error caught) invalidHandled++; } } console.log(` Valid operations processed: ${validProcessed}`); console.log(` Invalid operations handled: ${invalidHandled}`); console.log(` Total operations: ${totalOperations}`); } catch (error) { console.log(` Mixed operations test failed: ${error.message}`); } return { validProcessed, invalidHandled, totalOperations }; }; // Test 3: Concurrent format conversions const testConcurrentFormatConversions = async () => { console.log('\nTest 3 - Concurrent format conversions:'); let conversionsSuccessful = 0; let conversionErrors = 0; try { // Create a base invoice const createBaseInvoice = () => { const einvoice = new EInvoice(); einvoice.issueDate = new Date(2024, 0, 1); einvoice.invoiceId = 'CONVERT-TEST'; einvoice.from = { type: 'company', name: 'Conversion Test Company', description: 'Testing format conversions', address: { streetName: 'Convert Street', houseNumber: '1', postalCode: '12345', city: 'Convert City', country: 'DE' }, status: 'active', foundedDate: { year: 2020, month: 1, day: 1 }, registrationDetails: { vatId: 'DE123456789', registrationId: 'HRB 12345', registrationName: 'Commercial Register' } }; einvoice.to = { type: 'person', name: 'Convert', surname: 'Customer', salutation: 'Mr' as const, sex: 'male' as const, title: 'Doctor' as const, description: 'Convert customer', address: { streetName: 'Customer Street', houseNumber: '2', postalCode: '54321', city: 'Customer City', country: 'DE' } }; einvoice.items = [{ position: 1, name: 'Convert Item', articleNumber: 'CONVERT-001', unitType: 'EA', unitQuantity: 1, unitNetPrice: 100, vatPercentage: 19 }]; return einvoice; }; const formats = ['ubl', 'cii', 'xrechnung']; const conversionPromises = []; for (let i = 0; i < 3; i++) { for (const format of formats) { const promise = (async () => { try { const einvoice = createBaseInvoice(); einvoice.invoiceId = `CONVERT-${format.toUpperCase()}-${i + 1}`; const xml = await einvoice.toXmlString(format as any); return { format, success: true, length: xml.length }; } catch (error) { return { format, success: false, error: error.message }; } })(); conversionPromises.push(promise); } } const results = await Promise.all(conversionPromises); conversionsSuccessful = results.filter(r => r.success).length; conversionErrors = results.filter(r => !r.success).length; console.log(` Successful conversions: ${conversionsSuccessful}/${results.length}`); console.log(` Conversion errors: ${conversionErrors}/${results.length}`); if (conversionErrors > 0) { const errorFormats = results.filter(r => !r.success).map(r => r.format); console.log(` Failed formats: ${errorFormats.join(', ')}`); } } catch (error) { console.log(` Concurrent conversions failed: ${error.message}`); } return { conversionsSuccessful, conversionErrors }; }; // Test 4: Error isolation between concurrent operations const testErrorIsolation = async () => { console.log('\nTest 4 - Error isolation between concurrent operations:'); let isolationWorking = false; let validOperationSucceeded = false; try { const operations = [ // This should fail async () => { const einvoice = new EInvoice(); await einvoice.fromXmlString('unclosed'); }, // This should succeed despite the other failing async () => { const einvoice = new EInvoice(); einvoice.issueDate = new Date(2024, 0, 1); einvoice.invoiceId = 'ISOLATION-TEST'; einvoice.from = { type: 'company', name: 'Isolation Company', description: 'Testing error isolation', address: { streetName: 'Isolation Street', houseNumber: '1', postalCode: '12345', city: 'Isolation City', country: 'DE' }, status: 'active', foundedDate: { year: 2020, month: 1, day: 1 }, registrationDetails: { vatId: 'DE123456789', registrationId: 'HRB 12345', registrationName: 'Commercial Register' } }; einvoice.to = { type: 'person', name: 'Isolation', surname: 'Customer', salutation: 'Mr' as const, sex: 'male' as const, title: 'Doctor' as const, description: 'Isolation customer', address: { streetName: 'Customer Street', houseNumber: '2', postalCode: '54321', city: 'Customer City', country: 'DE' } }; einvoice.items = [{ position: 1, name: 'Isolation Item', articleNumber: 'ISOLATION-001', unitType: 'EA', unitQuantity: 1, unitNetPrice: 100, vatPercentage: 19 }]; const xml = await einvoice.toXmlString('ubl'); return xml.includes('ISOLATION-TEST'); } ]; const results = await Promise.allSettled(operations); // First operation should fail const failedAsExpected = results[0].status === 'rejected'; // Second operation should succeed validOperationSucceeded = results[1].status === 'fulfilled' && typeof results[1].value === 'boolean' && results[1].value === true; isolationWorking = failedAsExpected && validOperationSucceeded; console.log(` Invalid operation failed as expected: ${failedAsExpected ? 'Yes' : 'No'}`); console.log(` Valid operation succeeded despite error: ${validOperationSucceeded ? 'Yes' : 'No'}`); console.log(` Error isolation working: ${isolationWorking ? 'Yes' : 'No'}`); } catch (error) { console.log(` Error isolation test failed: ${error.message}`); } return { isolationWorking, validOperationSucceeded }; }; // Run all tests const result1 = await testConcurrentInvoiceProcessing(); const result2 = await testMixedConcurrentOperations(); const result3 = await testConcurrentFormatConversions(); const result4 = await testErrorIsolation(); console.log('\n=== Concurrent Error Handling Summary ==='); console.log(`Concurrent processing: ${result1.allProcessed ? 'Working' : 'Partial/Failed'}`); console.log(`Mixed operations: ${result2.validProcessed > 0 ? 'Working' : 'Failed'}`); console.log(`Format conversions: ${result3.conversionsSuccessful > 0 ? 'Working' : 'Failed'}`); console.log(`Error isolation: ${result4.isolationWorking ? 'Working' : 'Failed'}`); // Test passes if core concurrent processing capabilities work const basicConcurrentWorks = result1.allProcessed; // All 5 invoices processed const formatConversionsWork = result3.conversionsSuccessful === 9; // All 9 conversions successful const mixedOperationsWork = result2.validProcessed > 0; // Valid operations work in mixed scenarios expect(basicConcurrentWorks).toBeTrue(); // Must process multiple invoices concurrently expect(formatConversionsWork).toBeTrue(); // Must handle concurrent format conversions expect(mixedOperationsWork).toBeTrue(); // Must handle mixed valid/invalid operations }); tap.start();