598 lines
21 KiB
TypeScript
598 lines
21 KiB
TypeScript
|
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||
|
import * as plugins from '../../../ts/plugins.ts';
|
||
|
import { EInvoice } from '../../../ts/classes.xinvoice.ts';
|
||
|
import { CorpusLoader } from '../../helpers/corpus.loader.ts';
|
||
|
import { PerformanceTracker } from '../../helpers/performance.tracker.ts';
|
||
|
|
||
|
const testTimeout = 300000; // 5 minutes timeout for corpus processing
|
||
|
|
||
|
// VAL-13: Validation Error Reporting
|
||
|
// Tests validation error reporting functionality including error messages,
|
||
|
// error codes, error context, and error aggregation
|
||
|
|
||
|
tap.test('VAL-13: Error Reporting - Error Message Quality', async (tools) => {
|
||
|
const startTime = Date.now();
|
||
|
|
||
|
// Test validation errors with clear, actionable messages
|
||
|
const errorTestCases = [
|
||
|
{
|
||
|
name: 'Missing Required Field',
|
||
|
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
||
|
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
||
|
<IssueDate>2024-01-01</IssueDate>
|
||
|
<InvoiceTypeCode>380</InvoiceTypeCode>
|
||
|
</Invoice>`,
|
||
|
expectedErrorType: 'missing-required-field',
|
||
|
expectedFieldName: 'ID'
|
||
|
},
|
||
|
{
|
||
|
name: 'Invalid Date Format',
|
||
|
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
||
|
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
||
|
<ID>TEST-001</ID>
|
||
|
<IssueDate>31-01-2024</IssueDate>
|
||
|
<InvoiceTypeCode>380</InvoiceTypeCode>
|
||
|
</Invoice>`,
|
||
|
expectedErrorType: 'invalid-date-format',
|
||
|
expectedFieldName: 'IssueDate'
|
||
|
},
|
||
|
{
|
||
|
name: 'Invalid Currency Code',
|
||
|
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
||
|
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
||
|
<ID>TEST-001</ID>
|
||
|
<IssueDate>2024-01-01</IssueDate>
|
||
|
<InvoiceTypeCode>380</InvoiceTypeCode>
|
||
|
<DocumentCurrencyCode>INVALID</DocumentCurrencyCode>
|
||
|
</Invoice>`,
|
||
|
expectedErrorType: 'invalid-currency-code',
|
||
|
expectedFieldName: 'DocumentCurrencyCode'
|
||
|
},
|
||
|
{
|
||
|
name: 'Invalid Numeric Value',
|
||
|
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
||
|
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
||
|
<ID>TEST-001</ID>
|
||
|
<IssueDate>2024-01-01</IssueDate>
|
||
|
<InvoiceTypeCode>380</InvoiceTypeCode>
|
||
|
<LegalMonetaryTotal>
|
||
|
<PayableAmount currencyID="EUR">NOT_A_NUMBER</PayableAmount>
|
||
|
</LegalMonetaryTotal>
|
||
|
</Invoice>`,
|
||
|
expectedErrorType: 'invalid-numeric-value',
|
||
|
expectedFieldName: 'PayableAmount'
|
||
|
}
|
||
|
];
|
||
|
|
||
|
for (const testCase of errorTestCases) {
|
||
|
try {
|
||
|
const invoice = new EInvoice();
|
||
|
const parseResult = await invoice.fromXmlString(testCase.xml);
|
||
|
|
||
|
let validationResult;
|
||
|
if (parseResult) {
|
||
|
validationResult = await invoice.validate();
|
||
|
}
|
||
|
|
||
|
// Expect validation to fail
|
||
|
if (validationResult && validationResult.valid) {
|
||
|
tools.log(`⚠ Expected validation to fail for ${testCase.name} but it passed`);
|
||
|
} else {
|
||
|
tools.log(`✓ ${testCase.name}: Validation correctly failed`);
|
||
|
|
||
|
// Check error quality if errors are available
|
||
|
if (validationResult?.errors && validationResult.errors.length > 0) {
|
||
|
const errors = validationResult.errors;
|
||
|
|
||
|
// Check for descriptive error messages
|
||
|
for (const error of errors) {
|
||
|
expect(error.message).toBeTruthy();
|
||
|
expect(error.message.length).toBeGreaterThan(10); // Should be descriptive
|
||
|
|
||
|
tools.log(` Error: ${error.message}`);
|
||
|
|
||
|
// Check if error message contains relevant context
|
||
|
if (testCase.expectedFieldName) {
|
||
|
const containsFieldName = error.message.toLowerCase().includes(testCase.expectedFieldName.toLowerCase()) ||
|
||
|
error.path?.includes(testCase.expectedFieldName);
|
||
|
if (containsFieldName) {
|
||
|
tools.log(` ✓ Error message includes field name: ${testCase.expectedFieldName}`);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} catch (parseError) {
|
||
|
// Parse errors are also valid for testing error reporting
|
||
|
tools.log(`✓ ${testCase.name}: Parse error caught: ${parseError.message}`);
|
||
|
expect(parseError.message).toBeTruthy();
|
||
|
expect(parseError.message.length).toBeGreaterThan(5);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const duration = Date.now() - startTime;
|
||
|
PerformanceTracker.recordMetric('error-reporting-message-quality', duration);
|
||
|
});
|
||
|
|
||
|
tap.test('VAL-13: Error Reporting - Error Code Classification', async (tools) => {
|
||
|
const startTime = Date.now();
|
||
|
|
||
|
// Test error classification and categorization
|
||
|
const errorClassificationTests = [
|
||
|
{
|
||
|
name: 'Syntax Error',
|
||
|
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
||
|
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
||
|
<ID>TEST-001</ID>
|
||
|
<IssueDate>2024-01-01</IssueDate>
|
||
|
<InvoiceTypeCode>380</InvoiceTypeCode>
|
||
|
<UnclosedTag>
|
||
|
</Invoice>`,
|
||
|
expectedCategory: 'syntax',
|
||
|
expectedSeverity: 'error'
|
||
|
},
|
||
|
{
|
||
|
name: 'Business Rule Violation',
|
||
|
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
||
|
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
||
|
<ID>TEST-001</ID>
|
||
|
<IssueDate>2024-01-01</IssueDate>
|
||
|
<InvoiceTypeCode>380</InvoiceTypeCode>
|
||
|
<TaxTotal>
|
||
|
<TaxAmount currencyID="EUR">20.00</TaxAmount>
|
||
|
<TaxSubtotal>
|
||
|
<TaxableAmount currencyID="EUR">100.00</TaxableAmount>
|
||
|
<TaxAmount currencyID="EUR">19.00</TaxAmount>
|
||
|
<TaxCategory><Percent>19.00</Percent></TaxCategory>
|
||
|
</TaxSubtotal>
|
||
|
</TaxTotal>
|
||
|
</Invoice>`,
|
||
|
expectedCategory: 'business-rule',
|
||
|
expectedSeverity: 'error'
|
||
|
},
|
||
|
{
|
||
|
name: 'Format Warning',
|
||
|
xml: `<?xml version="1.0" encoding="UTF-8"?>
|
||
|
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
||
|
<ID>TEST-001</ID>
|
||
|
<IssueDate>2024-01-01</IssueDate>
|
||
|
<InvoiceTypeCode>380</InvoiceTypeCode>
|
||
|
<Note>This is a very long note that exceeds recommended character limits for invoice notes and should trigger a warning about readability and processing efficiency in some systems</Note>
|
||
|
</Invoice>`,
|
||
|
expectedCategory: 'format',
|
||
|
expectedSeverity: 'warning'
|
||
|
}
|
||
|
];
|
||
|
|
||
|
for (const test of errorClassificationTests) {
|
||
|
try {
|
||
|
const invoice = new EInvoice();
|
||
|
let parseResult;
|
||
|
|
||
|
try {
|
||
|
parseResult = await invoice.fromXmlString(test.xml);
|
||
|
} catch (parseError) {
|
||
|
// Handle syntax errors at parse level
|
||
|
if (test.expectedCategory === 'syntax') {
|
||
|
tools.log(`✓ ${test.name}: Syntax error correctly detected at parse time`);
|
||
|
expect(parseError.message).toBeTruthy();
|
||
|
continue;
|
||
|
} else {
|
||
|
throw parseError;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (parseResult) {
|
||
|
const validationResult = await invoice.validate();
|
||
|
|
||
|
if (validationResult && !validationResult.valid && validationResult.errors) {
|
||
|
tools.log(`✓ ${test.name}: Validation errors detected`);
|
||
|
|
||
|
for (const error of validationResult.errors) {
|
||
|
tools.log(` Error: ${error.message}`);
|
||
|
|
||
|
// Check error classification properties
|
||
|
if (error.code) {
|
||
|
tools.log(` Code: ${error.code}`);
|
||
|
}
|
||
|
|
||
|
if (error.severity) {
|
||
|
tools.log(` Severity: ${error.severity}`);
|
||
|
expect(['error', 'warning', 'info']).toContain(error.severity);
|
||
|
}
|
||
|
|
||
|
if (error.category) {
|
||
|
tools.log(` Category: ${error.category}`);
|
||
|
}
|
||
|
|
||
|
if (error.path) {
|
||
|
tools.log(` Path: ${error.path}`);
|
||
|
}
|
||
|
}
|
||
|
} else if (test.expectedCategory !== 'format') {
|
||
|
tools.log(`⚠ Expected validation errors for ${test.name} but validation passed`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} catch (error) {
|
||
|
tools.log(`Error processing ${test.name}: ${error.message}`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const duration = Date.now() - startTime;
|
||
|
PerformanceTracker.recordMetric('error-reporting-classification', duration);
|
||
|
});
|
||
|
|
||
|
tap.test('VAL-13: Error Reporting - Error Context and Location', async (tools) => {
|
||
|
const startTime = Date.now();
|
||
|
|
||
|
// Test error context information (line numbers, XPath, etc.)
|
||
|
const contextTestXml = `<?xml version="1.0" encoding="UTF-8"?>
|
||
|
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
||
|
<ID>CONTEXT-TEST-001</ID>
|
||
|
<IssueDate>2024-01-01</IssueDate>
|
||
|
<InvoiceTypeCode>380</InvoiceTypeCode>
|
||
|
<DocumentCurrencyCode>EUR</DocumentCurrencyCode>
|
||
|
<AccountingSupplierParty>
|
||
|
<Party>
|
||
|
<PartyName>
|
||
|
<Name></Name>
|
||
|
</PartyName>
|
||
|
<PostalAddress>
|
||
|
<StreetName>Test Street</StreetName>
|
||
|
<CityName></CityName>
|
||
|
<PostalZone>12345</PostalZone>
|
||
|
<Country>
|
||
|
<IdentificationCode>DE</IdentificationCode>
|
||
|
</Country>
|
||
|
</PostalAddress>
|
||
|
</Party>
|
||
|
</AccountingSupplierParty>
|
||
|
<LegalMonetaryTotal>
|
||
|
<PayableAmount currencyID="EUR">INVALID_AMOUNT</PayableAmount>
|
||
|
</LegalMonetaryTotal>
|
||
|
</Invoice>`;
|
||
|
|
||
|
try {
|
||
|
const invoice = new EInvoice();
|
||
|
const parseResult = await invoice.fromXmlString(contextTestXml);
|
||
|
|
||
|
if (parseResult) {
|
||
|
const validationResult = await invoice.validate();
|
||
|
|
||
|
if (validationResult && !validationResult.valid && validationResult.errors) {
|
||
|
tools.log(`Error context testing - found ${validationResult.errors.length} errors:`);
|
||
|
|
||
|
for (const error of validationResult.errors) {
|
||
|
tools.log(`\nError: ${error.message}`);
|
||
|
|
||
|
// Check for location information
|
||
|
if (error.path) {
|
||
|
tools.log(` XPath/Path: ${error.path}`);
|
||
|
expect(error.path).toBeTruthy();
|
||
|
}
|
||
|
|
||
|
if (error.lineNumber) {
|
||
|
tools.log(` Line: ${error.lineNumber}`);
|
||
|
expect(error.lineNumber).toBeGreaterThan(0);
|
||
|
}
|
||
|
|
||
|
if (error.columnNumber) {
|
||
|
tools.log(` Column: ${error.columnNumber}`);
|
||
|
expect(error.columnNumber).toBeGreaterThan(0);
|
||
|
}
|
||
|
|
||
|
// Check for additional context
|
||
|
if (error.context) {
|
||
|
tools.log(` Context: ${JSON.stringify(error.context)}`);
|
||
|
}
|
||
|
|
||
|
if (error.element) {
|
||
|
tools.log(` Element: ${error.element}`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
tools.log(`✓ Error context information available`);
|
||
|
} else {
|
||
|
tools.log(`⚠ Expected validation errors but validation passed`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} catch (error) {
|
||
|
tools.log(`Context test failed: ${error.message}`);
|
||
|
}
|
||
|
|
||
|
const duration = Date.now() - startTime;
|
||
|
PerformanceTracker.recordMetric('error-reporting-context', duration);
|
||
|
});
|
||
|
|
||
|
tap.test('VAL-13: Error Reporting - Error Aggregation and Summarization', async (tools) => {
|
||
|
const startTime = Date.now();
|
||
|
|
||
|
// Test error aggregation for multiple issues
|
||
|
const multiErrorXml = `<?xml version="1.0" encoding="UTF-8"?>
|
||
|
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
||
|
<ID></ID>
|
||
|
<IssueDate>invalid-date</IssueDate>
|
||
|
<InvoiceTypeCode>999</InvoiceTypeCode>
|
||
|
<DocumentCurrencyCode>INVALID</DocumentCurrencyCode>
|
||
|
<AccountingSupplierParty>
|
||
|
<Party>
|
||
|
<PartyName>
|
||
|
<Name></Name>
|
||
|
</PartyName>
|
||
|
</Party>
|
||
|
</AccountingSupplierParty>
|
||
|
<AccountingCustomerParty>
|
||
|
<Party>
|
||
|
<PartyName>
|
||
|
<Name></Name>
|
||
|
</PartyName>
|
||
|
</Party>
|
||
|
</AccountingCustomerParty>
|
||
|
<InvoiceLine>
|
||
|
<ID></ID>
|
||
|
<InvoicedQuantity unitCode="">0</InvoicedQuantity>
|
||
|
<LineExtensionAmount currencyID="EUR">invalid-amount</LineExtensionAmount>
|
||
|
</InvoiceLine>
|
||
|
<LegalMonetaryTotal>
|
||
|
<PayableAmount currencyID="EUR">another-invalid-amount</PayableAmount>
|
||
|
</LegalMonetaryTotal>
|
||
|
</Invoice>`;
|
||
|
|
||
|
try {
|
||
|
const invoice = new EInvoice();
|
||
|
const parseResult = await invoice.fromXmlString(multiErrorXml);
|
||
|
|
||
|
if (parseResult) {
|
||
|
const validationResult = await invoice.validate();
|
||
|
|
||
|
if (validationResult && !validationResult.valid && validationResult.errors) {
|
||
|
const errors = validationResult.errors;
|
||
|
tools.log(`Error aggregation test - found ${errors.length} errors:`);
|
||
|
|
||
|
// Group errors by category
|
||
|
const errorsByCategory = {};
|
||
|
const errorsBySeverity = {};
|
||
|
|
||
|
for (const error of errors) {
|
||
|
// Count by category
|
||
|
const category = error.category || 'unknown';
|
||
|
errorsByCategory[category] = (errorsByCategory[category] || 0) + 1;
|
||
|
|
||
|
// Count by severity
|
||
|
const severity = error.severity || 'error';
|
||
|
errorsBySeverity[severity] = (errorsBySeverity[severity] || 0) + 1;
|
||
|
|
||
|
tools.log(` - ${error.message}`);
|
||
|
if (error.path) {
|
||
|
tools.log(` Path: ${error.path}`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Display error summary
|
||
|
tools.log(`\nError Summary:`);
|
||
|
tools.log(` Total errors: ${errors.length}`);
|
||
|
|
||
|
tools.log(` By category:`);
|
||
|
for (const [category, count] of Object.entries(errorsByCategory)) {
|
||
|
tools.log(` ${category}: ${count}`);
|
||
|
}
|
||
|
|
||
|
tools.log(` By severity:`);
|
||
|
for (const [severity, count] of Object.entries(errorsBySeverity)) {
|
||
|
tools.log(` ${severity}: ${count}`);
|
||
|
}
|
||
|
|
||
|
// Expect multiple errors to be found
|
||
|
expect(errors.length).toBeGreaterThan(3);
|
||
|
|
||
|
// Check that errors are properly structured
|
||
|
for (const error of errors) {
|
||
|
expect(error.message).toBeTruthy();
|
||
|
expect(typeof error.message).toBe('string');
|
||
|
}
|
||
|
|
||
|
tools.log(`✓ Error aggregation and categorization working`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} catch (error) {
|
||
|
tools.log(`Error aggregation test failed: ${error.message}`);
|
||
|
}
|
||
|
|
||
|
const duration = Date.now() - startTime;
|
||
|
PerformanceTracker.recordMetric('error-reporting-aggregation', duration);
|
||
|
});
|
||
|
|
||
|
tap.test('VAL-13: Error Reporting - Localized Error Messages', async (tools) => {
|
||
|
const startTime = Date.now();
|
||
|
|
||
|
// Test error message localization (if supported)
|
||
|
const localizationTestXml = `<?xml version="1.0" encoding="UTF-8"?>
|
||
|
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
||
|
<ID>LOC-TEST-001</ID>
|
||
|
<IssueDate>2024-01-01</IssueDate>
|
||
|
<InvoiceTypeCode>380</InvoiceTypeCode>
|
||
|
<DocumentCurrencyCode>INVALID</DocumentCurrencyCode>
|
||
|
</Invoice>`;
|
||
|
|
||
|
const locales = ['en', 'de', 'fr'];
|
||
|
|
||
|
for (const locale of locales) {
|
||
|
try {
|
||
|
const invoice = new EInvoice();
|
||
|
|
||
|
// Set locale if the API supports it
|
||
|
if (typeof invoice.setLocale === 'function') {
|
||
|
invoice.setLocale(locale);
|
||
|
tools.log(`Testing error messages in locale: ${locale}`);
|
||
|
} else {
|
||
|
tools.log(`Locale setting not supported, testing default messages`);
|
||
|
}
|
||
|
|
||
|
const parseResult = await invoice.fromXmlString(localizationTestXml);
|
||
|
|
||
|
if (parseResult) {
|
||
|
const validationResult = await invoice.validate();
|
||
|
|
||
|
if (validationResult && !validationResult.valid && validationResult.errors) {
|
||
|
for (const error of validationResult.errors) {
|
||
|
tools.log(` ${locale}: ${error.message}`);
|
||
|
|
||
|
// Check that error message is not empty and reasonably descriptive
|
||
|
expect(error.message).toBeTruthy();
|
||
|
expect(error.message.length).toBeGreaterThan(5);
|
||
|
|
||
|
// Check for locale-specific characteristics (if implemented)
|
||
|
if (locale === 'de' && error.message.includes('ungültig')) {
|
||
|
tools.log(` ✓ German localization detected`);
|
||
|
} else if (locale === 'fr' && error.message.includes('invalide')) {
|
||
|
tools.log(` ✓ French localization detected`);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} catch (error) {
|
||
|
tools.log(`Localization test failed for ${locale}: ${error.message}`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const duration = Date.now() - startTime;
|
||
|
PerformanceTracker.recordMetric('error-reporting-localization', duration);
|
||
|
});
|
||
|
|
||
|
tap.test('VAL-13: Error Reporting - Corpus Error Analysis', { timeout: testTimeout }, async (tools) => {
|
||
|
const startTime = Date.now();
|
||
|
|
||
|
const errorStatistics = {
|
||
|
totalFiles: 0,
|
||
|
filesWithErrors: 0,
|
||
|
totalErrors: 0,
|
||
|
errorsByCategory: {},
|
||
|
errorsBySeverity: {},
|
||
|
mostCommonErrors: {}
|
||
|
};
|
||
|
|
||
|
try {
|
||
|
// Analyze errors across corpus files
|
||
|
const categories = ['UBL_XML_RECHNUNG', 'CII_XML_RECHNUNG'];
|
||
|
|
||
|
for (const category of categories) {
|
||
|
try {
|
||
|
const files = await CorpusLoader.getFiles(category);
|
||
|
|
||
|
for (const filePath of files.slice(0, 8)) { // Process first 8 files per category
|
||
|
errorStatistics.totalFiles++;
|
||
|
|
||
|
try {
|
||
|
const invoice = new EInvoice();
|
||
|
const parseResult = await invoice.fromFile(filePath);
|
||
|
|
||
|
if (parseResult) {
|
||
|
const validationResult = await invoice.validate();
|
||
|
|
||
|
if (validationResult && !validationResult.valid && validationResult.errors) {
|
||
|
errorStatistics.filesWithErrors++;
|
||
|
errorStatistics.totalErrors += validationResult.errors.length;
|
||
|
|
||
|
for (const error of validationResult.errors) {
|
||
|
// Count by category
|
||
|
const category = error.category || 'unknown';
|
||
|
errorStatistics.errorsByCategory[category] = (errorStatistics.errorsByCategory[category] || 0) + 1;
|
||
|
|
||
|
// Count by severity
|
||
|
const severity = error.severity || 'error';
|
||
|
errorStatistics.errorsBySeverity[severity] = (errorStatistics.errorsBySeverity[severity] || 0) + 1;
|
||
|
|
||
|
// Track common error patterns
|
||
|
const errorKey = error.code || error.message.substring(0, 50);
|
||
|
errorStatistics.mostCommonErrors[errorKey] = (errorStatistics.mostCommonErrors[errorKey] || 0) + 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} catch (error) {
|
||
|
errorStatistics.filesWithErrors++;
|
||
|
errorStatistics.totalErrors++;
|
||
|
tools.log(`Parse error in ${plugins.path.basename(filePath)}: ${error.message}`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} catch (error) {
|
||
|
tools.log(`Failed to process category ${category}: ${error.message}`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Display error analysis results
|
||
|
tools.log(`\n=== Corpus Error Analysis ===`);
|
||
|
tools.log(`Total files analyzed: ${errorStatistics.totalFiles}`);
|
||
|
tools.log(`Files with errors: ${errorStatistics.filesWithErrors} (${(errorStatistics.filesWithErrors / errorStatistics.totalFiles * 100).toFixed(1)}%)`);
|
||
|
tools.log(`Total errors found: ${errorStatistics.totalErrors}`);
|
||
|
tools.log(`Average errors per file: ${(errorStatistics.totalErrors / errorStatistics.totalFiles).toFixed(1)}`);
|
||
|
|
||
|
if (Object.keys(errorStatistics.errorsByCategory).length > 0) {
|
||
|
tools.log(`\nErrors by category:`);
|
||
|
for (const [category, count] of Object.entries(errorStatistics.errorsByCategory)) {
|
||
|
tools.log(` ${category}: ${count}`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (Object.keys(errorStatistics.errorsBySeverity).length > 0) {
|
||
|
tools.log(`\nErrors by severity:`);
|
||
|
for (const [severity, count] of Object.entries(errorStatistics.errorsBySeverity)) {
|
||
|
tools.log(` ${severity}: ${count}`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Show most common errors
|
||
|
const commonErrors = Object.entries(errorStatistics.mostCommonErrors)
|
||
|
.sort(([,a], [,b]) => b - a)
|
||
|
.slice(0, 5);
|
||
|
|
||
|
if (commonErrors.length > 0) {
|
||
|
tools.log(`\nMost common errors:`);
|
||
|
for (const [errorKey, count] of commonErrors) {
|
||
|
tools.log(` ${count}x: ${errorKey}`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Error analysis should complete successfully
|
||
|
expect(errorStatistics.totalFiles).toBeGreaterThan(0);
|
||
|
|
||
|
} catch (error) {
|
||
|
tools.log(`Corpus error analysis failed: ${error.message}`);
|
||
|
throw error;
|
||
|
}
|
||
|
|
||
|
const totalDuration = Date.now() - startTime;
|
||
|
PerformanceTracker.recordMetric('error-reporting-corpus', totalDuration);
|
||
|
|
||
|
expect(totalDuration).toBeLessThan(120000); // 2 minutes max
|
||
|
tools.log(`Error analysis completed in ${totalDuration}ms`);
|
||
|
});
|
||
|
|
||
|
tap.test('VAL-13: Performance Summary', async (tools) => {
|
||
|
const operations = [
|
||
|
'error-reporting-message-quality',
|
||
|
'error-reporting-classification',
|
||
|
'error-reporting-context',
|
||
|
'error-reporting-aggregation',
|
||
|
'error-reporting-localization',
|
||
|
'error-reporting-corpus'
|
||
|
];
|
||
|
|
||
|
tools.log(`\n=== Error Reporting Performance Summary ===`);
|
||
|
|
||
|
for (const operation of operations) {
|
||
|
const summary = await PerformanceTracker.getSummary(operation);
|
||
|
if (summary) {
|
||
|
tools.log(`${operation}:`);
|
||
|
tools.log(` avg=${summary.average}ms, min=${summary.min}ms, max=${summary.max}ms, p95=${summary.p95}ms`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
tools.log(`\nError reporting testing completed successfully.`);
|
||
|
});
|