299 lines
9.6 KiB
TypeScript
299 lines
9.6 KiB
TypeScript
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
|
import { EInvoice } from '../../../ts/index.js';
|
|
|
|
tap.test('ERR-02: Validation Errors - should handle validation errors gracefully', async () => {
|
|
console.log('Testing validation error handling...\n');
|
|
|
|
// Test 1: Invalid XML structure
|
|
const testInvalidXmlStructure = async () => {
|
|
console.log('Test 1 - Invalid XML structure:');
|
|
|
|
let errorCaught = false;
|
|
let errorMessage = '';
|
|
|
|
try {
|
|
const einvoice = new EInvoice();
|
|
// This should fail - invalid XML structure
|
|
await einvoice.fromXmlString('<?xml version="1.0"?><Invoice>broken xml');
|
|
} catch (error) {
|
|
errorCaught = true;
|
|
errorMessage = error.message || 'Unknown error';
|
|
console.log(` Error caught: ${errorMessage}`);
|
|
}
|
|
|
|
const gracefulHandling = errorCaught && !errorMessage.includes('FATAL');
|
|
console.log(` Error was caught: ${errorCaught}`);
|
|
console.log(` Graceful handling: ${gracefulHandling}`);
|
|
|
|
return { errorCaught, gracefulHandling, errorMessage };
|
|
};
|
|
|
|
// Test 2: Invalid e-invoice format
|
|
const testInvalidEInvoiceFormat = async () => {
|
|
console.log('\nTest 2 - Invalid e-invoice format:');
|
|
|
|
let errorCaught = false;
|
|
let errorMessage = '';
|
|
|
|
try {
|
|
const einvoice = new EInvoice();
|
|
// Valid XML but not a valid e-invoice format
|
|
await einvoice.fromXmlString(`<?xml version="1.0"?>
|
|
<SomeOtherDocument>
|
|
<Field>Value</Field>
|
|
</SomeOtherDocument>`);
|
|
} catch (error) {
|
|
errorCaught = true;
|
|
errorMessage = error.message || 'Unknown error';
|
|
console.log(` Error caught: ${errorMessage}`);
|
|
}
|
|
|
|
const gracefulHandling = errorCaught && !errorMessage.includes('FATAL');
|
|
console.log(` Error was caught: ${errorCaught}`);
|
|
console.log(` Graceful handling: ${gracefulHandling}`);
|
|
|
|
return { errorCaught, gracefulHandling, errorMessage };
|
|
};
|
|
|
|
// Test 3: Missing mandatory fields
|
|
const testMissingMandatoryFields = async () => {
|
|
console.log('\nTest 3 - Missing mandatory fields:');
|
|
|
|
let errorCaught = false;
|
|
let errorMessage = '';
|
|
|
|
try {
|
|
const einvoice = new EInvoice();
|
|
// Try to export without setting mandatory fields
|
|
await einvoice.toXmlString('ubl');
|
|
} catch (error) {
|
|
errorCaught = true;
|
|
errorMessage = error.message || 'Unknown error';
|
|
console.log(` Error caught: ${errorMessage}`);
|
|
}
|
|
|
|
const gracefulHandling = errorCaught && !errorMessage.includes('FATAL');
|
|
console.log(` Error was caught: ${errorCaught}`);
|
|
console.log(` Graceful handling: ${gracefulHandling}`);
|
|
|
|
return { errorCaught, gracefulHandling, errorMessage };
|
|
};
|
|
|
|
// Test 4: Invalid field values
|
|
const testInvalidFieldValues = async () => {
|
|
console.log('\nTest 4 - Invalid field values:');
|
|
|
|
let errorCaught = false;
|
|
let errorMessage = '';
|
|
|
|
try {
|
|
const einvoice = new EInvoice();
|
|
einvoice.issueDate = new Date(2024, 0, 1);
|
|
einvoice.invoiceId = 'TEST-001';
|
|
|
|
// Invalid country code (should be 2 characters)
|
|
einvoice.from = {
|
|
type: 'company',
|
|
name: 'Test Company',
|
|
description: 'Testing invalid values',
|
|
address: {
|
|
streetName: 'Test Street',
|
|
houseNumber: '1',
|
|
postalCode: '12345',
|
|
city: 'Test City',
|
|
country: 'INVALID_COUNTRY_CODE' // This should cause validation error
|
|
},
|
|
status: 'active',
|
|
foundedDate: { year: 2020, month: 1, day: 1 },
|
|
registrationDetails: {
|
|
vatId: 'DE123456789',
|
|
registrationId: 'HRB 12345',
|
|
registrationName: 'Commercial Register'
|
|
}
|
|
};
|
|
|
|
einvoice.to = {
|
|
type: 'person',
|
|
name: 'Test',
|
|
surname: 'Customer',
|
|
salutation: 'Mr' as const,
|
|
sex: 'male' as const,
|
|
title: 'Doctor' as const,
|
|
description: 'Test customer',
|
|
address: {
|
|
streetName: 'Customer Street',
|
|
houseNumber: '2',
|
|
postalCode: '54321',
|
|
city: 'Customer City',
|
|
country: 'DE'
|
|
}
|
|
};
|
|
|
|
einvoice.items = [{
|
|
position: 1,
|
|
name: 'Test Item',
|
|
articleNumber: 'TEST-001',
|
|
unitType: 'EA',
|
|
unitQuantity: 1,
|
|
unitNetPrice: 100,
|
|
vatPercentage: 19
|
|
}];
|
|
|
|
await einvoice.toXmlString('ubl');
|
|
} catch (error) {
|
|
errorCaught = true;
|
|
errorMessage = error.message || 'Unknown error';
|
|
console.log(` Error caught: ${errorMessage}`);
|
|
}
|
|
|
|
const gracefulHandling = errorCaught && !errorMessage.includes('FATAL');
|
|
console.log(` Error was caught: ${errorCaught}`);
|
|
console.log(` Graceful handling: ${gracefulHandling}`);
|
|
|
|
return { errorCaught, gracefulHandling, errorMessage };
|
|
};
|
|
|
|
// Test 5: Recovery after error
|
|
const testRecoveryAfterError = async () => {
|
|
console.log('\nTest 5 - Recovery after error:');
|
|
|
|
const einvoice = new EInvoice();
|
|
|
|
// First cause an error
|
|
try {
|
|
await einvoice.fromXmlString('<?xml version="1.0"?><InvalidXML>broken');
|
|
} catch (error) {
|
|
console.log(` Expected error occurred: ${error.message}`);
|
|
}
|
|
|
|
// Now try normal operation - should work
|
|
let canRecover = false;
|
|
try {
|
|
einvoice.issueDate = new Date(2024, 0, 1);
|
|
einvoice.invoiceId = 'RECOVERY-TEST';
|
|
|
|
einvoice.from = {
|
|
type: 'company',
|
|
name: 'Test Company',
|
|
description: 'Testing error recovery',
|
|
address: {
|
|
streetName: 'Test Street',
|
|
houseNumber: '1',
|
|
postalCode: '12345',
|
|
city: 'Test City',
|
|
country: 'DE'
|
|
},
|
|
status: 'active',
|
|
foundedDate: { year: 2020, month: 1, day: 1 },
|
|
registrationDetails: {
|
|
vatId: 'DE123456789',
|
|
registrationId: 'HRB 12345',
|
|
registrationName: 'Commercial Register'
|
|
}
|
|
};
|
|
|
|
einvoice.to = {
|
|
type: 'person',
|
|
name: 'Test',
|
|
surname: 'Customer',
|
|
salutation: 'Mr' as const,
|
|
sex: 'male' as const,
|
|
title: 'Doctor' as const,
|
|
description: 'Test customer',
|
|
address: {
|
|
streetName: 'Customer Street',
|
|
houseNumber: '2',
|
|
postalCode: '54321',
|
|
city: 'Customer City',
|
|
country: 'DE'
|
|
}
|
|
};
|
|
|
|
einvoice.items = [{
|
|
position: 1,
|
|
name: 'Test Product',
|
|
articleNumber: 'TEST-001',
|
|
unitType: 'EA',
|
|
unitQuantity: 1,
|
|
unitNetPrice: 100,
|
|
vatPercentage: 19
|
|
}];
|
|
|
|
// Try to export after error
|
|
const xml = await einvoice.toXmlString('ubl');
|
|
canRecover = xml.includes('RECOVERY-TEST');
|
|
console.log(` Recovery successful: ${canRecover}`);
|
|
} catch (error) {
|
|
console.log(` Recovery failed: ${error.message}`);
|
|
canRecover = false;
|
|
}
|
|
|
|
return { canRecover };
|
|
};
|
|
|
|
// Test 6: Multiple error scenarios
|
|
const testMultipleErrorScenarios = async () => {
|
|
console.log('\nTest 6 - Multiple error scenarios:');
|
|
|
|
const errorScenarios = [
|
|
{
|
|
name: 'Empty XML',
|
|
xml: ''
|
|
},
|
|
{
|
|
name: 'Malformed XML',
|
|
xml: '<?xml version="1.0"?><root><unclosed>'
|
|
},
|
|
{
|
|
name: 'Wrong namespace',
|
|
xml: '<?xml version="1.0"?><WrongNamespace xmlns="http://wrong.namespace"><Field>Value</Field></WrongNamespace>'
|
|
}
|
|
];
|
|
|
|
let errorsHandled = 0;
|
|
|
|
for (const scenario of errorScenarios) {
|
|
try {
|
|
const einvoice = new EInvoice();
|
|
await einvoice.fromXmlString(scenario.xml);
|
|
console.log(` ${scenario.name}: No error thrown (unexpected)`);
|
|
} catch (error) {
|
|
console.log(` ${scenario.name}: Error caught gracefully`);
|
|
errorsHandled++;
|
|
}
|
|
}
|
|
|
|
const allHandled = errorsHandled === errorScenarios.length;
|
|
console.log(` Errors handled: ${errorsHandled}/${errorScenarios.length}`);
|
|
|
|
return { allHandled, errorsHandled };
|
|
};
|
|
|
|
// Run all tests
|
|
const result1 = await testInvalidXmlStructure();
|
|
const result2 = await testInvalidEInvoiceFormat();
|
|
const result3 = await testMissingMandatoryFields();
|
|
const result4 = await testInvalidFieldValues();
|
|
const result5 = await testRecoveryAfterError();
|
|
const result6 = await testMultipleErrorScenarios();
|
|
|
|
console.log('\n=== Validation Errors Error Handling Summary ===');
|
|
console.log(`Invalid XML structure: ${result1.errorCaught ? 'Handled' : 'Not handled'}`);
|
|
console.log(`Invalid e-invoice format: ${result2.errorCaught ? 'Handled' : 'Not handled'}`);
|
|
console.log(`Missing mandatory fields: ${result3.errorCaught ? 'Handled' : 'Not handled'}`);
|
|
console.log(`Invalid field values: ${result4.errorCaught ? 'Handled' : 'Not handled'}`);
|
|
console.log(`Recovery after error: ${result5.canRecover ? 'Successful' : 'Failed'}`);
|
|
console.log(`Multiple error scenarios: ${result6.allHandled ? 'All handled' : 'Some failed'}`);
|
|
|
|
// Test passes if core validation works (EN16931 validation and format detection)
|
|
const en16931ValidationWorks = result3.errorCaught; // Missing mandatory fields
|
|
const formatValidationWorks = result2.errorCaught; // Invalid e-invoice format
|
|
const multipleErrorHandling = result6.allHandled; // Multiple error scenarios
|
|
|
|
// Core validation should work for EN16931 compliance
|
|
expect(en16931ValidationWorks).toBeTrue(); // Must catch missing mandatory fields
|
|
expect(formatValidationWorks).toBeTrue(); // Must catch wrong document format
|
|
expect(multipleErrorHandling).toBeTrue(); // Must handle malformed XML gracefully
|
|
});
|
|
|
|
tap.start(); |