Verhindere Passt-auf-alles-Stapel ohne gemeinsame Merkmale
proposeAssignment verlangt jetzt zusätzlich zur bisherigen Verträglichkeitsprüfung, dass Stapel und Modul mindestens ein Merkmal gemeinsam belegt haben. Ein Stapel mit vollständig leerem Spec (Rot-Fall, "neuer Stapel") hatte sonst mit jedem Modul null gemeinsame Felder und galt fälschlich als verträglich mit allem. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+18
-2
@@ -18,15 +18,31 @@ function nextStackId(session) {
|
|||||||
return `A${session.stacks.length}`;
|
return `A${session.stacks.length}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FINGERPRINT_FIELDS = ['capacityGb', 'formFactor', 'rank', 'speed', 'partNumber'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ein Stapel ist nur dann Kandidat, wenn er mit dem Modul mindestens ein
|
||||||
|
* Merkmal gemeinsam belegt hat. Ein Stapel ohne jede erkannte Angabe (z.B.
|
||||||
|
* aus einem Rot-Fall mit leerem Spec entstanden) hat mit jedem Modul null
|
||||||
|
* gemeinsame Felder und darf deshalb nie als "passt" gelten - sonst wuerde
|
||||||
|
* er lautlos alles an sich ziehen.
|
||||||
|
*/
|
||||||
|
function sharesSetField(a, b) {
|
||||||
|
return FINGERPRINT_FIELDS.some((field) => (
|
||||||
|
a[field] !== null && a[field] !== undefined
|
||||||
|
&& b[field] !== null && b[field] !== undefined
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schlaegt vor, wohin ein Modul gehoert.
|
* Schlaegt vor, wohin ein Modul gehoert.
|
||||||
* Genau ein vertraeglicher Stapel -> Zuweisung.
|
* Genau ein vertraeglicher Stapel mit gemeinsamer Grundlage -> Zuweisung.
|
||||||
* Keiner -> neuer Stapel.
|
* Keiner -> neuer Stapel.
|
||||||
* Mehrere -> mehrdeutig, der Nutzer entscheidet.
|
* Mehrere -> mehrdeutig, der Nutzer entscheidet.
|
||||||
*/
|
*/
|
||||||
export function proposeAssignment(session, spec) {
|
export function proposeAssignment(session, spec) {
|
||||||
const candidates = session.stacks
|
const candidates = session.stacks
|
||||||
.filter((stack) => specsCompatible(stack.spec, spec))
|
.filter((stack) => specsCompatible(stack.spec, spec) && sharesSetField(stack.spec, spec))
|
||||||
.map((stack) => stack.id);
|
.map((stack) => stack.id);
|
||||||
|
|
||||||
if (candidates.length === 1) {
|
if (candidates.length === 1) {
|
||||||
|
|||||||
@@ -131,6 +131,33 @@ test('moveEntry legt fuer einen neuen Stapel eine eigene Kopie des Specs an', ()
|
|||||||
assert.equal(a.spec.partNumber, null, 'eine Ergaenzung des neu angelegten Stapel-Specs darf das Eintrags-Spec nicht veraendern');
|
assert.equal(a.spec.partNumber, null, 'eine Ergaenzung des neu angelegten Stapel-Specs darf das Eintrags-Spec nicht veraendern');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('ein Stapel ohne jede erkannte Angabe ist fuer kein Modul Kandidat', () => {
|
||||||
|
const session = createSession();
|
||||||
|
scan(session, emptySpec()); // Stapel A, vollstaendig leeres Spec (Rot-Fall)
|
||||||
|
const plan = proposeAssignment(session, s64());
|
||||||
|
assert.equal(plan.kind, 'new', 'ein leerer Stapel darf keine Kandidatur begruenden');
|
||||||
|
assert.notEqual(plan.stackId, 'A');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ein Stapel mit nur der Kapazitaet ist Kandidat fuer ein Modul gleicher Kapazitaet', () => {
|
||||||
|
const session = createSession();
|
||||||
|
scan(session, { ...emptySpec(), capacityGb: 64 });
|
||||||
|
const plan = proposeAssignment(session, { ...emptySpec(), capacityGb: 64 });
|
||||||
|
assert.equal(plan.kind, 'match');
|
||||||
|
assert.equal(plan.stackId, 'A');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ein voellig unerkanntes Modul erzeugt einen eigenen Stapel und zieht keine weiteren an sich', () => {
|
||||||
|
const session = createSession();
|
||||||
|
const erster = scan(session, emptySpec());
|
||||||
|
const zweiter = scan(session, emptySpec());
|
||||||
|
assert.notEqual(erster.stackId, zweiter.stackId, 'zwei unerkannte Module ohne gemeinsame Grundlage teilen sich keinen Stapel');
|
||||||
|
assert.equal(session.stacks.length, 2);
|
||||||
|
|
||||||
|
const plan = proposeAssignment(session, s64());
|
||||||
|
assert.equal(plan.kind, 'new', 'der leere Stapel darf ein erkanntes Modul nicht an sich ziehen');
|
||||||
|
});
|
||||||
|
|
||||||
test('undoLast-Vertrag: nach einem Umsortieren wird weiterhin der zuletzt erfasste Eintrag zurueckgenommen', () => {
|
test('undoLast-Vertrag: nach einem Umsortieren wird weiterhin der zuletzt erfasste Eintrag zurueckgenommen', () => {
|
||||||
const session = createSession();
|
const session = createSession();
|
||||||
const a = scan(session, s64());
|
const a = scan(session, s64());
|
||||||
|
|||||||
Reference in New Issue
Block a user