Files
reverse-engineering-anything/src/domain/analysisView/environmentStateFormWrites.test.ts
T
Edgar ArakelyanandClaude Opus 5.5 28cda1f3f6 fix(environment): learn a sort from a rename only when moving its entities reaches it
Serving moves only the entities a write changes in a collection kept sorted,
so replay now tries a sort after a rename only when the entities no write the
read exercised changed already follow it. Seeded folders listed out of title
order no longer teach a title sort that serving would answer with another
order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 16:06:20 -07:00

239 lines
7.9 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
timedCaptureEvidence,
type TimedCaptureRow,
} from "../../../tests/fixtures/analysisView.js";
import {
BULK_EMPTY_TAG_ROWS,
CREATION_ORDER_ROWS,
DERIVED_TEXT_ROWS,
formLabelRows,
ID_OR_TITLE_ROWS,
LISTED_ID_ROWS,
MINIMAL_CONTACT_ROWS,
NOTE_DEFAULT_ROWS,
RENAMED_LABEL_ROWS,
SHELF_SORTED_ROWS,
TITLE_ORDER_ROWS,
UNLOADED_ANSWER_ROWS,
UNSORTED_SEED_ROWS,
} from "../../../tests/fixtures/environmentStateFormWrites.js";
import { environmentStateViewOf } from "../../../tests/fixtures/environmentStateWrites.js";
import { mergedCreatePlan } from "./environmentStateWriteCreateCalls.js";
import { resortedMembers } from "./environmentStateWriteExplain.js";
import type { WriteBody } from "./environmentStateWriteRules.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;
};
const BY_NAME = { rule: "sorted", field: "name", direction: "ascending" };
describe("create members a form sent empty", () => {
it("requires only the members some recorded create filled in, and applies the others as they were sent", () => {
expect(planOf(formLabelRows(), "post_labels")).toMatchObject({
served: true,
body: {
accepted: ["name", "description", "color", "icon"],
required: ["name"],
blank: [
{ member: "color", sent: "" },
{ member: "icon", sent: "" },
{ member: "description", sent: "" },
],
},
});
});
it("accepts either spelling of a member two creates sent empty differently, filling the earliest", () => {
const body: WriteBody = {
accepted: ["name", "note"],
fixed: [],
required: ["name"],
identity: null,
kinds: [],
blank: [{ member: "note", sent: "" }],
};
const rules = [
{ field: "id", rule: "identity", generator: "sequence" },
{ field: "name", rule: "echo" },
{ field: "note", rule: "echo" },
] as const;
const merged = mergedCreatePlan(
{ rules: [...rules], body },
{ id: 1, name: "One", note: "" },
{
entity: { id: 2, name: "Two", note: null },
body: { name: "Two", note: null },
rules: [...rules],
contract: { ...body, blank: [{ member: "note", sent: null }] },
},
);
expect(merged?.body).toMatchObject({
required: ["name"],
blank: [
{ member: "note", sent: "" },
{ member: "note", sent: null },
],
});
});
});
describe("empty values and defaults", () => {
it("keeps accepting a recorded empty value after a later call that left the member out teaches its default", () => {
expect(planOf(NOTE_DEFAULT_ROWS, "post_notes")).toMatchObject({
served: true,
body: {
required: ["name"],
defaults: [{ member: "note", value: "" }],
blank: [{ member: "note", sent: null }],
},
});
});
});
describe("a create with only what a person must give", () => {
it("serves a create whose minimal call left string members `null` and an empty flag spelled another way", () => {
expect(planOf(MINIMAL_CONTACT_ROWS, "post_contacts")).toMatchObject({
served: true,
body: {
required: ["lastName"],
blank: expect.arrayContaining([
{ member: "firstName", sent: null },
{ member: "email", sent: null },
{ member: "emailOptedOut", sent: false },
{ member: "emailOptedOut", sent: null },
]),
},
});
});
});
describe("collections kept sorted", () => {
it("moves an updated entity by the field its collection's creates insert by", () => {
expect(planOf(RENAMED_LABEL_ROWS, "patch_labels_by_id")).toMatchObject({
served: true,
insert: BY_NAME,
});
});
it("learns the sort from a rename that moves a folder, and inserts creates by it", () => {
const byTitle = { rule: "sorted", field: "title", direction: "ascending" };
expect(planOf(TITLE_ORDER_ROWS, "put_folders_by_id")).toMatchObject({
served: true,
insert: byTitle,
});
expect(planOf(TITLE_ORDER_ROWS, "post_folders")).toMatchObject({
served: true,
insert: byTitle,
});
});
it("learns the title order from a rename when the creates fit both id and title order", () => {
const byTitle = { rule: "sorted", field: "title", direction: "ascending" };
expect(planOf(ID_OR_TITLE_ROWS, "put_folders_by_id")).toMatchObject({
served: true,
insert: byTitle,
});
expect(planOf(ID_OR_TITLE_ROWS, "post_folders")).toMatchObject({
insert: byTitle,
});
});
it("learns no sort from a rename that an earlier read of the creates' order rules out", () => {
const byTitle = { rule: "sorted", field: "title", direction: "ascending" };
expect(planOf(CREATION_ORDER_ROWS, "post_folders").insert).not.toEqual(
byTitle,
);
expect(planOf(CREATION_ORDER_ROWS, "put_folders_by_id")).toMatchObject({
served: false,
});
});
it("learns the name order from a rename when a read sorted by its query placed the create", () => {
const byName = { rule: "sorted", field: "name", direction: "ascending" };
expect(planOf(SHELF_SORTED_ROWS, "put_books_by_id")).toMatchObject({
served: true,
insert: byName,
});
});
it("learns no sort from a rename that moving the renamed entity alone cannot reach", () => {
const plan = planOf(UNSORTED_SEED_ROWS, "put_folders_by_id");
expect(plan.served).toBe(false);
expect(plan.insert).toBeNull();
});
it("serves a bulk update whose recordings echo the empty values each call sent", () => {
expect(planOf(BULK_EMPTY_TAG_ROWS, "patch_labels")).toMatchObject({
served: true,
});
});
it("moves each written entity in turn, against the others' values before their own writes", () => {
const entity = (id: string, name: string) => ({ id, fields: { name } });
const [a, b, c, d] = ["a", "b", "c", "d"].map((name, index) =>
entity(String(index + 1), name),
) as [ReturnType<typeof entity>, ...ReturnType<typeof entity>[]];
const order = resortedMembers(
{ rule: "sorted", field: "name", direction: "ascending" },
[a, b!, c!, d!],
[
{ before: b!, after: entity("2", "h") },
{ before: c!, after: entity("3", "i") },
],
);
expect(order.map(({ fields }) => fields["name"])).toEqual([
"a",
"d",
"h",
"i",
]);
});
it("renders a list envelope's ids of the listed entities from the state", () => {
const view = environmentStateViewOf(
timedCaptureEvidence(LISTED_ID_ROWS),
).views.find((candidate) => candidate.tool === "get_tags");
expect(view).toMatchObject({
shape: "list",
listed_ids: [{ member: "all", field: "id" }],
});
expect(planOf(LISTED_ID_ROWS, "post_tags")).toMatchObject({
served: true,
insert: BY_NAME,
});
});
});
describe("answers that show stored values otherwise than reads", () => {
it("takes the later read's values where a create's answer had not loaded them", () => {
expect(planOf(UNLOADED_ANSWER_ROWS, "post_tasks")).toMatchObject({
served: true,
observation: { read: { shape: "item" } },
});
});
it("reads an empty list spelled `[]` in an update's answer and `null` in reads as unchanged", () => {
expect(planOf(UNLOADED_ANSWER_ROWS, "patch_tasks_by_id")).toMatchObject({
served: true,
rules: [{ field: "done", rule: "echo" }],
});
});
it("withholds a later create that stored an unruled field otherwise, without blocking checks of later writes", () => {
expect(planOf(DERIVED_TEXT_ROWS, "post_tags")).toMatchObject({
served: false,
reason: "unexplained-change",
});
expect(planOf(DERIVED_TEXT_ROWS, "delete_tags_by_id")).toMatchObject({
served: true,
});
});
});