mirror of
https://github.com/morluto/rea.git
synced 2026-11-15 11:13:21 +00:00
fix(environment): check recorded member and sequence values
This commit is contained in:
1 parent
310878cc06
commit
af1bfc2ee8
3 files changed
+78
-6
No files matched your search
@@ -80,4 +80,60 @@ describe("writes whose calls send different members", () => {
|
||||
expect(plan).toMatchObject({ served: true });
|
||||
expect(plan.body?.defaults).toEqual([{ member: "owner", value: null }]);
|
||||
});
|
||||
|
||||
it("recognizes a sent array when learning a later omitted-member default", () => {
|
||||
const a = { id: 1, name: "A", tags: ["x"] };
|
||||
const b = { id: 2, name: "B", tags: ["y"] };
|
||||
const changedA = { ...a, name: "AA" };
|
||||
const changedB = { ...b, name: "BB", tags: [] };
|
||||
const rows: readonly TimedCaptureRow[] = [
|
||||
["GET", "/api/tags", [a, b]],
|
||||
["GET", "/api/tags/1", a],
|
||||
["GET", "/api/tags/2", b],
|
||||
["PUT", "/api/tags/1", changedA, 200, { name: "AA", tags: ["x"] }],
|
||||
["GET", "/api/tags/1", changedA],
|
||||
["PUT", "/api/tags/2", changedB, 200, { name: "BB" }],
|
||||
["GET", "/api/tags/2", changedB],
|
||||
["GET", "/api/tags", [changedA, changedB]],
|
||||
];
|
||||
expect(planOf(rows, "put_tags_by_id")).toMatchObject({ served: true });
|
||||
});
|
||||
|
||||
it("withholds a sent sequence value contradicted by its recorded answer", () => {
|
||||
const rows: readonly TimedCaptureRow[] = [
|
||||
...NUMBERED_UNLESS_SENT_ROWS.map((row) =>
|
||||
row[0] === "GET" && row[1] === "/api/bundles" && Array.isArray(row[2])
|
||||
? ([
|
||||
row[0],
|
||||
row[1],
|
||||
(row[2] as { id: number; name: string }[]).map(
|
||||
({ id, name }) => ({ id, name }),
|
||||
),
|
||||
] as TimedCaptureRow)
|
||||
: row,
|
||||
),
|
||||
[
|
||||
"POST",
|
||||
"/api/bundles",
|
||||
{ id: 5, name: "Echo", order: 10 },
|
||||
201,
|
||||
{ name: "Echo", order: 9 },
|
||||
],
|
||||
[
|
||||
"GET",
|
||||
"/api/bundles",
|
||||
[
|
||||
{ id: 1, name: "Alpha" },
|
||||
{ id: 2, name: "Bravo" },
|
||||
{ id: 3, name: "Charlie" },
|
||||
{ id: 4, name: "Delta" },
|
||||
{ id: 5, name: "Echo" },
|
||||
],
|
||||
],
|
||||
];
|
||||
expect(planOf(rows, "post_bundles")).toMatchObject({
|
||||
served: false,
|
||||
reason: "unexplained-change",
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -105,6 +105,7 @@ const sameObserved = (left: ObservedValue, right: ObservedValue): boolean =>
|
||||
export interface UpdateCall {
|
||||
readonly shown: ReadonlyMap<string, ObservedValue>;
|
||||
readonly sent: ReadonlyMap<string, JsonValue>;
|
||||
readonly sentMembers: ReadonlySet<string>;
|
||||
readonly kept: readonly string[];
|
||||
}
|
||||
|
||||
@@ -337,7 +338,7 @@ export const defaultsDisagree = (
|
||||
({ member, value }) =>
|
||||
calls.earlier.some(
|
||||
(call) =>
|
||||
!call.sent.has(member) &&
|
||||
!call.sentMembers.has(member) &&
|
||||
call.shown.has(member) &&
|
||||
!same(call.shown.get(member), observedValue(value)),
|
||||
) ||
|
||||
@@ -376,8 +377,22 @@ export const rememberCall = (
|
||||
]),
|
||||
);
|
||||
for (const field of unstamped) shown.delete(field);
|
||||
const facts = { shown, sent: scalars(call.sent), kept };
|
||||
retain(64 + jsonTextCodeUnits([...facts.shown, ...facts.sent, kept]) * 2);
|
||||
const facts = {
|
||||
shown,
|
||||
sent: scalars(call.sent),
|
||||
sentMembers: new Set(Object.keys(call.sent)),
|
||||
kept,
|
||||
};
|
||||
retain(
|
||||
64 +
|
||||
jsonTextCodeUnits([
|
||||
...facts.shown,
|
||||
...facts.sent,
|
||||
[...facts.sentMembers],
|
||||
kept,
|
||||
]) *
|
||||
2,
|
||||
);
|
||||
draft.updates.push(facts);
|
||||
for (const field of steadyFields(draft.plan.rules, call.before, call.after))
|
||||
draft.steady.add(field);
|
||||
|
||||
@@ -509,13 +509,14 @@ const followsSequences = (
|
||||
draft: WriteDraft,
|
||||
created: Pick<StateEntity, "type" | "collection" | "fields"> & {
|
||||
readonly sent: JsonObject;
|
||||
readonly observed: JsonObject;
|
||||
},
|
||||
): boolean =>
|
||||
sequencesOf(draft, created.collection).every(({ rule, key }) => {
|
||||
if (rule.sent === true && Object.hasOwn(created.sent, rule.field))
|
||||
return same(
|
||||
ownMember(created.fields, rule.field),
|
||||
created.sent[rule.field],
|
||||
return (
|
||||
!Object.hasOwn(created.observed, rule.field) ||
|
||||
same(ownMember(created.observed, rule.field), created.sent[rule.field])
|
||||
);
|
||||
const scope = [
|
||||
...context.state.ofType(created.type),
|
||||
|
||||
Reference in new issue
Block a user