mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
test: give oversized registration groups semantic owners
This commit is contained in:
1 parent
a03096cb85
commit
a3eae6e236
8 files changed
+345
-311
No files matched your search
@@ -18,7 +18,7 @@ import { err, ok } from "../domain/result.js";
|
||||
|
||||
const provider = { id: "fixture", name: "Fixture", version: "1" };
|
||||
|
||||
describe("analysis context queries: navigation", () => {
|
||||
const registerNavigationSelectionValidationTests = (): void => {
|
||||
it.each([
|
||||
[null, "0x401000", "current_document"],
|
||||
["fixture", null, "current_address"],
|
||||
@@ -47,6 +47,10 @@ describe("analysis context queries: navigation", () => {
|
||||
);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
describe("analysis context queries: navigation", () => {
|
||||
registerNavigationSelectionValidationTests();
|
||||
|
||||
it("aggregates volatile navigation and treats a missing procedure as null", async () => {
|
||||
const calls: {
|
||||
|
||||
@@ -419,6 +419,76 @@ describe("root input presentation", () => {
|
||||
});
|
||||
});
|
||||
|
||||
const registerBooleanLiteralCompatibilityTests = (): void => {
|
||||
it("keeps canonical boolean-literal inputs accepted after advertisement", () => {
|
||||
const canonical = {
|
||||
type: "object",
|
||||
properties: {
|
||||
enabled: { type: "boolean", const: false },
|
||||
},
|
||||
required: ["enabled"],
|
||||
additionalProperties: false,
|
||||
};
|
||||
const advertised = presentInputJsonSchema(canonical, () => undefined);
|
||||
const canonicalValidate = new Ajv2020({ strict: false }).compile(canonical);
|
||||
const advertisedValidate = new Ajv2020({ strict: false }).compile(
|
||||
advertised,
|
||||
);
|
||||
|
||||
expect(canonicalValidate({ enabled: false })).toBe(true);
|
||||
expect(canonicalValidate({ enabled: true })).toBe(false);
|
||||
expect(advertisedValidate({ enabled: false })).toBe(true);
|
||||
expect(advertisedValidate({ enabled: true })).toBe(true);
|
||||
expect(advertised.properties).toEqual({ enabled: { type: "boolean" } });
|
||||
});
|
||||
|
||||
it("drops affected negation and conditional groups without rejecting canonical inputs", () => {
|
||||
const negated = {
|
||||
type: "object",
|
||||
properties: {
|
||||
flag: { type: "boolean", not: { const: false } },
|
||||
},
|
||||
};
|
||||
const negatedAdvertisement = presentInputJsonSchema(
|
||||
negated,
|
||||
() => undefined,
|
||||
);
|
||||
const canonicalNegated = new Ajv2020({ strict: false }).compile(negated);
|
||||
const advertisedNegated = new Ajv2020({ strict: false }).compile(
|
||||
negatedAdvertisement,
|
||||
);
|
||||
expect(canonicalNegated({ flag: true })).toBe(true);
|
||||
expect(canonicalNegated({ flag: false })).toBe(false);
|
||||
expect(advertisedNegated({ flag: true })).toBe(true);
|
||||
|
||||
const conditional = {
|
||||
type: "object",
|
||||
properties: {
|
||||
choice: {
|
||||
if: { const: false },
|
||||
then: { type: "string" },
|
||||
else: { type: "boolean" },
|
||||
},
|
||||
},
|
||||
};
|
||||
const conditionalAdvertisement = presentInputJsonSchema(
|
||||
conditional,
|
||||
() => undefined,
|
||||
);
|
||||
const canonicalConditional = new Ajv2020({ strict: false }).compile(
|
||||
conditional,
|
||||
);
|
||||
const advertisedConditional = new Ajv2020({ strict: false }).compile(
|
||||
conditionalAdvertisement,
|
||||
);
|
||||
expect(canonicalConditional({ choice: true })).toBe(true);
|
||||
expect(advertisedConditional({ choice: true })).toBe(true);
|
||||
expect(conditionalAdvertisement.properties).toEqual({
|
||||
choice: {},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
describe("advertised client-compatible literals", () => {
|
||||
it.each([{ const: "" }, { enum: ["", "named"] }])(
|
||||
"widens unsupported empty-string literals without changing canonical validation: %j",
|
||||
@@ -510,73 +580,7 @@ describe("advertised client-compatible literals", () => {
|
||||
expect(schema.safeParse(input).success).toBe(true);
|
||||
expect(input).not.toHaveProperty("only_left");
|
||||
});
|
||||
it("keeps canonical boolean-literal inputs accepted after advertisement", () => {
|
||||
const canonical = {
|
||||
type: "object",
|
||||
properties: {
|
||||
enabled: { type: "boolean", const: false },
|
||||
},
|
||||
required: ["enabled"],
|
||||
additionalProperties: false,
|
||||
};
|
||||
const advertised = presentInputJsonSchema(canonical, () => undefined);
|
||||
const canonicalValidate = new Ajv2020({ strict: false }).compile(canonical);
|
||||
const advertisedValidate = new Ajv2020({ strict: false }).compile(
|
||||
advertised,
|
||||
);
|
||||
|
||||
expect(canonicalValidate({ enabled: false })).toBe(true);
|
||||
expect(canonicalValidate({ enabled: true })).toBe(false);
|
||||
expect(advertisedValidate({ enabled: false })).toBe(true);
|
||||
expect(advertisedValidate({ enabled: true })).toBe(true);
|
||||
expect(advertised.properties).toEqual({ enabled: { type: "boolean" } });
|
||||
});
|
||||
|
||||
it("drops affected negation and conditional groups without rejecting canonical inputs", () => {
|
||||
const negated = {
|
||||
type: "object",
|
||||
properties: {
|
||||
flag: { type: "boolean", not: { const: false } },
|
||||
},
|
||||
};
|
||||
const negatedAdvertisement = presentInputJsonSchema(
|
||||
negated,
|
||||
() => undefined,
|
||||
);
|
||||
const canonicalNegated = new Ajv2020({ strict: false }).compile(negated);
|
||||
const advertisedNegated = new Ajv2020({ strict: false }).compile(
|
||||
negatedAdvertisement,
|
||||
);
|
||||
expect(canonicalNegated({ flag: true })).toBe(true);
|
||||
expect(canonicalNegated({ flag: false })).toBe(false);
|
||||
expect(advertisedNegated({ flag: true })).toBe(true);
|
||||
|
||||
const conditional = {
|
||||
type: "object",
|
||||
properties: {
|
||||
choice: {
|
||||
if: { const: false },
|
||||
then: { type: "string" },
|
||||
else: { type: "boolean" },
|
||||
},
|
||||
},
|
||||
};
|
||||
const conditionalAdvertisement = presentInputJsonSchema(
|
||||
conditional,
|
||||
() => undefined,
|
||||
);
|
||||
const canonicalConditional = new Ajv2020({ strict: false }).compile(
|
||||
conditional,
|
||||
);
|
||||
const advertisedConditional = new Ajv2020({ strict: false }).compile(
|
||||
conditionalAdvertisement,
|
||||
);
|
||||
expect(canonicalConditional({ choice: true })).toBe(true);
|
||||
expect(advertisedConditional({ choice: true })).toBe(true);
|
||||
expect(conditionalAdvertisement.properties).toEqual({
|
||||
choice: {},
|
||||
});
|
||||
});
|
||||
registerBooleanLiteralCompatibilityTests();
|
||||
});
|
||||
|
||||
describe("advertised Boolean schema composition", () => {
|
||||
|
||||
@@ -103,53 +103,7 @@ const graphInput = (): HistoricalSourceGraphInput => ({
|
||||
limitations: [],
|
||||
});
|
||||
|
||||
describe("historical source graph", () => {
|
||||
it("validates supplementary paths and derived indexes by code point", () => {
|
||||
const paths = ["a", "aa", "\uE000", "\u{10000}"];
|
||||
const input: HistoricalSourceGraphInput = {
|
||||
...graphInput(),
|
||||
entries: paths.map((path) =>
|
||||
fileEntry({
|
||||
path,
|
||||
language: path,
|
||||
classifications: ["manifest"],
|
||||
}),
|
||||
),
|
||||
relationships: [],
|
||||
languages: paths,
|
||||
manifests: paths,
|
||||
};
|
||||
const graph = createHistoricalSourceGraph(input);
|
||||
expect(parseHistoricalSourceGraph(graph)).toEqual(graph);
|
||||
expect(() =>
|
||||
createHistoricalSourceGraph({
|
||||
...input,
|
||||
entries: [...input.entries].sort((left, right) =>
|
||||
left.path < right.path ? -1 : left.path > right.path ? 1 : 0,
|
||||
),
|
||||
}),
|
||||
).toThrow(/Unicode code point/u);
|
||||
});
|
||||
|
||||
it("omits absent local diagnostics and rejects explicitly undefined non-JSON metadata", () => {
|
||||
const input = graphInput();
|
||||
const graph = createHistoricalSourceGraph(input);
|
||||
expect(
|
||||
graph.entries.every(
|
||||
(entry) => !Object.hasOwn(entry, "local_diagnostics"),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(() =>
|
||||
createHistoricalSourceGraph({
|
||||
...input,
|
||||
entries: input.entries.map((entry) => ({
|
||||
...entry,
|
||||
local_diagnostics: undefined,
|
||||
})),
|
||||
}),
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
const registerSourceCommitmentTests = (): void => {
|
||||
it("builds and verifies internal root and manifest commitments", () => {
|
||||
const graph = createHistoricalSourceGraph(graphInput());
|
||||
const first = createHistoricalSourceManifest(graph);
|
||||
@@ -224,6 +178,56 @@ describe("historical source graph", () => {
|
||||
graphWith("*.env").root_sha256,
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
describe("historical source graph", () => {
|
||||
it("validates supplementary paths and derived indexes by code point", () => {
|
||||
const paths = ["a", "aa", "\uE000", "\u{10000}"];
|
||||
const input: HistoricalSourceGraphInput = {
|
||||
...graphInput(),
|
||||
entries: paths.map((path) =>
|
||||
fileEntry({
|
||||
path,
|
||||
language: path,
|
||||
classifications: ["manifest"],
|
||||
}),
|
||||
),
|
||||
relationships: [],
|
||||
languages: paths,
|
||||
manifests: paths,
|
||||
};
|
||||
const graph = createHistoricalSourceGraph(input);
|
||||
expect(parseHistoricalSourceGraph(graph)).toEqual(graph);
|
||||
expect(() =>
|
||||
createHistoricalSourceGraph({
|
||||
...input,
|
||||
entries: [...input.entries].sort((left, right) =>
|
||||
left.path < right.path ? -1 : left.path > right.path ? 1 : 0,
|
||||
),
|
||||
}),
|
||||
).toThrow(/Unicode code point/u);
|
||||
});
|
||||
|
||||
it("omits absent local diagnostics and rejects explicitly undefined non-JSON metadata", () => {
|
||||
const input = graphInput();
|
||||
const graph = createHistoricalSourceGraph(input);
|
||||
expect(
|
||||
graph.entries.every(
|
||||
(entry) => !Object.hasOwn(entry, "local_diagnostics"),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(() =>
|
||||
createHistoricalSourceGraph({
|
||||
...input,
|
||||
entries: input.entries.map((entry) => ({
|
||||
...entry,
|
||||
local_diagnostics: undefined,
|
||||
})),
|
||||
}),
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
registerSourceCommitmentTests();
|
||||
|
||||
it("retains actual external targets and rejects placeholder targets", () => {
|
||||
const input = { ...graphInput(), inventory_state: "partial" as const };
|
||||
@@ -277,7 +281,7 @@ describe("historical source graph", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("partial historical source graphs", () => {
|
||||
const registerLocalDiagnosticCommitmentTests = (): void => {
|
||||
it("keeps unknown-entry diagnostics outside the root commitment", () => {
|
||||
const base = graphInput();
|
||||
base.inventory_state = "partial";
|
||||
@@ -332,6 +336,10 @@ describe("partial historical source graphs", () => {
|
||||
computeHistoricalSourceGraphSha256(createHistoricalSourceGraph(moved)),
|
||||
).not.toBe(computeHistoricalSourceGraphSha256(graph));
|
||||
});
|
||||
};
|
||||
|
||||
describe("partial historical source graphs", () => {
|
||||
registerLocalDiagnosticCommitmentTests();
|
||||
|
||||
it("keeps an unreadable symlink target unknown and out of root identity", () => {
|
||||
const base = graphInput();
|
||||
|
||||
@@ -75,7 +75,7 @@ const omissionRejected = (schema: z.ZodObject | undefined): string[] =>
|
||||
const acceptsOmittedKey = (key: string, field: z.ZodType): boolean =>
|
||||
z.object({ [key]: field }).safeParse({}).success;
|
||||
|
||||
describe("CLI advertised input schemas", () => {
|
||||
const registerParserAdvertisementInventoryTests = (): void => {
|
||||
it("require exactly the inputs whose omission the parser rejects", async () => {
|
||||
const cli = createCli({});
|
||||
const mismatches: string[] = [];
|
||||
@@ -111,6 +111,10 @@ describe("CLI advertised input schemas", () => {
|
||||
}
|
||||
expect(mismatches).toEqual([]);
|
||||
});
|
||||
};
|
||||
|
||||
describe("CLI advertised input schemas", () => {
|
||||
registerParserAdvertisementInventoryTests();
|
||||
|
||||
it("preserves client choices, selected path bounds and required decoded steps", async () => {
|
||||
const cli = createCli({});
|
||||
|
||||
@@ -10,50 +10,52 @@ import { historicalSourceGraphSchema } from "../../../src/domain/referenceSource
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
import { cliTest } from "../../support/cli/cliFixture.js";
|
||||
|
||||
const historicalModuleFixture = async () => {
|
||||
const root = await createTestTempDirectory("rea-reference-module-semantics-");
|
||||
const files: Record<string, string> = {
|
||||
"package.json": '{"type":"module"}\n',
|
||||
"main.js": 'require("./dep.cjs"); export {};\n',
|
||||
"dep.cjs": 'throw new Error("must never load");\n',
|
||||
"directory.cjs": 'require("./nested/");\n',
|
||||
"nested/index.js": 'module.exports = "fallback";\n',
|
||||
"optional.cjs":
|
||||
'require.resolve?.("./dep.cjs"); require?.resolve("./dep.cjs");\n',
|
||||
"overwritten.cjs":
|
||||
'require.resolve = other; require.resolve?.("./dep.cjs");\n',
|
||||
"shadowed.cjs": 'function f(require) { require?.resolve("./dep.cjs"); }\n',
|
||||
"types.d.ts":
|
||||
'declare module "consumer" { import type { Marker } from "dependency"; export type A = import("./dep").Marker; export type B = typeof import("./dep"); }\n',
|
||||
"dep.ts": "export interface Marker { id: string }\n",
|
||||
"nested/package.json": '{"type":"commonjs","main":"main.js"}\n',
|
||||
"nested/main.js": 'require("../dep.cjs");\n',
|
||||
"untyped/package.json": "{}\n",
|
||||
"untyped/main.js": 'require("../dep.cjs");\n',
|
||||
"unknown/package.json": "{invalid",
|
||||
"unknown/main.js": 'require("../dep.cjs");\n',
|
||||
"url.mjs":
|
||||
'import "./dep%20name.mjs?v=1#first"; import "./dep%20name.mjs?v=2#second"; import "./dep%2520name.mjs"; import "./bad%2Fname.mjs"; import "./bad%.mjs";\n',
|
||||
"dep name.mjs": "export const actual = 1;\n",
|
||||
"dep%20name.mjs": "export const literalPercent = 2;\n",
|
||||
"literal.cjs":
|
||||
'require.resolve("./dep%20name.mjs"); require.resolve("./dep?literal.cjs");\n',
|
||||
"dep?literal.cjs": 'throw new Error("must never load");\n',
|
||||
"oracle.mjs":
|
||||
'import { fileURLToPath } from "node:url"; import { createRequire } from "node:module"; const require = createRequire(import.meta.url); console.log(JSON.stringify([fileURLToPath(import.meta.resolve("./dep%20name.mjs?v=1#first")), fileURLToPath(import.meta.resolve("./dep%2520name.mjs")), require.resolve("./dep%20name.mjs"), require.resolve("./dep?literal.cjs")]));\n',
|
||||
};
|
||||
for (const directory of ["nested", "unknown", "untyped"])
|
||||
await mkdir(join(root, directory));
|
||||
await Promise.all(
|
||||
Object.entries(files).map(([path, source]) =>
|
||||
writeFile(join(root, path), source),
|
||||
),
|
||||
);
|
||||
return { root, files };
|
||||
};
|
||||
|
||||
cliTest(
|
||||
"preserves historical module syntax, loader scope and URL request identity",
|
||||
async ({ cli }) => {
|
||||
const root = await createTestTempDirectory(
|
||||
"rea-reference-module-semantics-",
|
||||
);
|
||||
const files: Record<string, string> = {
|
||||
"package.json": '{"type":"module"}\n',
|
||||
"main.js": 'require("./dep.cjs"); export {};\n',
|
||||
"dep.cjs": 'throw new Error("must never load");\n',
|
||||
"directory.cjs": 'require("./nested/");\n',
|
||||
"nested/index.js": 'module.exports = "fallback";\n',
|
||||
"optional.cjs":
|
||||
'require.resolve?.("./dep.cjs"); require?.resolve("./dep.cjs");\n',
|
||||
"overwritten.cjs":
|
||||
'require.resolve = other; require.resolve?.("./dep.cjs");\n',
|
||||
"shadowed.cjs":
|
||||
'function f(require) { require?.resolve("./dep.cjs"); }\n',
|
||||
"types.d.ts":
|
||||
'declare module "consumer" { import type { Marker } from "dependency"; export type A = import("./dep").Marker; export type B = typeof import("./dep"); }\n',
|
||||
"dep.ts": "export interface Marker { id: string }\n",
|
||||
"nested/package.json": '{"type":"commonjs","main":"main.js"}\n',
|
||||
"nested/main.js": 'require("../dep.cjs");\n',
|
||||
"untyped/package.json": "{}\n",
|
||||
"untyped/main.js": 'require("../dep.cjs");\n',
|
||||
"unknown/package.json": "{invalid",
|
||||
"unknown/main.js": 'require("../dep.cjs");\n',
|
||||
"url.mjs":
|
||||
'import "./dep%20name.mjs?v=1#first"; import "./dep%20name.mjs?v=2#second"; import "./dep%2520name.mjs"; import "./bad%2Fname.mjs"; import "./bad%.mjs";\n',
|
||||
"dep name.mjs": "export const actual = 1;\n",
|
||||
"dep%20name.mjs": "export const literalPercent = 2;\n",
|
||||
"literal.cjs":
|
||||
'require.resolve("./dep%20name.mjs"); require.resolve("./dep?literal.cjs");\n',
|
||||
"dep?literal.cjs": 'throw new Error("must never load");\n',
|
||||
"oracle.mjs":
|
||||
'import { fileURLToPath } from "node:url"; import { createRequire } from "node:module"; const require = createRequire(import.meta.url); console.log(JSON.stringify([fileURLToPath(import.meta.resolve("./dep%20name.mjs?v=1#first")), fileURLToPath(import.meta.resolve("./dep%2520name.mjs")), require.resolve("./dep%20name.mjs"), require.resolve("./dep?literal.cjs")]));\n',
|
||||
};
|
||||
for (const directory of ["nested", "unknown", "untyped"])
|
||||
await mkdir(join(root, directory));
|
||||
await Promise.all(
|
||||
Object.entries(files).map(([path, source]) =>
|
||||
writeFile(join(root, path), source),
|
||||
),
|
||||
);
|
||||
const { root, files } = await historicalModuleFixture();
|
||||
// This self-authored resolver control never loads a dependency.
|
||||
const oracle = await promisify(execFile)(
|
||||
process.execPath,
|
||||
|
||||
@@ -22,7 +22,7 @@ const application = async () => {
|
||||
return { root, directory };
|
||||
};
|
||||
|
||||
describe("JavaScript application input selection", () => {
|
||||
const registerSelectedPathResolutionTests = (): void => {
|
||||
it("preserves the selected path and host reason for an overlong name", async () => {
|
||||
const { root } = await application();
|
||||
const longPath = join(root, "x".repeat(300));
|
||||
@@ -90,6 +90,10 @@ describe("JavaScript application input selection", () => {
|
||||
expect(loopError).not.toEqual(chainError);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
describe("JavaScript application input selection", () => {
|
||||
registerSelectedPathResolutionTests();
|
||||
|
||||
it("reports a missing input path as invalid input, not an artifact read failure", async () => {
|
||||
const { root } = await application();
|
||||
|
||||
@@ -18,6 +18,76 @@ import {
|
||||
createJavaScriptApplicationNode,
|
||||
} from "../../../src/domain/javascript/javascriptApplicationGraph.js";
|
||||
|
||||
const registerImplicitInvocationComparisonTests = (): void => {
|
||||
it("preserves accessor-effect uncertainty through analyzed Evidence and export comparison", async () => {
|
||||
const [left, right] = await analyzeSources({
|
||||
left: `export default function observe() {
|
||||
const box = { value: 1, get trigger() { this.value = 2; return 0; } };
|
||||
box.trigger;
|
||||
return { type: "event", value: box.value, stable: "kept" };
|
||||
}`,
|
||||
right: `export default function observe() {
|
||||
return { type: "event", value: 2, stable: "kept" };
|
||||
}`,
|
||||
});
|
||||
const execution = compareJavaScriptExportShapesEvidence({
|
||||
left: left.evidence,
|
||||
right: right.evidence,
|
||||
left_module_path: "parser.mjs",
|
||||
left_export_name: "default",
|
||||
right_module_path: "parser.mjs",
|
||||
right_export_name: "default",
|
||||
});
|
||||
if (!execution.ok) throw execution.error;
|
||||
expect(execution.value.result.changes).toEqual([
|
||||
expect.objectContaining({
|
||||
status: "unknown",
|
||||
path: "/value",
|
||||
left: expect.objectContaining({ availability: "unknown" }),
|
||||
right: { availability: "literal", value: 2 },
|
||||
}),
|
||||
]);
|
||||
expect(execution.value.sources).toContain(left.evidence);
|
||||
expect(execution.value.result.coverage.status).toBe("partial");
|
||||
expect(inventoryProperties(execution.value.result, "left", 0)).toEqual(
|
||||
expect.arrayContaining(["/stable", "/value", "/type"]),
|
||||
);
|
||||
});
|
||||
|
||||
it("preserves independent implicit invocation unknowns through retained export Evidence", async () => {
|
||||
const [left, right] = await analyzeSources({
|
||||
left: `export default function observe() {
|
||||
const getter = { value: 1, get trigger() { this.value = 2; return 0; } };
|
||||
const copy = { ...getter };
|
||||
const coercion = { [Symbol.toPrimitive]() { this.value = 2; return "key"; }, value: 1 };
|
||||
const keyed = { [coercion]: 0 };
|
||||
const disposal = { [Symbol.dispose]() { this.value = 2; }, value: 1 };
|
||||
{ using owned = disposal; }
|
||||
return { type: "event", getter: getter.value, coercion: coercion.value, disposal: disposal.value, stable: "kept" };
|
||||
}`,
|
||||
right: `export default function observe() {
|
||||
return { type: "event", getter: 2, coercion: 2, disposal: 2, stable: "kept" };
|
||||
}`,
|
||||
});
|
||||
const result = compare(left, right);
|
||||
expect(result.changes).toHaveLength(3);
|
||||
expect(result.changes).toEqual(
|
||||
expect.arrayContaining(
|
||||
["/getter", "/coercion", "/disposal"].map((path) =>
|
||||
expect.objectContaining({
|
||||
path,
|
||||
status: "unknown",
|
||||
left: expect.objectContaining({ availability: "unknown" }),
|
||||
right: { availability: "literal", value: 2 },
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(result.coverage.status).toBe("partial");
|
||||
expect(inventoryProperties(result, "left", 0)).toContain("/stable");
|
||||
});
|
||||
};
|
||||
|
||||
describe("JavaScript export return-shape comparison", () => {
|
||||
it("propagates proven ancestor absence to nested additions and removals", async () => {
|
||||
const [left, right] = await analyzeSources({
|
||||
@@ -130,73 +200,7 @@ describe("JavaScript export return-shape comparison", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("preserves accessor-effect uncertainty through analyzed Evidence and export comparison", async () => {
|
||||
const [left, right] = await analyzeSources({
|
||||
left: `export default function observe() {
|
||||
const box = { value: 1, get trigger() { this.value = 2; return 0; } };
|
||||
box.trigger;
|
||||
return { type: "event", value: box.value, stable: "kept" };
|
||||
}`,
|
||||
right: `export default function observe() {
|
||||
return { type: "event", value: 2, stable: "kept" };
|
||||
}`,
|
||||
});
|
||||
const execution = compareJavaScriptExportShapesEvidence({
|
||||
left: left.evidence,
|
||||
right: right.evidence,
|
||||
left_module_path: "parser.mjs",
|
||||
left_export_name: "default",
|
||||
right_module_path: "parser.mjs",
|
||||
right_export_name: "default",
|
||||
});
|
||||
if (!execution.ok) throw execution.error;
|
||||
expect(execution.value.result.changes).toEqual([
|
||||
expect.objectContaining({
|
||||
status: "unknown",
|
||||
path: "/value",
|
||||
left: expect.objectContaining({ availability: "unknown" }),
|
||||
right: { availability: "literal", value: 2 },
|
||||
}),
|
||||
]);
|
||||
expect(execution.value.sources).toContain(left.evidence);
|
||||
expect(execution.value.result.coverage.status).toBe("partial");
|
||||
expect(inventoryProperties(execution.value.result, "left", 0)).toEqual(
|
||||
expect.arrayContaining(["/stable", "/value", "/type"]),
|
||||
);
|
||||
});
|
||||
|
||||
it("preserves independent implicit invocation unknowns through retained export Evidence", async () => {
|
||||
const [left, right] = await analyzeSources({
|
||||
left: `export default function observe() {
|
||||
const getter = { value: 1, get trigger() { this.value = 2; return 0; } };
|
||||
const copy = { ...getter };
|
||||
const coercion = { [Symbol.toPrimitive]() { this.value = 2; return "key"; }, value: 1 };
|
||||
const keyed = { [coercion]: 0 };
|
||||
const disposal = { [Symbol.dispose]() { this.value = 2; }, value: 1 };
|
||||
{ using owned = disposal; }
|
||||
return { type: "event", getter: getter.value, coercion: coercion.value, disposal: disposal.value, stable: "kept" };
|
||||
}`,
|
||||
right: `export default function observe() {
|
||||
return { type: "event", getter: 2, coercion: 2, disposal: 2, stable: "kept" };
|
||||
}`,
|
||||
});
|
||||
const result = compare(left, right);
|
||||
expect(result.changes).toHaveLength(3);
|
||||
expect(result.changes).toEqual(
|
||||
expect.arrayContaining(
|
||||
["/getter", "/coercion", "/disposal"].map((path) =>
|
||||
expect.objectContaining({
|
||||
path,
|
||||
status: "unknown",
|
||||
left: expect.objectContaining({ availability: "unknown" }),
|
||||
right: { availability: "literal", value: 2 },
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(result.coverage.status).toBe("partial");
|
||||
expect(inventoryProperties(result, "left", 0)).toContain("/stable");
|
||||
});
|
||||
registerImplicitInvocationComparisonTests();
|
||||
|
||||
it("keeps additions unknown when spread coverage is incomplete", async () => {
|
||||
const [left, right] = await analyzeSources({
|
||||
|
||||
@@ -602,6 +602,94 @@ describe("reference source manifest inventory", () => {
|
||||
});
|
||||
});
|
||||
|
||||
const registerReferenceRelocationTests = (): void => {
|
||||
it("is relocation-stable, resolves imports, and excludes secrets before capture", async () => {
|
||||
const parent = await createTestTempDirectory("rea-reference-import-");
|
||||
try {
|
||||
const leftRoot = await fixture(parent, "left");
|
||||
const rightRoot = await fixture(parent, "right");
|
||||
const [left, right] = await Promise.all([
|
||||
importTree(leftRoot),
|
||||
importTree(rightRoot),
|
||||
]);
|
||||
if (!left.ok || !right.ok) throw new Error("expected imports to pass");
|
||||
expect(createHistoricalSourceManifest(left.value)).toEqual(
|
||||
createHistoricalSourceManifest(right.value),
|
||||
);
|
||||
expect(left.value.relationships).toContainEqual(
|
||||
expect.objectContaining({
|
||||
from_path: "src/main.ts",
|
||||
to: "src/dep.ts",
|
||||
resolution: "internal",
|
||||
}),
|
||||
);
|
||||
expect(left.value.parse_failures).toHaveLength(1);
|
||||
expect(left.value.exclusions).toContainEqual({
|
||||
path: ".env",
|
||||
entry_kind: "file",
|
||||
reason: "configured-secret",
|
||||
pattern: ".env",
|
||||
});
|
||||
expect(JSON.stringify(left.value)).not.toContain("SECRET_SENTINEL");
|
||||
expect(JSON.stringify(left.value)).not.toContain(leftRoot);
|
||||
} finally {
|
||||
await rm(parent, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it
|
||||
.skipIf(process.platform === "win32" || process.getuid?.() === 0)
|
||||
.each(["file", "directory"] as const)(
|
||||
"commits equivalent unreadable %s inventories independently of relocation and retains exact local diagnostics",
|
||||
async (kind) => {
|
||||
const parent = await createTestTempDirectory(
|
||||
"rea-reference-failure-relocation-",
|
||||
);
|
||||
const roots = [join(parent, "left"), join(parent, "right")];
|
||||
const denied: string[] = [];
|
||||
try {
|
||||
for (const root of roots) {
|
||||
await mkdir(root);
|
||||
const path = join(root, kind === "file" ? "blocked.ts" : "blocked");
|
||||
if (kind === "file")
|
||||
await writeFile(path, "export const answer = 42;\n");
|
||||
else await mkdir(path);
|
||||
denied.push(path);
|
||||
await chmod(path, 0);
|
||||
}
|
||||
const [left, right] = await Promise.all(
|
||||
roots.map((root) => importTree(root)),
|
||||
);
|
||||
if (left === undefined || right === undefined || !left.ok || !right.ok)
|
||||
throw new Error("Expected partial imports");
|
||||
expect(left.value.inventory_state).toBe("partial");
|
||||
expect(createHistoricalSourceManifest(left.value)).toEqual(
|
||||
createHistoricalSourceManifest(right.value),
|
||||
);
|
||||
for (const [index, graph] of [left.value, right.value].entries()) {
|
||||
const entry = graph.entries[0];
|
||||
const root = roots[index];
|
||||
if (entry === undefined || root === undefined)
|
||||
throw new Error("Expected failed entry");
|
||||
expect(entry.limitations.join(" ")).toContain("EACCES");
|
||||
expect(entry.limitations.join(" ")).toMatch(/Check.*permissions/u);
|
||||
expect(entry.limitations.join(" ")).not.toContain(root);
|
||||
expect(entry.local_diagnostics).toEqual([
|
||||
expect.stringContaining(await realpath(root)),
|
||||
]);
|
||||
expect(
|
||||
createHistoricalSourceSummary(graph, 2).outline.entries[0],
|
||||
).toMatchObject({ local_diagnostics: entry.local_diagnostics });
|
||||
}
|
||||
} finally {
|
||||
for (const path of denied)
|
||||
await chmod(path, kind === "file" ? 0o600 : 0o700);
|
||||
await rm(parent, { recursive: true, force: true });
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
describe("reference source import behavior", () => {
|
||||
// The importer declares no byte ceiling and no entry-count ceiling. Scale
|
||||
// independence cannot be asserted as a relationship over a bounded fixture,
|
||||
@@ -697,91 +785,7 @@ describe("reference source import behavior", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("is relocation-stable, resolves imports, and excludes secrets before capture", async () => {
|
||||
const parent = await createTestTempDirectory("rea-reference-import-");
|
||||
try {
|
||||
const leftRoot = await fixture(parent, "left");
|
||||
const rightRoot = await fixture(parent, "right");
|
||||
const [left, right] = await Promise.all([
|
||||
importTree(leftRoot),
|
||||
importTree(rightRoot),
|
||||
]);
|
||||
if (!left.ok || !right.ok) throw new Error("expected imports to pass");
|
||||
expect(createHistoricalSourceManifest(left.value)).toEqual(
|
||||
createHistoricalSourceManifest(right.value),
|
||||
);
|
||||
expect(left.value.relationships).toContainEqual(
|
||||
expect.objectContaining({
|
||||
from_path: "src/main.ts",
|
||||
to: "src/dep.ts",
|
||||
resolution: "internal",
|
||||
}),
|
||||
);
|
||||
expect(left.value.parse_failures).toHaveLength(1);
|
||||
expect(left.value.exclusions).toContainEqual({
|
||||
path: ".env",
|
||||
entry_kind: "file",
|
||||
reason: "configured-secret",
|
||||
pattern: ".env",
|
||||
});
|
||||
expect(JSON.stringify(left.value)).not.toContain("SECRET_SENTINEL");
|
||||
expect(JSON.stringify(left.value)).not.toContain(leftRoot);
|
||||
} finally {
|
||||
await rm(parent, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it
|
||||
.skipIf(process.platform === "win32" || process.getuid?.() === 0)
|
||||
.each(["file", "directory"] as const)(
|
||||
"commits equivalent unreadable %s inventories independently of relocation and retains exact local diagnostics",
|
||||
async (kind) => {
|
||||
const parent = await createTestTempDirectory(
|
||||
"rea-reference-failure-relocation-",
|
||||
);
|
||||
const roots = [join(parent, "left"), join(parent, "right")];
|
||||
const denied: string[] = [];
|
||||
try {
|
||||
for (const root of roots) {
|
||||
await mkdir(root);
|
||||
const path = join(root, kind === "file" ? "blocked.ts" : "blocked");
|
||||
if (kind === "file")
|
||||
await writeFile(path, "export const answer = 42;\n");
|
||||
else await mkdir(path);
|
||||
denied.push(path);
|
||||
await chmod(path, 0);
|
||||
}
|
||||
const [left, right] = await Promise.all(
|
||||
roots.map((root) => importTree(root)),
|
||||
);
|
||||
if (left === undefined || right === undefined || !left.ok || !right.ok)
|
||||
throw new Error("Expected partial imports");
|
||||
expect(left.value.inventory_state).toBe("partial");
|
||||
expect(createHistoricalSourceManifest(left.value)).toEqual(
|
||||
createHistoricalSourceManifest(right.value),
|
||||
);
|
||||
for (const [index, graph] of [left.value, right.value].entries()) {
|
||||
const entry = graph.entries[0];
|
||||
const root = roots[index];
|
||||
if (entry === undefined || root === undefined)
|
||||
throw new Error("Expected failed entry");
|
||||
expect(entry.limitations.join(" ")).toContain("EACCES");
|
||||
expect(entry.limitations.join(" ")).toMatch(/Check.*permissions/u);
|
||||
expect(entry.limitations.join(" ")).not.toContain(root);
|
||||
expect(entry.local_diagnostics).toEqual([
|
||||
expect.stringContaining(await realpath(root)),
|
||||
]);
|
||||
expect(
|
||||
createHistoricalSourceSummary(graph, 2).outline.entries[0],
|
||||
).toMatchObject({ local_diagnostics: entry.local_diagnostics });
|
||||
}
|
||||
} finally {
|
||||
for (const path of denied)
|
||||
await chmod(path, kind === "file" ? 0o600 : 0o700);
|
||||
await rm(parent, { recursive: true, force: true });
|
||||
}
|
||||
},
|
||||
);
|
||||
registerReferenceRelocationTests();
|
||||
|
||||
it("imports the caller-selected directory and honors cancellation", async () => {
|
||||
const outside = await createTestTempDirectory("rea-reference-outside-");
|
||||
|
||||
Reference in new issue
Block a user