Files
reverse-engineering-anything/src/domain/analysisView/environmentStateWriteViews.test.ts
T
113679c9a6 feat(environment): serve creates whose answers show less than reads, and whole-object updates (#7080)
* feat(environment): take created entities' read-backs and re-derived update members

- A create's observation takes the fields the first read of its entity
  shows that the answer lacks, and an item read's clock spellings, when the
  read agrees with the answer otherwise. The plan records the read
  (observation.read with its shape); an item read then observes rather than
  checks, a list read only adds fields and still checks. Serving merges the
  same recorded objects.
- An update member sent back as the clock the entity held and stored as a
  fresh clock near the call is a clock rule; one stored as the slug of
  another member stored as sent is a slug rule. Both are accepted with any
  value, as whole-object writes send what they read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): record the write-views verification

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): reconcile write views without hiding contradictions

* fix(environment): reject incompatible slug-only saves

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-25 15:06:27 +08:00

173 lines
5.5 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { JsonValue } from "../jsonValue.js";
import {
timedCaptureEvidence,
type TimedCaptureRow,
} from "../../../tests/fixtures/analysisView.js";
import {
LISTED_LABEL_ROWS,
READ_BACK_TAG_ROWS,
RESTAMPED_ROWS,
STALE_SLUG_ROWS,
clockSaveRows,
slugSaveRows,
slugOnlySaveRows,
} from "../../../tests/fixtures/environmentStateWriteViews.js";
import { environmentStateViewOf } from "../../../tests/fixtures/environmentStateWrites.js";
const planOf = (rows: readonly TimedCaptureRow[], tool: string) => {
const plan = environmentStateViewOf(timedCaptureEvidence(rows)).writes.find(
(candidate) => candidate.tool === tool,
);
if (plan === undefined) throw new Error(`No plan for ${tool}`);
return plan;
};
describe("creates whose answer shows the entity otherwise than reads do", () => {
it.each(["123456", "123457"])(
"checks read-back precision beyond milliseconds (%s)",
(fraction) => {
const rows = READ_BACK_TAG_ROWS.map(
([method, path, body = null, ...rest]): TimedCaptureRow => {
const rewrite = (value: JsonValue): JsonValue => {
if (Array.isArray(value)) return value.map(rewrite);
if (value !== null && typeof value === "object")
return Object.fromEntries(
Object.entries(value).map(([key, child]) => [
key,
key === "created" && typeof child === "string"
? child.replace(
/(?:\.\d+)?Z$/u,
method === "POST" ? ".123456000Z" : `.${fraction}Z`,
)
: child,
]),
);
return value;
};
return [method, path, rewrite(body), ...rest];
},
);
expect(planOf(rows, "post_tags").served).toBe(fraction === "123456");
},
);
it("keeps an item read's omission as contradictory evidence even when a later list confirms the added fields", () => {
const rows = READ_BACK_TAG_ROWS.map(
([method, path, body = null, ...rest]): TimedCaptureRow => [
method,
path,
body !== null &&
typeof body === "object" &&
!Array.isArray(body) &&
(method === "POST" || path === "/api/tags/1" || path === "/api/tags/2")
? { ...body, detail: "Only in the answer" }
: body,
...rest,
],
);
expect(planOf(rows, "post_tags")).toMatchObject({
served: false,
reason: "contradicted",
});
});
it("serves a create whose item read adds a count and spells the clock otherwise, taking the read's fields", () => {
expect(planOf(READ_BACK_TAG_ROWS, "post_tags")).toMatchObject({
served: true,
observation: {
ordinal: 3,
pointer: "",
read: { ordinal: 4, pointer: "", shape: "item" },
},
rules: expect.arrayContaining([
{
field: "created",
rule: "clock",
format: expect.objectContaining({ fraction: 0 }),
},
]),
});
});
it("serves a create whose list read adds a count the answer lacks", () => {
expect(planOf(LISTED_LABEL_ROWS, "post_labels")).toMatchObject({
served: true,
observation: { read: { ordinal: 2, pointer: "/2", shape: "list" } },
});
});
});
describe("whole-object updates that send back what the application derives", () => {
it.each([
[true, true],
[false, true],
[true, false],
[false, false],
] as const)(
"checks slug-only saves too (derived first: %s, changed: %s)",
(derivedFirst, changed) => {
expect(
planOf(slugOnlySaveRows(derivedFirst, changed), "patch_tags_by_id")
.served,
).toBe(!changed);
},
);
it.each([true, false])(
"reconciles slug saves in either order (derived first: %s)",
(derivedFirst) => {
expect(
planOf(slugSaveRows(derivedFirst), "put_tags_by_id"),
).toMatchObject({
served: true,
rules: expect.arrayContaining([
{ field: "slug", rule: "slug", from: "name" },
]),
});
},
);
it.each([true, false])(
"rejects a slug rule contradicted by an explicit custom slug (derived first: %s)",
(derivedFirst) => {
expect(
planOf(slugSaveRows(derivedFirst, true), "put_tags_by_id").served,
).toBe(false);
},
);
it.each([true, false])(
"reconciles held and fresh clocks in either order (derived first: %s)",
(derivedFirst) => {
expect(
planOf(clockSaveRows(derivedFirst), "put_posts_by_id"),
).toMatchObject({
served: true,
rules: expect.arrayContaining([
expect.objectContaining({ field: "updated_at", rule: "clock" }),
]),
});
},
);
it("slugs a sent slug anew from the name the call stored", () => {
expect(planOf(STALE_SLUG_ROWS, "put_tags_by_id")).toMatchObject({
served: true,
rules: expect.arrayContaining([
{ field: "slug", rule: "slug", from: "name" },
{ field: "name", rule: "echo" },
]),
body: { accepted: expect.arrayContaining(["slug", "name"]) },
});
});
it("stamps anew a modification time each call sends back as it read it", () => {
expect(planOf(RESTAMPED_ROWS, "put_posts_by_id")).toMatchObject({
served: true,
rules: expect.arrayContaining([
expect.objectContaining({ field: "updated_at", rule: "clock" }),
]),
body: { accepted: expect.arrayContaining(["updated_at"]) },
});
});
});