2025-05-25 19:45:37 +00:00
|
|
|
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';
|
2025-05-27 12:23:50 +00:00
|
|
|
import * as plugins from '../../../ts/plugins.js';
|
2025-05-25 19:45:37 +00:00
|
|
|
|
|
|
|
tap.test('VAL-01: XML Syntax Validation - should validate XML syntax of invoice files', async () => {
|
|
|
|
// Get XML test files from various categories
|
|
|
|
const ciiFiles = await CorpusLoader.getFiles('CII_XMLRECHNUNG');
|
|
|
|
const ublFiles = await CorpusLoader.getFiles('UBL_XMLRECHNUNG');
|
|
|
|
const en16931CiiFiles = await CorpusLoader.getFiles('EN16931_CII');
|
|
|
|
|
|
|
|
// Combine and limit for testing
|
|
|
|
const allXmlFiles = [...ciiFiles, ...ublFiles, ...en16931CiiFiles]
|
|
|
|
.filter(f => f.endsWith('.xml'))
|
|
|
|
.slice(0, 20); // Test first 20 files
|
|
|
|
|
|
|
|
console.log(`Testing XML syntax validation on ${allXmlFiles.length} files`);
|
|
|
|
|
|
|
|
let validCount = 0;
|
|
|
|
let invalidCount = 0;
|
|
|
|
const errors: { file: string; error: string }[] = [];
|
|
|
|
|
|
|
|
for (const filePath of allXmlFiles) {
|
|
|
|
try {
|
|
|
|
// Read XML content
|
|
|
|
const xmlContent = await fs.readFile(filePath, 'utf-8');
|
|
|
|
|
|
|
|
// Track performance of XML validation
|
|
|
|
const { result: isValid } = await PerformanceTracker.track(
|
|
|
|
'xml-syntax-validation',
|
|
|
|
async () => {
|
|
|
|
try {
|
|
|
|
// Use DOMParser to validate XML syntax
|
2025-05-27 12:23:50 +00:00
|
|
|
const parser = new plugins.DOMParser();
|
2025-05-25 19:45:37 +00:00
|
|
|
const doc = parser.parseFromString(xmlContent, 'application/xml');
|
|
|
|
|
|
|
|
// Check for parsing errors
|
|
|
|
const parseError = doc.getElementsByTagName('parsererror');
|
|
|
|
if (parseError.length > 0) {
|
|
|
|
throw new Error(`XML Parse Error: ${parseError[0].textContent}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Additional basic validation
|
|
|
|
if (!doc.documentElement) {
|
|
|
|
throw new Error('No document element found');
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
} catch (error) {
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
file: path.basename(filePath),
|
|
|
|
size: xmlContent.length
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
if (isValid) {
|
|
|
|
validCount++;
|
|
|
|
} else {
|
|
|
|
invalidCount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
invalidCount++;
|
|
|
|
errors.push({
|
|
|
|
file: path.basename(filePath),
|
|
|
|
error: error.message
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Report results
|
|
|
|
console.log(`\nXML Syntax Validation Results:`);
|
|
|
|
console.log(`✓ Valid: ${validCount}/${allXmlFiles.length} (${(validCount/allXmlFiles.length*100).toFixed(1)}%)`);
|
|
|
|
console.log(`✗ Invalid: ${invalidCount}/${allXmlFiles.length} (${(invalidCount/allXmlFiles.length*100).toFixed(1)}%)`);
|
|
|
|
|
|
|
|
if (errors.length > 0) {
|
|
|
|
console.log(`\nValidation Errors:`);
|
|
|
|
errors.slice(0, 5).forEach(e => console.log(` - ${e.file}: ${e.error}`));
|
|
|
|
if (errors.length > 5) {
|
|
|
|
console.log(` ... and ${errors.length - 5} more errors`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Performance summary
|
|
|
|
const perfSummary = await PerformanceTracker.getSummary('xml-syntax-validation');
|
|
|
|
if (perfSummary) {
|
|
|
|
console.log(`\nPerformance Summary:`);
|
|
|
|
console.log(` Average: ${perfSummary.average.toFixed(2)}ms`);
|
|
|
|
console.log(` Min: ${perfSummary.min.toFixed(2)}ms`);
|
|
|
|
console.log(` Max: ${perfSummary.max.toFixed(2)}ms`);
|
|
|
|
console.log(` P95: ${perfSummary.p95.toFixed(2)}ms`);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Expect high success rate for XML syntax validation
|
|
|
|
expect(validCount / allXmlFiles.length).toBeGreaterThan(0.95);
|
|
|
|
});
|
|
|
|
|
|
|
|
tap.test('VAL-01: XML Well-formedness - should validate XML well-formedness', async () => {
|
|
|
|
const testCases = [
|
|
|
|
{
|
|
|
|
name: 'Valid XML',
|
|
|
|
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
|
|
|
<ID>TEST-001</ID>
|
|
|
|
</Invoice>`,
|
|
|
|
shouldBeValid: true
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Invalid XML - Unclosed tag',
|
|
|
|
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
|
|
|
<ID>TEST-001</ID>`,
|
|
|
|
shouldBeValid: false
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Invalid XML - Mismatched tags',
|
|
|
|
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
<Invoice>
|
|
|
|
<ID>TEST-001</Invoice>
|
|
|
|
</ID>`,
|
|
|
|
shouldBeValid: false
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Invalid XML - Invalid characters',
|
|
|
|
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
<Invoice>
|
|
|
|
<ID>TEST-001 & invalid</ID>
|
|
|
|
</Invoice>`,
|
|
|
|
shouldBeValid: false
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
|
|
|
for (const testCase of testCases) {
|
|
|
|
try {
|
|
|
|
const { result: isValid } = await PerformanceTracker.track(
|
|
|
|
'xml-wellformedness-check',
|
|
|
|
async () => {
|
|
|
|
try {
|
2025-05-27 12:23:50 +00:00
|
|
|
const parser = new plugins.DOMParser();
|
2025-05-25 19:45:37 +00:00
|
|
|
const doc = parser.parseFromString(testCase.xml, 'application/xml');
|
|
|
|
|
|
|
|
const parseError = doc.getElementsByTagName('parsererror');
|
|
|
|
return parseError.length === 0 && doc.documentElement !== null;
|
|
|
|
} catch (error) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
console.log(`${testCase.name}: ${isValid ? 'Valid' : 'Invalid'}`);
|
|
|
|
expect(isValid).toEqual(testCase.shouldBeValid);
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
console.log(`${testCase.name}: Error - ${error.message}`);
|
|
|
|
expect(testCase.shouldBeValid).toEqual(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
tap.test('VAL-01: XML Encoding Validation - should handle different encodings', async () => {
|
|
|
|
const encodingTests = [
|
|
|
|
{
|
|
|
|
name: 'UTF-8 encoding',
|
|
|
|
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
<Invoice><ID>Tëst-001</ID></Invoice>`,
|
|
|
|
encoding: 'utf-8'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'ISO-8859-1 encoding',
|
|
|
|
xml: `<?xml version="1.0" encoding="ISO-8859-1"?>
|
|
|
|
<Invoice><ID>Test-001</ID></Invoice>`,
|
|
|
|
encoding: 'iso-8859-1'
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
|
|
|
for (const test of encodingTests) {
|
|
|
|
const { result: isValid } = await PerformanceTracker.track(
|
|
|
|
'xml-encoding-validation',
|
|
|
|
async () => {
|
|
|
|
try {
|
2025-05-27 12:23:50 +00:00
|
|
|
const parser = new plugins.DOMParser();
|
2025-05-25 19:45:37 +00:00
|
|
|
const doc = parser.parseFromString(test.xml, 'application/xml');
|
|
|
|
|
|
|
|
const parseError = doc.getElementsByTagName('parsererror');
|
|
|
|
return parseError.length === 0;
|
|
|
|
} catch (error) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
console.log(`${test.name}: ${isValid ? 'Valid' : 'Invalid'}`);
|
|
|
|
expect(isValid).toEqual(true);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
tap.start();
|