einvoice/test/suite/einvoice_parsing/test.parse-05.namespace-resolution.ts

301 lines
9.9 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-05: Namespace Resolution - Basic namespace declarations', async () => {
console.log('Testing namespace resolution in e-invoices...\n');
const namespaceTests = [
{
name: 'Default namespace',
xml: `<?xml version="1.0"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
<ID>TEST-001</ID>
<IssueDate>2024-01-01</IssueDate>
</Invoice>`,
expectedNamespaces: [{
prefix: '',
uri: 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'
}]
},
{
name: 'Prefixed namespace',
xml: `<?xml version="1.0"?>
<ubl:Invoice xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
<ubl:ID>TEST-002</ubl:ID>
<ubl:IssueDate>2024-01-01</ubl:IssueDate>
</ubl:Invoice>`,
expectedNamespaces: [{
prefix: 'ubl',
uri: 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'
}]
},
{
name: 'Multiple namespaces',
xml: `<?xml version="1.0"?>
<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>TEST-003</cbc:ID>
<cac:AccountingSupplierParty>
<cac:Party>
<cbc:Name>Test Supplier</cbc:Name>
</cac:Party>
</cac:AccountingSupplierParty>
</ubl:Invoice>`,
expectedNamespaces: [
{ prefix: 'ubl', uri: 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2' },
{ prefix: 'cac', uri: 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2' },
{ prefix: 'cbc', uri: 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2' }
]
}
];
for (const test of namespaceTests) {
console.log(`\n${test.name}:`);
// Extract namespace declarations
const namespaceMatches = test.xml.matchAll(/xmlns(?::([^=]+))?="([^"]+)"/g);
const foundNamespaces = Array.from(namespaceMatches).map(match => ({
prefix: match[1] || '',
uri: match[2]
}));
console.log(` Expected: ${test.expectedNamespaces.length} namespaces`);
console.log(` Found: ${foundNamespaces.length} namespaces`);
for (const ns of foundNamespaces) {
console.log(` ${ns.prefix ? `${ns.prefix}:` : '(default)'} ${ns.uri}`);
}
// Verify parsing
try {
const invoice = new einvoice.EInvoice();
await invoice.fromXmlString(test.xml);
console.log(' ✓ Parsed successfully with namespaces');
// Verify the invoice was parsed correctly
expect(invoice.id).toBeDefined();
} catch (error) {
console.log(` ✗ Parse error: ${error.message}`);
}
}
});
tap.test('PARSE-05: Namespace Resolution - Namespace scope and inheritance', async () => {
console.log('\nTesting namespace scope and inheritance...\n');
const scopeTests = [
{
name: 'Namespace inheritance',
xml: `<?xml version="1.0"?>
<root xmlns="http://example.com/default">
<parent>
<child>Inherits default namespace</child>
</parent>
</root>`,
description: 'Child elements inherit parent namespace'
},
{
name: 'Namespace override',
xml: `<?xml version="1.0"?>
<root xmlns="http://example.com/default">
<parent>
<child xmlns="http://example.com/child">Different namespace</child>
</parent>
</root>`,
description: 'Child can override inherited namespace'
},
{
name: 'Mixed namespace scopes',
xml: `<?xml version="1.0"?>
<root xmlns:a="http://example.com/a" xmlns:b="http://example.com/b">
<a:element1>
<a:child>Same namespace as parent</a:child>
<b:child>Different namespace prefix</b:child>
<unqualified>No namespace prefix</unqualified>
</a:element1>
</root>`,
description: 'Multiple namespace prefixes in scope'
}
];
for (const test of scopeTests) {
console.log(`${test.name}:`);
console.log(` Description: ${test.description}`);
try {
const invoice = new einvoice.EInvoice();
await invoice.fromXmlString(test.xml);
console.log(' ✓ Namespace scope handled correctly');
} catch (error) {
// Expected to fail for non-invoice XML
console.log(` Not a valid invoice format (expected)`);
}
}
});
tap.test('PARSE-05: Namespace Resolution - Real invoice formats', async () => {
console.log('\nTesting namespace resolution in real invoice formats...\n');
const formatTests = [
{
name: 'UBL Invoice',
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>UBL-NS-TEST</cbc:ID>
<cbc:IssueDate>2024-01-01</cbc:IssueDate>
<cac:AccountingSupplierParty>
<cac:Party>
<cac:PartyName>
<cbc:Name>Namespace Test Supplier</cbc:Name>
</cac:PartyName>
</cac:Party>
</cac:AccountingSupplierParty>
</ubl:Invoice>`,
expectedFormat: 'UBL'
},
{
name: 'CII Invoice',
xml: `<?xml version="1.0" encoding="UTF-8"?>
<rsm:CrossIndustryInvoice
xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:100"
xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
<rsm:ExchangedDocumentContext>
<ram:GuidelineSpecifiedDocumentContextParameter>
<ram:ID>urn:cen.eu:en16931:2017</ram:ID>
</ram:GuidelineSpecifiedDocumentContextParameter>
</rsm:ExchangedDocumentContext>
<rsm:ExchangedDocument>
<ram:ID>CII-NS-TEST</ram:ID>
</rsm:ExchangedDocument>
</rsm:CrossIndustryInvoice>`,
expectedFormat: 'CII'
}
];
for (const test of formatTests) {
console.log(`${test.name}:`);
try {
const invoice = new einvoice.EInvoice();
await invoice.fromXmlString(test.xml);
console.log(` ✓ Parsed successfully`);
console.log(` Format: ${invoice.getFormat ? invoice.getFormat() : 'Unknown'}`);
console.log(` ID: ${invoice.id}`);
expect(invoice.id).toBeDefined();
} catch (error) {
console.log(` ✗ Parse error: ${error.message}`);
}
}
});
tap.test('PARSE-05: Namespace Resolution - Complex namespace scenarios', async () => {
console.log('\nTesting complex namespace scenarios...\n');
// Test namespace prefix conflicts
const conflictTest = {
name: 'Namespace prefix redefinition',
xml: `<?xml version="1.0"?>
<root xmlns:ns="http://example.com/ns1">
<ns:element1>Using namespace 1</ns:element1>
<child xmlns:ns="http://example.com/ns2">
<ns:element2>Using namespace 2 (redefined)</ns:element2>
</child>
</root>`
};
console.log(`${conflictTest.name}:`);
try {
// Extract all namespace declarations with their scope
const lines = conflictTest.xml.split('\n');
let depth = 0;
lines.forEach((line, index) => {
const nsMatch = line.match(/xmlns:(\w+)="([^"]+)"/);
if (nsMatch) {
console.log(` Line ${index + 1}: Prefix '${nsMatch[1]}' = ${nsMatch[2]}`);
}
});
console.log(' ✓ Namespace prefix conflicts are allowed in different scopes');
} catch (error) {
console.log(` ✗ Error: ${error.message}`);
}
// Test empty namespace (undeclaration)
const undeclarationTest = {
name: 'Namespace undeclaration',
xml: `<?xml version="1.0"?>
<root xmlns="http://example.com/default">
<parent>
<child xmlns="">No namespace</child>
</parent>
</root>`
};
console.log(`\n${undeclarationTest.name}:`);
console.log(' Empty xmlns="" removes default namespace from element and children');
console.log(' ✓ Valid XML construct for namespace undeclaration');
});
tap.test('PARSE-05: Namespace Resolution - Performance considerations', async () => {
console.log('\nTesting namespace resolution performance...\n');
// Generate invoice with many namespaces
const generateComplexNamespaceInvoice = () => {
return `<?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"
xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<cbc:ID>PERF-NS-TEST</cbc:ID>
<cbc:IssueDate>2024-01-01</cbc:IssueDate>
${Array.from({length: 10}, (_, i) => `
<cac:InvoiceLine>
<cbc:ID>${i + 1}</cbc:ID>
<cbc:InvoicedQuantity unitCode="EA">1</cbc:InvoicedQuantity>
<cac:Item>
<cbc:Name>Item ${i + 1}</cbc:Name>
<cac:SellersItemIdentification>
<cbc:ID>ITEM-${i + 1}</cbc:ID>
</cac:SellersItemIdentification>
</cac:Item>
</cac:InvoiceLine>`).join('')}
</ubl:Invoice>`;
};
const xml = generateComplexNamespaceInvoice();
const startTime = Date.now();
try {
const invoice = new einvoice.EInvoice();
await invoice.fromXmlString(xml);
const duration = Date.now() - startTime;
console.log('Complex namespace invoice parsing:');
console.log(` ✓ Parsed successfully in ${duration}ms`);
console.log(` Invoice ID: ${invoice.id}`);
console.log(` Line items: ${invoice.items?.length || 0}`);
expect(duration).toBeLessThan(100); // Should parse quickly
} catch (error) {
console.log(` ✗ Parse error: ${error.message}`);
}
});
// Run the tests
tap.start();