2025-05-27 15:26:22 +00:00
import { tap , expect } from '@git.zone/tstest/tapbundle' ;
2025-05-26 04:04:51 +00:00
import { EInvoice } from '../../../ts/index.js' ;
2025-05-27 12:23:50 +00:00
import { PerformanceTracker } from '../../helpers/performance.tracker.js' ;
2025-05-27 15:26:22 +00:00
import { ValidationLevel } from '../../../ts/interfaces/common.js' ;
2025-05-26 04:04:51 +00:00
2025-05-27 12:23:50 +00:00
tap . test ( 'EDGE-02: Gigabyte-Size Invoices - should handle extremely large invoice files' , async ( ) = > {
2025-05-27 15:26:22 +00:00
console . log ( 'Testing large invoice handling...' ) ;
2025-05-26 04:04:51 +00:00
2025-05-27 15:26:22 +00:00
// Test 1: Invoice with many line items
console . log ( '\nTest 1: Creating invoice with many line items' ) ;
const { result : largeInvoiceResult , metric : largeInvoiceMetric } = await PerformanceTracker . track (
'large-invoice-creation' ,
2025-05-26 04:04:51 +00:00
async ( ) = > {
2025-05-27 15:26:22 +00:00
const einvoice = new EInvoice ( ) ;
// Set basic invoice data
einvoice . id = 'LARGE-INVOICE-001' ;
einvoice . issueDate = new Date ( '2024-01-01' ) ;
einvoice . currency = 'EUR' ;
// Set supplier
einvoice . from = {
type : 'company' ,
name : 'Test Supplier GmbH' ,
description : 'Large invoice test supplier' ,
address : {
streetName : 'Test Street' ,
houseNumber : '1' ,
postalCode : '12345' ,
city : 'Berlin' ,
country : 'DE'
} ,
status : 'active' ,
foundedDate : { year : 2020 , month : 1 , day : 1 } ,
registrationDetails : {
vatId : 'DE123456789' ,
registrationId : 'HRB 12345' ,
registrationName : 'Berlin Registry'
2025-05-26 04:04:51 +00:00
}
} ;
2025-05-27 15:26:22 +00:00
// Set customer
einvoice . to = {
type : 'company' ,
name : 'Test Customer AG' ,
description : 'Large invoice test customer' ,
address : {
streetName : 'Market Street' ,
houseNumber : '42' ,
postalCode : '54321' ,
city : 'Munich' ,
country : 'DE'
} ,
status : 'active' ,
foundedDate : { year : 2018 , month : 6 , day : 15 } ,
registrationDetails : {
vatId : 'DE987654321' ,
registrationId : 'HRB 54321' ,
registrationName : 'Munich Registry'
2025-05-26 04:04:51 +00:00
}
2025-05-27 15:26:22 +00:00
} ;
2025-05-26 04:04:51 +00:00
2025-05-27 15:26:22 +00:00
// Create many line items
const itemCount = 500 ; // Reasonable number for testing
einvoice . items = [ ] ;
for ( let i = 0 ; i < itemCount ; i ++ ) {
einvoice . items . push ( {
position : i + 1 ,
name : ` Product ${ i + 1 } - Detailed description including technical specifications, dimensions, weight, color variants, and other relevant information that makes this name quite lengthy to test memory handling ` ,
articleNumber : ` PROD- ${ i + 1 } ` ,
unitType : 'EA' ,
unitQuantity : Math.floor ( Math . random ( ) * 10 ) + 1 ,
unitNetPrice : 99.99 ,
vatPercentage : 19
2025-05-26 04:04:51 +00:00
} ) ;
}
2025-05-27 15:26:22 +00:00
// Test XML generation
const xmlGenStart = Date . now ( ) ;
const xmlString = await einvoice . toXmlString ( 'ubl' ) ;
const xmlGenTime = Date . now ( ) - xmlGenStart ;
2025-05-26 04:04:51 +00:00
2025-05-27 15:26:22 +00:00
// Test parsing back
const parseStart = Date . now ( ) ;
const parsedInvoice = new EInvoice ( ) ;
await parsedInvoice . fromXmlString ( xmlString ) ;
const parseTime = Date . now ( ) - parseStart ;
2025-05-26 04:04:51 +00:00
2025-05-27 15:26:22 +00:00
// Test validation
const validationStart = Date . now ( ) ;
const validationResult = await parsedInvoice . validate ( ValidationLevel . SYNTAX ) ;
const validationTime = Date . now ( ) - validationStart ;
2025-05-26 04:04:51 +00:00
return {
2025-05-27 15:26:22 +00:00
itemCount ,
xmlSize : Buffer.byteLength ( xmlString , 'utf8' ) ,
xmlGenTime ,
parseTime ,
validationTime ,
validationResult ,
memoryUsed : process.memoryUsage ( ) . heapUsed
2025-05-26 04:04:51 +00:00
} ;
}
) ;
2025-05-27 15:26:22 +00:00
console . log ( ` Created invoice with ${ largeInvoiceResult . itemCount } items ` ) ;
console . log ( ` XML size: ${ ( largeInvoiceResult . xmlSize / 1024 ) . toFixed ( 2 ) } KB ` ) ;
console . log ( ` XML generation time: ${ largeInvoiceResult . xmlGenTime } ms ` ) ;
console . log ( ` Parse time: ${ largeInvoiceResult . parseTime } ms ` ) ;
console . log ( ` Validation time: ${ largeInvoiceResult . validationTime } ms ` ) ;
console . log ( ` Total processing time: ${ largeInvoiceMetric . duration } ms ` ) ;
console . log ( ` Memory used: ${ ( largeInvoiceResult . memoryUsed / 1024 / 1024 ) . toFixed ( 2 ) } MB ` ) ;
expect ( largeInvoiceResult . itemCount ) . toEqual ( 500 ) ;
expect ( largeInvoiceResult . xmlSize ) . toBeGreaterThan ( 50000 ) ; // At least 50KB
expect ( largeInvoiceResult . validationResult . valid ) . toBeTrue ( ) ;
2025-05-26 04:04:51 +00:00
2025-05-27 15:26:22 +00:00
// Test 2: Invoice with large text content
console . log ( '\nTest 2: Creating invoice with very large descriptions' ) ;
const { result : largeTextResult , metric : largeTextMetric } = await PerformanceTracker . track (
'large-text-content' ,
2025-05-26 04:04:51 +00:00
async ( ) = > {
2025-05-27 15:26:22 +00:00
const einvoice = new EInvoice ( ) ;
// Set basic invoice data
einvoice . id = 'LARGE-TEXT-001' ;
einvoice . issueDate = new Date ( '2024-01-01' ) ;
einvoice . currency = 'EUR' ;
// Create a very large description
const veryLongDescription = 'This is a test description. ' . repeat ( 1000 ) ; // ~30KB per item
einvoice . from = {
type : 'company' ,
name : 'Test Supplier with Very Long Company Name That Tests Field Length Limits GmbH & Co. KG' ,
description : veryLongDescription.substring ( 0 , 5000 ) , // Limit to reasonable size
address : {
streetName : 'Very Long Street Name That Goes On And On Testing Field Limits' ,
houseNumber : '999999' ,
postalCode : '99999' ,
city : 'City With Extremely Long Name Testing Municipality Name Length Limits' ,
country : 'DE'
} ,
status : 'active' ,
foundedDate : { year : 2020 , month : 1 , day : 1 } ,
registrationDetails : {
vatId : 'DE123456789' ,
registrationId : 'HRB 12345' ,
registrationName : 'Berlin Registry'
}
2025-05-26 04:04:51 +00:00
} ;
2025-05-27 15:26:22 +00:00
einvoice . to = {
type : 'company' ,
name : 'Customer Inc' ,
description : 'Normal customer' ,
address : {
streetName : 'Main St' ,
houseNumber : '1' ,
postalCode : '12345' ,
city : 'Berlin' ,
country : 'DE'
} ,
status : 'active' ,
foundedDate : { year : 2019 , month : 3 , day : 10 } ,
registrationDetails : {
vatId : 'DE987654321' ,
registrationId : 'HRB 98765' ,
registrationName : 'Berlin Registry'
2025-05-26 04:04:51 +00:00
}
2025-05-27 15:26:22 +00:00
} ;
2025-05-26 04:04:51 +00:00
2025-05-27 15:26:22 +00:00
// Add items with large descriptions
einvoice . items = [ ] ;
for ( let i = 0 ; i < 10 ; i ++ ) {
einvoice . items . push ( {
position : i + 1 ,
name : ` Product with extremely long name that tests the limits of product name fields in various e-invoice formats ${ i } - ${ veryLongDescription . substring ( 0 , 1000 ) } ` ,
articleNumber : ` LONG- ${ i + 1 } ` ,
unitType : 'EA' ,
unitQuantity : 1 ,
unitNetPrice : 100 ,
vatPercentage : 19
} ) ;
2025-05-26 04:04:51 +00:00
}
2025-05-27 15:26:22 +00:00
// Test XML generation
const xmlString = await einvoice . toXmlString ( 'ubl' ) ;
2025-05-26 04:04:51 +00:00
2025-05-27 15:26:22 +00:00
// Test parsing
const parsedInvoice = new EInvoice ( ) ;
await parsedInvoice . fromXmlString ( xmlString ) ;
2025-05-26 04:04:51 +00:00
2025-05-27 15:26:22 +00:00
return {
xmlSize : Buffer.byteLength ( xmlString , 'utf8' ) ,
itemCount : parsedInvoice.items?.length || 0 ,
fromNameLength : parsedInvoice.from?.name?.length || 0 ,
itemNameLength : parsedInvoice.items?. [ 0 ] ? . name ? . length || 0
} ;
2025-05-26 04:04:51 +00:00
}
) ;
2025-05-27 15:26:22 +00:00
console . log ( ` XML size with large text: ${ ( largeTextResult . xmlSize / 1024 ) . toFixed ( 2 ) } KB ` ) ;
console . log ( ` Processing time: ${ largeTextMetric . duration } ms ` ) ;
console . log ( ` Preserved ${ largeTextResult . itemCount } items ` ) ;
console . log ( ` Company name length: ${ largeTextResult . fromNameLength } chars ` ) ;
console . log ( ` Item name length: ${ largeTextResult . itemNameLength } chars ` ) ;
expect ( largeTextResult . xmlSize ) . toBeGreaterThan ( 30000 ) ; // At least 30KB
expect ( largeTextResult . itemCount ) . toEqual ( 10 ) ;
2025-05-26 04:04:51 +00:00
2025-05-27 15:26:22 +00:00
// Test 3: Memory efficiency test
console . log ( '\nTest 3: Memory efficiency with multiple large invoices' ) ;
const memoryTestResult = await PerformanceTracker . track (
'memory-efficiency' ,
2025-05-26 04:04:51 +00:00
async ( ) = > {
2025-05-27 15:26:22 +00:00
const startMemory = process . memoryUsage ( ) . heapUsed ;
const invoices = [ ] ;
// Create multiple invoices
for ( let i = 0 ; i < 10 ; i ++ ) {
const invoice = new EInvoice ( ) ;
invoice . id = ` MEMORY-TEST- ${ i } ` ;
invoice . issueDate = new Date ( ) ;
invoice . currency = 'EUR' ;
2025-05-26 04:04:51 +00:00
2025-05-27 15:26:22 +00:00
invoice . from = {
type : 'company' ,
name : ` Supplier ${ i } ` ,
description : 'Test supplier' ,
address : {
streetName : 'Test St' ,
houseNumber : '1' ,
postalCode : '12345' ,
city : 'Berlin' ,
country : 'DE'
} ,
status : 'active' ,
foundedDate : { year : 2020 , month : 1 , day : 1 } ,
registrationDetails : {
vatId : ` DE12345678 ${ i } ` ,
registrationId : ` HRB 1234 ${ i } ` ,
registrationName : 'Berlin Registry'
2025-05-26 04:04:51 +00:00
}
2025-05-27 15:26:22 +00:00
} ;
invoice . to = {
type : 'company' ,
name : ` Customer ${ i } ` ,
description : 'Test customer' ,
address : {
streetName : 'Main St' ,
houseNumber : '2' ,
postalCode : '54321' ,
city : 'Munich' ,
country : 'DE'
} ,
status : 'active' ,
foundedDate : { year : 2019 , month : 6 , day : 1 } ,
registrationDetails : {
vatId : ` DE98765432 ${ i } ` ,
registrationId : ` HRB 5432 ${ i } ` ,
registrationName : 'Munich Registry'
2025-05-26 04:04:51 +00:00
}
2025-05-27 15:26:22 +00:00
} ;
2025-05-26 04:04:51 +00:00
2025-05-27 15:26:22 +00:00
// Add 100 items each
invoice . items = [ ] ;
for ( let j = 0 ; j < 100 ; j ++ ) {
invoice . items . push ( {
position : j + 1 ,
name : ` Product ${ j } - Description for invoice ${ i } item ${ j } ` ,
articleNumber : ` MEM- ${ i } - ${ j } ` ,
unitType : 'EA' ,
unitQuantity : 2 ,
unitNetPrice : 50 ,
vatPercentage : 19
} ) ;
}
2025-05-26 04:04:51 +00:00
2025-05-27 15:26:22 +00:00
invoices . push ( invoice ) ;
2025-05-26 04:04:51 +00:00
}
2025-05-27 15:26:22 +00:00
// Convert all to XML
const xmlStrings = await Promise . all (
invoices . map ( inv = > inv . toXmlString ( 'ubl' ) )
) ;
2025-05-26 04:04:51 +00:00
2025-05-27 15:26:22 +00:00
const endMemory = process . memoryUsage ( ) . heapUsed ;
const totalSize = xmlStrings . reduce ( ( sum , xml ) = > sum + Buffer . byteLength ( xml , 'utf8' ) , 0 ) ;
2025-05-26 04:04:51 +00:00
2025-05-27 15:26:22 +00:00
return {
invoiceCount : invoices.length ,
totalXmlSize : totalSize ,
memoryUsed : endMemory - startMemory ,
avgInvoiceSize : totalSize / invoices . length
} ;
2025-05-26 04:04:51 +00:00
}
) ;
2025-05-27 15:26:22 +00:00
console . log ( ` Created ${ memoryTestResult . result . invoiceCount } invoices ` ) ;
console . log ( ` Total XML size: ${ ( memoryTestResult . result . totalXmlSize / 1024 / 1024 ) . toFixed ( 2 ) } MB ` ) ;
console . log ( ` Memory used: ${ ( memoryTestResult . result . memoryUsed / 1024 / 1024 ) . toFixed ( 2 ) } MB ` ) ;
console . log ( ` Average invoice size: ${ ( memoryTestResult . result . avgInvoiceSize / 1024 ) . toFixed ( 2 ) } KB ` ) ;
console . log ( ` Processing time: ${ memoryTestResult . metric . duration } ms ` ) ;
2025-05-26 04:04:51 +00:00
2025-05-27 15:26:22 +00:00
expect ( memoryTestResult . result . invoiceCount ) . toEqual ( 10 ) ;
expect ( memoryTestResult . result . totalXmlSize ) . toBeGreaterThan ( 500000 ) ; // At least 500KB total
2025-05-26 04:04:51 +00:00
2025-05-27 15:26:22 +00:00
console . log ( '\n✓ All large invoice tests completed successfully' ) ;
} ) ;
2025-05-26 04:04:51 +00:00
tap . start ( ) ;