Fix shared spec references in session; clarify undoLast contract
commitAssignment and moveEntry stored the same spec object in both stack.spec and entry.spec. A later, more complete scan enriching stack.spec would retroactively mutate the entry that first created the stack, making it appear as if it had been scanned with data it never actually had. Both places now store independent shallow copies so the entries log and the stacks spec (comparison baseline) can't leak into each other. Also documents that undoLast intentionally reverts the last *recorded* entry, not the last *action* - a subsequent moveEntry/removeEntry on a different entry does not change what undoLast will take back - and adds a test pinning that contract after a reorder.
This commit is contained in:
+12
-4
@@ -42,7 +42,7 @@ export function proposeAssignment(session, spec) {
|
|||||||
export function commitAssignment(session, spec, source, stackId) {
|
export function commitAssignment(session, spec, source, stackId) {
|
||||||
let stack = session.stacks.find((candidate) => candidate.id === stackId);
|
let stack = session.stacks.find((candidate) => candidate.id === stackId);
|
||||||
if (!stack) {
|
if (!stack) {
|
||||||
stack = { id: stackId, spec, count: 0 };
|
stack = { id: stackId, spec: { ...spec }, count: 0 };
|
||||||
session.stacks.push(stack);
|
session.stacks.push(stack);
|
||||||
session.stacks.sort((a, b) => a.id.localeCompare(b.id));
|
session.stacks.sort((a, b) => a.id.localeCompare(b.id));
|
||||||
} else {
|
} else {
|
||||||
@@ -55,7 +55,9 @@ export function commitAssignment(session, spec, source, stackId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stack.count += 1;
|
stack.count += 1;
|
||||||
const entry = { entryId: session.nextEntryId++, spec, stackId, source };
|
// entry.spec ist das Protokoll dessen, was tatsaechlich gescannt wurde, und muss
|
||||||
|
// unabhaengig vom Stapel-Spec bleiben - eigene Kopie, kein geteiltes Objekt.
|
||||||
|
const entry = { entryId: session.nextEntryId++, spec: { ...spec }, stackId, source };
|
||||||
session.entries.push(entry);
|
session.entries.push(entry);
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
@@ -64,7 +66,13 @@ function dropEmptyStacks(session) {
|
|||||||
session.stacks = session.stacks.filter((stack) => stack.count > 0);
|
session.stacks = session.stacks.filter((stack) => stack.count > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Nimmt den zuletzt erfassten Eintrag zurueck. */
|
/**
|
||||||
|
* Nimmt den zuletzt erfassten Eintrag zurueck - nicht die zuletzt ausgefuehrte
|
||||||
|
* Handlung. Die Oberflaeche zeigt neben der Rueckgaengig-Flaeche stets den
|
||||||
|
* zuletzt erfassten Eintrag an, und genau der wird hier zurueckgenommen. Ein
|
||||||
|
* zwischenzeitliches Umsortieren (moveEntry) oder Entfernen (removeEntry)
|
||||||
|
* eines anderen Eintrags bleibt davon unberuehrt.
|
||||||
|
*/
|
||||||
export function undoLast(session) {
|
export function undoLast(session) {
|
||||||
const entry = session.entries.pop();
|
const entry = session.entries.pop();
|
||||||
if (!entry) return null;
|
if (!entry) return null;
|
||||||
@@ -84,7 +92,7 @@ export function moveEntry(session, entryId, stackId) {
|
|||||||
|
|
||||||
let to = session.stacks.find((candidate) => candidate.id === stackId);
|
let to = session.stacks.find((candidate) => candidate.id === stackId);
|
||||||
if (!to) {
|
if (!to) {
|
||||||
to = { id: stackId, spec: entry.spec, count: 0 };
|
to = { id: stackId, spec: { ...entry.spec }, count: 0 };
|
||||||
session.stacks.push(to);
|
session.stacks.push(to);
|
||||||
session.stacks.sort((a, b) => a.id.localeCompare(b.id));
|
session.stacks.sort((a, b) => a.id.localeCompare(b.id));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,3 +102,48 @@ test('removeEntry entfernt einen einzelnen Eintrag', () => {
|
|||||||
assert.equal(session.entries.length, 1);
|
assert.equal(session.entries.length, 1);
|
||||||
assert.equal(session.stacks[0].count, 1);
|
assert.equal(session.stacks[0].count, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Ergaenzung des Stapel-Specs veraendert nicht das Spec eines frueheren Eintrags', () => {
|
||||||
|
const session = createSession();
|
||||||
|
const unvollstaendig = { ...s64(), partNumber: null };
|
||||||
|
const entry1 = scan(session, unvollstaendig);
|
||||||
|
const stack = session.stacks.find((s) => s.id === entry1.stackId);
|
||||||
|
assert.notEqual(stack.spec, entry1.spec, 'Stapel-Spec und Eintrags-Spec duerfen kein gemeinsames Objekt sein');
|
||||||
|
assert.deepEqual(stack.spec, entry1.spec, 'inhaltlich sind beide Specs zunaechst gleich');
|
||||||
|
assert.equal(entry1.spec.partNumber, null);
|
||||||
|
|
||||||
|
const vollstaendiger = s64();
|
||||||
|
scan(session, vollstaendiger);
|
||||||
|
|
||||||
|
assert.equal(entry1.spec.partNumber, null, 'das Spec des ersten Eintrags darf durch eine spaetere Ergaenzung des Stapel-Specs nicht veraendert werden');
|
||||||
|
assert.equal(stack.spec.partNumber, 'M386A8K40BM1-CRC4Y');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('moveEntry legt fuer einen neuen Stapel eine eigene Kopie des Specs an', () => {
|
||||||
|
const session = createSession();
|
||||||
|
const unvollstaendig = { ...s32(), partNumber: null };
|
||||||
|
const a = scan(session, unvollstaendig);
|
||||||
|
moveEntry(session, a.entryId, 'Z');
|
||||||
|
const stapelZ = session.stacks.find((s) => s.id === 'Z');
|
||||||
|
assert.notEqual(stapelZ.spec, a.spec, 'Stapel-Spec und Eintrags-Spec duerfen kein gemeinsames Objekt sein');
|
||||||
|
|
||||||
|
stapelZ.spec.partNumber = 'M393A4K40BB1-CTD';
|
||||||
|
assert.equal(a.spec.partNumber, null, 'eine Ergaenzung des neu angelegten Stapel-Specs darf das Eintrags-Spec nicht veraendern');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('undoLast-Vertrag: nach einem Umsortieren wird weiterhin der zuletzt erfasste Eintrag zurueckgenommen', () => {
|
||||||
|
const session = createSession();
|
||||||
|
const a = scan(session, s64());
|
||||||
|
const b = scan(session, s32());
|
||||||
|
moveEntry(session, a.entryId, 'B');
|
||||||
|
// Zuletzt ausgefuehrte Handlung war das Umsortieren von a; zuletzt erfasst wurde jedoch b.
|
||||||
|
const entfernt = undoLast(session);
|
||||||
|
assert.equal(entfernt.entryId, b.entryId, 'undoLast nimmt den zuletzt erfassten Eintrag zurueck, nicht die zuletzt ausgefuehrte Handlung (das Umsortieren)');
|
||||||
|
assert.equal(session.entries.length, 1);
|
||||||
|
assert.equal(session.entries[0].entryId, a.entryId);
|
||||||
|
assert.equal(session.entries[0].stackId, 'B', 'das Umsortieren von a bleibt von der Rueckname des unbeteiligten Eintrags b unberuehrt');
|
||||||
|
const stapelA = session.stacks.find((s) => s.id === 'A');
|
||||||
|
const stapelB = session.stacks.find((s) => s.id === 'B');
|
||||||
|
assert.equal(stapelA, undefined, 'Stapel A ist seit dem Umsortieren leer und wurde bereits entfernt');
|
||||||
|
assert.equal(stapelB.count, 1, 'Stapel B enthaelt weiterhin a, nur b wurde zurueckgenommen');
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user