Files
ocr_scanner/test/storage.test.js
T
vchuser 61dde3eb08 Stapel merken sich gelesene Barcode-Inhalte, entscheiden die Zuordnung
Jeder Stapel fuehrt jetzt die Menge der Barcode-Zeichenketten, die bei
seinen Modulen gelesen wurden (stack.codes). Traegt ein gescanntes Modul
einen Code, den ein Stapel bereits kennt, hat das Vorrang vor dem Vergleich
der technischen Angaben - ein Barcode ist exakt gelesen, abgeleitete Angaben
koennen fehlerbehaftet sein. Genau ein Stapel ueber einen bekannten Code
-> Zuweisung, auch bei unvollstaendigen Angaben; mehrere -> weiterhin
mehrdeutig, der Nutzer entscheidet; kein passender Code -> unveraendert die
bisherige Regel. Eine Seriennummer, die bei jedem Modul anders ist, laeuft
dabei einfach ins Leere, ohne die Zuordnung ueber die Teilenummer zu
verhindern.

proposeAssignment und commitAssignment bekommen dafuer einen neuen, optional
en codes-Parameter (Vorgabe []); bestehende Aufrufe ohne diesen Parameter
verhalten sich unveraendert.

storage.js sichert und prueft stack.codes jetzt mit: fehlt das Feld (Stand
aus einer aelteren Fassung), gilt der Stapel als ohne bekannte Codes statt
den ganzen Stand zu verwerfen; ist es vorhanden, muss es eine Liste von
Zeichenketten sein wie jedes andere Feld auch.
2026-07-29 09:05:23 +02:00

311 lines
10 KiB
JavaScript

import { test } from 'node:test';
import assert from 'node:assert/strict';
import { saveSession, loadSession, clearSession } from '../src/storage.js';
import {
createSession,
commitAssignment,
proposeAssignment,
moveEntry,
removeEntry,
} from '../src/session.js';
import { emptySpec } from '../src/spec.js';
function fakeStore() {
const data = new Map();
return {
data,
getItem: (key) => (data.has(key) ? data.get(key) : null),
setItem: (key, value) => data.set(key, value),
removeItem: (key) => data.delete(key),
};
}
test('leerer Speicher liefert null', () => {
assert.equal(loadSession(fakeStore()), null);
});
test('Sitzung ueberlebt Sichern und Laden', () => {
const store = fakeStore();
const session = createSession();
commitAssignment(session, { ...emptySpec(), capacityGb: 64 }, 'barcode', 'A');
saveSession(session, store);
const reloaded = loadSession(store);
assert.equal(reloaded.stacks.length, 1);
assert.equal(reloaded.stacks[0].count, 1);
assert.equal(reloaded.entries[0].spec.capacityGb, 64);
assert.equal(reloaded.nextEntryId, 2);
});
test('clearSession raeumt auf', () => {
const store = fakeStore();
saveSession(createSession(), store);
clearSession(store);
assert.equal(loadSession(store), null);
});
test('kaputter Inhalt liefert null statt einer Ausnahme', () => {
const store = fakeStore();
store.setItem('ram-sortierhilfe:session', '{kein json');
assert.equal(loadSession(store), null);
});
test('Sichern ohne funktionierenden Speicher wirft nicht', () => {
const broken = {
getItem: () => null,
setItem: () => { throw new Error('quota exceeded'); },
removeItem: () => {},
};
assert.doesNotThrow(() => saveSession(createSession(), broken));
});
test('stacks mit Nicht-Objekten liefert null', () => {
const store = fakeStore();
store.setItem('ram-sortierhilfe:session', JSON.stringify({
stacks: ['x', 'y'],
entries: [],
nextEntryId: 1,
}));
assert.equal(loadSession(store), null);
});
test('fehlender nextEntryId liefert null', () => {
const store = fakeStore();
store.setItem('ram-sortierhilfe:session', JSON.stringify({
stacks: [],
entries: [],
}));
assert.equal(loadSession(store), null);
});
test('nextEntryId als Zeichenkette liefert null', () => {
const store = fakeStore();
store.setItem('ram-sortierhilfe:session', JSON.stringify({
stacks: [],
entries: [],
nextEntryId: '2',
}));
assert.equal(loadSession(store), null);
});
test('Stapel mit Zaehler als Zeichenkette liefert null', () => {
const store = fakeStore();
store.setItem('ram-sortierhilfe:session', JSON.stringify({
stacks: [{ id: 'A', count: '3', spec: emptySpec() }],
entries: [],
nextEntryId: 1,
}));
assert.equal(loadSession(store), null);
});
test('Eintrag ohne Spec liefert null', () => {
const store = fakeStore();
store.setItem('ram-sortierhilfe:session', JSON.stringify({
stacks: [],
entries: [{ entryId: 1, stackId: 'A' }],
nextEntryId: 2,
}));
assert.equal(loadSession(store), null);
});
test('Stapelzaehler wird beim Laden aus den Eintraegen neu berechnet', () => {
const store = fakeStore();
store.setItem('ram-sortierhilfe:session', JSON.stringify({
stacks: [{ id: 'A', count: 5, spec: emptySpec() }],
entries: [{ entryId: 1, stackId: 'A', spec: emptySpec(), source: 'barcode' }],
nextEntryId: 2,
}));
const reloaded = loadSession(store);
assert.notEqual(reloaded, null);
assert.equal(reloaded.stacks[0].count, 1);
});
test('Stapelzaehler werden beim Laden fuer mehrere Stapel unabhaengig neu berechnet, auch fuer einen ohne Eintraege', () => {
const store = fakeStore();
store.setItem('ram-sortierhilfe:session', JSON.stringify({
stacks: [
{ id: 'A', count: 99, spec: emptySpec() },
{ id: 'B', count: 99, spec: emptySpec() },
{ id: 'C', count: 99, spec: emptySpec() },
],
entries: [
{ entryId: 1, stackId: 'A', spec: emptySpec(), source: 'barcode' },
{ entryId: 2, stackId: 'A', spec: emptySpec(), source: 'barcode' },
{ entryId: 3, stackId: 'B', spec: emptySpec(), source: 'ocr' },
],
nextEntryId: 4,
}));
const reloaded = loadSession(store);
assert.notEqual(reloaded, null);
assert.equal(reloaded.stacks.find((s) => s.id === 'A').count, 2, 'Stapel A hat zwei zugeordnete Eintraege');
assert.equal(reloaded.stacks.find((s) => s.id === 'B').count, 1, 'Stapel B hat einen zugeordneten Eintrag');
assert.equal(reloaded.stacks.find((s) => s.id === 'C').count, 0, 'Stapel C ohne jeden Eintrag wird auf 0 neu berechnet, nicht auf den gespeicherten Wert');
});
test('Eintrag mit unbekannter Stapelkennung liefert null', () => {
const store = fakeStore();
store.setItem('ram-sortierhilfe:session', JSON.stringify({
stacks: [],
entries: [{ entryId: 1, stackId: 'Z', spec: emptySpec(), source: 'barcode' }],
nextEntryId: 2,
}));
assert.equal(loadSession(store), null);
});
test('doppelte Eintragsnummern liefert null', () => {
const store = fakeStore();
store.setItem('ram-sortierhilfe:session', JSON.stringify({
stacks: [{ id: 'A', count: 2, spec: emptySpec() }],
entries: [
{ entryId: 1, stackId: 'A', spec: emptySpec(), source: 'barcode' },
{ entryId: 1, stackId: 'A', spec: emptySpec(), source: 'barcode' },
],
nextEntryId: 2,
}));
assert.equal(loadSession(store), null);
});
test('doppelte Stapelkennungen liefert null', () => {
const store = fakeStore();
store.setItem('ram-sortierhilfe:session', JSON.stringify({
stacks: [
{ id: 'A', count: 0, spec: emptySpec() },
{ id: 'A', count: 0, spec: emptySpec() },
],
entries: [],
nextEntryId: 1,
}));
assert.equal(loadSession(store), null);
});
test('nextEntryId nicht groesser als vorhandene Eintragsnummer liefert null', () => {
const store = fakeStore();
store.setItem('ram-sortierhilfe:session', JSON.stringify({
stacks: [{ id: 'A', count: 1, spec: emptySpec() }],
entries: [{ entryId: 5, stackId: 'A', spec: emptySpec(), source: 'barcode' }],
nextEntryId: 5,
}));
assert.equal(loadSession(store), null);
});
test('nach Sichern und Laden funktioniert Weiterarbeiten weiterhin', () => {
const store = fakeStore();
const session = createSession();
const spec = { ...emptySpec(), capacityGb: 64 };
commitAssignment(session, spec, 'barcode', 'A');
const secondEntry = commitAssignment(session, spec, 'barcode', 'A');
saveSession(session, store);
const reloaded = loadSession(store);
const proposal = proposeAssignment(reloaded, spec);
assert.equal(proposal.kind, 'match');
assert.equal(proposal.stackId, 'A');
commitAssignment(reloaded, spec, 'barcode', proposal.stackId);
assert.equal(reloaded.stacks[0].count, 3);
assert.equal(reloaded.nextEntryId, 4);
moveEntry(reloaded, secondEntry.entryId, 'B');
assert.equal(reloaded.stacks.find((s) => s.id === 'A').count, 2);
assert.equal(reloaded.stacks.find((s) => s.id === 'B').count, 1);
removeEntry(reloaded, secondEntry.entryId);
assert.equal(reloaded.entries.find((e) => e.entryId === secondEntry.entryId), undefined);
assert.equal(reloaded.stacks.find((s) => s.id === 'B'), undefined);
});
test('nextEntryId als negative Zahl liefert null', () => {
const store = fakeStore();
store.setItem('ram-sortierhilfe:session', JSON.stringify({
stacks: [],
entries: [],
nextEntryId: -5,
}));
assert.equal(loadSession(store), null);
});
test('nextEntryId als gebrochene Zahl liefert null', () => {
const store = fakeStore();
store.setItem('ram-sortierhilfe:session', JSON.stringify({
stacks: [],
entries: [],
nextEntryId: 1.5,
}));
assert.equal(loadSession(store), null);
});
test('nextEntryId als Null liefert null', () => {
const store = fakeStore();
store.setItem('ram-sortierhilfe:session', JSON.stringify({
stacks: [],
entries: [],
nextEntryId: 0,
}));
assert.equal(loadSession(store), null);
});
// --- Erweiterung: Barcode-Inhalte je Stapel ueberleben Sichern und Laden ---
test('Codes eines Stapels ueberleben Sichern und Laden', () => {
const store = fakeStore();
const session = createSession();
commitAssignment(
session,
{ ...emptySpec(), partNumber: 'HMA84GL7AFR4N-UH' },
'barcode',
'A',
['HMA84GL7AFR4N-UH', 'SN-000'],
);
saveSession(session, store);
const reloaded = loadSession(store);
assert.notEqual(reloaded, null);
assert.deepEqual([...reloaded.stacks[0].codes].sort(), ['HMA84GL7AFR4N-UH', 'SN-000'].sort());
const plan = proposeAssignment(reloaded, emptySpec(), ['HMA84GL7AFR4N-UH']);
assert.equal(plan.kind, 'match');
assert.equal(plan.stackId, 'A');
});
test('ein Stand ohne codes-Feld (aeltere Fassung) wird nicht verworfen, der Stapel gilt als ohne bekannte Codes', () => {
const store = fakeStore();
store.setItem('ram-sortierhilfe:session', JSON.stringify({
stacks: [{ id: 'A', count: 1, spec: { ...emptySpec(), capacityGb: 64 } }],
entries: [{ entryId: 1, stackId: 'A', spec: { ...emptySpec(), capacityGb: 64 }, source: 'barcode' }],
nextEntryId: 2,
}));
const reloaded = loadSession(store);
assert.notEqual(reloaded, null, 'ein Stand ohne codes-Feld darf nicht verworfen werden');
assert.deepEqual(reloaded.stacks[0].codes, []);
});
test('Stapel mit codes als Nicht-Array liefert null', () => {
const store = fakeStore();
store.setItem('ram-sortierhilfe:session', JSON.stringify({
stacks: [{ id: 'A', count: 0, spec: emptySpec(), codes: 'nicht-array' }],
entries: [],
nextEntryId: 1,
}));
assert.equal(loadSession(store), null);
});
test('Stapel mit codes-Array, das Nicht-Zeichenketten enthaelt, liefert null', () => {
const store = fakeStore();
store.setItem('ram-sortierhilfe:session', JSON.stringify({
stacks: [{ id: 'A', count: 0, spec: emptySpec(), codes: [123] }],
entries: [],
nextEntryId: 1,
}));
assert.equal(loadSession(store), null);
});
test('Eintrag mit gebrochener entryId liefert null', () => {
const store = fakeStore();
store.setItem('ram-sortierhilfe:session', JSON.stringify({
stacks: [{ id: 'A', count: 1, spec: emptySpec() }],
entries: [{ entryId: 1.5, stackId: 'A', spec: emptySpec(), source: 'barcode' }],
nextEntryId: 2,
}));
assert.equal(loadSession(store), null);
});