import { expect, tap } from '@git.zone/tstest/tapbundle';
import * as einvoice from '../../../ts/index.js';
import * as plugins from '../../plugins.js';
tap.test('PARSE-09: Entity Reference Resolution - Handle XML entities correctly', async () => {
console.log('\n=== Testing Entity Reference Resolution ===\n');
// Test predefined XML entities
console.log('Testing predefined XML entities:');
const predefinedEntities = [
{ name: 'Ampersand', entity: '&', character: '&' },
{ name: 'Less than', entity: '<', character: '<' },
{ name: 'Greater than', entity: '>', character: '>' },
{ name: 'Quote', entity: '"', character: '"' },
{ name: 'Apostrophe', entity: ''', character: "'" }
];
for (const entity of predefinedEntities) {
const testXml = `
Test ${entity.entity} Company
Text with ${entity.entity} entity
`;
console.log(`\n${entity.name} entity (${entity.entity} = "${entity.character}")`);
try {
const invoice = new einvoice.EInvoice();
if (invoice.fromXmlString) {
await invoice.fromXmlString(testXml);
console.log(' ✓ Entity parsed successfully');
} else {
console.log(' ⚠️ fromXmlString not available');
}
} catch (error) {
console.log(` ✗ Error: ${error.message}`);
}
}
// Test numeric character references
console.log('\n\nTesting numeric character references:');
const numericRefs = [
{ ref: 'A', char: 'A', description: 'Latin capital A' },
{ ref: '€', char: '€', description: 'Euro sign' },
{ ref: '©', char: '©', description: 'Copyright' },
{ ref: 'A', char: 'A', description: 'Latin A (hex)' },
{ ref: '€', char: '€', description: 'Euro (hex)' }
];
for (const test of numericRefs) {
const xml = `
100.00
${test.ref} 2024
`;
console.log(`\n${test.ref} = "${test.char}" (${test.description})`);
try {
const invoice = new einvoice.EInvoice();
if (invoice.fromXmlString) {
await invoice.fromXmlString(xml);
console.log(' ✓ Numeric reference parsed');
}
} catch (error) {
console.log(` ✗ Error: ${error.message}`);
}
}
// Test entity security
console.log('\n\nTesting entity security:');
const securityTests = [
{
name: 'External entity (XXE)',
xml: `
]>
&xxe;
`
},
{
name: 'Entity expansion',
xml: `
]>
&lol2;
`
}
];
for (const test of securityTests) {
console.log(`\n${test.name}:`);
try {
const invoice = new einvoice.EInvoice();
if (invoice.fromXmlString) {
await invoice.fromXmlString(test.xml);
console.log(' ⚠️ WARNING: Parser allowed potentially dangerous entities');
}
} catch (error) {
console.log(' ✓ Parser correctly rejected dangerous entities');
console.log(` Error: ${error.message}`);
}
}
// Test entity usage in real e-invoice patterns
console.log('\n\nTesting common e-invoice entity patterns:');
const einvoicePatterns = [
{
name: 'Company with ampersand',
xml: `
Smith & Jones Ltd.
AT&T Communications
`
},
{
name: 'Currency symbols',
xml: `
Price: €100.00
Alternative: £85.00
`
},
{
name: 'Legal symbols',
xml: `
Product®
`
}
];
for (const pattern of einvoicePatterns) {
console.log(`\n${pattern.name}:`);
try {
const invoice = new einvoice.EInvoice();
if (invoice.fromXmlString) {
await invoice.fromXmlString(pattern.xml);
console.log(' ✓ Pattern parsed successfully');
}
} catch (error) {
console.log(` ✗ Error: ${error.message}`);
}
}
// Test entity resolution performance
console.log('\n\nTesting entity resolution performance:');
const sizes = [10, 50, 100];
for (const size of sizes) {
let xml = '\n\n';
for (let i = 0; i < size; i++) {
xml += ` Text & more € symbols ©\n`;
}
xml += '';
const startTime = performance.now();
try {
const invoice = new einvoice.EInvoice();
if (invoice.fromXmlString) {
await invoice.fromXmlString(xml);
const elapsed = performance.now() - startTime;
console.log(` ${size * 3} entities: ${elapsed.toFixed(2)}ms`);
}
} catch (error) {
console.log(` Error with ${size} fields: ${error.message}`);
}
}
// Summary
console.log('\n\nEntity Reference Resolution Summary:');
console.log('- Predefined XML entities should be supported');
console.log('- Numeric character references are common in e-invoices');
console.log('- Security: External entities should be disabled');
console.log('- Performance: Entity resolution adds minimal overhead');
console.log('- Common patterns: Company names, currency symbols, legal marks');
});
tap.start();