mirror of
https://github.com/morluto/rea.git
synced 2026-11-05 22:36:08 +00:00
fix(environment): reject contradictory learned sequences
This commit is contained in:
1 parent
420bae068b
commit
ba8f1a236e
5 files changed
+37
No files matched your search
@@ -296,6 +296,7 @@ export class EnvironmentStateWriteSimulation {
|
||||
updates: [],
|
||||
sequences: new Map(),
|
||||
starts: new Map(),
|
||||
createCollections: [],
|
||||
steady: new Set(),
|
||||
unstamped: new Set(),
|
||||
kept: new Set(),
|
||||
|
||||
@@ -29,6 +29,29 @@ const ruleFor = (plan: ReturnType<typeof createOf>, field: string): unknown =>
|
||||
plan.rules.find((rule) => rule.field === field);
|
||||
|
||||
describe("values an application counts and makes with each created entity", () => {
|
||||
it("does not adopt a sequence that contradicts an earlier accepted create", () => {
|
||||
const a = { id: 1, title: "A", index: 1 };
|
||||
const b = { id: 2, title: "B", index: 2 };
|
||||
const c = { id: 3, title: "C", index: 15 };
|
||||
const d = { id: 4, title: "D", index: 15 };
|
||||
const e = { id: 5, title: "E", index: 16 };
|
||||
const rows: readonly TimedCaptureRow[] = [
|
||||
["GET", "/api/tasks", [a, b]],
|
||||
["GET", "/api/tasks/1", a],
|
||||
["POST", "/api/tasks", c, 201, { title: "C" }],
|
||||
["GET", "/api/tasks/3", c],
|
||||
["GET", "/api/tasks", [a, b, c]],
|
||||
["POST", "/api/tasks", d, 201, { title: "D" }],
|
||||
["GET", "/api/tasks/4", d],
|
||||
["GET", "/api/tasks", [a, b, c, d]],
|
||||
["POST", "/api/tasks", e, 201, { title: "E" }],
|
||||
["GET", "/api/tasks/5", e],
|
||||
["GET", "/api/tasks", [a, b, c, d, e]],
|
||||
];
|
||||
const plan = createOf(rows);
|
||||
expect(plan).toMatchObject({ served: false });
|
||||
expect(ruleFor(plan, "index")).toBeUndefined();
|
||||
});
|
||||
it("learns a counter from where the first create stood, a template around it and the object made with each task", () => {
|
||||
const plan = createOf(COUNTED_TASK_ROWS);
|
||||
expect(plan).toMatchObject({
|
||||
|
||||
@@ -311,6 +311,7 @@ const earlierOf = (draft: WriteDraft) => ({
|
||||
kept: draft.kept,
|
||||
clockOf: (field: string) => clockEvidence(draft, field),
|
||||
startOf: (field: string) => draft.starts.get(field),
|
||||
createCollections: draft.createCollections,
|
||||
ampersands: draft.ampersands,
|
||||
});
|
||||
|
||||
@@ -968,6 +969,8 @@ export const applyCreate = (
|
||||
draft.plan.rules,
|
||||
);
|
||||
const inserted = insertCreated(context, draft, entity(stored, id));
|
||||
context.io.retain(collection.length * 2 + 16);
|
||||
draft.createCollections.push(collection);
|
||||
return {
|
||||
kind: "applied",
|
||||
targets: [inserted, collectionTarget(type, collection)],
|
||||
|
||||
@@ -32,6 +32,7 @@ export interface EarlierEvidence {
|
||||
readonly clockOf: (field: string) => JsonValue | undefined;
|
||||
/** The integer the exemplar showed in an unruled field above every value its type held, where a sequence may start. */
|
||||
readonly startOf?: (field: string) => number | undefined;
|
||||
readonly createCollections?: readonly string[];
|
||||
/** How the plan's accepted calls and the type's held entities spelled `&` in each field's slug; absent where none showed one. */
|
||||
readonly ampersands?: ReadonlyMap<string, AmpersandSpelling>;
|
||||
}
|
||||
@@ -269,6 +270,13 @@ const spellsExemplar = (
|
||||
}
|
||||
if (rule.rule !== "sequence") return undefined;
|
||||
const start = calls.earlier.startOf?.(field);
|
||||
const collections = calls.earlier.createCollections ?? [];
|
||||
if (
|
||||
(rule.scope === "type" && collections.length > 1) ||
|
||||
(rule.scope === "collection" &&
|
||||
new Set(collections).size !== collections.length)
|
||||
)
|
||||
return undefined;
|
||||
return start !== undefined && sequenceNumber(value) === start
|
||||
? { ...rule, start }
|
||||
: undefined;
|
||||
|
||||
@@ -96,6 +96,8 @@ export interface WriteDraft {
|
||||
readonly sequences: Map<string, number>;
|
||||
/** Integers the exemplar showed in fields no rule wrote, above every value its type held, where a later call's sequence may start. */
|
||||
readonly starts: Map<string, number>;
|
||||
/** Collections of accepted create calls, used to check late sequence adoption. */
|
||||
readonly createCollections: string[];
|
||||
/** The collection a create writes into when its own path names no state type: the one its returned id was read under. */
|
||||
readonly linked?: string | undefined;
|
||||
/** Fields an item action's call found already at the value it sets them to. */
|
||||
|
||||
Reference in new issue
Block a user