Files
ocr_scanner/test/pipeline.test.js
T
vchuser be437a5fc5 Add adapter timeouts and drop unreachable spec-merge code
- Barcode- und OCR-Adapter erhalten je eine eigene, ueberschreibbare
  Zeitobergrenze (deps.barcodeTimeoutMs / deps.ocrTimeoutMs). Ein
  Adapter, der nie einloest, blockiert recognize() damit nicht mehr
  dauerhaft und faellt stattdessen auf den Ersatzwert zurueck, genau
  wie bei einem geworfenen Fehler. Kein offener Timer bleibt zurueck.
- mergeCompatibleSpecs entfernt: vertraegliche verwertbare
  Barcode-Treffer sind wegen der Kopplung von partNumber und den
  uebrigen Feldern ohnehin identisch, der erste Treffer genuegt.
  Test entsprechend umbenannt, um das tatsaechlich gepruefte
  Verhalten zu benennen statt eine nicht mehr vorhandene
  Auffuell-Semantik zu behaupten.
- Kommentar in pn-tables.js von einer inhaltlichen Wiederholung
  befreit.
2026-07-28 15:36:52 +02:00

158 lines
6.1 KiB
JavaScript

import { test } from 'node:test';
import assert from 'node:assert/strict';
import { recognize } from '../src/pipeline.js';
import { emptySpec } from '../src/spec.js';
const OCR_TEXT = '64GB 4DRx4 PC4-2400T-LD1-11-MC0 M386A8K40BM1-CRC4Y 1908';
const deps = ({ codes = [], text = '' }) => ({
decodeBarcodes: async () => codes,
runOcr: async () => text,
});
test('Barcode mit bekannter Teilenummer ergibt gruen und ueberspringt OCR', async () => {
let ocrAufgerufen = false;
const result = await recognize({}, {
decodeBarcodes: async () => ['M386A8K40BM1-CRC4Y'],
runOcr: async () => { ocrAufgerufen = true; return ''; },
});
assert.equal(result.source, 'barcode');
assert.equal(result.confidence, 'green');
assert.equal(result.spec.capacityGb, 64);
assert.equal(ocrAufgerufen, false, 'OCR darf bei gruenem Barcode nicht laufen');
});
test('Barcode ohne bekannte Teilenummer faellt auf OCR zurueck', async () => {
const result = await recognize({}, deps({ codes: ['7325773'], text: OCR_TEXT }));
assert.equal(result.source, 'ocr');
assert.equal(result.confidence, 'yellow');
assert.equal(result.spec.capacityGb, 64);
});
test('OCR-Ergebnis behaelt die Teilenummer aus dem Barcode bei', async () => {
const result = await recognize({}, deps({ codes: ['7325773'], text: '64GB 4DRx4 PC4-2400' }));
assert.equal(result.spec.partNumber, '7325773');
});
test('ohne Barcode und ohne brauchbaren Text ergibt rot', async () => {
const result = await recognize({}, deps({ codes: [], text: 'Made in Philippines' }));
assert.equal(result.source, 'none');
assert.equal(result.confidence, 'red');
});
test('OCR-Fehler fuehrt zu rot statt zu einem Absturz', async () => {
const result = await recognize({}, {
decodeBarcodes: async () => [],
runOcr: async () => { throw new Error('tesseract nicht geladen'); },
});
assert.equal(result.confidence, 'red');
assert.equal(result.source, 'none');
});
test('Barcode-Fehler fuehrt nicht zum Abbruch, OCR uebernimmt', async () => {
const result = await recognize({}, {
decodeBarcodes: async () => { throw new Error('zxing nicht geladen'); },
runOcr: async () => OCR_TEXT,
});
assert.equal(result.source, 'ocr');
assert.equal(result.spec.capacityGb, 64);
});
// --- Beanstandung 1: unerwartete Rueckgabeform statt eines Fehlers ---
test('decodeBarcodes liefert null statt einer Liste, kein Absturz', async () => {
const result = await recognize({}, {
decodeBarcodes: async () => null,
runOcr: async () => OCR_TEXT,
});
assert.equal(result.source, 'ocr');
assert.equal(result.confidence, 'yellow');
assert.equal(result.spec.capacityGb, 64);
});
test('decodeBarcodes liefert undefined statt einer Liste, kein Absturz', async () => {
const result = await recognize({}, {
decodeBarcodes: async () => undefined,
runOcr: async () => OCR_TEXT,
});
assert.equal(result.source, 'ocr');
assert.equal(result.confidence, 'yellow');
assert.equal(result.spec.capacityGb, 64);
});
test('decodeBarcodes liefert ein Objekt statt einer Liste, kein Absturz', async () => {
const result = await recognize({}, {
decodeBarcodes: async () => ({ notAList: true }),
runOcr: async () => OCR_TEXT,
});
assert.equal(result.source, 'ocr');
assert.equal(result.confidence, 'yellow');
assert.equal(result.spec.capacityGb, 64);
});
test('runOcr liefert keine Zeichenkette, wird wie leerer Text behandelt', async () => {
const result = await recognize({}, {
decodeBarcodes: async () => [],
runOcr: async () => ({ unerwartet: true }),
});
assert.equal(result.source, 'none');
assert.equal(result.confidence, 'red');
});
// --- Beanstandung 2: mehrere verwertbare Barcodes im selben Bild ---
test('mehrere unvertraegliche Barcodes ergeben rot statt eines ungeprueften ersten Treffers', async () => {
let ocrAufgerufen = false;
const result = await recognize({}, {
// Gleiche Bauform und Dichte, aber widersprechende Geschwindigkeit:
// zwei unterschiedliche Module im selben Bild.
decodeBarcodes: async () => ['M386A8K40BM1-CRC4Y', 'M386A8K40BM1-CWE4Y'],
runOcr: async () => { ocrAufgerufen = true; return ''; },
});
assert.equal(result.source, 'none');
assert.equal(result.confidence, 'red');
assert.deepEqual(result.spec, emptySpec());
assert.equal(ocrAufgerufen, false, 'bei mehrdeutigem Barcode-Ergebnis soll nicht geraten werden');
});
test('mehrere untereinander vertraegliche Barcodes ergeben gruen (der erste Treffer entscheidet, es gibt nichts zusammenzufuehren)', async () => {
let ocrAufgerufen = false;
const result = await recognize({}, {
// Zwei Lesungen desselben Etiketts (z.B. Mehrfachtreffer durch
// Spiegelung) - inhaltlich identisch, nur die Schreibweise weicht
// an einer verwechslungstoleranten Stelle ab (B/8).
decodeBarcodes: async () => ['M386A8K40BM1-CRC4Y', 'M386A8K408M1-CRC4Y'],
runOcr: async () => { ocrAufgerufen = true; return ''; },
});
assert.equal(result.source, 'barcode');
assert.equal(result.confidence, 'green');
assert.equal(result.spec.capacityGb, 64);
assert.equal(result.spec.formFactor, 'LRDIMM');
assert.equal(result.spec.rank, '4DRx4');
assert.equal(result.spec.speed, 'PC4-2400');
assert.equal(ocrAufgerufen, false, 'OCR darf bei gruenem Mehrfachtreffer nicht laufen');
});
// --- Beanstandung 1: haengender Adapter darf die Erkennung nicht dauerhaft blockieren ---
test('haengender Barcode-Adapter blockiert nicht dauerhaft, OCR uebernimmt', { timeout: 500 }, async () => {
const result = await recognize({}, {
decodeBarcodes: () => new Promise(() => {}), // loest nie ein und wirft nie
runOcr: async () => OCR_TEXT,
barcodeTimeoutMs: 20,
});
assert.equal(result.source, 'ocr');
assert.equal(result.confidence, 'yellow');
assert.equal(result.spec.capacityGb, 64);
});
test('haengender OCR-Adapter blockiert nicht dauerhaft, Erkennung ergibt rot', { timeout: 500 }, async () => {
const result = await recognize({}, {
decodeBarcodes: async () => [],
runOcr: () => new Promise(() => {}), // loest nie ein und wirft nie
ocrTimeoutMs: 20,
});
assert.equal(result.source, 'none');
assert.equal(result.confidence, 'red');
});