Erzwinge innere Stimmigkeit in loadSession statt nur Typprüfung
Stapelzähler wird beim Laden immer aus den zugeordneten Einträgen neu berechnet statt aus dem gespeicherten Wert übernommen. Zusätzlich wird verworfen (null): Einträge mit unbekannter Stapelkennung, doppelte Eintragsnummern, doppelte Stapelkennungen und ein nextEntryId, der nicht größer als jede vorhandene Eintragsnummer ist. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -49,6 +49,15 @@ function isUsableEntry(entry) {
|
|||||||
* zurueckgegeben. Ein halb brauchbarer Zustand wird nicht repariert oder
|
* zurueckgegeben. Ein halb brauchbarer Zustand wird nicht repariert oder
|
||||||
* teilweise uebernommen - dann lieber die Sitzung verlieren (null) als mit
|
* teilweise uebernommen - dann lieber die Sitzung verlieren (null) als mit
|
||||||
* falschen Zaehlern oder doppelten entryIds lautlos weiterarbeiten.
|
* 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) {
|
export function loadSession(store) {
|
||||||
try {
|
try {
|
||||||
@@ -59,6 +68,22 @@ export function loadSession(store) {
|
|||||||
if (!isFiniteNumber(parsed.nextEntryId)) return null;
|
if (!isFiniteNumber(parsed.nextEntryId)) return null;
|
||||||
if (!parsed.stacks.every(isUsableStack)) return null;
|
if (!parsed.stacks.every(isUsableStack)) return null;
|
||||||
if (!parsed.entries.every(isUsableEntry)) 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;
|
return parsed;
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -108,6 +108,64 @@ test('Eintrag ohne Spec liefert null', () => {
|
|||||||
assert.equal(loadSession(store), 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', () => {
|
test('nach Sichern und Laden funktioniert Weiterarbeiten weiterhin', () => {
|
||||||
const store = fakeStore();
|
const store = fakeStore();
|
||||||
const session = createSession();
|
const session = createSession();
|
||||||
|
|||||||
Reference in New Issue
Block a user