import { tap, expect } from '@git.zone/tstest/tapbundle'; import * as plugins from '../../../ts/plugins.js'; import { EInvoice } from '../../../ts/index.js'; import { CorpusLoader } from '../../helpers/corpus.loader.js'; import { PerformanceTracker } from '../../helpers/performance.tracker.js'; const testTimeout = 300000; // 5 minutes timeout for corpus processing // VAL-09: Semantic Level Validation // Tests semantic-level validation including data types, value ranges, // and cross-field dependencies according to EN16931 semantic model tap.test('VAL-09: Semantic Level Validation - Data Type Validation', async (tools) => { const startTime = Date.now(); // Test numeric field validation const numericValidationTests = [ { value: '123.45', field: 'InvoiceTotal', valid: true }, { value: '0.00', field: 'InvoiceTotal', valid: true }, { value: 'abc', field: 'InvoiceTotal', valid: false }, { value: '', field: 'InvoiceTotal', valid: false }, { value: '123.456', field: 'InvoiceTotal', valid: true }, // Should handle rounding { value: '-123.45', field: 'InvoiceTotal', valid: false }, // Negative not allowed ]; for (const test of numericValidationTests) { try { // Create a minimal test invoice with the value to test const testXml = ` TEST-001 2024-01-01 380 ${test.value} `; const invoice = new EInvoice(); const parseResult = await invoice.fromXmlString(testXml); if (test.valid) { expect(parseResult).toBeTruthy(); console.log(`✓ Valid numeric value '${test.value}' accepted for ${test.field}`); } else { // Should either fail parsing or validation const validationResult = await invoice.validate(); expect(validationResult.valid).toBeFalse(); console.log(`✓ Invalid numeric value '${test.value}' rejected for ${test.field}`); } } catch (error) { if (!test.valid) { console.log(`✓ Invalid numeric value '${test.value}' properly rejected with error: ${error.message}`); } else { throw error; } } } const duration = Date.now() - startTime; // PerformanceTracker.recordMetric('semantic-validation-datatypes', duration); }); tap.test('VAL-09: Semantic Level Validation - Date Format Validation', async (tools) => { const startTime = Date.now(); // Test date format validation according to ISO 8601 const dateValidationTests = [ { value: '2024-01-01', valid: true }, { value: '2024-12-31', valid: true }, { value: '2024-02-29', valid: true }, // Leap year { value: '2023-02-29', valid: false }, // Not a leap year { value: '2024-13-01', valid: false }, // Invalid month { value: '2024-01-32', valid: false }, // Invalid day { value: '24-01-01', valid: false }, // Wrong format { value: '2024/01/01', valid: false }, // Wrong separator { value: '', valid: false }, // Empty { value: 'invalid-date', valid: false }, // Non-date string ]; for (const test of dateValidationTests) { try { const testXml = ` TEST-001 ${test.value} 380 `; const invoice = new EInvoice(); const parseResult = await invoice.fromXmlString(testXml); if (test.valid) { expect(parseResult).toBeTruthy(); const validationResult = await invoice.validate(); expect(validationResult.valid).toBeTrue(); console.log(`✓ Valid date '${test.value}' accepted`); } else { // Should either fail parsing or validation if (parseResult) { const validationResult = await invoice.validate(); expect(validationResult.valid).toBeFalse(); } console.log(`✓ Invalid date '${test.value}' rejected`); } } catch (error) { if (!test.valid) { console.log(`✓ Invalid date '${test.value}' properly rejected with error: ${error.message}`); } else { throw error; } } } const duration = Date.now() - startTime; // PerformanceTracker.recordMetric('semantic-validation-dates', duration); }); tap.test('VAL-09: Semantic Level Validation - Currency Code Validation', async (tools) => { const startTime = Date.now(); // Test currency code validation according to ISO 4217 const currencyValidationTests = [ { code: 'EUR', valid: true }, { code: 'USD', valid: true }, { code: 'GBP', valid: true }, { code: 'JPY', valid: true }, { code: 'CHF', valid: true }, { code: 'SEK', valid: true }, { code: 'XXX', valid: false }, // Invalid currency { code: 'ABC', valid: false }, // Non-existent currency { code: 'eur', valid: false }, // Lowercase { code: 'EURO', valid: false }, // Too long { code: 'EU', valid: false }, // Too short { code: '', valid: false }, // Empty { code: '123', valid: false }, // Numeric ]; for (const test of currencyValidationTests) { try { const testXml = ` TEST-001 2024-01-01 380 ${test.code} 100.00 `; const invoice = new EInvoice(); const parseResult = await invoice.fromXmlString(testXml); if (test.valid) { expect(parseResult).toBeTruthy(); console.log(`✓ Valid currency code '${test.code}' accepted`); } else { // Should either fail parsing or validation if (parseResult) { const validationResult = await invoice.validate(); expect(validationResult.valid).toBeFalse(); } console.log(`✓ Invalid currency code '${test.code}' rejected`); } } catch (error) { if (!test.valid) { console.log(`✓ Invalid currency code '${test.code}' properly rejected with error: ${error.message}`); } else { throw error; } } } const duration = Date.now() - startTime; // PerformanceTracker.recordMetric('semantic-validation-currency', duration); }); tap.test('VAL-09: Semantic Level Validation - Cross-Field Dependencies', async (tools) => { const startTime = Date.now(); // Test semantic dependencies between fields const dependencyTests = [ { name: 'Tax Amount vs Tax Rate', xml: ` TEST-001 2024-01-01 380 19.00 100.00 19.00 19.00 `, valid: true }, { name: 'Inconsistent Tax Calculation', xml: ` TEST-001 2024-01-01 380 20.00 100.00 19.00 19.00 `, valid: false } ]; for (const test of dependencyTests) { try { const invoice = new EInvoice(); const parseResult = await invoice.fromXmlString(test.xml); if (parseResult) { const validationResult = await invoice.validate(); if (test.valid) { expect(validationResult.valid).toBeTrue(); console.log(`✓ ${test.name}: Valid cross-field dependency accepted`); } else { expect(validationResult.valid).toBeFalse(); console.log(`✓ ${test.name}: Invalid cross-field dependency rejected`); } } else if (!test.valid) { console.log(`✓ ${test.name}: Invalid dependency rejected at parse time`); } else { throw new Error(`Expected valid parse for ${test.name}`); } } catch (error) { if (!test.valid) { console.log(`✓ ${test.name}: Invalid dependency properly rejected with error: ${error.message}`); } else { throw error; } } } const duration = Date.now() - startTime; // PerformanceTracker.recordMetric('semantic-validation-dependencies', duration); }); tap.test('VAL-09: Semantic Level Validation - Value Range Validation', async (tools) => { const startTime = Date.now(); // Test value range constraints const rangeTests = [ { field: 'Tax Percentage', value: '19.00', valid: true, description: 'Normal tax rate' }, { field: 'Tax Percentage', value: '0.00', valid: true, description: 'Zero tax rate' }, { field: 'Tax Percentage', value: '100.00', valid: true, description: 'Maximum tax rate' }, { field: 'Tax Percentage', value: '-5.00', valid: false, description: 'Negative tax rate' }, { field: 'Tax Percentage', value: '150.00', valid: false, description: 'Unrealistic high tax rate' } ]; for (const test of rangeTests) { try { const testXml = ` TEST-001 2024-01-01 380 ${test.value} `; const invoice = new EInvoice(); const parseResult = await invoice.fromXmlString(testXml); if (test.valid) { expect(parseResult).toBeTruthy(); console.log(`✓ ${test.description}: Valid value '${test.value}' accepted for ${test.field}`); } else { // Should either fail parsing or validation if (parseResult) { const validationResult = await invoice.validate(); expect(validationResult.valid).toBeFalse(); } console.log(`✓ ${test.description}: Invalid value '${test.value}' rejected for ${test.field}`); } } catch (error) { if (!test.valid) { console.log(`✓ ${test.description}: Invalid value properly rejected with error: ${error.message}`); } else { throw error; } } } const duration = Date.now() - startTime; // PerformanceTracker.recordMetric('semantic-validation-ranges', duration); }); tap.test('VAL-09: Semantic Level Validation - Corpus Semantic Validation', { timeout: testTimeout }, async (tools) => { const startTime = Date.now(); let processedFiles = 0; let validFiles = 0; let semanticErrors = 0; // Test semantic validation against UBL corpus files try { const ublFiles = await CorpusLoader.getFiles('UBL_XML_RECHNUNG'); for (const filePath of ublFiles.slice(0, 10)) { // Process first 10 files for performance try { const invoice = new EInvoice(); const parseResult = await invoice.fromFile(filePath); processedFiles++; if (parseResult) { const validationResult = await invoice.validate(); if (validationResult.valid) { validFiles++; } else { // Check if errors are semantic-level const semanticErrorTypes = ['data-type', 'range', 'dependency', 'format']; const hasSemanticErrors = validationResult.errors?.some(error => semanticErrorTypes.some(type => error.message.toLowerCase().includes(type)) ); if (hasSemanticErrors) { semanticErrors++; console.log(`Semantic validation errors in ${plugins.path.basename(filePath)}`); } } } // Performance check if (processedFiles % 5 === 0) { const currentDuration = Date.now() - startTime; const avgPerFile = currentDuration / processedFiles; console.log(`Processed ${processedFiles} files, avg ${avgPerFile.toFixed(0)}ms per file`); } } catch (error) { console.log(`Failed to process ${plugins.path.basename(filePath)}: ${error.message}`); } } const successRate = processedFiles > 0 ? (validFiles / processedFiles) * 100 : 0; const semanticErrorRate = processedFiles > 0 ? (semanticErrors / processedFiles) * 100 : 0; console.log(`Semantic validation completed:`); console.log(`- Processed: ${processedFiles} files`); console.log(`- Valid: ${validFiles} files (${successRate.toFixed(1)}%)`); console.log(`- Semantic errors: ${semanticErrors} files (${semanticErrorRate.toFixed(1)}%)`); // Semantic validation should have high success rate for well-formed corpus expect(successRate).toBeGreaterThan(70); } catch (error) { console.log(`Corpus semantic validation failed: ${error.message}`); throw error; } const totalDuration = Date.now() - startTime; // PerformanceTracker.recordMetric('semantic-validation-corpus', totalDuration); // Performance expectation: should complete within reasonable time expect(totalDuration).toBeLessThan(60000); // 60 seconds max console.log(`Semantic validation performance: ${totalDuration}ms total`); }); tap.test('VAL-09: Performance Summary', async (tools) => { const operations = [ 'semantic-validation-datatypes', 'semantic-validation-dates', 'semantic-validation-currency', 'semantic-validation-dependencies', 'semantic-validation-ranges', 'semantic-validation-corpus' ]; for (const operation of operations) { const summary = await PerformanceTracker.getSummary(operation); if (summary) { console.log(`${operation}: avg=${summary.average}ms, min=${summary.min}ms, max=${summary.max}ms, p95=${summary.p95}ms`); } } }); // Start the test tap.start(); // Export for test runner compatibility export default tap;