import { tap, expect } from '@git.zone/tstest/tapbundle'; import { PeppolValidator } from '../ts/formats/validation/peppol.validator.js'; import type { EInvoice } from '../ts/einvoice.js'; tap.test('PEPPOL Validator - basic instantiation', async () => { const validator = PeppolValidator.create(); expect(validator).toBeInstanceOf(PeppolValidator); // Singleton pattern const validator2 = PeppolValidator.create(); expect(validator2).toEqual(validator); }); tap.test('PEPPOL Validator - endpoint ID validation', async () => { const validator = PeppolValidator.create(); const invoice: Partial = { metadata: { profileId: 'urn:fdc:peppol.eu:2017:poacc:billing:3.0', extensions: { sellerEndpointId: '0088:1234567890128', // Valid GLN buyerEndpointId: '0192:123456789' // Valid Norwegian org } } }; const results = validator.validatePeppol(invoice as EInvoice); const endpointErrors = results.filter(r => r.ruleId.startsWith('PEPPOL-T00')); console.log('Endpoint validation results:', endpointErrors); expect(endpointErrors.length).toEqual(0); }); tap.test('PEPPOL Validator - invalid GLN endpoint', async () => { const validator = PeppolValidator.create(); const invoice: Partial = { metadata: { profileId: 'urn:fdc:peppol.eu:2017:poacc:billing:3.0', extensions: { sellerEndpointId: '0088:123456789012', // Invalid GLN (wrong check digit) } } }; const results = validator.validatePeppol(invoice as EInvoice); const endpointErrors = results.filter(r => r.ruleId === 'PEPPOL-T001'); expect(endpointErrors.length).toBeGreaterThan(0); expect(endpointErrors[0].message).toInclude('Invalid seller endpoint ID'); }); tap.test('PEPPOL Validator - invalid endpoint format', async () => { const validator = PeppolValidator.create(); const invoice: Partial = { metadata: { profileId: 'urn:fdc:peppol.eu:2017:poacc:billing:3.0', extensions: { sellerEndpointId: 'invalid-format', // No scheme } } }; const results = validator.validatePeppol(invoice as EInvoice); const endpointErrors = results.filter(r => r.ruleId === 'PEPPOL-T001'); expect(endpointErrors.length).toBeGreaterThan(0); expect(endpointErrors[0].severity).toEqual('error'); }); tap.test('PEPPOL Validator - document type validation', async () => { const validator = PeppolValidator.create(); const invoice: Partial = { metadata: { profileId: 'urn:fdc:peppol.eu:2017:poacc:billing:3.0', extensions: { documentTypeId: 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0::2.1' } } }; const results = validator.validatePeppol(invoice as EInvoice); const docTypeErrors = results.filter(r => r.ruleId === 'PEPPOL-T003'); expect(docTypeErrors.length).toEqual(0); }); tap.test('PEPPOL Validator - process ID validation', async () => { const validator = PeppolValidator.create(); const invoice: Partial = { metadata: { profileId: 'urn:fdc:peppol.eu:2017:poacc:billing:3.0', extensions: { processId: 'urn:fdc:peppol.eu:2017:poacc:billing:01:1.0' } } }; const results = validator.validatePeppol(invoice as EInvoice); const processErrors = results.filter(r => r.ruleId === 'PEPPOL-T004'); expect(processErrors.length).toEqual(0); }); tap.test('PEPPOL Validator - invalid process ID', async () => { const validator = PeppolValidator.create(); const invoice: Partial = { metadata: { profileId: 'urn:fdc:peppol.eu:2017:poacc:billing:3.0', extensions: { processId: 'invalid:process:id' } } }; const results = validator.validatePeppol(invoice as EInvoice); const processErrors = results.filter(r => r.ruleId === 'PEPPOL-T004'); expect(processErrors.length).toBeGreaterThan(0); expect(processErrors[0].severity).toEqual('warning'); }); tap.test('PEPPOL Validator - business rules', async () => { const validator = PeppolValidator.create(); const invoice: Partial = { metadata: { profileId: 'urn:fdc:peppol.eu:2017:poacc:billing:3.0', // Missing both buyer reference and purchase order reference }, from: { type: 'company', name: 'Test Company' // Missing email } }; const results = validator.validatePeppol(invoice as EInvoice); // Should have error for missing buyer reference const buyerRefErrors = results.filter(r => r.ruleId === 'PEPPOL-B-01'); expect(buyerRefErrors.length).toBeGreaterThan(0); // Should have warning for missing seller email const emailWarnings = results.filter(r => r.ruleId === 'PEPPOL-B-02'); expect(emailWarnings.length).toBeGreaterThan(0); }); tap.test('PEPPOL Validator - buyer reference present', async () => { const validator = PeppolValidator.create(); const invoice: Partial = { metadata: { profileId: 'urn:fdc:peppol.eu:2017:poacc:billing:3.0', buyerReference: 'REF-12345' } }; const results = validator.validatePeppol(invoice as EInvoice); const buyerRefErrors = results.filter(r => r.ruleId === 'PEPPOL-B-01'); expect(buyerRefErrors.length).toEqual(0); }); tap.test('PEPPOL Validator - purchase order reference present', async () => { const validator = PeppolValidator.create(); const invoice: Partial = { metadata: { profileId: 'urn:fdc:peppol.eu:2017:poacc:billing:3.0', extensions: { purchaseOrderReference: 'PO-2025-001' } } }; const results = validator.validatePeppol(invoice as EInvoice); const buyerRefErrors = results.filter(r => r.ruleId === 'PEPPOL-B-01'); expect(buyerRefErrors.length).toEqual(0); }); tap.test('PEPPOL Validator - payment means validation', async () => { const validator = PeppolValidator.create(); const invoice: Partial = { metadata: { profileId: 'urn:fdc:peppol.eu:2017:poacc:billing:3.0', extensions: { paymentMeans: { paymentMeansCode: '30' // Valid code for credit transfer } } } }; const results = validator.validatePeppol(invoice as EInvoice); const paymentErrors = results.filter(r => r.ruleId === 'PEPPOL-B-04'); expect(paymentErrors.length).toEqual(0); }); tap.test('PEPPOL Validator - invalid payment means', async () => { const validator = PeppolValidator.create(); const invoice: Partial = { metadata: { profileId: 'urn:fdc:peppol.eu:2017:poacc:billing:3.0', extensions: { paymentMeans: { paymentMeansCode: '999' // Invalid code } } } }; const results = validator.validatePeppol(invoice as EInvoice); const paymentErrors = results.filter(r => r.ruleId === 'PEPPOL-B-04'); expect(paymentErrors.length).toBeGreaterThan(0); expect(paymentErrors[0].severity).toEqual('error'); }); tap.test('PEPPOL Validator - non-PEPPOL invoice skips validation', async () => { const validator = PeppolValidator.create(); const invoice: Partial = { metadata: { profileId: 'urn:cen.eu:en16931:2017', // Not PEPPOL } }; const results = validator.validatePeppol(invoice as EInvoice); expect(results.length).toEqual(0); }); tap.test('PEPPOL Validator - scheme ID validation', async () => { const validator = PeppolValidator.create(); const invoice: Partial = { metadata: { profileId: 'urn:fdc:peppol.eu:2017:poacc:billing:3.0', extensions: { buyerPartyId: { schemeId: '0088', // Valid GLN scheme id: '1234567890128' } } }, from: { type: 'company', name: 'Test Company', registrationDetails: { partyIdentification: { schemeId: '9906', // Valid IT:VAT scheme id: 'IT12345678901' } } } as any }; const results = validator.validatePeppol(invoice as EInvoice); const schemeErrors = results.filter(r => r.ruleId === 'PEPPOL-T005' || r.ruleId === 'PEPPOL-T006' ); expect(schemeErrors.length).toEqual(0); }); tap.test('PEPPOL Validator - invalid scheme ID', async () => { const validator = PeppolValidator.create(); const invoice: Partial = { metadata: { profileId: 'urn:fdc:peppol.eu:2017:poacc:billing:3.0', extensions: { buyerPartyId: { schemeId: '9999', // Invalid scheme id: '12345' } } } }; const results = validator.validatePeppol(invoice as EInvoice); const schemeErrors = results.filter(r => r.ruleId === 'PEPPOL-T006'); expect(schemeErrors.length).toBeGreaterThan(0); expect(schemeErrors[0].severity).toEqual('warning'); }); tap.test('PEPPOL Validator - B2G detection', async () => { const validator = PeppolValidator.create(); const invoice: Partial = { metadata: { profileId: 'urn:fdc:peppol.eu:2017:poacc:billing:3.0', extensions: { buyerPartyId: { schemeId: '0204', // German government Leitweg-ID id: '991-12345-01' }, buyerCategory: 'government' } }, to: { type: 'company', name: 'Government Agency' } }; const results = validator.validatePeppol(invoice as EInvoice); // B2G should require endpoint IDs const endpointErrors = results.filter(r => r.ruleId === 'PEPPOL-T001' || r.ruleId === 'PEPPOL-T002' ); expect(endpointErrors.length).toBeGreaterThan(0); expect(endpointErrors[0].message).toInclude('mandatory for PEPPOL B2G'); }); export default tap.start();