fix(compliance): improve compliance
This commit is contained in:
@ -1,60 +1,170 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
import { EInvoice } from '../../../ts/index.js';
|
||||
import { PerformanceTracker } from '../performance.tracker.js';
|
||||
|
||||
tap.test('ENC-10: Cross-Format Encoding - should handle encoding across different invoice formats', async () => {
|
||||
// ENC-10: Verify handling of Cross-Format Encoding encoded documents
|
||||
console.log('Testing cross-format encoding consistency...\n');
|
||||
|
||||
// Test 1: Direct Cross-Format Encoding encoding (expected to fail)
|
||||
console.log('\nTest 1: Direct Cross-Format Encoding encoding');
|
||||
const { result: directResult, metric: directMetric } = await PerformanceTracker.track(
|
||||
'cross-direct',
|
||||
async () => {
|
||||
// XML parsers typically don't support Cross-Format Encoding directly
|
||||
const xmlContent = `<?xml version="1.0" encoding="Cross-Format Encoding"?>
|
||||
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
||||
<UBLVersionID>2.1</UBLVersionID>
|
||||
<ID>CROSS-TEST</ID>
|
||||
<IssueDate>2025-01-25</IssueDate>
|
||||
<DocumentCurrencyCode>EUR</DocumentCurrencyCode>
|
||||
</Invoice>`;
|
||||
|
||||
let success = false;
|
||||
let error = null;
|
||||
|
||||
try {
|
||||
const newInvoice = new EInvoice();
|
||||
await newInvoice.fromXmlString(xmlContent);
|
||||
success = newInvoice.id === 'CROSS-TEST' ||
|
||||
newInvoice.invoiceId === 'CROSS-TEST' ||
|
||||
newInvoice.accountingDocId === 'CROSS-TEST';
|
||||
} catch (e) {
|
||||
error = e;
|
||||
console.log(` Cross-Format Encoding not directly supported: ${e.message}`);
|
||||
// Test 1: UBL to CII encoding consistency
|
||||
const testUblToCiiEncoding = async () => {
|
||||
const einvoice = new EInvoice();
|
||||
einvoice.id = 'CROSS-FORMAT-TEST';
|
||||
einvoice.issueDate = new Date(2025, 0, 25);
|
||||
einvoice.subject = 'Cross-format test with special chars: éñüß';
|
||||
|
||||
einvoice.from = {
|
||||
type: 'company',
|
||||
name: 'Test Company éñüß',
|
||||
description: 'Testing cross-format encoding: €£¥',
|
||||
address: {
|
||||
streetName: 'Straße with ümlaut',
|
||||
houseNumber: '1',
|
||||
postalCode: '12345',
|
||||
city: 'München',
|
||||
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: 'José',
|
||||
surname: 'Müller',
|
||||
salutation: 'Mr' as const,
|
||||
sex: 'male' as const,
|
||||
title: 'Doctor' as const,
|
||||
description: 'Customer with spëcial chars',
|
||||
address: {
|
||||
streetName: 'Côte d\'Azur',
|
||||
houseNumber: '2',
|
||||
postalCode: '54321',
|
||||
city: 'São Paulo',
|
||||
country: 'BR'
|
||||
}
|
||||
};
|
||||
|
||||
einvoice.items = [{
|
||||
position: 1,
|
||||
name: 'Product with éñü symbols',
|
||||
articleNumber: 'CROSS-001',
|
||||
unitType: 'EA',
|
||||
unitQuantity: 1,
|
||||
unitNetPrice: 100,
|
||||
vatPercentage: 19
|
||||
}];
|
||||
|
||||
try {
|
||||
// Export as UBL
|
||||
const ublXml = await einvoice.toXmlString('ubl');
|
||||
|
||||
return { success, error };
|
||||
// Export as CII
|
||||
const ciiXml = await einvoice.toXmlString('cii');
|
||||
|
||||
// Verify both formats preserve special characters
|
||||
const ublHasSpecialChars = ublXml.includes('éñüß') && ublXml.includes('München') && ublXml.includes('José');
|
||||
const ciiHasSpecialChars = ciiXml.includes('éñüß') && ciiXml.includes('München') && ciiXml.includes('José');
|
||||
|
||||
// Test round-trip for both formats
|
||||
const ublInvoice = new EInvoice();
|
||||
await ublInvoice.fromXmlString(ublXml);
|
||||
|
||||
const ciiInvoice = new EInvoice();
|
||||
await ciiInvoice.fromXmlString(ciiXml);
|
||||
|
||||
const ublRoundTrip = ublInvoice.from?.name?.includes('éñüß') && ublInvoice.to?.name?.includes('José');
|
||||
const ciiRoundTrip = ciiInvoice.from?.name?.includes('éñüß') && ciiInvoice.to?.name?.includes('José');
|
||||
|
||||
console.log(`Test 1 - UBL to CII encoding:`);
|
||||
console.log(` UBL preserves special chars: ${ublHasSpecialChars ? 'Yes' : 'No'}`);
|
||||
console.log(` CII preserves special chars: ${ciiHasSpecialChars ? 'Yes' : 'No'}`);
|
||||
console.log(` UBL round-trip successful: ${ublRoundTrip ? 'Yes' : 'No'}`);
|
||||
console.log(` CII round-trip successful: ${ciiRoundTrip ? 'Yes' : 'No'}`);
|
||||
|
||||
return { ublHasSpecialChars, ciiHasSpecialChars, ublRoundTrip, ciiRoundTrip };
|
||||
} catch (error) {
|
||||
console.log(`Test 1 - UBL to CII encoding:`);
|
||||
console.log(` Cross-format encoding failed: ${error.message}`);
|
||||
|
||||
return { ublHasSpecialChars: false, ciiHasSpecialChars: false, ublRoundTrip: false, ciiRoundTrip: false };
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
console.log(` Cross-Format Encoding direct test completed in ${directMetric.duration}ms`);
|
||||
// Test 2: Different encoding declarations consistency
|
||||
const testEncodingDeclarations = async () => {
|
||||
const ublWithUnicodeXml = `<?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:UBLVersionID>2.1</cbc:UBLVersionID>
|
||||
<cbc:ID>ENCODING-CONSISTENCY-TEST</cbc:ID>
|
||||
<cbc:IssueDate>2025-01-25</cbc:IssueDate>
|
||||
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
|
||||
<cac:AccountingSupplierParty>
|
||||
<cac:Party>
|
||||
<cac:PartyName>
|
||||
<cbc:Name>Ünîcödë Company €éñ</cbc:Name>
|
||||
</cac:PartyName>
|
||||
</cac:Party>
|
||||
</cac:AccountingSupplierParty>
|
||||
<cac:InvoiceLine>
|
||||
<cbc:ID>1</cbc:ID>
|
||||
<cbc:InvoicedQuantity unitCode="EA">1</cbc:InvoicedQuantity>
|
||||
<cac:Item>
|
||||
<cbc:Name>Product with spëcîãl chars</cbc:Name>
|
||||
</cac:Item>
|
||||
</cac:InvoiceLine>
|
||||
</Invoice>`;
|
||||
|
||||
try {
|
||||
// Parse UBL with Unicode content
|
||||
const ublInvoice = new EInvoice();
|
||||
await ublInvoice.fromXmlString(ublWithUnicodeXml);
|
||||
|
||||
// Convert to CII and back to UBL
|
||||
const ciiXml = await ublInvoice.toXmlString('cii');
|
||||
const ublFromCii = new EInvoice();
|
||||
await ublFromCii.fromXmlString(ciiXml);
|
||||
|
||||
// Check if special characters survive format conversion
|
||||
const originalHasUnicode = ublInvoice.from?.name?.includes('Ünîcödë') &&
|
||||
ublInvoice.from?.name?.includes('€éñ');
|
||||
|
||||
const ciiPreservesUnicode = ciiXml.includes('Ünîcödë') && ciiXml.includes('€éñ');
|
||||
|
||||
const roundTripPreservesUnicode = ublFromCii.from?.name?.includes('Ünîcödë') &&
|
||||
ublFromCii.from?.name?.includes('€éñ');
|
||||
|
||||
console.log(`\nTest 2 - Encoding declaration consistency:`);
|
||||
console.log(` Original UBL has Unicode: ${originalHasUnicode ? 'Yes' : 'No'}`);
|
||||
console.log(` CII conversion preserves Unicode: ${ciiPreservesUnicode ? 'Yes' : 'No'}`);
|
||||
console.log(` Round-trip preserves Unicode: ${roundTripPreservesUnicode ? 'Yes' : 'No'}`);
|
||||
|
||||
return { originalHasUnicode, ciiPreservesUnicode, roundTripPreservesUnicode };
|
||||
} catch (error) {
|
||||
console.log(`\nTest 2 - Encoding declaration consistency:`);
|
||||
console.log(` Encoding consistency test failed: ${error.message}`);
|
||||
|
||||
return { originalHasUnicode: false, ciiPreservesUnicode: false, roundTripPreservesUnicode: false };
|
||||
}
|
||||
};
|
||||
|
||||
// Test 2: UTF-8 fallback (should always work)
|
||||
console.log('\nTest 2: UTF-8 fallback');
|
||||
const { result: fallbackResult, metric: fallbackMetric } = await PerformanceTracker.track(
|
||||
'cross-fallback',
|
||||
async () => {
|
||||
// Test 3: Mixed format documents
|
||||
const testMixedFormatSupport = async () => {
|
||||
try {
|
||||
const einvoice = new EInvoice();
|
||||
einvoice.id = 'CROSS-FALLBACK-TEST';
|
||||
einvoice.id = 'MIXED-FORMAT-TEST';
|
||||
einvoice.issueDate = new Date(2025, 0, 25);
|
||||
einvoice.invoiceId = 'CROSS-FALLBACK-TEST';
|
||||
einvoice.accountingDocId = 'CROSS-FALLBACK-TEST';
|
||||
einvoice.subject = 'Cross-Format Encoding fallback test';
|
||||
einvoice.subject = 'Mixed format test';
|
||||
|
||||
einvoice.from = {
|
||||
type: 'company',
|
||||
name: 'Test Company',
|
||||
description: 'Testing Cross-Format Encoding encoding',
|
||||
name: 'Mixed Format Tëst Co.',
|
||||
description: 'Testing mixed formats with €áàâ',
|
||||
address: {
|
||||
streetName: 'Test Street',
|
||||
houseNumber: '1',
|
||||
@ -91,39 +201,138 @@ tap.test('ENC-10: Cross-Format Encoding - should handle encoding across differen
|
||||
einvoice.items = [{
|
||||
position: 1,
|
||||
name: 'Test Product',
|
||||
articleNumber: 'CROSS-001',
|
||||
articleNumber: 'MIXED-001',
|
||||
unitType: 'EA',
|
||||
unitQuantity: 1,
|
||||
unitNetPrice: 100,
|
||||
vatPercentage: 19
|
||||
}];
|
||||
|
||||
// Export as UTF-8 (our default)
|
||||
const utf8Xml = await einvoice.toXmlString('ubl');
|
||||
// Test multiple format exports and verify encoding consistency
|
||||
const ublXml = await einvoice.toXmlString('ubl');
|
||||
const ciiXml = await einvoice.toXmlString('cii');
|
||||
|
||||
// Verify UTF-8 works correctly
|
||||
const newInvoice = new EInvoice();
|
||||
await newInvoice.fromXmlString(utf8Xml);
|
||||
// All formats should have proper UTF-8 encoding declaration
|
||||
const ublHasUtf8 = ublXml.includes('encoding="UTF-8"') || !ublXml.includes('encoding=');
|
||||
const ciiHasUtf8 = ciiXml.includes('encoding="UTF-8"') || !ciiXml.includes('encoding=');
|
||||
|
||||
const success = newInvoice.id === 'CROSS-FALLBACK-TEST' ||
|
||||
newInvoice.invoiceId === 'CROSS-FALLBACK-TEST' ||
|
||||
newInvoice.accountingDocId === 'CROSS-FALLBACK-TEST';
|
||||
// Check if special characters are preserved across formats
|
||||
const ublPreservesChars = ublXml.includes('Tëst') && ublXml.includes('€áàâ');
|
||||
const ciiPreservesChars = ciiXml.includes('Tëst') && ciiXml.includes('€áàâ');
|
||||
|
||||
console.log(` UTF-8 fallback works: ${success}`);
|
||||
console.log(`\nTest 3 - Mixed format support:`);
|
||||
console.log(` UBL has UTF-8 encoding: ${ublHasUtf8 ? 'Yes' : 'No'}`);
|
||||
console.log(` CII has UTF-8 encoding: ${ciiHasUtf8 ? 'Yes' : 'No'}`);
|
||||
console.log(` UBL preserves special chars: ${ublPreservesChars ? 'Yes' : 'No'}`);
|
||||
console.log(` CII preserves special chars: ${ciiPreservesChars ? 'Yes' : 'No'}`);
|
||||
|
||||
return { success };
|
||||
return { ublHasUtf8, ciiHasUtf8, ublPreservesChars, ciiPreservesChars };
|
||||
} catch (error) {
|
||||
console.log(`\nTest 3 - Mixed format support:`);
|
||||
console.log(` Mixed format test failed: ${error.message}`);
|
||||
|
||||
return { ublHasUtf8: false, ciiHasUtf8: false, ublPreservesChars: false, ciiPreservesChars: false };
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
console.log(` Cross-Format Encoding fallback test completed in ${fallbackMetric.duration}ms`);
|
||||
// Test 4: Encoding header consistency
|
||||
const testEncodingHeaders = async () => {
|
||||
try {
|
||||
const einvoice = new EInvoice();
|
||||
einvoice.id = 'HEADER-TEST';
|
||||
einvoice.issueDate = new Date(2025, 0, 25);
|
||||
einvoice.subject = 'Encoding header test';
|
||||
|
||||
einvoice.from = {
|
||||
type: 'company',
|
||||
name: 'Header Test Company',
|
||||
description: 'Testing encoding headers',
|
||||
address: {
|
||||
streetName: 'Test Street',
|
||||
houseNumber: '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: 'Test Product',
|
||||
articleNumber: 'HEADER-001',
|
||||
unitType: 'EA',
|
||||
unitQuantity: 1,
|
||||
unitNetPrice: 100,
|
||||
vatPercentage: 19
|
||||
}];
|
||||
|
||||
// Generate both formats and check XML headers
|
||||
const ublXml = await einvoice.toXmlString('ubl');
|
||||
const ciiXml = await einvoice.toXmlString('cii');
|
||||
|
||||
// Check if both start with proper XML declaration
|
||||
const ublHasXmlDecl = ublXml.startsWith('<?xml version="1.0"');
|
||||
const ciiHasXmlDecl = ciiXml.startsWith('<?xml version="1.0"');
|
||||
|
||||
// Check if encoding is consistent
|
||||
const ublConsistentEncoding = !ublXml.includes('encoding=') || ublXml.includes('encoding="UTF-8"');
|
||||
const ciiConsistentEncoding = !ciiXml.includes('encoding=') || ciiXml.includes('encoding="UTF-8"');
|
||||
|
||||
console.log(`\nTest 4 - Encoding header consistency:`);
|
||||
console.log(` UBL has XML declaration: ${ublHasXmlDecl ? 'Yes' : 'No'}`);
|
||||
console.log(` CII has XML declaration: ${ciiHasXmlDecl ? 'Yes' : 'No'}`);
|
||||
console.log(` UBL encoding consistent: ${ublConsistentEncoding ? 'Yes' : 'No'}`);
|
||||
console.log(` CII encoding consistent: ${ciiConsistentEncoding ? 'Yes' : 'No'}`);
|
||||
|
||||
return { ublHasXmlDecl, ciiHasXmlDecl, ublConsistentEncoding, ciiConsistentEncoding };
|
||||
} catch (error) {
|
||||
console.log(`\nTest 4 - Encoding header consistency:`);
|
||||
console.log(` Header consistency test failed: ${error.message}`);
|
||||
|
||||
return { ublHasXmlDecl: false, ciiHasXmlDecl: false, ublConsistentEncoding: false, ciiConsistentEncoding: false };
|
||||
}
|
||||
};
|
||||
|
||||
// Summary
|
||||
console.log('\n=== Cross-Format Encoding Encoding Test Summary ===');
|
||||
console.log(`Cross-Format Encoding Direct: ${directResult.success ? 'Supported' : 'Not supported (acceptable)'}`);
|
||||
console.log(`UTF-8 Fallback: ${fallbackResult.success ? 'Working' : 'Failed'}`);
|
||||
// Run all tests
|
||||
const crossFormatResult = await testUblToCiiEncoding();
|
||||
const encodingDeclResult = await testEncodingDeclarations();
|
||||
const mixedFormatResult = await testMixedFormatSupport();
|
||||
const headerResult = await testEncodingHeaders();
|
||||
|
||||
// The test passes if UTF-8 fallback works, since Cross-Format Encoding support is optional
|
||||
expect(fallbackResult.success).toBeTrue();
|
||||
console.log(`\n=== Cross-Format Encoding Test Summary ===`);
|
||||
console.log(`UBL-CII encoding consistency: ${crossFormatResult.ublRoundTrip && crossFormatResult.ciiRoundTrip ? 'Working' : 'Issues'}`);
|
||||
console.log(`Format conversion encoding: ${encodingDeclResult.roundTripPreservesUnicode ? 'Working' : 'Issues'}`);
|
||||
console.log(`Mixed format support: ${mixedFormatResult.ublPreservesChars && mixedFormatResult.ciiPreservesChars ? 'Working' : 'Issues'}`);
|
||||
console.log(`Encoding header consistency: ${headerResult.ublConsistentEncoding && headerResult.ciiConsistentEncoding ? 'Working' : 'Issues'}`);
|
||||
console.log(`Cross-format round-trip: ${crossFormatResult.ublRoundTrip && crossFormatResult.ciiRoundTrip ? 'Working' : 'Issues'}`);
|
||||
|
||||
// Test passes if basic cross-format consistency works
|
||||
expect(crossFormatResult.ublRoundTrip || crossFormatResult.ciiRoundTrip).toBeTrue();
|
||||
expect(headerResult.ublHasXmlDecl && headerResult.ciiHasXmlDecl).toBeTrue();
|
||||
});
|
||||
|
||||
// Run the test
|
||||
|
Reference in New Issue
Block a user