diff --git a/src/storage.js b/src/storage.js index 9b5b469..422db13 100644 --- a/src/storage.js +++ b/src/storage.js @@ -49,6 +49,15 @@ function isUsableEntry(entry) { * zurueckgegeben. Ein halb brauchbarer Zustand wird nicht repariert oder * teilweise uebernommen - dann lieber die Sitzung verlieren (null) als mit * falschen Zaehlern oder doppelten entryIds lautlos weiterarbeiten. + * + * Der gespeicherte Stapelzaehler (stack.count) wird nie uebernommen, sondern + * beim Laden stets aus der Zahl der ihm tatsaechlich zugeordneten Eintraege + * neu berechnet - er dient in isUsableStack nur noch dazu, grob kaputten + * Inhalt schon an der Typprueung scheitern zu lassen. Zeigt ein Eintrag auf + * eine Stapelkennung, die es nicht gibt, kommt eine Eintragsnummer oder eine + * Stapelkennung mehrfach vor, oder ist nextEntryId nicht groesser als jede + * vorhandene Eintragsnummer, ist der Inhalt in sich unstimmig und wird + * komplett verworfen (null) statt lautlos falsch weiterverwendet zu werden. */ export function loadSession(store) { try { @@ -59,6 +68,22 @@ export function loadSession(store) { if (!isFiniteNumber(parsed.nextEntryId)) return null; if (!parsed.stacks.every(isUsableStack)) return null; if (!parsed.entries.every(isUsableEntry)) return null; + + const stackIds = parsed.stacks.map((stack) => stack.id); + if (new Set(stackIds).size !== stackIds.length) return null; + + const entryIds = parsed.entries.map((entry) => entry.entryId); + if (new Set(entryIds).size !== entryIds.length) return null; + + const stackIdSet = new Set(stackIds); + if (!parsed.entries.every((entry) => stackIdSet.has(entry.stackId))) return null; + + if (entryIds.some((entryId) => entryId >= parsed.nextEntryId)) return null; + + for (const stack of parsed.stacks) { + stack.count = parsed.entries.filter((entry) => entry.stackId === stack.id).length; + } + return parsed; } catch { return null; diff --git a/test/storage.test.js b/test/storage.test.js index 4822897..9aec02c 100644 --- a/test/storage.test.js +++ b/test/storage.test.js @@ -108,6 +108,64 @@ test('Eintrag ohne Spec liefert null', () => { 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 wieder = loadSession(store); + assert.notEqual(wieder, null); + assert.equal(wieder.stacks[0].count, 1); +}); + +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();