update
This commit is contained in:
@ -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();
|
Reference in New Issue
Block a user