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 PDF processing // PDF-05: PDF/A-3 Creation // Tests creation of PDF/A-3 compliant documents with embedded XML attachments // according to ISO 19005-3 standard and ZUGFeRD/Factur-X requirements tap.test('PDF-05: PDF/A-3 Creation - Basic PDF/A-3 Generation', async (tools) => { const startTime = Date.now(); // Test basic PDF/A-3 creation functionality try { const sampleXml = ` PDFA3-TEST-001 2024-01-01 380 EUR PDF/A-3 Test Supplier Test Street 123 Test City 12345 DE PDF/A-3 Test Customer 1 1 100.00 PDF/A-3 Test Item 100.00 19.00 100.00 100.00 119.00 119.00 `; const invoice = new EInvoice(); const parseResult = await invoice.fromXmlString(sampleXml); expect(parseResult).toBeTruthy(); // Test PDF/A-3 creation if supported if (typeof invoice.createPdfA3 === 'function') { tools.log('Testing PDF/A-3 creation...'); const outputPath = plugins.path.join(process.cwd(), '.nogit', 'test-pdfa3-basic.pdf'); await plugins.fs.ensureDir(plugins.path.dirname(outputPath)); try { const pdfA3Options = { outputPath: outputPath, xmlContent: sampleXml, attachmentName: 'ZUGFeRD-invoice.xml', pdfA3Compliance: true, title: 'Electronic Invoice PDFA3-TEST-001', author: 'EInvoice Test Suite', subject: 'PDF/A-3 compliant invoice', keywords: 'invoice, electronic, PDF/A-3, ZUGFeRD' }; const creationResult = await invoice.createPdfA3(pdfA3Options); if (creationResult) { tools.log('✓ PDF/A-3 creation completed'); // Verify output file const outputExists = await plugins.fs.pathExists(outputPath); if (outputExists) { const outputStats = await plugins.fs.stat(outputPath); tools.log(`✓ PDF/A-3 file created: ${(outputStats.size / 1024).toFixed(1)}KB`); // Basic PDF validation (check if it starts with PDF header) const pdfHeader = await plugins.fs.readFile(outputPath, { encoding: 'binary' }); if (pdfHeader.startsWith('%PDF-')) { tools.log('✓ Valid PDF header detected'); // Check for PDF/A-3 markers if possible const pdfContent = pdfHeader.substring(0, 1024); if (pdfContent.includes('PDF/A-3') || pdfContent.includes('PDFA-3')) { tools.log('✓ PDF/A-3 markers detected'); } } else { tools.log('⚠ Invalid PDF header'); } // Test XML extraction from created PDF/A-3 try { const extractionInvoice = new EInvoice(); const extractionResult = await extractionInvoice.fromFile(outputPath); if (extractionResult) { const extractedXml = await extractionInvoice.toXmlString(); if (extractedXml.includes('PDFA3-TEST-001')) { tools.log('✓ XML successfully extracted from PDF/A-3'); } else { tools.log('⚠ Extracted XML does not contain expected content'); } } else { tools.log('⚠ Could not extract XML from created PDF/A-3'); } } catch (extractionError) { tools.log(`⚠ XML extraction test failed: ${extractionError.message}`); } // Clean up await plugins.fs.remove(outputPath); } else { tools.log('⚠ PDF/A-3 file not created'); } } else { tools.log('⚠ PDF/A-3 creation returned no result'); } } catch (creationError) { tools.log(`⚠ PDF/A-3 creation failed: ${creationError.message}`); } } else if (typeof invoice.toPdf === 'function') { tools.log('⚠ Specific PDF/A-3 creation not available, testing general PDF creation...'); try { const pdfResult = await invoice.toPdf({ pdfACompliance: 'PDF/A-3' }); if (pdfResult) { tools.log('✓ General PDF creation with PDF/A-3 compliance completed'); } } catch (pdfError) { tools.log(`⚠ General PDF creation failed: ${pdfError.message}`); } } else { tools.log('⚠ PDF/A-3 creation functionality not available'); } } catch (error) { tools.log(`Basic PDF/A-3 creation test failed: ${error.message}`); } const duration = Date.now() - startTime; PerformanceTracker.recordMetric('pdfa3-creation-basic', duration); }); tap.test('PDF-05: PDF/A-3 Creation - Compliance Levels', async (tools) => { const startTime = Date.now(); // Test different PDF/A-3 compliance levels (A, B, U) const complianceLevels = [ { level: 'PDF/A-3B', description: 'PDF/A-3 Level B (visual appearance)', strictness: 'medium' }, { level: 'PDF/A-3A', description: 'PDF/A-3 Level A (accessibility)', strictness: 'high' }, { level: 'PDF/A-3U', description: 'PDF/A-3 Level U (Unicode)', strictness: 'medium' } ]; const testXml = ` COMPLIANCE-TEST-001 2024-01-01 380 EUR 100.00 `; for (const compliance of complianceLevels) { tools.log(`Testing ${compliance.description}...`); try { const invoice = new EInvoice(); await invoice.fromXmlString(testXml); if (typeof invoice.createPdfA3 === 'function') { const outputPath = plugins.path.join(process.cwd(), '.nogit', `test-${compliance.level.toLowerCase().replace(/\//g, '-')}.pdf`); await plugins.fs.ensureDir(plugins.path.dirname(outputPath)); const complianceOptions = { outputPath: outputPath, xmlContent: testXml, attachmentName: 'invoice.xml', complianceLevel: compliance.level, title: `${compliance.level} Test Invoice`, validateCompliance: true }; try { const creationResult = await invoice.createPdfA3(complianceOptions); if (creationResult) { tools.log(`✓ ${compliance.level} creation completed`); const outputExists = await plugins.fs.pathExists(outputPath); if (outputExists) { const outputStats = await plugins.fs.stat(outputPath); tools.log(` File size: ${(outputStats.size / 1024).toFixed(1)}KB`); // Basic compliance validation const pdfContent = await plugins.fs.readFile(outputPath, { encoding: 'binary' }); const headerSection = pdfContent.substring(0, 2048); // Look for PDF/A compliance indicators if (headerSection.includes('PDF/A-3') || headerSection.includes('PDFA-3') || headerSection.includes(compliance.level)) { tools.log(` ✓ ${compliance.level} compliance indicators found`); } else { tools.log(` ⚠ ${compliance.level} compliance indicators not clearly detected`); } // Clean up await plugins.fs.remove(outputPath); } else { tools.log(` ⚠ ${compliance.level} file not created`); } } else { tools.log(`⚠ ${compliance.level} creation returned no result`); } } catch (complianceError) { tools.log(`⚠ ${compliance.level} creation failed: ${complianceError.message}`); } } else { tools.log(`⚠ ${compliance.level} creation not supported`); } } catch (error) { tools.log(`✗ ${compliance.level} test failed: ${error.message}`); } } const duration = Date.now() - startTime; PerformanceTracker.recordMetric('pdfa3-creation-compliance-levels', duration); }); tap.test('PDF-05: PDF/A-3 Creation - ZUGFeRD Profile Creation', async (tools) => { const startTime = Date.now(); // Test PDF/A-3 creation with specific ZUGFeRD/Factur-X profiles const zugferdProfiles = [ { profile: 'MINIMUM', xml: ` urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p1:minimum ZUGFERD-MIN-001 380 20240101 EUR 100.00 ` }, { profile: 'BASIC', xml: ` urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p1:basic ZUGFERD-BASIC-001 380 20240101 ZUGFeRD Test Supplier ZUGFeRD Test Customer EUR 100.00 19.00 119.00 119.00 ` }, { profile: 'COMFORT', xml: ` urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p1:comfort ZUGFERD-COMFORT-001 380 20240101 1 ZUGFeRD Test Product 100.00 100.00 EUR 100.00 100.00 19.00 119.00 119.00 ` } ]; for (const zugferdTest of zugferdProfiles) { tools.log(`Testing ZUGFeRD ${zugferdTest.profile} profile PDF/A-3 creation...`); try { const invoice = new EInvoice(); await invoice.fromXmlString(zugferdTest.xml); if (typeof invoice.createPdfA3 === 'function') { const outputPath = plugins.path.join(process.cwd(), '.nogit', `test-zugferd-${zugferdTest.profile.toLowerCase()}.pdf`); await plugins.fs.ensureDir(plugins.path.dirname(outputPath)); const zugferdOptions = { outputPath: outputPath, xmlContent: zugferdTest.xml, attachmentName: 'ZUGFeRD-invoice.xml', zugferdProfile: zugferdTest.profile, zugferdVersion: '2.1', complianceLevel: 'PDF/A-3B', title: `ZUGFeRD ${zugferdTest.profile} Invoice`, conformanceLevel: 'PDFA_3B' }; try { const creationResult = await invoice.createPdfA3(zugferdOptions); if (creationResult) { tools.log(`✓ ZUGFeRD ${zugferdTest.profile} PDF/A-3 creation completed`); const outputExists = await plugins.fs.pathExists(outputPath); if (outputExists) { const outputStats = await plugins.fs.stat(outputPath); tools.log(` File size: ${(outputStats.size / 1024).toFixed(1)}KB`); // Test round-trip (extraction from created PDF) try { const extractionInvoice = new EInvoice(); const extractionResult = await extractionInvoice.fromFile(outputPath); if (extractionResult) { const extractedXml = await extractionInvoice.toXmlString(); const expectedId = `ZUGFERD-${zugferdTest.profile}-001`; if (extractedXml.includes(expectedId)) { tools.log(` ✓ Round-trip successful - extracted XML contains ${expectedId}`); } else { tools.log(` ⚠ Round-trip issue - expected ID ${expectedId} not found`); } // Check for profile-specific elements if (zugferdTest.profile === 'COMFORT' && extractedXml.includes('IncludedSupplyChainTradeLineItem')) { tools.log(` ✓ COMFORT profile line items preserved`); } } else { tools.log(` ⚠ Round-trip failed - could not extract XML`); } } catch (extractionError) { tools.log(` ⚠ Round-trip test failed: ${extractionError.message}`); } // Clean up await plugins.fs.remove(outputPath); } else { tools.log(` ⚠ ZUGFeRD ${zugferdTest.profile} file not created`); } } else { tools.log(`⚠ ZUGFeRD ${zugferdTest.profile} creation returned no result`); } } catch (creationError) { tools.log(`⚠ ZUGFeRD ${zugferdTest.profile} creation failed: ${creationError.message}`); } } else { tools.log(`⚠ ZUGFeRD ${zugferdTest.profile} PDF/A-3 creation not supported`); } } catch (error) { tools.log(`✗ ZUGFeRD ${zugferdTest.profile} test failed: ${error.message}`); } } const duration = Date.now() - startTime; PerformanceTracker.recordMetric('pdfa3-creation-zugferd-profiles', duration); }); tap.test('PDF-05: PDF/A-3 Creation - Metadata and Accessibility', async (tools) => { const startTime = Date.now(); // Test PDF/A-3 creation with comprehensive metadata and accessibility features const testXml = ` METADATA-ACCESSIBILITY-001 2024-01-01 380 EUR 100.00 `; const metadataTests = [ { name: 'Comprehensive Metadata', options: { title: 'Electronic Invoice METADATA-ACCESSIBILITY-001', author: 'EInvoice Test Suite', subject: 'PDF/A-3 compliant invoice with comprehensive metadata', keywords: 'invoice, electronic, PDF/A-3, ZUGFeRD, accessible', creator: 'EInvoice PDF Generator', producer: 'EInvoice Test Framework', creationDate: new Date('2024-01-01'), modificationDate: new Date(), language: 'en-US' } }, { name: 'Accessibility Features', options: { title: 'Accessible Electronic Invoice', tagged: true, // Structured PDF for screen readers displayDocTitle: true, linearized: true, // Fast web view complianceLevel: 'PDF/A-3A', // Accessibility compliance structuredPdf: true } }, { name: 'Internationalization', options: { title: 'Elektronische Rechnung / Facture Électronique', language: 'de-DE', keywords: 'Rechnung, elektronisch, PDF/A-3, ZUGFeRD, Factur-X', unicodeSupport: true, characterEncoding: 'UTF-8' } } ]; for (const metadataTest of metadataTests) { tools.log(`Testing ${metadataTest.name}...`); try { const invoice = new EInvoice(); await invoice.fromXmlString(testXml); if (typeof invoice.createPdfA3 === 'function') { const outputPath = plugins.path.join(process.cwd(), '.nogit', `test-${metadataTest.name.toLowerCase().replace(/\s+/g, '-')}.pdf`); await plugins.fs.ensureDir(plugins.path.dirname(outputPath)); const creationOptions = { outputPath: outputPath, xmlContent: testXml, attachmentName: 'invoice.xml', complianceLevel: 'PDF/A-3B', ...metadataTest.options }; try { const creationResult = await invoice.createPdfA3(creationOptions); if (creationResult) { tools.log(`✓ ${metadataTest.name} PDF/A-3 creation completed`); const outputExists = await plugins.fs.pathExists(outputPath); if (outputExists) { const outputStats = await plugins.fs.stat(outputPath); tools.log(` File size: ${(outputStats.size / 1024).toFixed(1)}KB`); // Basic metadata validation by reading PDF content const pdfContent = await plugins.fs.readFile(outputPath, { encoding: 'binary' }); // Check for metadata presence (simplified check) if (metadataTest.options.title && pdfContent.includes(metadataTest.options.title)) { tools.log(` ✓ Title metadata preserved`); } if (metadataTest.options.author && pdfContent.includes(metadataTest.options.author)) { tools.log(` ✓ Author metadata preserved`); } if (metadataTest.options.keywords && metadataTest.options.keywords.split(',').some(keyword => pdfContent.includes(keyword.trim()))) { tools.log(` ✓ Keywords metadata preserved`); } // Check for accessibility features if (metadataTest.options.tagged && (pdfContent.includes('/StructTreeRoot') || pdfContent.includes('/Marked'))) { tools.log(` ✓ PDF structure/tagging detected`); } // Check for compliance level if (metadataTest.options.complianceLevel && pdfContent.includes(metadataTest.options.complianceLevel)) { tools.log(` ✓ Compliance level preserved`); } // Clean up await plugins.fs.remove(outputPath); } else { tools.log(` ⚠ ${metadataTest.name} file not created`); } } else { tools.log(`⚠ ${metadataTest.name} creation returned no result`); } } catch (creationError) { tools.log(`⚠ ${metadataTest.name} creation failed: ${creationError.message}`); } } else { tools.log(`⚠ ${metadataTest.name} PDF/A-3 creation not supported`); } } catch (error) { tools.log(`✗ ${metadataTest.name} test failed: ${error.message}`); } } const duration = Date.now() - startTime; PerformanceTracker.recordMetric('pdfa3-creation-metadata-accessibility', duration); }); tap.test('PDF-05: PDF/A-3 Creation - Performance and Size Optimization', async (tools) => { const startTime = Date.now(); // Test PDF/A-3 creation performance with different optimization settings const optimizationTests = [ { name: 'Standard Quality', options: { imageQuality: 'standard', compression: 'standard', optimizeFor: 'balanced' } }, { name: 'High Quality', options: { imageQuality: 'high', compression: 'minimal', optimizeFor: 'quality' } }, { name: 'Small Size', options: { imageQuality: 'medium', compression: 'maximum', optimizeFor: 'size' } }, { name: 'Fast Generation', options: { imageQuality: 'medium', compression: 'fast', optimizeFor: 'speed' } } ]; const testXml = ` PERFORMANCE-TEST-001 2024-01-01 380 EUR 100.00 `; const performanceResults = []; for (const optimizationTest of optimizationTests) { tools.log(`Testing ${optimizationTest.name} optimization...`); try { const invoice = new EInvoice(); await invoice.fromXmlString(testXml); if (typeof invoice.createPdfA3 === 'function') { const outputPath = plugins.path.join(process.cwd(), '.nogit', `test-${optimizationTest.name.toLowerCase().replace(/\s+/g, '-')}.pdf`); await plugins.fs.ensureDir(plugins.path.dirname(outputPath)); const creationStartTime = Date.now(); const creationOptions = { outputPath: outputPath, xmlContent: testXml, attachmentName: 'invoice.xml', complianceLevel: 'PDF/A-3B', title: `Performance Test - ${optimizationTest.name}`, ...optimizationTest.options }; try { const creationResult = await invoice.createPdfA3(creationOptions); const creationTime = Date.now() - creationStartTime; if (creationResult) { const outputExists = await plugins.fs.pathExists(outputPath); if (outputExists) { const outputStats = await plugins.fs.stat(outputPath); const fileSizeKB = outputStats.size / 1024; const result = { name: optimizationTest.name, creationTimeMs: creationTime, fileSizeKB: fileSizeKB, ...optimizationTest.options }; performanceResults.push(result); tools.log(` Creation time: ${creationTime}ms`); tools.log(` File size: ${fileSizeKB.toFixed(1)}KB`); tools.log(` Performance ratio: ${(creationTime / fileSizeKB).toFixed(2)}ms/KB`); // Clean up await plugins.fs.remove(outputPath); } else { tools.log(` ⚠ ${optimizationTest.name} file not created`); } } else { tools.log(`⚠ ${optimizationTest.name} creation returned no result`); } } catch (creationError) { tools.log(`⚠ ${optimizationTest.name} creation failed: ${creationError.message}`); } } else { tools.log(`⚠ ${optimizationTest.name} PDF/A-3 creation not supported`); } } catch (error) { tools.log(`✗ ${optimizationTest.name} test failed: ${error.message}`); } } // Analyze performance results if (performanceResults.length > 0) { tools.log(`\nPDF/A-3 Performance Analysis:`); const fastestCreation = performanceResults.reduce((min, r) => r.creationTimeMs < min.creationTimeMs ? r : min); const smallestFile = performanceResults.reduce((min, r) => r.fileSizeKB < min.fileSizeKB ? r : min); const avgCreationTime = performanceResults.reduce((sum, r) => sum + r.creationTimeMs, 0) / performanceResults.length; const avgFileSize = performanceResults.reduce((sum, r) => sum + r.fileSizeKB, 0) / performanceResults.length; tools.log(`- Fastest creation: ${fastestCreation.name} (${fastestCreation.creationTimeMs}ms)`); tools.log(`- Smallest file: ${smallestFile.name} (${smallestFile.fileSizeKB.toFixed(1)}KB)`); tools.log(`- Average creation time: ${avgCreationTime.toFixed(1)}ms`); tools.log(`- Average file size: ${avgFileSize.toFixed(1)}KB`); // Performance expectations expect(avgCreationTime).toBeLessThan(5000); // 5 seconds max average expect(avgFileSize).toBeLessThan(500); // 500KB max average } const duration = Date.now() - startTime; PerformanceTracker.recordMetric('pdfa3-creation-performance-optimization', duration); }); tap.test('PDF-05: Performance Summary', async (tools) => { const operations = [ 'pdfa3-creation-basic', 'pdfa3-creation-compliance-levels', 'pdfa3-creation-zugferd-profiles', 'pdfa3-creation-metadata-accessibility', 'pdfa3-creation-performance-optimization' ]; tools.log(`\n=== PDF/A-3 Creation 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(`\nPDF/A-3 creation testing completed.`); });