Files
reverse-engineering-anything/src/domain/javascript/javascriptApplicationEndpointCorrespondence.test.ts
T

219 lines
6.7 KiB
TypeScript

import { expect, it } from "vitest";
import {
artifactEvidence,
completeCoverage,
fixtureNode,
} from "./javascriptApplicationGraph.fixture.js";
import { createJavaScriptApplicationGraph } from "./javascriptApplicationGraph.js";
import { compareJavaScriptApplicationVersions } from "./javascriptApplicationVersionComparison.js";
import type { ApplicationNode } from "./javascriptApplicationGraphSchemas.js";
const endpoint = (options: {
readonly sha: string;
readonly path?: string;
readonly line?: number;
readonly partial?: boolean;
readonly kind?: string;
readonly method?: string;
readonly candidate?: boolean;
readonly value?: string;
}): ApplicationNode => {
const path = options.path ?? "main.js";
const line = options.line ?? 1;
return fixtureNode(
"endpoint",
{
strategy: "artifact-local-key",
stability: "artifact-version",
artifact_sha256: options.sha,
namespace: options.kind ?? "fetch",
key: JSON.stringify([path, line, options.partial, options.candidate]),
},
"API_BASE",
{
endpoint_kind: options.kind ?? "fetch",
value: options.value ?? "API_BASE",
...(options.partial ? { resolution: "partial" } : {}),
...(options.candidate ? { interpretation: "candidate" } : {}),
...(options.method === undefined ? {} : { method: options.method }),
},
{
...artifactEvidence(options.sha, path, "ast-static-analysis"),
location: {
available: true,
value: {
kind: "source-range",
source: path,
start: { line, column: 0 },
end: { line, column: 15 },
},
},
},
);
};
const compare = (left: ApplicationNode, right: ApplicationNode) => {
const graph = (node: ApplicationNode) =>
createJavaScriptApplicationGraph({
schema: "JavaScriptApplicationGraph",
root_node_ids: [node.node_id],
nodes: [node],
edges: [],
coverage: completeCoverage,
limitations: [],
});
return compareJavaScriptApplicationVersions({
left: {
evidenceId: `ev_${"1".repeat(64)}`,
rootArtifactSha256: "a".repeat(64),
graph: graph(left),
},
right: {
evidenceId: `ev_${"2".repeat(64)}`,
rootArtifactSha256: "b".repeat(64),
graph: graph(right),
},
leftNativeEvidence: [],
rightNativeEvidence: [],
});
};
it.each(["moved-partial", "exact", "candidate"] as const)(
"does not select endpoint correspondence from equal display text for %s",
(control) => {
const result = compare(
endpoint({ sha: "a".repeat(64), partial: true }),
endpoint({
sha: "b".repeat(64),
path: control === "moved-partial" ? "other.js" : "main.js",
partial: control === "moved-partial",
candidate: control === "candidate",
}),
);
expect(result.items).toHaveLength(2);
expect(
result.items.every(
({ status, match }) =>
status === "unknown" && match.status === "unmatched",
),
).toBe(true);
},
);
it("aligns equal partial source sites while keeping target identity unknown across binding context changes", () => {
// Different source digests can hide changed bindings even when this call site stays fixed.
const result = compare(
endpoint({ sha: "a".repeat(64), partial: true }),
endpoint({ sha: "b".repeat(64), partial: true }),
);
expect(result.items).toHaveLength(1);
expect(result.items[0]).toMatchObject({
status: "unknown",
match: { status: "matched", basis: "semantic-key" },
limitations: expect.arrayContaining([
expect.stringContaining("target identity remains unresolved"),
]),
});
});
it.each([false, true])(
"preserves known metadata changes on an established endpoint pair (partial=%s)",
(partial) => {
const result = compare(
endpoint({ sha: "a".repeat(64), partial, method: "GET" }),
endpoint({ sha: "b".repeat(64), partial, method: "POST" }),
);
expect(result.items).toHaveLength(1);
expect(result.items[0]).toMatchObject({
status: "changed",
dimensions: ["properties"],
match: { status: "matched", basis: "semantic-key" },
});
},
);
it("keeps exact endpoint target matching portable across source relocation", () => {
const result = compare(
endpoint({ sha: "a".repeat(64) }),
endpoint({ sha: "b".repeat(64), path: "moved.js", line: 4 }),
);
expect(result.items).toHaveLength(1);
expect(result.items[0]).toMatchObject({
status: "unchanged",
dimensions: ["location"],
match: { status: "matched", basis: "semantic-key" },
});
});
it("keeps unrelated unresolved endpoint kinds from hiding exact target absence", () => {
const result = compare(
endpoint({ sha: "a".repeat(64) }),
endpoint({ sha: "b".repeat(64), kind: "route", partial: true }),
);
expect(
result.items.find(({ left_node_id }) => left_node_id !== null),
).toMatchObject({ status: "removed", match: { status: "unmatched" } });
expect(
result.items.find(({ right_node_id }) => right_node_id !== null),
).toMatchObject({ status: "unknown", match: { status: "unmatched" } });
});
it.each([null, "unresolved"] as const)(
"preserves explicit unknown endpoint resolution %s",
(resolution) => {
const right = endpoint({ sha: "b".repeat(64) });
const result = compare(endpoint({ sha: "a".repeat(64) }), {
...right,
observations: right.observations.map((observation) => ({
...observation,
properties: { ...observation.properties, resolution },
})),
});
expect(result.items).toHaveLength(2);
expect(
result.items.every(
({ status, match }) =>
status === "unknown" && match.status === "unmatched",
),
).toBe(true);
},
);
it.each(["unknown", "unavailable"] as const)(
"keeps %s endpoint Evidence from proving another target absent",
(state) => {
const right = endpoint({ sha: "b".repeat(64), value: "unestablished" });
const uncertain: ApplicationNode = {
...right,
observations: right.observations.map((observation) => ({
...observation,
evidence: {
...observation.evidence,
state,
authority: "unknown",
confidence: "unknown",
coverage: {
status: "unknown",
truncated: false,
omitted_count: null,
limits: [],
},
limitations: ["Endpoint target was not established."],
},
})),
};
const result = compare(
endpoint({ sha: "a".repeat(64), value: "known" }),
uncertain,
);
expect(result.items).toHaveLength(2);
expect(
result.items.every(
({ status, match }) =>
status === "unknown" && match.status === "unmatched",
),
).toBe(true);
},
);