import { expect, tap } from '@git.zone/tstest/tapbundle';
import { promises as fs } from 'fs';
import * as path from 'path';
import { CorpusLoader } from '../../helpers/corpus.loader.js';
import { PerformanceTracker } from '../../helpers/performance.tracker.js';
tap.test('VAL-06: Cross-Reference Validation - should validate references between invoice elements', async () => {
// Test files that should have proper cross-references
const ublFiles = await CorpusLoader.getFiles('UBL_XMLRECHNUNG');
const ciiFiles = await CorpusLoader.getFiles('CII_XMLRECHNUNG');
const testFiles = [...ublFiles.slice(0, 3), ...ciiFiles.slice(0, 3)];
console.log(`Testing cross-reference validation on ${testFiles.length} files`);
const { EInvoice } = await import('../../../ts/index.js');
let validReferences = 0;
let invalidReferences = 0;
let errorCount = 0;
const referenceIssues: { file: string; issues: string[] }[] = [];
for (const filePath of testFiles) {
const fileName = path.basename(filePath);
try {
const xmlContent = await fs.readFile(filePath, 'utf-8');
const { result: einvoice } = await PerformanceTracker.track(
'cross-ref-xml-loading',
async () => await EInvoice.fromXml(xmlContent)
);
const { result: validation } = await PerformanceTracker.track(
'cross-reference-validation',
async () => {
return await einvoice.validate(/* ValidationLevel.SEMANTIC */);
},
{ file: fileName }
);
if (validation.valid) {
validReferences++;
console.log(`✓ ${fileName}: Cross-references valid`);
} else {
invalidReferences++;
// Look for reference-specific errors
const refErrors = validation.errors ? validation.errors.filter(e =>
e.message && (
e.message.toLowerCase().includes('reference') ||
e.message.toLowerCase().includes('missing') ||
e.message.toLowerCase().includes('invalid') ||
e.message.toLowerCase().includes('link') ||
e.code && e.code.includes('REF')
)
) : [];
if (refErrors.length > 0) {
console.log(`○ ${fileName}: Reference issues found (${refErrors.length})`);
referenceIssues.push({
file: fileName,
issues: refErrors.map(e => `${e.code}: ${e.message}`)
});
} else {
console.log(`○ ${fileName}: Invalid but no specific reference errors`);
}
}
} catch (error) {
errorCount++;
console.log(`✗ ${fileName}: Error - ${error.message}`);
}
}
console.log('\n=== CROSS-REFERENCE VALIDATION SUMMARY ===');
console.log(`Valid references: ${validReferences}`);
console.log(`Invalid references: ${invalidReferences}`);
console.log(`Processing errors: ${errorCount}`);
// Show sample reference issues
if (referenceIssues.length > 0) {
console.log('\nSample reference issues:');
referenceIssues.slice(0, 3).forEach(item => {
console.log(` ${item.file}:`);
item.issues.slice(0, 2).forEach(issue => {
console.log(` - ${issue}`);
});
});
}
// Performance summary
const perfSummary = await PerformanceTracker.getSummary('cross-reference-validation');
if (perfSummary) {
console.log(`\nCross-Reference Validation Performance:`);
console.log(` Average: ${perfSummary.average.toFixed(2)}ms`);
console.log(` P95: ${perfSummary.p95.toFixed(2)}ms`);
}
// Expect files to be processed successfully
expect(validReferences + invalidReferences).toBeGreaterThan(0);
});
tap.test('VAL-06: Party Reference Validation - should validate party references and IDs', async () => {
const { EInvoice } = await import('../../../ts/index.js');
const partyReferenceTests = [
{
name: 'Valid party references',
xml: `
PARTY-REF-001
1234567890123
Supplier Company Ltd
9876543210987
Customer Company Ltd
`,
shouldBeValid: true,
description: 'Parties with proper identification'
},
{
name: 'Missing party identification',
xml: `
PARTY-REF-002
Supplier Without ID
`,
shouldBeValid: false,
description: 'Missing required party identification'
},
{
name: 'Invalid party ID scheme',
xml: `
PARTY-REF-003
123456
Supplier Company
`,
shouldBeValid: false,
description: 'Invalid party identification scheme'
}
];
for (const test of partyReferenceTests) {
try {
const { result: validation } = await PerformanceTracker.track(
'party-reference-test',
async () => {
const einvoice = await EInvoice.fromXml(test.xml);
return await einvoice.validate();
}
);
console.log(`${test.name}: ${validation.valid ? 'VALID' : 'INVALID'}`);
console.log(` ${test.description}`);
if (!test.shouldBeValid && !validation.valid) {
console.log(` ✓ Correctly detected party reference issues`);
if (validation.errors) {
const partyErrors = validation.errors.filter(e =>
e.message && (
e.message.toLowerCase().includes('party') ||
e.message.toLowerCase().includes('identification') ||
e.message.toLowerCase().includes('scheme')
)
);
console.log(` Party reference errors: ${partyErrors.length}`);
}
} else if (test.shouldBeValid && validation.valid) {
console.log(` ✓ Correctly validated party references`);
} else {
console.log(` ○ Unexpected result (party reference validation may need implementation)`);
}
} catch (error) {
console.log(`${test.name}: Error - ${error.message}`);
}
}
});
tap.test('VAL-06: Tax Category Reference Validation - should validate tax category references', async () => {
const { EInvoice } = await import('../../../ts/index.js');
const taxReferenceTests = [
{
name: 'Valid tax category references',
xml: `
TAX-REF-001
190.00
1000.00
190.00
S
19
VAT
1
S
19
VAT
`,
shouldBeValid: true,
description: 'Tax categories properly referenced between totals and line items'
},
{
name: 'Mismatched tax category references',
xml: `
TAX-REF-002
S
19
VAT
1
E
0
VAT
`,
shouldBeValid: false,
description: 'Tax category mismatch: S in total vs E in line item'
}
];
for (const test of taxReferenceTests) {
try {
const { result: validation } = await PerformanceTracker.track(
'tax-reference-test',
async () => {
const einvoice = await EInvoice.fromXml(test.xml);
return await einvoice.validate();
}
);
console.log(`${test.name}: ${validation.valid ? 'VALID' : 'INVALID'}`);
console.log(` ${test.description}`);
if (!test.shouldBeValid && !validation.valid) {
console.log(` ✓ Correctly detected tax reference mismatch`);
if (validation.errors) {
const taxErrors = validation.errors.filter(e =>
e.message && (
e.message.toLowerCase().includes('tax') ||
e.message.toLowerCase().includes('category') ||
e.message.toLowerCase().includes('mismatch')
)
);
console.log(` Tax reference errors: ${taxErrors.length}`);
}
} else if (test.shouldBeValid && validation.valid) {
console.log(` ✓ Correctly validated tax references`);
} else {
console.log(` ○ Unexpected result (tax reference validation may need implementation)`);
}
} catch (error) {
console.log(`${test.name}: Error - ${error.message}`);
}
}
});
tap.test('VAL-06: Payment Terms Reference Validation - should validate payment terms consistency', async () => {
const { EInvoice } = await import('../../../ts/index.js');
const paymentTermsTests = [
{
name: 'Consistent payment terms',
xml: `
PAY-TERMS-001
2024-01-01
2024-01-31
Payment due within 30 days
58
DE89370400440532013000
`,
shouldBeValid: true,
description: 'Due date matches payment terms (30 days)'
},
{
name: 'Inconsistent payment terms',
xml: `
PAY-TERMS-002
2024-01-01
2024-02-15
Payment due within 14 days
`,
shouldBeValid: false,
description: 'Due date (45 days) does not match payment terms (14 days)'
}
];
for (const test of paymentTermsTests) {
try {
const { result: validation } = await PerformanceTracker.track(
'payment-terms-test',
async () => {
const einvoice = await EInvoice.fromXml(test.xml);
return await einvoice.validate();
}
);
console.log(`${test.name}: ${validation.valid ? 'VALID' : 'INVALID'}`);
console.log(` ${test.description}`);
if (!test.shouldBeValid && !validation.valid) {
console.log(` ✓ Correctly detected payment terms inconsistency`);
if (validation.errors) {
const paymentErrors = validation.errors.filter(e =>
e.message && (
e.message.toLowerCase().includes('payment') ||
e.message.toLowerCase().includes('due') ||
e.message.toLowerCase().includes('terms')
)
);
console.log(` Payment terms errors: ${paymentErrors.length}`);
}
} else if (test.shouldBeValid && validation.valid) {
console.log(` ✓ Correctly validated payment terms`);
} else {
console.log(` ○ Unexpected result (payment terms validation may need implementation)`);
}
} catch (error) {
console.log(`${test.name}: Error - ${error.message}`);
}
}
});
tap.test('VAL-06: Document Reference Validation - should validate document references and IDs', async () => {
const { EInvoice } = await import('../../../ts/index.js');
const documentReferenceTests = [
{
name: 'Valid document references',
xml: `
DOC-REF-001
PO-2024-001
CONTRACT-2024-001
DELIVERY-NOTE-001
130
`,
shouldBeValid: true,
description: 'Proper document references with valid IDs'
},
{
name: 'Empty document references',
xml: `
DOC-REF-002
130
`,
shouldBeValid: false,
description: 'Empty or missing document reference IDs'
}
];
for (const test of documentReferenceTests) {
try {
const { result: validation } = await PerformanceTracker.track(
'document-reference-test',
async () => {
const einvoice = await EInvoice.fromXml(test.xml);
return await einvoice.validate();
}
);
console.log(`${test.name}: ${validation.valid ? 'VALID' : 'INVALID'}`);
console.log(` ${test.description}`);
if (!test.shouldBeValid && !validation.valid) {
console.log(` ✓ Correctly detected document reference issues`);
if (validation.errors) {
const docErrors = validation.errors.filter(e =>
e.message && (
e.message.toLowerCase().includes('document') ||
e.message.toLowerCase().includes('reference') ||
e.message.toLowerCase().includes('empty')
)
);
console.log(` Document reference errors: ${docErrors.length}`);
}
} else if (test.shouldBeValid && validation.valid) {
console.log(` ✓ Correctly validated document references`);
} else {
console.log(` ○ Unexpected result (document reference validation may need implementation)`);
}
} catch (error) {
console.log(`${test.name}: Error - ${error.message}`);
}
}
});
tap.start();