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-08: Profile Validation - should validate format-specific profiles and customizations', async () => { // Test XRechnung profile validation const ublFiles = await CorpusLoader.getFiles('UBL_XMLRECHNUNG'); const xrechnungFiles = ublFiles.filter(f => path.basename(f).toLowerCase().includes('xrechnung') ); console.log(`Testing profile validation on ${xrechnungFiles.length} XRechnung files`); const { EInvoice } = await import('../../../ts/index.js'); let validProfiles = 0; let invalidProfiles = 0; let errorCount = 0; const profileIssues: { file: string; profile?: string; issues: string[] }[] = []; for (const filePath of xrechnungFiles.slice(0, 5)) { // Test first 5 files const fileName = path.basename(filePath); try { const xmlContent = await fs.readFile(filePath, 'utf-8'); const { result: einvoice } = await PerformanceTracker.track( 'profile-xml-loading', async () => await EInvoice.fromXml(xmlContent) ); // Extract profile information const profileInfo = extractProfileInfo(xmlContent); const { result: validation } = await PerformanceTracker.track( 'profile-validation', async () => { return await einvoice.validate(/* ValidationLevel.PROFILE */); }, { file: fileName, profile: profileInfo.customizationId } ); if (validation.valid) { validProfiles++; console.log(`✓ ${fileName}: Profile valid (${profileInfo.customizationId || 'unknown'})`); } else { invalidProfiles++; // Look for profile-specific errors const profErrors = validation.errors ? validation.errors.filter(e => e.message && ( e.message.toLowerCase().includes('profile') || e.message.toLowerCase().includes('customization') || e.message.toLowerCase().includes('xrechnung') || e.code && e.code.includes('PROF') ) ) : []; profileIssues.push({ file: fileName, profile: profileInfo.customizationId, issues: profErrors.map(e => `${e.code}: ${e.message}`) }); console.log(`○ ${fileName}: Profile issues found (${profErrors.length})`); } } catch (error) { errorCount++; console.log(`✗ ${fileName}: Error - ${error.message}`); } } console.log('\n=== PROFILE VALIDATION SUMMARY ==='); console.log(`Valid profiles: ${validProfiles}`); console.log(`Invalid profiles: ${invalidProfiles}`); console.log(`Processing errors: ${errorCount}`); // Show sample profile issues if (profileIssues.length > 0) { console.log('\nProfile issues detected:'); profileIssues.slice(0, 3).forEach(item => { console.log(` ${item.file} (${item.profile || 'unknown'}):`); item.issues.slice(0, 2).forEach(issue => { console.log(` - ${issue}`); }); }); } // Performance summary const perfSummary = await PerformanceTracker.getSummary('profile-validation'); if (perfSummary) { console.log(`\nProfile Validation Performance:`); console.log(` Average: ${perfSummary.average.toFixed(2)}ms`); console.log(` P95: ${perfSummary.p95.toFixed(2)}ms`); } expect(validProfiles + invalidProfiles).toBeGreaterThan(0); }); tap.test('VAL-08: XRechnung Profile Validation - should validate XRechnung-specific requirements', async () => { const { EInvoice } = await import('../../../ts/index.js'); const xrechnungProfileTests = [ { name: 'Valid XRechnung 3.0 profile', xml: ` urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_3.0 urn:fdc:peppol.eu:2017:poacc:billing:01:1.0 XR-2024-001 2024-01-01 German Supplier GmbH `, shouldBeValid: true, profile: 'XRechnung 3.0' }, { name: 'Missing CustomizationID', xml: ` urn:fdc:peppol.eu:2017:poacc:billing:01:1.0 XR-2024-002 `, shouldBeValid: false, profile: 'Missing CustomizationID' }, { name: 'Invalid XRechnung CustomizationID', xml: ` urn:invalid:customization:id XR-2024-003 `, shouldBeValid: false, profile: 'Invalid CustomizationID' } ]; for (const test of xrechnungProfileTests) { try { const { result: validation } = await PerformanceTracker.track( 'xrechnung-profile-test', async () => { const einvoice = await EInvoice.fromXml(test.xml); return await einvoice.validate(); } ); console.log(`${test.name}: ${validation.valid ? 'VALID' : 'INVALID'}`); console.log(` Profile: ${test.profile}`); if (!test.shouldBeValid && !validation.valid) { console.log(` ✓ Correctly detected profile violation`); if (validation.errors) { const profileErrors = validation.errors.filter(e => e.message && ( e.message.toLowerCase().includes('customization') || e.message.toLowerCase().includes('profile') || e.message.toLowerCase().includes('xrechnung') ) ); console.log(` Profile errors: ${profileErrors.length}`); } } else if (test.shouldBeValid && validation.valid) { console.log(` ✓ Correctly validated XRechnung profile`); } else { console.log(` ○ Unexpected result (XRechnung profile validation may need implementation)`); } } catch (error) { console.log(`${test.name}: Error - ${error.message}`); } } }); tap.test('VAL-08: Factur-X Profile Validation - should validate Factur-X profile requirements', async () => { const { EInvoice } = await import('../../../ts/index.js'); const facturxProfileTests = [ { name: 'Valid Factur-X BASIC profile', xml: ` urn:cen.eu:en16931:2017#compliant#urn:factur-x.eu:1p0:basic FX-2024-001 380 `, shouldBeValid: true, profile: 'Factur-X BASIC' }, { name: 'Valid Factur-X EN16931 profile', xml: ` urn:cen.eu:en16931:2017#compliant#urn:factur-x.eu:1p0:en16931 FX-2024-002 380 `, shouldBeValid: true, profile: 'Factur-X EN16931' }, { name: 'Missing guideline parameter', xml: ` FX-2024-003 `, shouldBeValid: false, profile: 'Missing guideline' } ]; for (const test of facturxProfileTests) { try { const { result: validation } = await PerformanceTracker.track( 'facturx-profile-test', async () => { const einvoice = await EInvoice.fromXml(test.xml); return await einvoice.validate(); } ); console.log(`${test.name}: ${validation.valid ? 'VALID' : 'INVALID'}`); console.log(` Profile: ${test.profile}`); if (!test.shouldBeValid && !validation.valid) { console.log(` ✓ Correctly detected Factur-X profile violation`); if (validation.errors) { const profileErrors = validation.errors.filter(e => e.message && ( e.message.toLowerCase().includes('guideline') || e.message.toLowerCase().includes('profile') || e.message.toLowerCase().includes('factur') ) ); console.log(` Factur-X profile errors: ${profileErrors.length}`); } } else if (test.shouldBeValid && validation.valid) { console.log(` ✓ Correctly validated Factur-X profile`); } else { console.log(` ○ Unexpected result (Factur-X profile validation may need implementation)`); } } catch (error) { console.log(`${test.name}: Error - ${error.message}`); } } }); tap.test('VAL-08: ZUGFeRD Profile Validation - should validate ZUGFeRD profile requirements', async () => { const { EInvoice } = await import('../../../ts/index.js'); const zugferdProfileTests = [ { name: 'Valid ZUGFeRD BASIC profile', xml: ` urn:zugferd:2p1:basic ZF-2024-001 380 `, shouldBeValid: true, profile: 'ZUGFeRD BASIC' }, { name: 'Valid ZUGFeRD COMFORT profile', xml: ` urn:zugferd:2p1:comfort ZF-2024-002 380 `, shouldBeValid: true, profile: 'ZUGFeRD COMFORT' } ]; for (const test of zugferdProfileTests) { try { const { result: validation } = await PerformanceTracker.track( 'zugferd-profile-test', async () => { const einvoice = await EInvoice.fromXml(test.xml); return await einvoice.validate(); } ); console.log(`${test.name}: ${validation.valid ? 'VALID' : 'INVALID'}`); console.log(` Profile: ${test.profile}`); // ZUGFeRD profile validation depends on implementation if (validation.valid) { console.log(` ✓ ZUGFeRD profile validation passed`); } else { console.log(` ○ ZUGFeRD profile validation (may need implementation)`); if (validation.errors) { const profileErrors = validation.errors.filter(e => e.message && ( e.message.toLowerCase().includes('zugferd') || e.message.toLowerCase().includes('profile') ) ); console.log(` ZUGFeRD profile errors: ${profileErrors.length}`); } } } catch (error) { console.log(`${test.name}: Error - ${error.message}`); } } }); tap.test('VAL-08: Profile Compatibility Validation - should validate profile compatibility', async () => { const { EInvoice } = await import('../../../ts/index.js'); const compatibilityTests = [ { name: 'Compatible profiles (EN16931 compliant)', xml: ` urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_3.0 urn:fdc:peppol.eu:2017:poacc:billing:01:1.0 COMPAT-001 `, description: 'XRechnung with PEPPOL profile (compatible)' }, { name: 'Mixed format indicators', xml: ` urn:cen.eu:en16931:2017#compliant#urn:factur-x.eu:1p0:basic urn:zugferd:2p1:basic `, description: 'Multiple conflicting profile indicators' } ]; for (const test of compatibilityTests) { try { const { result: validation } = await PerformanceTracker.track( 'profile-compatibility-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 (validation.errors && validation.errors.length > 0) { const compatErrors = validation.errors.filter(e => e.message && ( e.message.toLowerCase().includes('compatible') || e.message.toLowerCase().includes('conflict') || e.message.toLowerCase().includes('profile') ) ); console.log(` Compatibility issues: ${compatErrors.length}`); } else { console.log(` No compatibility issues detected`); } } catch (error) { console.log(`${test.name}: Error - ${error.message}`); } } }); // Helper function to extract profile information from XML function extractProfileInfo(xml: string): { customizationId?: string; profileId?: string } { const customizationMatch = xml.match(/]*>([^<]+)<\/cbc:CustomizationID>/); const profileMatch = xml.match(/]*>([^<]+)<\/cbc:ProfileID>/); const guidelineMatch = xml.match(/]*>([^<]+)<\/ram:ID>/); return { customizationId: customizationMatch?.[1] || guidelineMatch?.[1], profileId: profileMatch?.[1] }; } tap.start();