einvoice/test/suite/einvoice_parsing/test.parse-10.cdata-sections.ts

306 lines
11 KiB
TypeScript
Raw Normal View History

2025-05-25 19:45:37 +00:00
import { expect, tap } from '@git.zone/tstest/tapbundle';
import * as einvoice from '../../../ts/index.js';
import * as plugins from '../../plugins.js';
2025-05-28 18:46:18 +00:00
tap.test('PARSE-10: CDATA Section Handling in e-invoices', async () => {
console.log('Testing CDATA section handling in e-invoices...\n');
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
// Test basic CDATA sections in invoice fields
const cdataTests = [
{
name: 'Simple CDATA content in notes',
xml: `<?xml version="1.0" encoding="UTF-8"?>
<ubl:Invoice xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:ID>CDATA-001</cbc:ID>
<cbc:Note><![CDATA[This is plain text content with special chars: < > & " ']]></cbc:Note>
</ubl:Invoice>`,
expectedNote: "This is plain text content with special chars: < > & \" '",
description: 'Basic CDATA section preserves special characters'
},
{
name: 'CDATA with XML-like content',
xml: `<?xml version="1.0" encoding="UTF-8"?>
<ubl:Invoice xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:ID>CDATA-002</cbc:ID>
<cbc:Note><![CDATA[<html><body>Invoice contains <b>HTML</b> markup</body></html>]]></cbc:Note>
</ubl:Invoice>`,
expectedNote: '<html><body>Invoice contains <b>HTML</b> markup</body></html>',
description: 'XML/HTML markup preserved as text in CDATA'
},
{
name: 'CDATA with line breaks and formatting',
xml: `<?xml version="1.0" encoding="UTF-8"?>
<ubl:Invoice xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
<cbc:ID>CDATA-003</cbc:ID>
<cac:AccountingSupplierParty>
<cac:Party>
<cac:PartyName>
<cbc:Name><![CDATA[Company & Co.
Special Division
"International Sales"]]></cbc:Name>
</cac:PartyName>
</cac:Party>
</cac:AccountingSupplierParty>
</ubl:Invoice>`,
expectedSupplierName: 'Company & Co.\nSpecial Division\n"International Sales"',
description: 'CDATA preserves line breaks and special chars in company names'
},
{
name: 'Empty CDATA section',
xml: `<?xml version="1.0" encoding="UTF-8"?>
<ubl:Invoice xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:ID>CDATA-004</cbc:ID>
<cbc:Note><![CDATA[]]></cbc:Note>
</ubl:Invoice>`,
expectedNote: '',
description: 'Empty CDATA section is valid'
},
{
name: 'CDATA with code snippets',
xml: `<?xml version="1.0" encoding="UTF-8"?>
<ubl:Invoice xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:ID>CDATA-005</cbc:ID>
<cbc:Note><![CDATA[if (price < 100 && quantity > 5) { discount = 0.1; }]]></cbc:Note>
</ubl:Invoice>`,
expectedNote: 'if (price < 100 && quantity > 5) { discount = 0.1; }',
description: 'Code snippets with operators preserved'
2025-05-25 19:45:37 +00:00
}
2025-05-28 18:46:18 +00:00
];
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
for (const test of cdataTests) {
console.log(`\n${test.name}:`);
console.log(` Description: ${test.description}`);
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
try {
const invoice = new einvoice.EInvoice();
await invoice.fromXmlString(test.xml);
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
console.log(' ✓ CDATA parsed successfully');
console.log(` Invoice ID: ${invoice.id}`);
if (test.expectedNote !== undefined) {
const actualNote = invoice.notes?.[0] || '';
console.log(` Expected note: "${test.expectedNote}"`);
console.log(` Actual note: "${actualNote}"`);
expect(actualNote).toEqual(test.expectedNote);
}
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
if (test.expectedSupplierName !== undefined) {
const actualName = invoice.from?.name || '';
console.log(` Expected supplier: "${test.expectedSupplierName}"`);
console.log(` Actual supplier: "${actualName}"`);
expect(actualName).toEqual(test.expectedSupplierName);
2025-05-25 19:45:37 +00:00
}
2025-05-28 18:46:18 +00:00
} catch (error) {
console.log(` ✗ Error: ${error.message}`);
2025-05-25 19:45:37 +00:00
}
2025-05-28 18:46:18 +00:00
}
});
tap.test('PARSE-10: CDATA edge cases and security', async () => {
console.log('\nTesting CDATA edge cases and security aspects...\n');
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
const edgeCases = [
{
name: 'CDATA-like content (not actual CDATA)',
xml: `<?xml version="1.0" encoding="UTF-8"?>
<ubl:Invoice xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:ID>EDGE-001</cbc:ID>
<cbc:Note>Text with &lt;![CDATA[ fake CDATA ]]&gt; markers</cbc:Note>
</ubl:Invoice>`,
expectedNote: 'Text with <![CDATA[ fake CDATA ]]> markers',
description: 'Escaped CDATA markers are just text'
},
{
name: 'Multiple CDATA sections',
xml: `<?xml version="1.0" encoding="UTF-8"?>
<ubl:Invoice xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:ID>EDGE-002</cbc:ID>
<cbc:Note><![CDATA[Part 1]]> and <![CDATA[Part 2]]></cbc:Note>
</ubl:Invoice>`,
expectedNote: 'Part 1 and Part 2',
description: 'Multiple CDATA sections in one element'
},
{
name: 'CDATA with Unicode characters',
xml: `<?xml version="1.0" encoding="UTF-8"?>
<ubl:Invoice xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:ID>EDGE-003</cbc:ID>
<cbc:Note><![CDATA[Unicode: £ ¥ © ® العربية]]></cbc:Note>
</ubl:Invoice>`,
expectedNote: 'Unicode: € £ ¥ © ® ™ 中文 العربية',
description: 'Unicode characters in CDATA'
}
];
for (const test of edgeCases) {
console.log(`${test.name}:`);
console.log(` Description: ${test.description}`);
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
try {
const invoice = new einvoice.EInvoice();
await invoice.fromXmlString(test.xml);
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
const actualNote = invoice.notes?.[0] || '';
console.log(` Expected: "${test.expectedNote}"`);
console.log(` Actual: "${actualNote}"`);
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
if (test.expectedNote) {
expect(actualNote).toEqual(test.expectedNote);
console.log(' ✓ CDATA edge case handled correctly');
2025-05-25 19:45:37 +00:00
}
2025-05-28 18:46:18 +00:00
} catch (error) {
console.log(` Result: ${error.message}`);
2025-05-25 19:45:37 +00:00
}
2025-05-28 18:46:18 +00:00
}
});
tap.test('PARSE-10: CDATA in real invoice scenarios', async () => {
console.log('\nTesting CDATA usage in real invoice scenarios...\n');
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
// Test CDATA in various invoice contexts
const realScenarios = [
{
name: 'Legal disclaimer with special formatting',
xml: `<?xml version="1.0" encoding="UTF-8"?>
<ubl:Invoice xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:ID>REAL-001</cbc:ID>
<cbc:Note><![CDATA[
TERMS & CONDITIONS:
2025-05-25 19:45:37 +00:00
1. Payment due within 30 days
2025-05-28 18:46:18 +00:00
2. Late payment charge: 1.5% per month
3. All prices exclude VAT (currently 19%)
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
For questions contact: billing@company.com
]]></cbc:Note>
</ubl:Invoice>`,
description: 'Legal terms with special characters and formatting'
},
{
name: 'Product description with technical specs',
xml: `<?xml version="1.0" encoding="UTF-8"?>
<ubl:Invoice xmlns:ubl="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>REAL-002</cbc:ID>
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cac:Item>
<cbc:Name>Technical Component</cbc:Name>
<cbc:Description><![CDATA[
Component specs:
- Voltage: 12V DC
- Current: < 2A
- Temperature: -20°C to +85°C
- Compliance: CE & RoHS
- Dimensions: 50mm x 30mm x 15mm
]]></cbc:Description>
</cac:Item>
</cac:InvoiceLine>
</ubl:Invoice>`,
description: 'Technical specifications with symbols'
},
{
name: 'Address with special formatting',
xml: `<?xml version="1.0" encoding="UTF-8"?>
<ubl:Invoice xmlns:ubl="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>REAL-003</cbc:ID>
<cac:AccountingSupplierParty>
<cac:Party>
<cac:PartyName>
<cbc:Name><![CDATA[Smith & Jones Ltd.]]></cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:AdditionalStreetName><![CDATA[Building "A" - 3rd Floor]]></cbc:AdditionalStreetName>
</cac:PostalAddress>
</cac:Party>
</cac:AccountingSupplierParty>
</ubl:Invoice>`,
description: 'Company name and address with special characters'
2025-05-25 19:45:37 +00:00
}
2025-05-28 18:46:18 +00:00
];
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
for (const scenario of realScenarios) {
console.log(`${scenario.name}:`);
console.log(` Use case: ${scenario.description}`);
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
try {
const invoice = new einvoice.EInvoice();
await invoice.fromXmlString(scenario.xml);
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
console.log(` ✓ Invoice parsed: ID ${invoice.id}`);
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
if (invoice.notes?.length > 0) {
console.log(` Notes found: ${invoice.notes.length}`);
2025-05-25 19:45:37 +00:00
}
2025-05-28 18:46:18 +00:00
if (invoice.items?.length > 0) {
console.log(` Line items: ${invoice.items.length}`);
}
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
if (invoice.from?.name) {
console.log(` Supplier: ${invoice.from.name}`);
}
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
} catch (error) {
console.log(` ✗ Error: ${error.message}`);
2025-05-25 19:45:37 +00:00
}
2025-05-28 18:46:18 +00:00
}
});
tap.test('PARSE-10: CDATA performance with large content', async () => {
console.log('\nTesting CDATA performance with large content...\n');
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
// Generate invoices with varying CDATA content sizes
const sizes = [
{ name: 'Small', chars: 100 },
{ name: 'Medium', chars: 1000 },
{ name: 'Large', chars: 10000 }
];
for (const size of sizes) {
// Generate content with special characters that would need escaping
const content = Array(size.chars / 10).fill('Text with <>&" chars ').join('');
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<ubl:Invoice xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:ID>PERF-${size.name}</cbc:ID>
<cbc:Note><![CDATA[${content}]]></cbc:Note>
</ubl:Invoice>`;
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
const startTime = Date.now();
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
try {
const invoice = new einvoice.EInvoice();
await invoice.fromXmlString(xml);
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
const parseTime = Date.now() - startTime;
2025-05-25 19:45:37 +00:00
2025-05-28 18:46:18 +00:00
console.log(`${size.name} CDATA (${size.chars} chars):`);
console.log(` Parse time: ${parseTime}ms`);
console.log(` Note length: ${invoice.notes?.[0]?.length || 0} chars`);
console.log(` ✓ Successfully parsed`);
} catch (error) {
console.log(` ✗ Error: ${error.message}`);
2025-05-25 19:45:37 +00:00
}
2025-05-28 18:46:18 +00:00
}
2025-05-25 19:45:37 +00:00
});
2025-05-28 18:46:18 +00:00
// Run the tests
2025-05-25 19:45:37 +00:00
tap.start();