import { expect, tap } from '@git.zone/tstest/tapbundle'; import * as einvoice from '../../../ts/index.js'; import * as plugins from '../../plugins.js'; import { CorpusLoader } from '../../helpers/corpus.loader.js'; import { PerformanceTracker } from '../../helpers/performance.tracker.js'; tap.test('PARSE-05: Namespace Resolution - Handle XML namespaces correctly', async (t) => { const performanceTracker = new PerformanceTracker('PARSE-05'); await t.test('Basic namespace declarations', async () => { performanceTracker.startOperation('basic-namespaces'); const namespaceTests = [ { name: 'Default namespace', xml: ` TEST-001 2024-01-01 `, expectedNamespaces: [{ prefix: '', uri: 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2' }] }, { name: 'Prefixed namespace', xml: ` TEST-002 2024-01-01 `, expectedNamespaces: [{ prefix: 'ubl', uri: 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2' }] }, { name: 'Multiple namespaces', xml: ` TEST-003 Test Supplier `, 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' } ] }, { name: 'Namespace with schema location', xml: ` TEST-004 `, expectedNamespaces: [ { prefix: '', uri: 'http://www.example.com/invoice' }, { prefix: 'xsi', uri: 'http://www.w3.org/2001/XMLSchema-instance' } ] } ]; for (const test of namespaceTests) { const startTime = performance.now(); console.log(`${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(); if (invoice.fromXmlString) { await invoice.fromXmlString(test.xml); console.log(' ✓ Parsed successfully with namespaces'); } } catch (error) { console.log(` ✗ Parse error: ${error.message}`); } performanceTracker.recordMetric('namespace-declaration', performance.now() - startTime); } performanceTracker.endOperation('basic-namespaces'); }); await t.test('Namespace scope and inheritance', async () => { performanceTracker.startOperation('namespace-scope'); const scopeTests = [ { name: 'Namespace inheritance', xml: ` Inherits default namespace `, description: 'Child elements inherit parent namespace' }, { name: 'Namespace override', xml: ` Different namespace `, description: 'Child can override inherited namespace' }, { name: 'Mixed namespace scopes', xml: ` Same namespace as parent Different namespace prefix No namespace prefix `, description: 'Multiple namespace prefixes in scope' }, { name: 'Namespace undeclaration', xml: ` No namespace `, description: 'Empty xmlns removes default namespace' } ]; for (const test of scopeTests) { const startTime = performance.now(); console.log(`${test.name}:`); console.log(` Description: ${test.description}`); try { const invoice = new einvoice.EInvoice(); if (invoice.fromXmlString) { await invoice.fromXmlString(test.xml); console.log(' ✓ Namespace scope handled correctly'); } } catch (error) { console.log(` ✗ Error: ${error.message}`); } performanceTracker.recordMetric('namespace-scope', performance.now() - startTime); } performanceTracker.endOperation('namespace-scope'); }); await t.test('Namespace prefix conflicts', async () => { performanceTracker.startOperation('namespace-conflicts'); const conflictTests = [ { name: 'Duplicate prefix - different URIs', xml: ` Namespace 1 Namespace 2 (redefined) `, issue: 'Same prefix maps to different URIs in nested scopes' }, { name: 'Multiple prefixes - same URI', xml: ` Using ns1 Using ns2 (same namespace) `, issue: 'Different prefixes for the same namespace URI' }, { name: 'Prefix collision with attributes', xml: ` Which namespace? `, issue: 'Attribute uses prefix before redefinition' } ]; for (const test of conflictTests) { const startTime = performance.now(); console.log(`${test.name}:`); console.log(` Issue: ${test.issue}`); try { const invoice = new einvoice.EInvoice(); if (invoice.fromXmlString) { await invoice.fromXmlString(test.xml); console.log(' ✓ Conflict handled gracefully'); } } catch (error) { console.log(` ⚠️ Parser warning: ${error.message}`); } performanceTracker.recordMetric('namespace-conflict', performance.now() - startTime); } performanceTracker.endOperation('namespace-conflicts'); }); await t.test('Common e-invoice namespace patterns', async () => { performanceTracker.startOperation('einvoice-namespaces'); const einvoiceNamespaces = [ { name: 'UBL Invoice', namespaces: { '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', 'xmlns:ext': 'urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2' }, rootElement: 'Invoice' }, { name: 'Cross Industry Invoice (CII)', namespaces: { 'xmlns:rsm': 'urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100', 'xmlns:ram': 'urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100', 'xmlns:qdt': 'urn:un:unece:uncefact:data:standard:QualifiedDataType:100', 'xmlns:udt': 'urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100' }, rootElement: 'rsm:CrossIndustryInvoice' }, { name: 'FatturaPA', namespaces: { 'xmlns:p': 'http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.2', 'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance' }, rootElement: 'p:FatturaElettronica' }, { name: 'PEPPOL BIS', namespaces: { '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' }, rootElement: 'Invoice', profile: 'PEPPOL BIS Billing 3.0' } ]; for (const format of einvoiceNamespaces) { console.log(`\n${format.name}:`); console.log(` Root element: ${format.rootElement}`); if (format.profile) { console.log(` Profile: ${format.profile}`); } console.log(' Namespaces:'); for (const [attr, uri] of Object.entries(format.namespaces)) { const prefix = attr === 'xmlns' ? '(default)' : attr.replace('xmlns:', ''); console.log(` ${prefix}: ${uri}`); } // Generate sample XML const sampleXml = generateSampleXml(format); try { const invoice = new einvoice.EInvoice(); if (invoice.fromXmlString) { await invoice.fromXmlString(sampleXml); console.log(' ✓ Sample parsed successfully'); } } catch (error) { console.log(` ⚠️ Parse issue: ${error.message}`); } } performanceTracker.endOperation('einvoice-namespaces'); }); await t.test('Namespace validation and well-formedness', async () => { performanceTracker.startOperation('namespace-validation'); const validationTests = [ { name: 'Undefined namespace prefix', xml: ` No namespace declaration for 'undefined' `, valid: false, error: 'Undefined namespace prefix' }, { name: 'Invalid namespace URI', xml: ` Invalid namespace URI `, valid: true, // XML parsers typically don't validate URI format error: null }, { name: 'Reserved namespace prefix', xml: ` Wrong URI for xml prefix `, valid: false, error: 'xml prefix must be bound to http://www.w3.org/XML/1998/namespace' }, { name: 'Circular namespace reference', xml: ` Which namespace? `, valid: true, error: null // Valid but potentially confusing } ]; for (const test of validationTests) { const startTime = performance.now(); console.log(`${test.name}:`); console.log(` Expected: ${test.valid ? 'Valid' : 'Invalid'}`); if (test.error) { console.log(` Expected error: ${test.error}`); } try { const invoice = new einvoice.EInvoice(); if (invoice.fromXmlString) { await invoice.fromXmlString(test.xml); if (test.valid) { console.log(' ✓ Parsed as expected'); } else { console.log(' ✗ Should have failed validation'); } } } catch (error) { if (!test.valid) { console.log(` ✓ Validation failed as expected: ${error.message}`); } else { console.log(` ✗ Unexpected error: ${error.message}`); } } performanceTracker.recordMetric('namespace-validation', performance.now() - startTime); } performanceTracker.endOperation('namespace-validation'); }); await t.test('Corpus namespace analysis', async () => { performanceTracker.startOperation('corpus-namespaces'); const corpusLoader = new CorpusLoader(); const xmlFiles = await corpusLoader.getFiles(/\.(xml|ubl|cii)$/); console.log(`\nAnalyzing namespaces in ${xmlFiles.length} corpus files...`); const namespaceStats = { total: 0, byFormat: new Map(), prefixUsage: new Map(), uniqueURIs: new Set(), avgNamespacesPerFile: 0, errors: 0 }; const sampleSize = Math.min(100, xmlFiles.length); const sampledFiles = xmlFiles.slice(0, sampleSize); let totalNamespaces = 0; for (const file of sampledFiles) { namespaceStats.total++; try { const content = await plugins.fs.readFile(file.path, 'utf8'); // Extract all namespace declarations const namespaceMatches = content.matchAll(/xmlns(?::([^=]+))?="([^"]+)"/g); const namespaces = Array.from(namespaceMatches); totalNamespaces += namespaces.length; for (const match of namespaces) { const prefix = match[1] || '(default)'; const uri = match[2]; // Track prefix usage namespaceStats.prefixUsage.set( prefix, (namespaceStats.prefixUsage.get(prefix) || 0) + 1 ); // Track unique URIs namespaceStats.uniqueURIs.add(uri); // Detect format by namespace if (uri.includes('ubl:schema:xsd')) { namespaceStats.byFormat.set( 'UBL', (namespaceStats.byFormat.get('UBL') || 0) + 1 ); } else if (uri.includes('uncefact:data:standard')) { namespaceStats.byFormat.set( 'CII', (namespaceStats.byFormat.get('CII') || 0) + 1 ); } else if (uri.includes('agenziaentrate.gov.it')) { namespaceStats.byFormat.set( 'FatturaPA', (namespaceStats.byFormat.get('FatturaPA') || 0) + 1 ); } } } catch (error) { namespaceStats.errors++; } } namespaceStats.avgNamespacesPerFile = totalNamespaces / namespaceStats.total; console.log('\nNamespace Statistics:'); console.log(`Files analyzed: ${namespaceStats.total}`); console.log(`Average namespaces per file: ${namespaceStats.avgNamespacesPerFile.toFixed(2)}`); console.log(`Unique namespace URIs: ${namespaceStats.uniqueURIs.size}`); console.log('\nFormat detection by namespace:'); for (const [format, count] of namespaceStats.byFormat.entries()) { console.log(` ${format}: ${count} files`); } console.log('\nMost common prefixes:'); const sortedPrefixes = Array.from(namespaceStats.prefixUsage.entries()) .sort((a, b) => b[1] - a[1]) .slice(0, 10); for (const [prefix, count] of sortedPrefixes) { console.log(` ${prefix}: ${count} occurrences`); } console.log(`\nErrors: ${namespaceStats.errors}`); performanceTracker.endOperation('corpus-namespaces'); }); await t.test('Namespace resolution performance', async () => { performanceTracker.startOperation('namespace-performance'); // Generate XML with varying namespace complexity const complexityLevels = [ { namespaces: 1, elements: 10 }, { namespaces: 5, elements: 50 }, { namespaces: 10, elements: 100 }, { namespaces: 20, elements: 200 } ]; for (const level of complexityLevels) { const xml = generateComplexNamespaceXml(level.namespaces, level.elements); const startTime = performance.now(); try { const invoice = new einvoice.EInvoice(); if (invoice.fromXmlString) { await invoice.fromXmlString(xml); } const parseTime = performance.now() - startTime; console.log(`Complexity: ${level.namespaces} namespaces, ${level.elements} elements`); console.log(` Parse time: ${parseTime.toFixed(2)}ms`); console.log(` Time per element: ${(parseTime / level.elements).toFixed(3)}ms`); performanceTracker.recordMetric(`ns-complexity-${level.namespaces}`, parseTime); } catch (error) { console.log(` Error: ${error.message}`); } } performanceTracker.endOperation('namespace-performance'); }); // Helper functions function generateSampleXml(format: any): string { const namespaceAttrs = Object.entries(format.namespaces) .map(([attr, uri]) => `${attr}="${uri}"`) .join('\n '); return ` <${format.rootElement} ${namespaceAttrs}> `; } function generateComplexNamespaceXml(nsCount: number, elemCount: number): string { let xml = '\nContent ${i}\n`; } xml += ''; return xml; } // Performance summary console.log('\n' + performanceTracker.getSummary()); // Namespace resolution best practices console.log('\nNamespace Resolution Best Practices:'); console.log('1. Always declare namespaces before use'); console.log('2. Use consistent prefixes across documents'); console.log('3. Avoid redefining prefixes in nested scopes'); console.log('4. Validate namespace URIs match expected schemas'); console.log('5. Handle both default and prefixed namespaces'); console.log('6. Preserve namespace context for accurate processing'); console.log('7. Support all common e-invoice namespace patterns'); console.log('8. Optimize namespace resolution for large documents'); }); tap.start();