This commit is contained in:
2025-05-29 13:35:36 +00:00
parent 756964aabd
commit 960bbc2208
15 changed files with 2373 additions and 3396 deletions

View File

@ -8,9 +8,9 @@ import * as plugins from '../../plugins.js';
import { EInvoice } from '../../../ts/index.js';
import { CorpusLoader } from '../../suite/corpus.loader.js';
import { PerformanceTracker } from '../../suite/performance.tracker.js';
import { FormatDetector } from '../../../ts/formats/utils/format.detector.js';
import { Readable, Writable, Transform } from 'stream';
const corpusLoader = new CorpusLoader();
const performanceTracker = new PerformanceTracker('PERF-09: Streaming Performance');
tap.test('PERF-09: Streaming Performance - should handle streaming operations efficiently', async (t) => {
@ -18,7 +18,6 @@ tap.test('PERF-09: Streaming Performance - should handle streaming operations ef
const streamingXMLParsing = await performanceTracker.measureAsync(
'streaming-xml-parsing',
async () => {
const einvoice = new EInvoice();
const results = {
tests: [],
memoryEfficiency: null
@ -118,8 +117,8 @@ tap.test('PERF-09: Streaming Performance - should handle streaming operations ef
// Parse accumulated XML
const xml = chunks.join('');
const format = await einvoice.detectFormat(xml);
const invoice = await einvoice.parseInvoice(xml, format || 'ubl');
const format = FormatDetector.detectFormat(xml);
const invoice = await EInvoice.fromXml(xml);
const endTime = Date.now();
const endMemory = process.memoryUsage();
@ -133,7 +132,7 @@ tap.test('PERF-09: Streaming Performance - should handle streaming operations ef
peakMemory: Math.max(...memorySnapshots).toFixed(2),
avgMemory: (memorySnapshots.reduce((a, b) => a + b, 0) / memorySnapshots.length).toFixed(2),
throughput: ((totalBytes / 1024) / ((endTime - startTime) / 1000)).toFixed(2),
itemsProcessed: invoice.data.items?.length || 0
itemsProcessed: size.items
});
resolve(null);
@ -175,7 +174,6 @@ tap.test('PERF-09: Streaming Performance - should handle streaming operations ef
const streamTransformation = await performanceTracker.measureAsync(
'stream-transformation-pipeline',
async () => {
const einvoice = new EInvoice();
const results = {
pipelines: [],
transformationStats: null
@ -183,13 +181,13 @@ tap.test('PERF-09: Streaming Performance - should handle streaming operations ef
// Create transformation streams
class FormatDetectionStream extends Transform {
constructor(private einvoice: EInvoice) {
constructor() {
super({ objectMode: true });
}
async _transform(chunk: any, encoding: string, callback: Function) {
try {
const format = await this.einvoice.detectFormat(chunk.content);
const format = FormatDetector.detectFormat(chunk.content);
this.push({ ...chunk, format });
callback();
} catch (error) {
@ -199,16 +197,16 @@ tap.test('PERF-09: Streaming Performance - should handle streaming operations ef
}
class ValidationStream extends Transform {
constructor(private einvoice: EInvoice) {
constructor() {
super({ objectMode: true });
}
async _transform(chunk: any, encoding: string, callback: Function) {
try {
if (chunk.format && chunk.format !== 'unknown') {
const invoice = await this.einvoice.parseInvoice(chunk.content, chunk.format);
const validation = await this.einvoice.validateInvoice(invoice);
this.push({ ...chunk, valid: validation.isValid, errors: validation.errors?.length || 0 });
const invoice = await EInvoice.fromXml(chunk.content);
const validation = await invoice.validate();
this.push({ ...chunk, valid: validation.valid, errors: validation.errors?.length || 0 });
} else {
this.push({ ...chunk, valid: false, errors: -1 });
}
@ -286,11 +284,11 @@ tap.test('PERF-09: Streaming Performance - should handle streaming operations ef
let pipeline = inputStream;
if (config.stages.includes('detect')) {
pipeline = pipeline.pipe(new FormatDetectionStream(einvoice));
pipeline = pipeline.pipe(new FormatDetectionStream());
}
if (config.stages.includes('validate')) {
pipeline = pipeline.pipe(new ValidationStream(einvoice));
pipeline = pipeline.pipe(new ValidationStream());
}
// Process
@ -348,7 +346,6 @@ tap.test('PERF-09: Streaming Performance - should handle streaming operations ef
const backpressureHandling = await performanceTracker.measureAsync(
'backpressure-handling',
async () => {
const einvoice = new EInvoice();
const results = {
scenarios: [],
backpressureStats: null
@ -425,7 +422,7 @@ tap.test('PERF-09: Streaming Performance - should handle streaming operations ef
await new Promise(resolve => setTimeout(resolve, scenario.consumerDelay));
// Process invoice
const format = await einvoice.detectFormat(chunk.content);
const format = FormatDetector.detectFormat(chunk.content);
metrics.consumed++;
metrics.buffered = metrics.produced - metrics.consumed;
@ -486,8 +483,7 @@ tap.test('PERF-09: Streaming Performance - should handle streaming operations ef
const corpusStreaming = await performanceTracker.measureAsync(
'corpus-streaming-analysis',
async () => {
const files = await corpusLoader.getFilesByPattern('**/*.xml');
const einvoice = new EInvoice();
const files = await CorpusLoader.loadPattern('**/*.xml');
const results = {
streamableFiles: 0,
nonStreamableFiles: 0,
@ -503,16 +499,16 @@ tap.test('PERF-09: Streaming Performance - should handle streaming operations ef
for (const file of sampleFiles) {
try {
const stats = await plugins.fs.stat(file);
const stats = await plugins.fs.stat(file.path);
const fileSize = stats.size;
// Traditional processing
const traditionalStart = Date.now();
const content = await plugins.fs.readFile(file, 'utf-8');
const format = await einvoice.detectFormat(content);
const content = await plugins.fs.readFile(file.path, 'utf-8');
const format = FormatDetector.detectFormat(content);
if (format && format !== 'unknown') {
const invoice = await einvoice.parseInvoice(content, format);
await einvoice.validateInvoice(invoice);
const invoice = await EInvoice.fromXml(content);
await invoice.validate();
}
const traditionalEnd = Date.now();
@ -527,15 +523,16 @@ tap.test('PERF-09: Streaming Performance - should handle streaming operations ef
const chunks = [];
// Read in chunks
const fd = await plugins.fs.open(file, 'r');
const fd = await plugins.fs.open(file.path, 'r');
const buffer = Buffer.alloc(chunkSize);
let position = 0;
while (true) {
const { bytesRead } = await fd.read(buffer, 0, chunkSize, position);
const result = await fd.read(buffer, 0, chunkSize, position);
const bytesRead = result.bytesRead;
if (bytesRead === 0) break;
chunks.push(buffer.slice(0, bytesRead).toString('utf-8'));
chunks.push(Buffer.from(buffer.slice(0, bytesRead)).toString('utf-8'));
position += bytesRead;
}
@ -543,10 +540,10 @@ tap.test('PERF-09: Streaming Performance - should handle streaming operations ef
// Process accumulated content
const streamedContent = chunks.join('');
const streamedFormat = await einvoice.detectFormat(streamedContent);
const streamedFormat = FormatDetector.detectFormat(streamedContent);
if (streamedFormat && streamedFormat !== 'unknown') {
const invoice = await einvoice.parseInvoice(streamedContent, streamedFormat);
await einvoice.validateInvoice(invoice);
const invoice = await EInvoice.fromXml(streamedContent);
await invoice.validate();
}
const streamingEnd = Date.now();
@ -603,7 +600,6 @@ tap.test('PERF-09: Streaming Performance - should handle streaming operations ef
const realtimeStreaming = await performanceTracker.measureAsync(
'realtime-streaming',
async () => {
const einvoice = new EInvoice();
const results = {
latencyTests: [],
jitterAnalysis: null
@ -638,9 +634,9 @@ tap.test('PERF-09: Streaming Performance - should handle streaming operations ef
try {
const processStart = Date.now();
const format = await einvoice.detectFormat(item.content);
const invoice = await einvoice.parseInvoice(item.content, format || 'ubl');
await einvoice.validateInvoice(invoice);
const format = FormatDetector.detectFormat(item.content);
const invoice = await EInvoice.fromXml(item.content);
await invoice.validate();
const latency = Date.now() - item.arrivalTime;
latencies.push(latency);
@ -733,81 +729,79 @@ tap.test('PERF-09: Streaming Performance - should handle streaming operations ef
);
// Summary
t.comment('\n=== PERF-09: Streaming Performance Test Summary ===');
console.log('\n=== PERF-09: Streaming Performance Test Summary ===');
t.comment('\nStreaming XML Parsing:');
t.comment(' Stream Size | Items | Data | Duration | Memory | Peak | Throughput');
t.comment(' ------------|-------|---------|----------|--------|--------|----------');
streamingXMLParsing.result.tests.forEach(test => {
console.log('\nStreaming XML Parsing:');
console.log(' Stream Size | Items | Data | Duration | Memory | Peak | Throughput');
console.log(' ------------|-------|---------|----------|--------|--------|----------');
streamingXMLParsing.tests.forEach((test: any) => {
if (!test.error) {
t.comment(` ${test.size.padEnd(11)} | ${String(test.items).padEnd(5)} | ${test.totalBytes.padEnd(7)}KB | ${String(test.duration + 'ms').padEnd(8)} | ${test.memoryUsed.padEnd(6)}MB | ${test.peakMemory.padEnd(6)}MB | ${test.throughput}KB/s`);
console.log(` ${test.size.padEnd(11)} | ${String(test.items).padEnd(5)} | ${test.totalBytes.padEnd(7)}KB | ${String(test.duration + 'ms').padEnd(8)} | ${test.memoryUsed.padEnd(6)}MB | ${test.peakMemory.padEnd(6)}MB | ${test.throughput}KB/s`);
}
});
if (streamingXMLParsing.result.memoryEfficiency) {
t.comment(` Memory efficiency: ${streamingXMLParsing.result.memoryEfficiency.efficient ? 'GOOD ✅' : 'POOR ⚠️'}`);
t.comment(` Scaling: ${streamingXMLParsing.result.memoryEfficiency.memoryScaling}x memory for ${streamingXMLParsing.result.memoryEfficiency.itemScaling}x items`);
if (streamingXMLParsing.memoryEfficiency) {
console.log(` Memory efficiency: ${streamingXMLParsing.memoryEfficiency.efficient ? 'GOOD ✅' : 'POOR ⚠️'}`);
console.log(` Scaling: ${streamingXMLParsing.memoryEfficiency.memoryScaling}x memory for ${streamingXMLParsing.memoryEfficiency.itemScaling}x items`);
}
t.comment('\nStream Transformation Pipeline:');
streamTransformation.result.pipelines.forEach(pipeline => {
console.log('\nStream Transformation Pipeline:');
streamTransformation.pipelines.forEach((pipeline: any) => {
if (!pipeline.error) {
t.comment(` ${pipeline.name}:`);
t.comment(` - Stages: ${pipeline.stages}, Items: ${pipeline.itemsProcessed}`);
t.comment(` - Duration: ${pipeline.duration}ms, Throughput: ${pipeline.throughput}/s`);
t.comment(` - Valid: ${pipeline.validItems}, Errors: ${pipeline.errorItems}`);
console.log(` ${pipeline.name}:`);
console.log(` - Stages: ${pipeline.stages}, Items: ${pipeline.itemsProcessed}`);
console.log(` - Duration: ${pipeline.duration}ms, Throughput: ${pipeline.throughput}/s`);
console.log(` - Valid: ${pipeline.validItems}, Errors: ${pipeline.errorItems}`);
}
});
if (streamTransformation.result.transformationStats) {
t.comment(` Best pipeline: ${streamTransformation.result.transformationStats.bestPipeline} (${streamTransformation.result.transformationStats.bestThroughput}/s)`);
if (streamTransformation.transformationStats) {
console.log(` Best pipeline: ${streamTransformation.transformationStats.bestPipeline} (${streamTransformation.transformationStats.bestThroughput}/s)`);
}
t.comment('\nBackpressure Handling:');
t.comment(' Scenario | Duration | Produced | Consumed | Max Buffer | BP Events | Efficiency');
t.comment(' ----------------------------|----------|----------|----------|------------|-----------|----------');
backpressureHandling.result.scenarios.forEach(scenario => {
console.log('\nBackpressure Handling:');
console.log(' Scenario | Duration | Produced | Consumed | Max Buffer | BP Events | Efficiency');
console.log(' ----------------------------|----------|----------|----------|------------|-----------|----------');
backpressureHandling.scenarios.forEach((scenario: any) => {
if (!scenario.error) {
t.comment(` ${scenario.name.padEnd(27)} | ${String(scenario.duration + 'ms').padEnd(8)} | ${String(scenario.produced).padEnd(8)} | ${String(scenario.consumed).padEnd(8)} | ${String(scenario.maxBuffered).padEnd(10)} | ${String(scenario.backpressureEvents).padEnd(9)} | ${scenario.efficiency}%`);
console.log(` ${scenario.name.padEnd(27)} | ${String(scenario.duration + 'ms').padEnd(8)} | ${String(scenario.produced).padEnd(8)} | ${String(scenario.consumed).padEnd(8)} | ${String(scenario.maxBuffered).padEnd(10)} | ${String(scenario.backpressureEvents).padEnd(9)} | ${scenario.efficiency}%`);
}
});
if (backpressureHandling.result.backpressureStats) {
t.comment(` ${backpressureHandling.result.backpressureStats.recommendation}`);
if (backpressureHandling.backpressureStats) {
console.log(` ${backpressureHandling.backpressureStats.recommendation}`);
}
t.comment('\nCorpus Streaming Analysis:');
t.comment(` Streamable files: ${corpusStreaming.result.streamableFiles}`);
t.comment(` Non-streamable files: ${corpusStreaming.result.nonStreamableFiles}`);
if (corpusStreaming.result.comparison) {
t.comment(` Traditional avg: ${corpusStreaming.result.comparison.avgTraditionalTime}ms`);
t.comment(` Streamed avg: ${corpusStreaming.result.comparison.avgStreamedTime}ms`);
t.comment(` Overhead: ${corpusStreaming.result.comparison.overheadPercent}%`);
t.comment(` Large file improvement: ${corpusStreaming.result.comparison.largeFileImprovement}%`);
t.comment(` ${corpusStreaming.result.comparison.recommendation}`);
console.log('\nCorpus Streaming Analysis:');
console.log(` Streamable files: ${corpusStreaming.streamableFiles}`);
console.log(` Non-streamable files: ${corpusStreaming.nonStreamableFiles}`);
if (corpusStreaming.comparison) {
console.log(` Traditional avg: ${corpusStreaming.comparison.avgTraditionalTime}ms`);
console.log(` Streamed avg: ${corpusStreaming.comparison.avgStreamedTime}ms`);
console.log(` Overhead: ${corpusStreaming.comparison.overheadPercent}%`);
console.log(` Large file improvement: ${corpusStreaming.comparison.largeFileImprovement}%`);
console.log(` ${corpusStreaming.comparison.recommendation}`);
}
t.comment('\nReal-time Streaming:');
t.comment(' Rate | Target | Actual | Processed | Dropped | Avg Latency | P95 | Jitter');
t.comment(' ------------|--------|--------|-----------|---------|-------------|--------|-------');
realtimeStreaming.result.latencyTests.forEach(test => {
t.comment(` ${test.rate.padEnd(11)} | ${String(test.targetRate).padEnd(6)} | ${test.actualRate.padEnd(6)} | ${String(test.processed).padEnd(9)} | ${test.dropRate.padEnd(7)}% | ${test.avgLatency.padEnd(11)}ms | ${String(test.p95Latency).padEnd(6)}ms | ${test.avgJitter}ms`);
console.log('\nReal-time Streaming:');
console.log(' Rate | Target | Actual | Processed | Dropped | Avg Latency | P95 | Jitter');
console.log(' ------------|--------|--------|-----------|---------|-------------|--------|-------');
realtimeStreaming.latencyTests.forEach((test: any) => {
console.log(` ${test.rate.padEnd(11)} | ${String(test.targetRate).padEnd(6)} | ${test.actualRate.padEnd(6)} | ${String(test.processed).padEnd(9)} | ${test.dropRate.padEnd(7)}% | ${test.avgLatency.padEnd(11)}ms | ${String(test.p95Latency).padEnd(6)}ms | ${test.avgJitter}ms`);
});
if (realtimeStreaming.result.jitterAnalysis) {
t.comment(` System stability: ${realtimeStreaming.result.jitterAnalysis.stable ? 'STABLE ✅' : 'UNSTABLE ⚠️'}`);
t.comment(` ${realtimeStreaming.result.jitterAnalysis.recommendation}`);
if (realtimeStreaming.jitterAnalysis) {
console.log(` System stability: ${realtimeStreaming.jitterAnalysis.stable ? 'STABLE ✅' : 'UNSTABLE ⚠️'}`);
console.log(` ${realtimeStreaming.jitterAnalysis.recommendation}`);
}
// Performance targets check
t.comment('\n=== Performance Targets Check ===');
const streamingEfficient = streamingXMLParsing.result.memoryEfficiency?.efficient || false;
const realtimeStable = realtimeStreaming.result.jitterAnalysis?.stable || false;
console.log('\n=== Performance Targets Check ===');
const streamingEfficient = streamingXMLParsing.memoryEfficiency?.efficient || false;
const realtimeStable = realtimeStreaming.jitterAnalysis?.stable || false;
t.comment(`Streaming memory efficiency: ${streamingEfficient ? 'EFFICIENT ✅' : 'INEFFICIENT ⚠️'}`);
t.comment(`Real-time stability: ${realtimeStable ? 'STABLE ✅' : 'UNSTABLE ⚠️'}`);
console.log(`Streaming memory efficiency: ${streamingEfficient ? 'EFFICIENT ✅' : 'INEFFICIENT ⚠️'}`);
console.log(`Real-time stability: ${realtimeStable ? 'STABLE ✅' : 'UNSTABLE ⚠️'}`);
// Overall performance summary
t.comment('\n=== Overall Performance Summary ===');
performanceTracker.logSummary();
t.end();
console.log('\n=== Overall Performance Summary ===');
console.log(performanceTracker.getSummary());
});
tap.start();