Files
ocr_scanner/test/pipeline.test.js
T
vchuserandClaude Opus 5 1ec3ca4054 Ersetze deutsche Bezeichner durch englische im Quelltext
Projektvorgabe: Kommentare und Oberflaechentexte deutsch, Bezeichner im Code
englisch. Betroffen: src/barcode.js (vorbereitungsPromise -> readyPromise,
inkl. Verweis-Kommentar in ocr.js), sowie lokale Variablen in
test/spec-match.test.js, test/session.test.js und test/pipeline.test.js
(u.a. ocrAufgerufen -> ocrCalled, gefunden bei vollstaendigem Durchgang
aller Test-Dateien). Testbeschreibungen (Prosa) und Kommentare bleiben
unangetastet - nur echte Bezeichner wurden umbenannt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 18:24:30 +02:00

194 lines
7.5 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 ocrCalled = false;
const result = await recognize({}, {
decodeBarcodes: async () => ['M386A8K40BM1-CRC4Y'],
runOcr: async () => { ocrCalled = true; return ''; },
});
assert.equal(result.source, 'barcode');
assert.equal(result.confidence, 'green');
assert.equal(result.spec.capacityGb, 64);
assert.equal(ocrCalled, 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 () => ({ unexpected: 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 ocrCalled = 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 () => { ocrCalled = true; return ''; },
});
assert.equal(result.source, 'none');
assert.equal(result.confidence, 'red');
assert.deepEqual(result.spec, emptySpec());
assert.equal(ocrCalled, 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 ocrCalled = 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 () => { ocrCalled = 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(ocrCalled, 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');
});
// --- Beanstandung 1 (3. Runde): Mehrdeutigkeit bei unverwertbaren Barcodes ---
test('zwei widersprechende unverwertbare Barcodes: keiner erhaelt Vorrang, gelesene Teilenummer bleibt', async () => {
const result = await recognize({}, {
// Beide ohne bekanntes Nummernschema, und sie widersprechen sich -
// keiner darf die aus dem Klartext gelesene Teilenummer ueberschreiben.
decodeBarcodes: async () => ['7325773', '9999999'],
runOcr: async () => OCR_TEXT,
});
assert.equal(result.source, 'ocr');
assert.equal(result.confidence, 'yellow');
assert.equal(result.spec.partNumber, 'M386A8K40BM1-CRC4Y');
assert.equal(result.spec.capacityGb, 64);
});
test('zwei unverwertbare Barcodes mit gleicher Teilenummer: Vorrang bleibt bestehen', async () => {
const result = await recognize({}, {
decodeBarcodes: async () => ['7325773', '7325773'],
runOcr: async () => OCR_TEXT,
});
assert.equal(result.source, 'ocr');
assert.equal(result.confidence, 'yellow');
assert.equal(result.spec.partNumber, '7325773');
});
test('zwei unverwertbare Barcodes, die sich nur in einer Verwechslung unterscheiden, gelten als derselbe', async () => {
const result = await recognize({}, {
// 'O' und '0' sind eine typische Verwechslung (siehe CONFUSIONS in spec.js).
decodeBarcodes: async () => ['732O773', '7320773'],
runOcr: async () => OCR_TEXT,
});
assert.equal(result.source, 'ocr');
assert.equal(result.confidence, 'yellow');
assert.equal(result.spec.partNumber, '732O773');
});