import { tap } from '@git.zone/tstest/tapbundle'; import { EInvoice } from '../../../ts/index.js'; import { PerformanceTracker } from '../performance.tracker.js'; tap.test('EDGE-07: Maximum Field Lengths - should handle fields at maximum allowed lengths', async () => { // Test 1: Standard field length limits await PerformanceTracker.track('standard-field-limits', async () => { const fieldTests = [ { field: 'invoiceId', maxLength: 200, testValue: 'X' }, { field: 'customerName', maxLength: 200, testValue: 'A' }, { field: 'streetName', maxLength: 1000, testValue: 'B' }, { field: 'notes', maxLength: 5000, testValue: 'C' } ]; for (const test of fieldTests) { const einvoice = new EInvoice(); einvoice.issueDate = new Date(2024, 0, 1); // Test at max length const maxValue = test.testValue.repeat(test.maxLength); if (test.field === 'invoiceId') { einvoice.invoiceId = maxValue; } einvoice.from = { type: 'company', name: test.field === 'customerName' ? maxValue : 'Test Company', description: 'Testing max field lengths', address: { streetName: test.field === 'streetName' ? maxValue : '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: 'company', name: 'Customer Company', description: 'Customer', address: { streetName: 'Customer Street', houseNumber: '2', postalCode: '54321', city: 'Customer City', country: 'DE' }, status: 'active', foundedDate: { year: 2019, month: 1, day: 1 }, registrationDetails: { vatId: 'DE987654321', registrationId: 'HRB 54321', registrationName: 'Commercial Register' } }; if (test.field === 'notes') { einvoice.notes = [maxValue]; } einvoice.items = [{ position: 1, name: 'Test Item', articleNumber: 'TEST-001', unitType: 'EA', unitQuantity: 1, unitNetPrice: 100, vatPercentage: 19 }]; try { const xmlString = await einvoice.toXmlString('ubl'); console.log(`Field ${test.field} at max length (${test.maxLength}): XML generated, size: ${xmlString.length} bytes`); // Test round-trip const newInvoice = new EInvoice(); await newInvoice.fromXmlString(xmlString); let preserved = false; if (test.field === 'invoiceId') { preserved = newInvoice.invoiceId === maxValue; } else if (test.field === 'customerName') { preserved = newInvoice.from.name === maxValue; } else if (test.field === 'streetName') { preserved = newInvoice.from.address.streetName === maxValue; } else if (test.field === 'notes') { preserved = newInvoice.notes?.[0] === maxValue; } console.log(`Field ${test.field} preservation: ${preserved ? 'preserved' : 'truncated'}`); } catch (error) { console.log(`Field ${test.field} at max length failed: ${error.message}`); } // Test over max length const overValue = test.testValue.repeat(test.maxLength + 100); const overInvoice = new EInvoice(); overInvoice.issueDate = new Date(2024, 0, 1); if (test.field === 'invoiceId') { overInvoice.invoiceId = overValue; } overInvoice.from = { type: 'company', name: test.field === 'customerName' ? overValue : 'Test Company', description: 'Testing over max field lengths', address: { streetName: test.field === 'streetName' ? overValue : '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' } }; overInvoice.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' } }; if (test.field === 'notes') { overInvoice.notes = [overValue]; } overInvoice.items = [{ position: 1, name: 'Test Item', articleNumber: 'TEST-001', unitType: 'EA', unitQuantity: 1, unitNetPrice: 100, vatPercentage: 19 }]; try { const overXmlString = await overInvoice.toXmlString('ubl'); console.log(`Field ${test.field} over max length: still generated XML (${overXmlString.length} bytes)`); } catch (error) { console.log(`Field ${test.field} over max length: properly rejected - ${error.message}`); } } }); // Test 2: Unicode character length vs byte length await PerformanceTracker.track('unicode-length-vs-bytes', async () => { const testCases = [ { name: 'ascii-only', char: 'A', bytesPerChar: 1 }, { name: 'latin-extended', char: 'ñ', bytesPerChar: 2 }, { name: 'chinese', char: '中', bytesPerChar: 3 }, { name: 'emoji', char: '😀', bytesPerChar: 4 } ]; const maxChars = 100; for (const test of testCases) { const value = test.char.repeat(maxChars); const byteLength = Buffer.from(value, 'utf8').length; const einvoice = new EInvoice(); einvoice.issueDate = new Date(2024, 0, 1); einvoice.invoiceId = 'UNICODE-TEST'; einvoice.from = { type: 'company', name: value, description: `Unicode test: ${test.name}`, address: { streetName: 'Unicode Street', houseNumber: '1', postalCode: '12345', city: 'Unicode City', country: 'DE' }, status: 'active', foundedDate: { year: 2020, month: 1, day: 1 }, registrationDetails: { vatId: 'DE123456789', registrationId: 'HRB 12345', registrationName: 'Commercial Register' } }; einvoice.to = { type: 'company', name: 'Customer Company', description: 'Customer', address: { streetName: 'Customer Street', houseNumber: '2', postalCode: '54321', city: 'Customer City', country: 'DE' }, status: 'active', foundedDate: { year: 2019, month: 1, day: 1 }, registrationDetails: { vatId: 'DE987654321', registrationId: 'HRB 54321', registrationName: 'Commercial Register' } }; einvoice.items = [{ position: 1, name: 'Test Item', articleNumber: 'TEST-001', unitType: 'EA', unitQuantity: 1, unitNetPrice: 100, vatPercentage: 19 }]; try { const xmlString = await einvoice.toXmlString('cii'); const newInvoice = new EInvoice(); await newInvoice.fromXmlString(xmlString); const retrievedValue = newInvoice.from.name; const preserved = retrievedValue === value; const retrievedBytes = Buffer.from(retrievedValue, 'utf8').length; console.log(`Unicode ${test.name}: chars=${value.length}, bytes=${byteLength}, expectedBytes=${maxChars * test.bytesPerChar}`); console.log(` Preserved: ${preserved}, retrieved chars=${retrievedValue.length}, bytes=${retrievedBytes}`); } catch (error) { console.log(`Unicode ${test.name} failed: ${error.message}`); } } }); // Test 3: Long invoice numbers await PerformanceTracker.track('long-invoice-numbers', async () => { const lengths = [50, 100, 200, 500]; for (const length of lengths) { const einvoice = new EInvoice(); einvoice.issueDate = new Date(2024, 0, 1); einvoice.invoiceId = 'INV-' + '0'.repeat(length - 4); einvoice.from = { type: 'company', name: 'Test Company', description: 'Testing long invoice numbers', 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 Item', articleNumber: 'TEST-001', unitType: 'EA', unitQuantity: 1, unitNetPrice: 100, vatPercentage: 19 }]; try { const xmlString = await einvoice.toXmlString('xrechnung'); const newInvoice = new EInvoice(); await newInvoice.fromXmlString(xmlString); console.log(`Invoice number length ${length}: ${newInvoice.invoiceId.length === length ? 'preserved' : 'modified'}`); } catch (error) { console.log(`Invoice number length ${length} failed: ${error.message}`); } } }); // Test 4: Line item count limits await PerformanceTracker.track('line-item-count-limits', async () => { const itemCounts = [100, 500, 1000]; for (const count of itemCounts) { const einvoice = new EInvoice(); einvoice.issueDate = new Date(2024, 0, 1); einvoice.invoiceId = `MANY-ITEMS-${count}`; einvoice.from = { type: 'company', name: 'Bulk Seller Company', description: 'Testing many line items', address: { streetName: 'Bulk Street', houseNumber: '1', postalCode: '12345', city: 'Bulk City', country: 'DE' }, status: 'active', foundedDate: { year: 2020, month: 1, day: 1 }, registrationDetails: { vatId: 'DE123456789', registrationId: 'HRB 12345', registrationName: 'Commercial Register' } }; einvoice.to = { type: 'company', name: 'Bulk Buyer Company', description: 'Customer buying many items', address: { streetName: 'Buyer Street', houseNumber: '2', postalCode: '54321', city: 'Buyer City', country: 'DE' }, status: 'active', foundedDate: { year: 2019, month: 1, day: 1 }, registrationDetails: { vatId: 'DE987654321', registrationId: 'HRB 54321', registrationName: 'Commercial Register' } }; // Create many items einvoice.items = []; for (let i = 0; i < count; i++) { einvoice.items.push({ position: i + 1, name: `Item ${i + 1}`, articleNumber: `ART-${String(i + 1).padStart(5, '0')}`, unitType: 'EA', unitQuantity: 1, unitNetPrice: 10 + (i % 100), vatPercentage: 19 }); } const startTime = Date.now(); try { const xmlString = await einvoice.toXmlString('ubl'); const endTime = Date.now(); const timeTaken = endTime - startTime; const newInvoice = new EInvoice(); await newInvoice.fromXmlString(xmlString); const itemsParsed = newInvoice.items.length; console.log(`Line items ${count}: parsed=${itemsParsed}, time=${timeTaken}ms, avg=${(timeTaken/count).toFixed(2)}ms/item`); } catch (error) { console.log(`Line items ${count} failed: ${error.message}`); } } }); // Test 5: Long email addresses await PerformanceTracker.track('long-email-addresses', async () => { const emailLengths = [50, 100, 254]; // RFC 5321 limit for (const length of emailLengths) { const localPart = 'x'.repeat(Math.max(1, length - 20)); const email = localPart + '@example.com'; const einvoice = new EInvoice(); einvoice.issueDate = new Date(2024, 0, 1); einvoice.invoiceId = 'EMAIL-TEST'; einvoice.electronicAddress = { scheme: 'EMAIL', value: email.substring(0, length) }; einvoice.from = { type: 'company', name: 'Email Test Company', description: 'Testing long email addresses', address: { streetName: 'Email Street', houseNumber: '1', postalCode: '12345', city: 'Email City', country: 'DE' }, status: 'active', foundedDate: { year: 2020, month: 1, day: 1 }, registrationDetails: { vatId: 'DE123456789', registrationId: 'HRB 12345', registrationName: 'Commercial Register' } }; einvoice.to = { type: 'company', name: 'Customer Company', description: 'Customer', address: { streetName: 'Customer Street', houseNumber: '2', postalCode: '54321', city: 'Customer City', country: 'DE' }, status: 'active', foundedDate: { year: 2019, month: 1, day: 1 }, registrationDetails: { vatId: 'DE987654321', registrationId: 'HRB 54321', registrationName: 'Commercial Register' } }; einvoice.items = [{ position: 1, name: 'Test Item', articleNumber: 'TEST-001', unitType: 'EA', unitQuantity: 1, unitNetPrice: 100, vatPercentage: 19 }]; try { const xmlString = await einvoice.toXmlString('ubl'); const newInvoice = new EInvoice(); await newInvoice.fromXmlString(xmlString); const preserved = newInvoice.electronicAddress?.value === email.substring(0, length); console.log(`Email length ${length}: ${preserved ? 'preserved' : 'modified'}`); } catch (error) { console.log(`Email length ${length} failed: ${error.message}`); } } }); // Test 6: Decimal precision limits await PerformanceTracker.track('decimal-precision-limits', async () => { const precisionTests = [ { decimals: 2, value: 12345678901234567890.12 }, { decimals: 4, value: 123456789012345.1234 }, { decimals: 6, value: 1234567890.123456 } ]; for (const test of precisionTests) { const einvoice = new EInvoice(); einvoice.issueDate = new Date(2024, 0, 1); einvoice.invoiceId = 'DECIMAL-TEST'; einvoice.from = { type: 'company', name: 'Decimal Test Company', description: 'Testing decimal precision', address: { streetName: 'Decimal Street', houseNumber: '1', postalCode: '12345', city: 'Decimal 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: 'High Precision Item', articleNumber: 'DECIMAL-001', unitType: 'EA', unitQuantity: 1, unitNetPrice: test.value, vatPercentage: 19.123456 // Test VAT precision too }]; try { const xmlString = await einvoice.toXmlString('cii'); const newInvoice = new EInvoice(); await newInvoice.fromXmlString(xmlString); const originalStr = test.value.toString(); const parsedValue = newInvoice.items[0].unitNetPrice; const parsedStr = parsedValue.toString(); console.log(`Decimal precision ${test.decimals}: original=${originalStr}, parsed=${parsedStr}`); console.log(` Preserved: ${parsedValue === test.value}`); } catch (error) { console.log(`Decimal precision ${test.decimals} failed: ${error.message}`); } } }); // Test 7: Long postal codes and phone numbers await PerformanceTracker.track('postal-codes-phone-numbers', async () => { const tests = [ { field: 'postalCode', lengths: [5, 10, 20] }, { field: 'phone', lengths: [10, 20, 30] } ]; for (const test of tests) { for (const length of test.lengths) { const einvoice = new EInvoice(); einvoice.issueDate = new Date(2024, 0, 1); einvoice.invoiceId = `${test.field.toUpperCase()}-${length}`; const value = '1234567890'.repeat(Math.ceil(length / 10)).substring(0, length); einvoice.from = { type: 'company', name: 'Field Length Test Company', description: `Testing ${test.field} length`, address: { streetName: 'Test Street', houseNumber: '1', postalCode: test.field === 'postalCode' ? value : '12345', city: 'Test City', country: 'DE' }, status: 'active', foundedDate: { year: 2020, month: 1, day: 1 }, registrationDetails: { vatId: 'DE123456789', registrationId: 'HRB 12345', registrationName: 'Commercial Register' } }; if (test.field === 'phone') { (einvoice.from as any).phone = value; } einvoice.to = { type: 'company', name: 'Customer Company', description: 'Customer', address: { streetName: 'Customer Street', houseNumber: '2', postalCode: '54321', city: 'Customer City', country: 'DE' }, status: 'active', foundedDate: { year: 2019, month: 1, day: 1 }, registrationDetails: { vatId: 'DE987654321', registrationId: 'HRB 54321', registrationName: 'Commercial Register' } }; einvoice.items = [{ position: 1, name: 'Test Item', articleNumber: 'TEST-001', unitType: 'EA', unitQuantity: 1, unitNetPrice: 100, vatPercentage: 19 }]; try { const xmlString = await einvoice.toXmlString('ubl'); const newInvoice = new EInvoice(); await newInvoice.fromXmlString(xmlString); let preserved = false; if (test.field === 'postalCode') { preserved = newInvoice.from.address.postalCode === value; } else if (test.field === 'phone') { preserved = (newInvoice.from as any).phone === value; } console.log(`${test.field} length ${length}: ${preserved ? 'preserved' : 'modified'}`); } catch (error) { console.log(`${test.field} length ${length} failed: ${error.message}`); } } } }); // Test 8: Performance impact of field lengths await PerformanceTracker.track('field-length-performance-impact', async () => { const lengths = [10, 100, 1000, 10000]; const performanceResults = []; for (const length of lengths) { const iterations = 5; const times = []; for (let i = 0; i < iterations; i++) { const value = 'X'.repeat(length); const einvoice = new EInvoice(); einvoice.issueDate = new Date(2024, 0, 1); einvoice.invoiceId = 'PERF-TEST'; einvoice.subject = value; einvoice.notes = [value, value, value]; einvoice.from = { type: 'company', name: value.substring(0, 200), description: value, address: { streetName: value.substring(0, 1000), 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: 'company', name: 'Customer Company', description: 'Customer', address: { streetName: 'Customer Street', houseNumber: '2', postalCode: '54321', city: 'Customer City', country: 'DE' }, status: 'active', foundedDate: { year: 2019, month: 1, day: 1 }, registrationDetails: { vatId: 'DE987654321', registrationId: 'HRB 54321', registrationName: 'Commercial Register' } }; einvoice.items = [{ position: 1, name: value.substring(0, 500), articleNumber: 'TEST-001', unitType: 'EA', unitQuantity: 1, unitNetPrice: 100, vatPercentage: 19 }]; const startTime = process.hrtime.bigint(); try { await einvoice.toXmlString('ubl'); } catch (error) { // Ignore errors for performance testing } const endTime = process.hrtime.bigint(); times.push(Number(endTime - startTime) / 1000000); // Convert to ms } const avgTime = times.reduce((a, b) => a + b, 0) / times.length; performanceResults.push({ fieldLength: length, avgParseTime: avgTime, timePerKB: avgTime / (length * 5 / 1024) // 5 fields with this length }); console.log(`Field length ${length}: avg time=${avgTime.toFixed(2)}ms, per KB=${(avgTime / (length * 5 / 1024)).toFixed(2)}ms`); } // Check performance scaling for (let i = 1; i < performanceResults.length; i++) { const ratio = performanceResults[i].avgParseTime / performanceResults[i-1].avgParseTime; const lengthRatio = performanceResults[i].fieldLength / performanceResults[i-1].fieldLength; console.log(`Performance scaling ${performanceResults[i-1].fieldLength}→${performanceResults[i].fieldLength}: time ratio=${ratio.toFixed(2)}, length ratio=${lengthRatio}`); } }); }); // Run the test tap.start();