einvoice/test/suite/einvoice_edge-cases/test.edge-08.mixed-formats.ts
Philipp Kunz 784a50bc7f fix(tests): Fixed ENC-01, ENC-02, and ENC-03 encoding tests
- Fixed UTF-8 encoding test (ENC-01) to accept multiple encoding declarations
- Fixed UTF-16 encoding test (ENC-02) by rewriting with correct API usage
- Fixed ISO-8859-1 encoding test (ENC-03) with proper address fields and methods
- All three encoding tests now pass successfully
- Updated edge-cases tests (EDGE-02 through EDGE-07) with new test structure
2025-05-28 13:05:59 +00:00

378 lines
12 KiB
TypeScript

import { tap, expect } from '@git.zone/tstest/tapbundle';
import { EInvoice } from '../../../ts/index.js';
import { PerformanceTracker } from '../performance.tracker.js';
import { FormatDetector } from '../../../ts/formats/utils/format.detector.js';
const performanceTracker = new PerformanceTracker('EDGE-08: Mixed Format Documents');
tap.test('EDGE-08: Mixed Format Documents - should handle documents with mixed or ambiguous formats', async () => {
console.log('Testing mixed format document handling...\n');
// Test 1: Invalid XML with mixed namespaces
const invalidMixedTest = await performanceTracker.measureAsync(
'invalid-mixed-xml',
async () => {
const invalidMixedXML = `<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cii="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100">
<!-- Mixing UBL and CII elements incorrectly -->
<ubl:ID>MIXED-001</ubl:ID>
<cii:ExchangedDocument>
<ram:ID>MIXED-001</ram:ID>
</cii:ExchangedDocument>
</Invoice>`;
try {
const format = FormatDetector.detectFormat(invalidMixedXML);
return {
detected: true,
format: format,
error: null
};
} catch (error) {
return {
detected: false,
format: null,
error: error.message
};
}
}
);
console.log(`Test 1 - Invalid mixed XML: ${invalidMixedTest.detected ? 'Handled' : 'Failed'}`);
if (invalidMixedTest.format) {
console.log(` Detected format: ${invalidMixedTest.format}`);
}
// Test 2: Valid UBL with unusual namespace declarations
const unusualNamespacesTest = await performanceTracker.measureAsync(
'unusual-namespaces',
async () => {
const unusualUBL = `<?xml version="1.0" encoding="UTF-8"?>
<ns0:Invoice xmlns:ns0="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:ns1="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
xmlns:ns2="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<ns2:ID>UNUSUAL-001</ns2:ID>
<ns2:IssueDate>2024-01-15</ns2:IssueDate>
<ns2:InvoiceTypeCode>380</ns2:InvoiceTypeCode>
<ns2:DocumentCurrencyCode>EUR</ns2:DocumentCurrencyCode>
<ns1:AccountingSupplierParty>
<ns1:Party>
<ns1:PartyName>
<ns2:Name>Test Supplier</ns2:Name>
</ns1:PartyName>
<ns1:PostalAddress>
<ns2:StreetName>Test Street</ns2:StreetName>
<ns2:CityName>Test City</ns2:CityName>
<ns2:PostalZone>12345</ns2:PostalZone>
<ns1:Country>
<ns2:IdentificationCode>DE</ns2:IdentificationCode>
</ns1:Country>
</ns1:PostalAddress>
</ns1:Party>
</ns1:AccountingSupplierParty>
<ns1:AccountingCustomerParty>
<ns1:Party>
<ns1:PartyName>
<ns2:Name>Test Customer</ns2:Name>
</ns1:PartyName>
<ns1:PostalAddress>
<ns2:StreetName>Customer Street</ns2:StreetName>
<ns2:CityName>Customer City</ns2:CityName>
<ns2:PostalZone>54321</ns2:PostalZone>
<ns1:Country>
<ns2:IdentificationCode>DE</ns2:IdentificationCode>
</ns1:Country>
</ns1:PostalAddress>
</ns1:Party>
</ns1:AccountingCustomerParty>
<ns1:LegalMonetaryTotal>
<ns2:PayableAmount currencyID="EUR">100.00</ns2:PayableAmount>
</ns1:LegalMonetaryTotal>
<ns1:InvoiceLine>
<ns2:ID>1</ns2:ID>
<ns2:InvoicedQuantity unitCode="EA">1</ns2:InvoicedQuantity>
<ns2:LineExtensionAmount currencyID="EUR">100.00</ns2:LineExtensionAmount>
<ns1:Item>
<ns2:Name>Test Item</ns2:Name>
</ns1:Item>
</ns1:InvoiceLine>
</ns0:Invoice>`;
try {
const einvoice = new EInvoice();
await einvoice.loadXml(unusualUBL);
const exported = await einvoice.toXmlString('ubl');
return {
success: true,
invoiceId: einvoice.id,
hasValidStructure: exported.includes('Invoice'),
preservedData: exported.includes('UNUSUAL-001')
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
);
console.log(`\nTest 2 - Unusual namespaces: ${unusualNamespacesTest.success ? 'Success' : 'Failed'}`);
if (unusualNamespacesTest.success) {
console.log(` Invoice ID: ${unusualNamespacesTest.invoiceId}`);
console.log(` Valid structure: ${unusualNamespacesTest.hasValidStructure}`);
console.log(` Data preserved: ${unusualNamespacesTest.preservedData}`);
}
expect(unusualNamespacesTest.success).toEqual(true);
// Test 3: Malformed but recoverable XML
const malformedRecoverableTest = await performanceTracker.measureAsync(
'malformed-recoverable',
async () => {
const malformedXML = `<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:ID>MALFORMED-001</cbc:ID>
<cbc:IssueDate>2024-01-15</cbc:IssueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<!-- Extra characters that shouldn't be here --> &amp;
<cac:AccountingSupplierParty>
<cac:Party>
<cac:PartyName>
<cbc:Name>Test &amp; Supplier</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Test Street</cbc:StreetName>
<cbc:CityName>Test City</cbc:CityName>
<cbc:PostalZone>12345</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty>
<cac:Party>
<cac:PartyName>
<cbc:Name>Test Customer</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Customer Street</cbc:StreetName>
<cbc:CityName>Customer City</cbc:CityName>
<cbc:PostalZone>54321</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
</cac:Party>
</cac:AccountingCustomerParty>
<cac:LegalMonetaryTotal>
<cbc:PayableAmount currencyID="EUR">100.00</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:InvoicedQuantity unitCode="EA">1</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">100.00</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Name>Test Item</cbc:Name>
</cac:Item>
</cac:InvoiceLine>
</Invoice>`;
try {
const einvoice = new EInvoice();
await einvoice.loadXml(malformedXML);
return {
parsed: true,
invoiceId: einvoice.id,
supplierName: einvoice.from?.name,
preserved: einvoice.from?.name?.includes('&')
};
} catch (error) {
return {
parsed: false,
error: error.message
};
}
}
);
console.log(`\nTest 3 - Malformed but recoverable: ${malformedRecoverableTest.parsed ? 'Parsed' : 'Failed'}`);
if (malformedRecoverableTest.parsed) {
console.log(` Invoice ID: ${malformedRecoverableTest.invoiceId}`);
console.log(` Supplier name: ${malformedRecoverableTest.supplierName}`);
console.log(` Special chars preserved: ${malformedRecoverableTest.preserved}`);
}
// Test 4: Format detection edge cases
const formatDetectionTest = await performanceTracker.measureAsync(
'format-detection-edge',
async () => {
const testCases = [
{
name: 'Empty namespace',
xml: `<?xml version="1.0" encoding="UTF-8"?><Invoice><ID>TEST</ID></Invoice>`
},
{
name: 'Wrong root element',
xml: `<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
<cbc:ID>TEST</cbc:ID>
</Document>`
},
{
name: 'Mixed case namespace',
xml: `<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:UBL:schema:xsd:Invoice-2">
<ID>TEST</ID>
</Invoice>`
}
];
const results = [];
for (const testCase of testCases) {
try {
const format = FormatDetector.detectFormat(testCase.xml);
results.push({
name: testCase.name,
detected: true,
format: format
});
} catch (error) {
results.push({
name: testCase.name,
detected: false,
error: error.message
});
}
}
return results;
}
);
console.log('\nTest 4 - Format detection edge cases:');
formatDetectionTest.forEach(result => {
console.log(` ${result.name}: ${result.detected ? `Detected as ${result.format}` : 'Failed'}`);
});
// Test 5: Round-trip with format confusion
const roundTripTest = await performanceTracker.measureAsync(
'round-trip-confusion',
async () => {
try {
// Start with a simple invoice
const einvoice = new EInvoice();
einvoice.id = 'ROUNDTRIP-001';
einvoice.date = Date.now();
einvoice.currency = 'EUR';
einvoice.from = {
type: 'company',
name: 'Round Trip Supplier',
description: 'Test supplier',
address: {
streetName: 'Test Street',
houseNumber: '1',
postalCode: '12345',
city: 'Test City',
country: 'DE'
},
status: 'active',
registrationDetails: {
vatId: 'DE123456789',
registrationId: 'HRB 12345',
registrationName: 'Test Registry'
},
foundedDate: { year: 2020, month: 1, day: 1 }
};
einvoice.to = {
type: 'company',
name: 'Round Trip Customer',
description: 'Test customer',
address: {
streetName: 'Customer Street',
houseNumber: '2',
postalCode: '54321',
city: 'Customer City',
country: 'DE'
},
status: 'active',
registrationDetails: {
vatId: 'FR987654321',
registrationId: 'RCS 54321',
registrationName: 'Customer Registry'
},
foundedDate: { year: 2021, month: 6, day: 15 }
};
einvoice.items = [{
position: 1,
name: 'Test Product',
unitType: 'EA',
unitQuantity: 1,
unitNetPrice: 100,
vatPercentage: 19
}];
// Convert to UBL
const ublXml = await einvoice.toXmlString('ubl');
// Load it back
const reloaded = new EInvoice();
await reloaded.loadXml(ublXml);
// Convert to CII
const ciiXml = await reloaded.toXmlString('cii');
// Load it back again
const finalInvoice = new EInvoice();
await finalInvoice.loadXml(ciiXml);
return {
success: true,
originalId: einvoice.id,
finalId: finalInvoice.id,
preservedSupplier: finalInvoice.from?.name === einvoice.from?.name,
preservedCustomer: finalInvoice.to?.name === einvoice.to?.name,
itemCount: finalInvoice.items?.length
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
);
console.log(`\nTest 5 - Round-trip with format changes: ${roundTripTest.success ? 'Success' : 'Failed'}`);
if (roundTripTest.success) {
console.log(` ID preserved: ${roundTripTest.originalId === roundTripTest.finalId}`);
console.log(` Supplier preserved: ${roundTripTest.preservedSupplier}`);
console.log(` Customer preserved: ${roundTripTest.preservedCustomer}`);
console.log(` Items preserved: ${roundTripTest.itemCount} item(s)`);
}
expect(roundTripTest.success).toEqual(true);
// Print performance summary
console.log('\n' + performanceTracker.getSummary());
// Verify at least some tests succeeded
const successfulTests = [
unusualNamespacesTest.success,
roundTripTest.success
].filter(Boolean).length;
expect(successfulTests).toBeGreaterThan(0);
console.log(`\n✓ ${successfulTests} out of 5 edge case tests handled successfully`);
});
tap.start();