Files
reverse-engineering-anything/src/domain/analysisView/environmentStateView.test.ts
T
Edgar ArakelyanandClaude Opus 5.5 ef49dca6d7 fix(environment): keep changed documents out of the state; mark OPTIONS and TRACE safe
Every non-entity GET read became a document from its latest initial
recording, bounded only by writes under related roots, so GET /api/me
read before and after POST /api/orders published the post-write value
as initial state (entities already leave such a type recorded). A
document whose value changed between two of its line's reads across
any recorded write is now recorded as fields-differ; an unchanged
value is still a document.

MCP hints treated OPTIONS and TRACE as non-idempotent writes, so a CORS
preflight tool fell to the destructive default. The RFC 9110 safe
methods (GET, HEAD, OPTIONS, TRACE) are read-only and idempotent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 08:37:47 -07:00

1127 lines
33 KiB
TypeScript

import { expect, it } from "vitest";
import {
analysisViewCaptureEvidence,
environmentManifestCaptureFixture,
environmentStateCaptureFixture,
} from "../../../tests/fixtures/analysisView.js";
import {
SCENARIO_EXCHANGES,
scenarioCaptureEvidence,
scenarioCaptureFixture,
} from "../../../tests/fixtures/browserScenarioCapture.js";
import type { Evidence } from "../evidence.js";
import type { JsonValue } from "../jsonValue.js";
import {
projectAnalysisView,
type AnalysisViewParent,
type AnalysisViewResult,
} from "./analysisView.js";
import { EnvironmentStateAccumulator } from "./EnvironmentStateAccumulator.js";
import {
at,
identityCandidates,
replaceAt,
type JsonObject,
} from "./environmentStateRules.js";
type State = Extract<AnalysisViewResult, { kind: "environment-state" }>;
const ALL_RULES = [
"after-uid-literal",
"opaque-token",
"sibling-variance",
] as const;
const parentOf = (
evidence: Evidence = analysisViewCaptureEvidence(
environmentStateCaptureFixture(),
),
): AnalysisViewParent => ({
evidenceId: evidence.evidence_id,
operation: evidence.operation,
normalizedResult: evidence.normalized_result,
subject: evidence.subject,
limitations: [],
parameters: evidence.parameters,
});
const stateOf = (parent: AnalysisViewParent = parentOf()): State => {
const result = projectAnalysisView(parent, {
kind: "environment-state",
template_rules: [...ALL_RULES],
});
if (!result.ok || result.value.kind !== "environment-state")
throw new Error("Expected an environment state");
return result.value;
};
type Row = readonly [
method: string,
path: string,
body?: JsonValue,
status?: number,
];
/** HAR entries one second apart on one origin; a string body is recorded as written. */
const captureOf = (rows: readonly Row[]): AnalysisViewParent =>
parentOf(
analysisViewCaptureEvidence(
environmentManifestCaptureFixture(
rows.map(([method, path, body = null, status = 200], index) => ({
startedDateTime: new Date(
Date.parse("2026-10-17T18:00:00.000Z") + index * 1000,
).toISOString(),
time: 5,
request: { method, url: `http://app.example${path}` },
response: {
status,
content: {
mimeType: "application/json",
text: typeof body === "string" ? body : JSON.stringify(body),
},
},
})),
),
),
);
const viewOf = (state: State, tool: string) =>
state.views.find((view) => view.tool === tool);
it("extracts the state the Grafana-shaped capture's reads observed", () => {
const state = stateOf();
expect(state.timeline).toBe("recorded-start-time");
expect(state.types).toContainEqual({
collection_template: "/api/folders",
id_field: "uid",
id_basis: "path-parameter",
init_before_ordinal: 20,
volatile_fields: [],
});
expect(state.types).toContainEqual({
collection_template: "/api/search",
id_field: "id",
id_basis: "field-name",
init_before_ordinal: null,
volatile_fields: [],
});
expect(
state.entities.filter((entity) => entity.type === "/api/folders"),
).toEqual([
{
type: "/api/folders",
collection: "/api/folders",
id: "fg1jo91e57e2ob",
fields: { id: 5, uid: "fg1jo91e57e2ob", title: "probe folder" },
view_fields: [],
record_ordinals: [12, 14],
},
{
type: "/api/folders",
collection: "/api/folders",
id: "general",
fields: { id: 0, uid: "general", title: "Dashboards" },
view_fields: [],
record_ordinals: [13],
},
]);
expect(viewOf(state, "get_folders")).toMatchObject({
shape: "list",
pointer: "",
membership: "recorded",
});
expect(viewOf(state, "get_folders_by_uid")).toMatchObject({
shape: "item",
envelope_ordinal: 12,
checked_record_ordinals: [12, 13],
});
expect(viewOf(state, "get_dashboards_by_uid")).toMatchObject({
shape: "item",
pointer: "/dashboard",
});
expect(viewOf(state, "get_annotations")).toEqual({
tool: "get_annotations",
shape: "recorded",
reason: "no-init-recording",
});
// The one team member listed proves no suffix-named identity, so the list is a document of its path.
expect(
state.documents.map((document) => [document.tool, document.path]),
).toEqual([
[
"get_datasources_health_by_uid",
"/api/datasources/uid/dg1jo96tern5sb/health",
],
["get_org", "/api/org"],
["get_teams_members_by_id", "/api/teams/1/members"],
["get_user", "/api/user"],
]);
expect(viewOf(state, "get_plugins_settings_by_plugin_id")).toEqual({
tool: "get_plugins_settings_by_plugin_id",
shape: "recorded",
reason: "path-varies",
});
expect(state.unknowns.map((unknown) => unknown.kind)).toEqual([
"authorization-model",
"response-semantics",
"state-transition",
]);
expect(
state.limitations.filter((limitation) =>
limitation.startsWith("Body shapes"),
),
).toEqual([]);
});
it("ends a type's state at the first write under its root, not at unrelated writes", () => {
const state = stateOf(
captureOf([
["GET", "/api/items/1", { id: 1, title: "Granite Ledger" }],
["PUT", "/api/notes/9", { id: 9 }],
["GET", "/api/items/1", { id: 1, title: "Granite Ledger" }],
["PUT", "/api/items/1", { id: 1, title: "Saltmarsh Depot" }],
["GET", "/api/items/1", { id: 1, title: "Saltmarsh Depot" }],
]),
);
expect(state.types).toEqual([
{
collection_template: "/api/items",
id_field: "id",
id_basis: "path-parameter",
init_before_ordinal: 3,
volatile_fields: [],
},
]);
expect(state.entities).toMatchObject([
{ id: "1", fields: { title: "Granite Ledger" }, record_ordinals: [0, 2] },
]);
expect(state.views).toContainEqual(
expect.objectContaining({ shape: "item", checked_record_ordinals: [2] }),
);
});
it("keeps the latest value of a field that changed with no related write and names it volatile", () => {
const state = stateOf(
captureOf([
["GET", "/api/items/1", { id: 1, title: "Lighthouse Rota", seen: 10 }],
["GET", "/api/items/1", { id: 1, title: "Lighthouse Rota", seen: 11 }],
["GET", "/api/items/2", { id: 2, title: "Blue Heron", seen: 3 }],
]),
);
expect(state.types[0]).toMatchObject({ volatile_fields: ["seen"] });
expect(state.entities[0]).toMatchObject({
id: "1",
fields: { seen: 11 },
});
expect(state.views).toContainEqual(
expect.objectContaining({
shape: "item",
checked_record_ordinals: [1, 2],
}),
);
});
it("leaves a type recorded when a value changed across a write under another root", () => {
const state = stateOf(
captureOf([
["GET", "/api/search", [{ id: 1, title: "Granite Ledger" }]],
["POST", "/api/dashboards/db", { id: 1, title: "Saltmarsh Depot" }],
["GET", "/api/search", [{ id: 1, title: "Saltmarsh Depot" }]],
]),
);
expect(state.types).toEqual([]);
expect(state.entities).toEqual([]);
expect(viewOf(state, "get_search")).toEqual({
tool: "get_search",
shape: "recorded",
reason: "fields-differ",
});
});
it("leaves a type recorded when a value changed across a write already in flight", () => {
const exchange = ([method, path, seconds, time, title]: readonly [
string,
string,
number,
number,
string,
]) => ({
startedDateTime: new Date(
Date.parse("2026-10-17T18:00:00.000Z") + seconds * 1000,
).toISOString(),
time,
request: { method, url: `http://app.example${path}` },
response: {
status: 200,
content: {
mimeType: "application/json",
text: JSON.stringify([{ id: 1, title }]),
},
},
});
const state = stateOf(
parentOf(
analysisViewCaptureEvidence(
environmentManifestCaptureFixture([
exchange([
"POST",
"/api/dashboards/db",
0,
30_000,
"Saltmarsh Depot",
]),
exchange(["GET", "/api/search", 10, 5, "Granite Ledger"]),
exchange(["GET", "/api/search", 40, 5, "Saltmarsh Depot"]),
]),
),
),
);
expect(viewOf(state, "get_search")).toEqual({
tool: "get_search",
shape: "recorded",
reason: "fields-differ",
});
});
it("does not list the whole collection when a counter exceeds the number listed", () => {
const state = stateOf(
captureOf([
[
"GET",
"/api/items",
{ total: 50, items: [{ id: 1, title: "Ada Finch" }] },
],
]),
);
expect(viewOf(state, "get_items")).toMatchObject({
shape: "list",
membership: "recorded",
counters: [],
});
});
it("lists the whole collection with its counter only when every recording did", () => {
const rows: Row[] = [
[
"GET",
"/api/items",
{
total: 2,
items: [
{ id: 1, title: "Ada Finch" },
{ id: 2, title: "Tomas Vey" },
],
},
],
["GET", "/api/items/1", { title: "Ada Finch", id: 1, notes: "first" }],
["GET", "/api/items/2", { id: 2, notes: "second", title: "Tomas Vey" }],
];
const whole = stateOf(captureOf(rows));
expect(whole.views).toContainEqual(
expect.objectContaining({
shape: "list",
pointer: "/items",
fields: ["id", "title"],
membership: "all",
counters: ["total"],
}),
);
expect(whole.entities.map((entity) => entity.fields)).toEqual([
{ id: 1, title: "Ada Finch", notes: "first" },
{ id: 2, notes: "second", title: "Tomas Vey" },
]);
const filtered = stateOf(
captureOf([
...rows,
[
"GET",
"/api/items?title=Ada",
{ total: 1, items: [{ id: 1, title: "Ada Finch" }] },
],
]),
);
expect(filtered.views).toContainEqual(
expect.objectContaining({ shape: "list", membership: "recorded" }),
);
});
it("leaves recorded the views it cannot reproduce, with the reason", () => {
const state = stateOf(
captureOf([
[
"GET",
"/api/boards/1",
{ meta: { viewer: "Ada Finch" }, board: { id: 1, name: "Rota" } },
],
[
"GET",
"/api/boards/2",
{ meta: { viewer: "Mina Okafor" }, board: { id: 2, name: "Depot" } },
],
["GET", "/api/ledgers/1", '{"id":1,"total":12345678901234567890}'],
["POST", "/api/boards/search", [{ id: 1, name: "Rota" }]],
]),
);
expect(
state.views.map((view) =>
view.shape === "recorded" ? [view.tool, view.reason] : view.tool,
),
).toEqual([
["post_boards_search", "query-body"],
["get_boards_by_id", "envelope-varies"],
["get_ledgers_by_id", "body-unusable"],
]);
expect(state.types).toEqual([]);
expect(state.entities).toEqual([]);
});
it("holds a written path's non-entity read as a document", () => {
const state = stateOf(
captureOf([
["GET", "/api/settings", { theme: "dark", density: "compact" }],
["PUT", "/api/settings", { theme: "light", density: "compact" }],
["GET", "/api/settings", { theme: "light", density: "compact" }],
]),
);
expect(state.documents).toEqual([
{
tool: "get_settings",
path: "/api/settings",
value: { theme: "dark", density: "compact" },
record_ordinal: 0,
},
]);
expect(viewOf(state, "get_settings")).toEqual({
tool: "get_settings",
shape: "document",
checked_record_ordinals: [0],
});
});
it("holds a read-only non-entity read as a document unless it varies by request line", () => {
const state = stateOf(
captureOf([
["GET", "/api/config", { theme: "dark", locale: "en-GB" }],
[
"GET",
"/api/rates",
[
{ currency: "EUR", rate: 1.1 },
{ currency: "EUR", rate: 1.2 },
],
],
["GET", "/api/stats?range=day", { visits: 3 }],
["GET", "/api/stats?range=week", { visits: 9 }],
]),
);
expect(state.documents).toEqual([
{
tool: "get_config",
path: "/api/config",
value: { theme: "dark", locale: "en-GB" },
record_ordinal: 0,
},
{
tool: "get_rates",
path: "/api/rates",
value: [
{ currency: "EUR", rate: 1.1 },
{ currency: "EUR", rate: 1.2 },
],
record_ordinal: 1,
},
]);
expect(viewOf(state, "get_stats")).toEqual({
tool: "get_stats",
shape: "recorded",
reason: "query-varies",
});
expect(state.types).toEqual([]);
});
it("infers a suffix-named identity only when no identity name identifies the listed items", () => {
const state = stateOf(
captureOf([
[
"GET",
"/api/memberships",
[
{ orgId: 7, userId: 1, role: "Admin" },
{ orgId: 8, userId: 1, role: "Viewer" },
],
],
[
"GET",
"/api/seats",
{
total: 2,
seats: [
{ parentId: 1, seat_id: "s-1" },
{ parentId: 1, seat_id: "s-2" },
],
},
],
[
"GET",
"/api/projects",
[
{ projectId: "a", id: 2 },
{ projectId: "b", id: 3 },
],
],
[
"GET",
"/api/grants",
[
{ userId: 1, orgId: 7 },
{ userId: 2, orgId: 8 },
],
],
["GET", "/api/members", []],
["GET", "/api/members", [{ memberId: "m-1" }, { memberId: "m-2" }]],
["GET", "/api/comments", [{ postId: 9, authorId: 3, text: "hi" }]],
]),
);
expect(
state.types.map(({ collection_template, id_field, id_basis }) => [
collection_template,
id_field,
id_basis,
]),
).toEqual([
// Both identify the grants; the earlier key in item order wins.
["/api/grants", "userId", "field-name"],
// The empty first recording constrains no field.
["/api/members", "memberId", "field-name"],
["/api/memberships", "orgId", "field-name"],
["/api/projects", "id", "field-name"],
["/api/seats", "seat_id", "field-name"],
]);
expect(viewOf(state, "get_seats")).toMatchObject({
shape: "list",
pointer: "/seats",
membership: "all",
counters: ["total"],
});
expect(viewOf(state, "get_members")).toMatchObject({ shape: "list" });
expect(
state.entities
.filter((entity) => entity.type === "/api/memberships")
.map((entity) => entity.id),
).toEqual(["7", "8"]);
// One listed item shows no field distinct, so its foreign keys identify nothing.
expect(viewOf(state, "get_comments")).toMatchObject({ shape: "document" });
});
it("chooses an envelope's array by identity name, then by a suffix-named field", () => {
const state = stateOf(
captureOf([
[
"GET",
"/api/board",
{
labels: [{ text: "urgent" }, { text: "later" }],
cards: [
{ cardId: 1, text: "Granite Ledger" },
{ cardId: 2, text: "Blue Heron" },
],
},
],
[
"GET",
"/api/directory",
{
owners: [{ orgId: 1 }, { orgId: 2 }],
teams: [
{ id: 1, orgId: 1 },
{ id: 2, orgId: 1 },
],
},
],
]),
);
expect(
state.types.map(({ collection_template, id_field }) => [
collection_template,
id_field,
]),
).toEqual([
["/api/board", "cardId"],
["/api/directory", "id"],
]);
expect(viewOf(state, "get_board")).toMatchObject({
shape: "list",
pointer: "/cards",
});
expect(viewOf(state, "get_directory")).toMatchObject({
shape: "list",
pointer: "/teams",
});
});
it("locates an item entity wrapped in a one-element array and merges it with the list view", () => {
const first = "6ad47b5c1023b500010873e3";
const second = "6ad47b5c1023b500010873e4";
const state = stateOf(
captureOf([
[
"GET",
"/ghost/api/admin/posts/",
{
posts: [
{ id: first, title: "Granite Ledger", status: "draft" },
{ id: second, title: "Blue Heron", status: "published" },
],
meta: { pagination: { page: 1, pages: 1 } },
},
],
[
"GET",
`/ghost/api/admin/posts/${first}/`,
{
posts: [
{
id: first,
title: "Granite Ledger",
status: "draft",
html: "<p>Quarry count</p>",
},
],
},
],
["GET", "/api/depots/5", [{ id: 5, name: "Saltmarsh Depot" }]],
["GET", "/api/rosters/9", [{ id: 31, roster: 9, name: "Ada Finch" }]],
]),
);
expect(viewOf(state, "get_rosters_by_id")).toEqual({
tool: "get_rosters_by_id",
shape: "recorded",
reason: "no-identity",
});
const posts = "/ghost/api/admin/posts";
expect(state.types).toContainEqual({
collection_template: posts,
id_field: "id",
id_basis: "path-parameter",
init_before_ordinal: null,
volatile_fields: [],
});
expect(state.views).toContainEqual(
expect.objectContaining({
shape: "item",
type: posts,
pointer: "/posts/0",
fields: ["id", "title", "status", "html"],
checked_record_ordinals: [1],
}),
);
expect(state.views).toContainEqual(
expect.objectContaining({
shape: "list",
type: posts,
pointer: "/posts",
membership: "all",
}),
);
expect(state.views).toContainEqual(
expect.objectContaining({
shape: "item",
type: "/api/depots",
pointer: "/0",
}),
);
expect(state.entities.filter((entity) => entity.type === posts)).toEqual([
{
type: posts,
collection: posts,
id: first,
fields: {
id: first,
title: "Granite Ledger",
status: "draft",
html: "<p>Quarry count</p>",
},
view_fields: [],
record_ordinals: [0, 1],
},
expect.objectContaining({ id: second, record_ordinals: [0] }),
]);
});
it("reads and replaces only the root, one member or the element of a one-element array", () => {
const body = { posts: [{ id: 1 }], meta: { posts: 1 } };
expect(at(body, "/posts/0")).toEqual({ id: 1 });
expect(replaceAt(body, "/posts/0", null)).toEqual({
posts: [null],
meta: { posts: 1 },
});
expect(at([{ id: 1 }], "/0")).toEqual({ id: 1 });
expect(replaceAt([{ id: 1 }], "/0", { id: 2 })).toEqual([{ id: 2 }]);
for (const pointer of ["/posts/1", "/meta/posts", "/posts/0/id"])
expect(at(body, pointer)).toBeUndefined();
expect(at({ posts: [{ id: 1 }, { id: 2 }] }, "/posts/0")).toBeUndefined();
expect(replaceAt(body, "/meta/posts", null)).toBe(body);
});
it("leaves an item path's non-entity read recorded", () => {
const state = stateOf(
captureOf([
["GET", "/api/files/123", { text: "initial document" }],
["PUT", "/api/files/123", null],
]),
);
expect(state.documents).toEqual([]);
expect(viewOf(state, "get_files_by_id")).toMatchObject({ shape: "recorded" });
});
it("holds a deleted path's initial non-entity read as a document", () => {
const state = stateOf(
captureOf([
["GET", "/api/settings", { text: "initial document" }],
["DELETE", "/api/settings", null],
]),
);
expect(state.documents).toEqual([
{
tool: "get_settings",
path: "/api/settings",
value: { text: "initial document" },
record_ordinal: 0,
},
]);
});
it("orders a scenario capture by request order and needs its response bodies", () => {
const state = stateOf(parentOf(scenarioCaptureEvidence()));
expect(state.timeline).toBe("record-order");
expect(state.types).toEqual([
{
collection_template: "/api/users",
id_field: "id",
id_basis: "path-parameter",
init_before_ordinal: null,
volatile_fields: [],
},
]);
expect(state.entities.map((entity) => entity.fields)).toEqual([
{ id: 2, login: "probe" },
{ id: 3, login: "viewer" },
]);
const headersOnly = scenarioCaptureEvidence(
scenarioCaptureFixture(SCENARIO_EXCHANGES, {
request_body: true,
response_body: false,
header_values: true,
}),
);
expect(
projectAnalysisView(parentOf(headersOnly), {
kind: "environment-state",
template_rules: [],
}),
).toMatchObject({
ok: false,
error: {
_tag: "AnalysisUnsupportedTargetError",
message: expect.stringContaining("response_body selected"),
},
});
});
it("refuses when extraction would exceed the projection budget", () => {
expect(
projectAnalysisView(
parentOf(),
{ kind: "environment-state", template_rules: [...ALL_RULES] },
{ state: new EnvironmentStateAccumulator(160 * 1024, [...ALL_RULES]) },
),
).toMatchObject({
ok: false,
error: {
_tag: "AnalysisResourceConstraintError",
resource: "memory",
message: expect.stringContaining("Environment-state"),
},
});
});
it("ends every type's state at a write whose URL was redacted, and says so", () => {
const parent = captureOf([
["GET", "/api/items/1", { id: 1, title: "Granite Ledger" }],
["PUT", "/api/items/1", { id: 1 }],
["GET", "/api/items/1", { id: 1, title: "Saltmarsh Depot" }],
]);
const result = structuredClone(parent.normalizedResult) as {
records: { reported: { request: { url: string | null } } }[];
};
// A scenario keeps a redacted URL as null, so the write's target is unknown.
result.records[1]!.reported.request.url = null;
const state = stateOf({ ...parent, normalizedResult: result });
expect(state.types).toMatchObject([{ init_before_ordinal: 1 }]);
expect(state.entities).toMatchObject([
{ fields: { title: "Granite Ledger" }, record_ordinals: [0] },
]);
expect(state.limitations).toContain(
"1 of the recorded writes had a redacted URL; each ends the initial state of every type, since its target is unknown.",
);
});
it("infers no identity for a list whose recordings held no items and holds it as a document", () => {
const state = stateOf(captureOf([["GET", "/api/widgets", []]]));
expect(state.types).toEqual([]);
expect(state.views).toEqual([
{ tool: "get_widgets", shape: "document", checked_record_ordinals: [0] },
]);
});
it("does not call a value volatile when it differs between request lines", () => {
const state = stateOf(
captureOf([
["GET", "/api/items/1?lang=fr", { id: 1, name: "Bonjour" }],
["GET", "/api/items/1", { id: 1, name: "Hello" }],
]),
);
expect(state.types).toEqual([]);
expect(
state.views.map((view) =>
view.shape === "recorded" ? [view.tool, view.reason] : view.tool,
),
).toEqual([["get_items_by_id", "fields-differ"]]);
});
it("orders entities by their earliest sighting, so a complete list keeps all membership", () => {
const state = stateOf(
captureOf([
[
"GET",
"/api/items",
[
{ id: 1, title: "Granite Ledger" },
{ id: 2, title: "Blue Heron" },
],
],
["GET", "/api/items/2", { id: 2, title: "Blue Heron" }],
["GET", "/api/items/1", { id: 1, title: "Granite Ledger" }],
]),
);
expect(state.entities.map((entity) => entity.id)).toEqual(["1", "2"]);
expect(state.views).toContainEqual(
expect.objectContaining({ tool: "get_items", membership: "all" }),
);
});
it("does not take a later recording that is itself an entity as the list", () => {
const state = stateOf(
captureOf([
["GET", "/api/items", { items: [{ id: 1, title: "Granite Ledger" }] }],
[
"GET",
"/api/items",
{ id: 99, items: [{ id: 2, title: "Blue Heron" }] },
],
]),
);
// Without one list across its recordings, the read is held as a document, not entities.
expect(state.views).toContainEqual(
expect.objectContaining({ tool: "get_items", shape: "document" }),
);
expect(state.entities).toEqual([]);
});
it("leaves a type recorded when two of its tools read different values across a write", () => {
const state = stateOf(
captureOf([
["GET", "/api/search", [{ id: 1, title: "Granite Ledger" }]],
["POST", "/api/dashboards/db", { id: 9 }],
["GET", "/api/search/1", { id: 1, title: "Saltmarsh Depot" }],
]),
);
expect(state.types).toEqual([]);
expect(state.entities).toEqual([]);
expect(
state.views.map((view) =>
view.shape === "recorded" ? [view.tool, view.reason] : view.tool,
),
).toEqual([
["get_search", "fields-differ"],
["get_search_by_id", "fields-differ"],
]);
});
it.each(["old", "new"])(
"retains field observations through sparse reads across a write (%s)",
(title) => {
const state = stateOf(
captureOf([
["GET", "/api/search", [{ id: 1, title: "old" }]],
["POST", "/api/items", { id: 2 }],
["GET", "/api/search?fields=id", [{ id: 1 }]],
["GET", "/api/search/1", { id: 1, title }],
]),
);
if (title === "new") {
expect(state.types).toEqual([]);
expect(state.entities).toEqual([]);
expect(
state.views.every(
(view) =>
view.shape === "recorded" && view.reason === "fields-differ",
),
).toBe(true);
} else {
expect(viewOf(state, "get_search_by_id")).toMatchObject({
shape: "item",
});
expect(state.entities[0]?.fields.title).toBe("old");
}
},
);
it("proves the identity field against every item response", () => {
const state = stateOf(
captureOf([
["GET", "/api/widgets/1", { left: 1, right: 1 }],
["GET", "/api/widgets/2", { right: 2, left: "other" }],
]),
);
expect(state.types).toMatchObject([
{ id_field: "right", id_basis: "path-parameter" },
]);
expect(state.entities.map((entity) => entity.id)).toEqual(["1", "2"]);
});
it.each(["/api", "/api/items/1/update"])(
"bounds a type by a write at a related root (%s)",
(path) => {
const state = stateOf(
captureOf([
["GET", "/api/items/1", { id: 1, title: "initial" }],
["POST", path, {}],
["GET", "/api/items/1", { id: 1, title: "after" }],
]),
);
expect(state.types).toMatchObject([{ init_before_ordinal: 1 }]);
expect(state.entities).toMatchObject([{ fields: { title: "initial" } }]);
},
);
it("chooses the earliest related write in timeline order, across ancestor and descendant roots", () => {
const parent = captureOf([
["GET", "/api/items/1", { id: 1, title: "initial" }],
["POST", "/api/items/1/update", {}],
["POST", "/api", {}],
["GET", "/api/items/1", { id: 1, title: "after" }],
]);
const data = structuredClone(parent.normalizedResult) as {
records: { reported: { startedDateTime: string } }[];
};
const first = data.records[1]!.reported.startedDateTime;
data.records[1]!.reported.startedDateTime =
data.records[2]!.reported.startedDateTime;
data.records[2]!.reported.startedDateTime = first;
const state = stateOf({ ...parent, normalizedResult: data });
expect(state.types).toMatchObject([{ init_before_ordinal: 2 }]);
expect(state.entities).toMatchObject([{ fields: { title: "initial" } }]);
});
it("bounds a parameterized namespace by concrete writes before publishing new items", () => {
const result = projectAnalysisView(
captureOf([
["GET", "/api/v1/repos/alice/alpha/issues/1", { id: 1 }],
["GET", "/api/v1/repos/bob/beta/issues/2", { id: 2 }],
["GET", "/api/v1/repos/carol/gamma/issues/3", { id: 3 }],
["POST", "/api/v1/repos/dana/delta/issues", { id: 4 }],
["GET", "/api/v1/repos/dana/delta/issues/4", { id: 4 }],
]),
{
kind: "environment-state",
template_rules: [...ALL_RULES, "sibling-pair-variance"],
},
);
if (!result.ok || result.value.kind !== "environment-state")
throw new Error("Expected state");
expect(result.value.entities.map((entity) => entity.id)).toEqual([
"1",
"2",
"3",
]);
expect(result.value.types).toMatchObject([{ init_before_ordinal: 3 }]);
});
it.each(["totalcount", "itemcount"])(
"recognizes a compact compound item total (%s)",
(name) => {
const state = stateOf(
captureOf([["GET", "/api/items", { [name]: 50, items: [{ id: 1 }] }]]),
);
expect(viewOf(state, "get_items")).toMatchObject({
shape: "list",
membership: "recorded",
counters: [],
});
},
);
it.each([
[[], [{ id: 1 }]],
[[{ id: 1 }], []],
[[{ id: 1 }], [{ id: 2 }]],
] as const)(
"keeps changed list membership across another root's write recorded (%j -> %j)",
(before, after) => {
const state = stateOf(
captureOf([
["GET", "/api/search", [...before]],
["POST", "/api/items", { id: 2 }],
["GET", "/api/search", [...after]],
]),
);
expect(state.types).toEqual([]);
expect(state.entities).toEqual([]);
expect(viewOf(state, "get_search")).toEqual({
tool: "get_search",
shape: "recorded",
reason: "fields-differ",
});
},
);
it("accepts stable membership across another root's write and changes confined before it", () => {
const state = stateOf(
captureOf([
["GET", "/api/search", []],
["GET", "/api/search", [{ id: 1 }]],
["POST", "/api/items", { id: 2 }],
["GET", "/api/search", [{ id: 1 }]],
]),
);
expect(viewOf(state, "get_search")).toMatchObject({ shape: "list" });
expect(state.entities.map((entity) => entity.id)).toEqual(["1"]);
});
it("keeps membership checks specific to the full request line", () => {
const state = stateOf(
captureOf([
["GET", "/api/search?page=1", [{ id: 1 }]],
["POST", "/api/items", { id: 2 }],
["GET", "/api/search?page=2", [{ id: 2 }]],
]),
);
expect(viewOf(state, "get_search")).toMatchObject({
shape: "list",
membership: "recorded",
});
});
it("does not treat a first observation as a membership change", () => {
const parent = captureOf([
["GET", "/api/search", [{ id: 1 }]],
["POST", "/api/items", { id: 2 }],
["GET", "/api/search", [{ id: 1 }]],
]);
const data = structuredClone(parent.normalizedResult) as {
records: { reported: { time: number } }[];
};
data.records[0]!.reported.time = 1500;
const state = stateOf({ ...parent, normalizedResult: data });
expect(viewOf(state, "get_search")).toMatchObject({ shape: "list" });
});
it.each([
[{ id: 1 }, { id: 1, title: "Created" }],
[{ id: 1, title: "Original" }, { id: 1 }],
])(
"keeps fields added or removed across another root's write recorded (%j -> %j)",
(before, after) => {
const state = stateOf(
captureOf([
["GET", "/api/search/1", before],
["POST", "/api/items", { id: 2 }],
["GET", "/api/search/1", after],
]),
);
expect(state.types).toEqual([]);
expect(state.entities).toEqual([]);
expect(viewOf(state, "get_search_by_id")).toMatchObject({
shape: "recorded",
reason: "fields-differ",
});
},
);
it("reads only item totals and next-page markers as signs of a partial list", () => {
const membership = (body: JsonValue) =>
stateOf(captureOf([["GET", "/api/items", body]])).views.map((view) =>
view.shape === "list" ? [view.membership, view.counters] : view.shape,
);
// Page and id numbers are not item totals, so a complete page stays whole.
expect(
membership({
page: 1,
total: 2,
total_pages: 1,
account_id: 77,
items: [{ id: 1 }, { id: 2 }],
}),
).toEqual([["all", ["total"]]]);
expect(membership({ has_more: true, items: [{ id: 1 }, { id: 2 }] })).toEqual(
[["recorded", []]],
);
expect(membership({ total: "50", items: [{ id: 1 }] })).toEqual([
["recorded", []],
]);
});
it("reads a sparse list once per candidate field when inferring identity", () => {
const fields = 2_000;
const wide: JsonObject = Object.fromEntries(
Array.from({ length: fields }, (_, index) => [
`f${String(index)}Id`,
index,
]),
);
let reads = 0;
const sparse = Array.from(
{ length: 5_000 },
() =>
new Proxy<JsonObject>(
{},
{
get: (target, key, receiver) => {
reads += 1;
return Reflect.get(target, key, receiver) as unknown;
},
},
),
);
expect(identityCandidates([wide, ...sparse])).toEqual([]);
// Each identity name and suffix field stops at the first sparse item.
expect(reads).toBeLessThanOrEqual(fields + 20);
});
it("keeps a document out of the state when its value changed across a write under another root", () => {
const changed = stateOf(
captureOf([
["GET", "/api/me", { credits: 10, plan: "free" }],
["POST", "/api/orders", { id: "o1" }],
["GET", "/api/me", { credits: 9, plan: "free" }],
]),
);
expect(changed.documents).toEqual([]);
expect(changed.views).toContainEqual({
tool: "get_me",
shape: "recorded",
reason: "fields-differ",
});
// A value the write left unchanged is still one initial state.
const unchanged = stateOf(
captureOf([
["GET", "/api/me", { credits: 10, plan: "free" }],
["POST", "/api/orders", { id: "o1" }],
["GET", "/api/me", { credits: 10, plan: "free" }],
]),
);
expect(unchanged.documents).toMatchObject([
{ tool: "get_me", value: { credits: 10, plan: "free" }, record_ordinal: 2 },
]);
});