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

306 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { expect, tap } from '@git.zone/tstest/tapbundle';
import * as einvoice from '../../../ts/index.js';
import * as plugins from '../../plugins.js';
tap.test('PARSE-10: CDATA Section Handling in e-invoices', async () => {
console.log('Testing CDATA section handling in e-invoices...\n');
// 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'
}
];
for (const test of cdataTests) {
console.log(`\n${test.name}:`);
console.log(` Description: ${test.description}`);
try {
const invoice = new einvoice.EInvoice();
await invoice.fromXmlString(test.xml);
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);
}
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);
}
} catch (error) {
console.log(` ✗ Error: ${error.message}`);
}
}
});
tap.test('PARSE-10: CDATA edge cases and security', async () => {
console.log('\nTesting CDATA edge cases and security aspects...\n');
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}`);
try {
const invoice = new einvoice.EInvoice();
await invoice.fromXmlString(test.xml);
const actualNote = invoice.notes?.[0] || '';
console.log(` Expected: "${test.expectedNote}"`);
console.log(` Actual: "${actualNote}"`);
if (test.expectedNote) {
expect(actualNote).toEqual(test.expectedNote);
console.log(' ✓ CDATA edge case handled correctly');
}
} catch (error) {
console.log(` Result: ${error.message}`);
}
}
});
tap.test('PARSE-10: CDATA in real invoice scenarios', async () => {
console.log('\nTesting CDATA usage in real invoice scenarios...\n');
// 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:
1. Payment due within 30 days
2. Late payment charge: 1.5% per month
3. All prices exclude VAT (currently 19%)
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'
}
];
for (const scenario of realScenarios) {
console.log(`${scenario.name}:`);
console.log(` Use case: ${scenario.description}`);
try {
const invoice = new einvoice.EInvoice();
await invoice.fromXmlString(scenario.xml);
console.log(` ✓ Invoice parsed: ID ${invoice.id}`);
if (invoice.notes?.length > 0) {
console.log(` Notes found: ${invoice.notes.length}`);
}
if (invoice.items?.length > 0) {
console.log(` Line items: ${invoice.items.length}`);
}
if (invoice.from?.name) {
console.log(` Supplier: ${invoice.from.name}`);
}
} catch (error) {
console.log(` ✗ Error: ${error.message}`);
}
}
});
tap.test('PARSE-10: CDATA performance with large content', async () => {
console.log('\nTesting CDATA performance with large content...\n');
// 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('');
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>`;
const startTime = Date.now();
try {
const invoice = new einvoice.EInvoice();
await invoice.fromXmlString(xml);
const parseTime = Date.now() - startTime;
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}`);
}
}
});
// Run the tests
tap.start();