- Update test-utils import path and refactor to helpers/utils.ts - Migrate all CorpusLoader usage from getFiles() to loadCategory() API - Add new EN16931 UBL validator with comprehensive validation rules - Add new XRechnung validator extending EN16931 with German requirements - Update validator factory to support new validators - Fix format detector for better XRechnung and EN16931 detection - Update all test files to use proper import paths - Improve error handling in security tests - Fix validation tests to use realistic thresholds - Add proper namespace handling in corpus validation tests - Update format detection tests for improved accuracy - Fix test imports from classes.xinvoice.ts to index.js All test suites now properly aligned with the updated APIs and realistic performance expectations.
222 lines
7.1 KiB
TypeScript
222 lines
7.1 KiB
TypeScript
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
|
import { EInvoice } from '../ts/einvoice.js';
|
|
import { ValidationLevel } from '../ts/interfaces/common.js';
|
|
import type { ExportFormat } from '../ts/interfaces/common.js';
|
|
|
|
// Basic EInvoice tests
|
|
tap.test('EInvoice should have the correct default properties', async () => {
|
|
const einvoice = new EInvoice();
|
|
|
|
expect(einvoice.type).toEqual('accounting-doc');
|
|
expect(einvoice.accountingDocType).toEqual('invoice');
|
|
expect(einvoice.status).toEqual('issued');
|
|
expect(einvoice.from).toBeTruthy();
|
|
expect(einvoice.to).toBeTruthy();
|
|
expect(einvoice.items).toBeArray();
|
|
expect(einvoice.currency).toEqual('EUR');
|
|
});
|
|
|
|
// Test XML export functionality
|
|
tap.test('EInvoice should export XML in the correct format', async () => {
|
|
const einvoice = new EInvoice();
|
|
einvoice.id = 'TEST-XML-EXPORT';
|
|
einvoice.accountingDocId = 'TEST-XML-EXPORT';
|
|
einvoice.from.name = 'Test Seller';
|
|
einvoice.from.address = {
|
|
streetName: 'Seller Street',
|
|
houseNumber: '1',
|
|
city: 'Berlin',
|
|
postalCode: '10115',
|
|
country: 'Germany'
|
|
};
|
|
einvoice.to.name = 'Test Buyer';
|
|
einvoice.to.address = {
|
|
streetName: 'Buyer Street',
|
|
houseNumber: '2',
|
|
city: 'Munich',
|
|
postalCode: '80331',
|
|
country: 'Germany'
|
|
};
|
|
|
|
// Add an item
|
|
einvoice.items.push({
|
|
position: 1,
|
|
name: 'Test Product',
|
|
articleNumber: 'TP-001',
|
|
unitType: 'EA',
|
|
unitQuantity: 2,
|
|
unitNetPrice: 100,
|
|
vatPercentage: 19
|
|
});
|
|
|
|
// Export as Factur-X
|
|
const xml = await einvoice.exportXml('facturx');
|
|
|
|
// Check that the XML contains the expected elements
|
|
expect(xml).toInclude('CrossIndustryInvoice');
|
|
expect(xml).toInclude('TEST-XML-EXPORT');
|
|
expect(xml).toInclude('Test Seller');
|
|
expect(xml).toInclude('Test Buyer');
|
|
expect(xml).toInclude('Test Product');
|
|
});
|
|
|
|
// Test XML loading functionality
|
|
tap.test('EInvoice should load XML correctly', async () => {
|
|
// Create a sample XML string
|
|
const sampleXml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
<rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
|
|
xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
|
|
xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
|
|
<rsm:ExchangedDocumentContext>
|
|
<ram:GuidelineSpecifiedDocumentContextParameter>
|
|
<ram:ID>urn:cen.eu:en16931:2017</ram:ID>
|
|
</ram:GuidelineSpecifiedDocumentContextParameter>
|
|
</rsm:ExchangedDocumentContext>
|
|
<rsm:ExchangedDocument>
|
|
<ram:ID>TEST-XML-LOAD</ram:ID>
|
|
<ram:TypeCode>380</ram:TypeCode>
|
|
<ram:IssueDateTime>
|
|
<udt:DateTimeString format="102">20230101</udt:DateTimeString>
|
|
</ram:IssueDateTime>
|
|
</rsm:ExchangedDocument>
|
|
<rsm:SupplyChainTradeTransaction>
|
|
<ram:ApplicableHeaderTradeAgreement>
|
|
<ram:SellerTradeParty>
|
|
<ram:Name>XML Seller</ram:Name>
|
|
<ram:PostalTradeAddress>
|
|
<ram:LineOne>Seller Street</ram:LineOne>
|
|
<ram:LineTwo>123</ram:LineTwo>
|
|
<ram:PostcodeCode>12345</ram:PostcodeCode>
|
|
<ram:CityName>Seller City</ram:CityName>
|
|
<ram:CountryID>DE</ram:CountryID>
|
|
</ram:PostalTradeAddress>
|
|
</ram:SellerTradeParty>
|
|
<ram:BuyerTradeParty>
|
|
<ram:Name>XML Buyer</ram:Name>
|
|
<ram:PostalTradeAddress>
|
|
<ram:LineOne>Buyer Street</ram:LineOne>
|
|
<ram:LineTwo>456</ram:LineTwo>
|
|
<ram:PostcodeCode>54321</ram:PostcodeCode>
|
|
<ram:CityName>Buyer City</ram:CityName>
|
|
<ram:CountryID>DE</ram:CountryID>
|
|
</ram:PostalTradeAddress>
|
|
</ram:BuyerTradeParty>
|
|
</ram:ApplicableHeaderTradeAgreement>
|
|
<ram:ApplicableHeaderTradeSettlement>
|
|
<ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
|
|
</ram:ApplicableHeaderTradeSettlement>
|
|
</rsm:SupplyChainTradeTransaction>
|
|
</rsm:CrossIndustryInvoice>`;
|
|
|
|
// Create EInvoice from XML
|
|
const einvoice = await EInvoice.fromXml(sampleXml);
|
|
|
|
// Check that the EInvoice instance has the expected properties
|
|
expect(einvoice.accountingDocId).toEqual('TEST-XML-LOAD');
|
|
expect(einvoice.from.name).toEqual('XML Seller');
|
|
expect(einvoice.to.name).toEqual('XML Buyer');
|
|
expect(einvoice.currency).toEqual('EUR');
|
|
});
|
|
|
|
// Test circular encoding/decoding
|
|
tap.test('EInvoice should maintain data integrity through export/import cycle', async () => {
|
|
// Create a sample invoice
|
|
const originalInvoice = new EInvoice();
|
|
originalInvoice.id = 'TEST-CIRCULAR';
|
|
originalInvoice.accountingDocId = 'TEST-CIRCULAR';
|
|
originalInvoice.from.name = 'Circular Seller';
|
|
originalInvoice.from.address = {
|
|
streetName: 'Circular Street',
|
|
houseNumber: '10',
|
|
city: 'Hamburg',
|
|
postalCode: '20095',
|
|
country: 'Germany'
|
|
};
|
|
originalInvoice.to.name = 'Circular Buyer';
|
|
originalInvoice.to.address = {
|
|
streetName: 'Buyer Avenue',
|
|
houseNumber: '20',
|
|
city: 'Frankfurt',
|
|
postalCode: '60311',
|
|
country: 'Germany'
|
|
};
|
|
|
|
// Add an item
|
|
originalInvoice.items.push({
|
|
position: 1,
|
|
name: 'Circular Product',
|
|
articleNumber: 'CP-001',
|
|
unitType: 'EA',
|
|
unitQuantity: 3,
|
|
unitNetPrice: 150,
|
|
vatPercentage: 19
|
|
});
|
|
|
|
// Export as Factur-X
|
|
const xml = await originalInvoice.exportXml('facturx');
|
|
|
|
// Create a new EInvoice from the XML
|
|
const importedInvoice = await EInvoice.fromXml(xml);
|
|
|
|
// Check that key properties match
|
|
expect(importedInvoice.accountingDocId).toEqual(originalInvoice.accountingDocId);
|
|
expect(importedInvoice.from.name).toEqual(originalInvoice.from.name);
|
|
expect(importedInvoice.to.name).toEqual(originalInvoice.to.name);
|
|
|
|
// Check that items match
|
|
expect(importedInvoice.items).toHaveLength(1);
|
|
expect(importedInvoice.items[0].name).toEqual('Circular Product');
|
|
expect(importedInvoice.items[0].unitQuantity).toEqual(3);
|
|
expect(importedInvoice.items[0].unitNetPrice).toEqual(150);
|
|
});
|
|
|
|
// Test validation
|
|
tap.test('EInvoice should validate XML correctly', async () => {
|
|
const einvoice = new EInvoice();
|
|
einvoice.id = 'TEST-VALIDATION';
|
|
einvoice.accountingDocId = 'TEST-VALIDATION';
|
|
einvoice.from.name = 'Validation Seller';
|
|
einvoice.from.address = {
|
|
streetName: 'Validation Street',
|
|
houseNumber: '5',
|
|
city: 'Stuttgart',
|
|
postalCode: '70173',
|
|
country: 'Germany'
|
|
};
|
|
einvoice.to.name = 'Validation Buyer';
|
|
einvoice.to.address = {
|
|
streetName: 'Test Road',
|
|
houseNumber: '15',
|
|
city: 'Cologne',
|
|
postalCode: '50667',
|
|
country: 'Germany'
|
|
};
|
|
|
|
// Add an item to pass BR-16 validation
|
|
einvoice.items.push({
|
|
position: 1,
|
|
name: 'Validation Product',
|
|
articleNumber: 'VP-001',
|
|
unitType: 'EA',
|
|
unitQuantity: 1,
|
|
unitNetPrice: 50,
|
|
vatPercentage: 19
|
|
});
|
|
|
|
// Export as Factur-X
|
|
const xml = await einvoice.exportXml('facturx');
|
|
|
|
// Create a new invoice from the XML to properly set format
|
|
const einvoiceForValidation = await EInvoice.fromXml(xml);
|
|
|
|
// Validate the XML
|
|
const result = await einvoiceForValidation.validate(ValidationLevel.SYNTAX);
|
|
|
|
// Check that validation passed
|
|
expect(result.valid).toBeTrue();
|
|
expect(result.errors).toHaveLength(0);
|
|
});
|
|
|
|
// Run the tests
|
|
tap.start();
|