mirror of
https://github.com/morluto/rea
synced 2026-10-26 06:16:17 -04:00
185 lines
9.2 KiB
TypeScript
185 lines
9.2 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { fileURLToPath } from "node:url";
|
|
import { RuleTester } from "oxlint/plugins-dev";
|
|
import rule from "../../scripts/lint/rules/no-positional-tool-contract.mjs";
|
|
import { collectToolCatalogSources } from "../../scripts/lint/rules/tool-catalog-sources.mjs";
|
|
|
|
RuleTester.describe = describe;
|
|
RuleTester.it = it;
|
|
const tester = new RuleTester({
|
|
languageOptions: { parserOptions: { lang: "ts" } },
|
|
});
|
|
const filename = fileURLToPath(
|
|
new URL("../../src/server/ruleControl.ts", import.meta.url),
|
|
);
|
|
const catalog =
|
|
'import { GPU_TOOL_CONTRACTS as contracts } from "../contracts/gpu/gpuToolContracts.js";';
|
|
const catalogSource = new URL(
|
|
"../../src/contracts/gpu/gpuToolContracts.js",
|
|
import.meta.url,
|
|
);
|
|
tester.run("no-positional-tool-contract", rule, {
|
|
valid: [
|
|
`import {GPU_TOOL_CONTRACTS as contracts} from ${JSON.stringify(catalogSource.href)}; function read(contracts: {name: string}[]) { return contracts[0].name; }`,
|
|
'import {GPU_TOOL_CONTRACTS as contracts} from "https://example.test/src/contracts/gpu/gpuToolContracts.js"; contracts[0].name;',
|
|
`import {GPU_TOOL_CONTRACTS as contracts} from ${JSON.stringify(`${catalogSource.href}#other`)}; contracts[0].name;`,
|
|
`${catalog} for (const contract of contracts) register(contract);`,
|
|
`${catalog} contracts.forEach(register);`,
|
|
`${catalog} const selected = contracts.find(contract => contract.name === "inspect_gpu_code_objects");`,
|
|
`${catalog} type Contract = (typeof contracts)[number];`,
|
|
`${catalog} const copies = [...contracts];`,
|
|
`${catalog} const [...copies] = contracts;`,
|
|
`${catalog} function read(contracts: {name: string}[]) { return contracts[0].name; }`,
|
|
`${catalog} function read() { const contracts = [{name: "local"}]; return contracts.at(0)?.name; }`,
|
|
'import { GPU_TOOL_CONTRACTS as contracts } from "../unrelated.js"; contracts[0].name;',
|
|
'import { unrelated as contracts } from "../contracts/gpu/gpuToolContracts.js"; contracts[0].name;',
|
|
'const TOOL_CONTRACTS = [{name: "local"}]; TOOL_CONTRACTS[0].name;',
|
|
`${catalog} const mutable = [...contracts]; mutable[0].inputSchema;`,
|
|
`${catalog} contracts.length;`,
|
|
`${catalog} contracts[0]; contracts.at(1); const [unused] = contracts;`,
|
|
`${catalog} expect(contracts[0]?.description).toContain("documented behavior");`,
|
|
`${catalog} const [first] = contracts; const toggled = contracts.map(contract => contract === first ? {...contract, returnsTargetContent: !contract.returnsTargetContent} : contract); createCatalogDigests(toggled);`,
|
|
`${catalog} const selected = contracts[0]; const alias = selected; display(alias.description);`,
|
|
`${catalog} const selected = contracts[0]; selected.name = "catalog-control";`,
|
|
`${catalog} const [selected] = contracts; const { description } = selected; display(description);`,
|
|
`${catalog} function toolRegistrationOptions(contract: unknown) {} toolRegistrationOptions(contracts[0]);`,
|
|
`${catalog} { const contracts = [{name: "local"}]; contracts[0].name; }`,
|
|
'import * as catalogs from "../contracts/gpu/gpuToolContracts.js"; function read(catalogs: {GPU_TOOL_CONTRACTS: {name: string}[]}) { return catalogs.GPU_TOOL_CONTRACTS[0].name; }',
|
|
'import * as catalogs from "../contracts/gpu/gpuToolContracts.js"; catalogs.other[0].name;',
|
|
].map((code) => ({ code, filename })),
|
|
invalid: [
|
|
`import {GPU_TOOL_CONTRACTS as contracts} from ${JSON.stringify(catalogSource.href)}; contracts[0].name;`,
|
|
`import {GPU_TOOL_CONTRACTS as contracts} from ${JSON.stringify(fileURLToPath(catalogSource))}; contracts[0].inputSchema;`,
|
|
`${catalog} import {toolRegistrationOptions as options} from ${JSON.stringify(new URL("../../src/server/toolRegistrationOptions.js", import.meta.url).href)}; options(contracts[0]);`,
|
|
`${catalog} register(contracts[0].name);`,
|
|
`${catalog} contracts["0"].inputSchema.parse(input);`,
|
|
`${catalog} contracts.at(-1)?.outputSchema;`,
|
|
`${catalog} contracts["at"](1)?.name;`,
|
|
`${catalog} const alias = contracts; alias[2].name;`,
|
|
`${catalog} const first = contracts; const second = first; second[0].outputSchema;`,
|
|
`${catalog} type Selected = (typeof contracts)[0];`,
|
|
`${catalog} const [first] = contracts; register(first.name);`,
|
|
`${catalog} const [, second] = contracts; const selected = second; selected.inputSchema;`,
|
|
`${catalog} const selected = contracts[0]; const alias = selected; alias.inputSchema;`,
|
|
`${catalog} const { inputSchema } = contracts[0];`,
|
|
`${catalog} import {toolRegistrationOptions as options} from "./toolRegistrationOptions.js"; options(contracts[0]);`,
|
|
`${catalog} import {applicationToolRegistrationOptions as options} from "./toolRegistrationOptions.ts"; const selected = contracts.at(0); options(selected);`,
|
|
`${catalog} const selected = contracts[0]; selected.name; selected.outputSchema;`,
|
|
'import * as catalogs from "../contracts/gpu/gpuToolContracts.js"; catalogs.GPU_TOOL_CONTRACTS[0].name;',
|
|
'import * as catalogs from "../contracts/gpu/gpuToolContracts.js"; type Selected = (typeof catalogs.GPU_TOOL_CONTRACTS)[0];',
|
|
'import { TOOL_CONTRACTS } from "../contracts/toolContracts.ts"; TOOL_CONTRACTS[0].name;',
|
|
'import { MODEL_TOOL_CONTRACTS as selected } from "../contracts/model/modelToolContracts.js"; selected[0].inputSchema;',
|
|
'import { IL2CPP_TOOL_CONTRACTS as selected } from "../contracts/il2cpp/il2cppToolContracts.js"; selected[0].outputSchema;',
|
|
'import { BROWSER_SCENARIO_TOOL_CONTRACTS as nested } from "../contracts/browserScenarioToolContracts.js"; nested[0].name;',
|
|
'import { FUNCTION_WORKFLOW_TOOL_CONTRACTS as nested } from "../contracts/functionWorkflowToolContracts.js"; nested.at(0)?.name;',
|
|
].map((code) => ({ code, filename, errors: [{ messageId: "positional" }] })),
|
|
});
|
|
|
|
const sourcePath = (name: string) =>
|
|
fileURLToPath(new URL(`./catalog-rule-fixture/${name}`, import.meta.url));
|
|
|
|
describe("canonical catalog source composition", () => {
|
|
it("discovers newly composed exports and nested groups without evaluating modules", () => {
|
|
const sources = new Map([
|
|
[
|
|
sourcePath("toolContracts.ts"),
|
|
`
|
|
import { NEW_CATALOG as renamed } from "./newCatalog.js";
|
|
import { GROUP } from "./group.js";
|
|
import { UNUSED } from "./unused.js";
|
|
const generated = buildContracts();
|
|
const local = [...renamed, ...GROUP, ...generated, ...UNUSED.map(makeContract)];
|
|
const alias = local;
|
|
export const TOOL_CONTRACTS = [...alias] as const;
|
|
`,
|
|
],
|
|
[
|
|
sourcePath("newCatalog.ts"),
|
|
`
|
|
throw new Error("Must never execute the runtime catalog");
|
|
export const NEW_CATALOG = [{name: "new_tool"}];
|
|
`,
|
|
],
|
|
[
|
|
sourcePath("group.ts"),
|
|
`
|
|
import { NESTED as alias } from "./nested.ts";
|
|
export const GROUP = [...alias] satisfies readonly unknown[];
|
|
`,
|
|
],
|
|
[
|
|
sourcePath("nested.ts"),
|
|
'export const NESTED = [{name: "nested_tool"}];',
|
|
],
|
|
]);
|
|
const actual = collectToolCatalogSources(
|
|
sourcePath("toolContracts.ts"),
|
|
(path) => {
|
|
const source = sources.get(path);
|
|
if (source === undefined)
|
|
throw new Error(`Unexpected source read: ${path}`);
|
|
return source;
|
|
},
|
|
);
|
|
expect(actual).toEqual(
|
|
new Map([
|
|
[sourcePath("toolContracts"), new Set(["TOOL_CONTRACTS"])],
|
|
[sourcePath("newCatalog"), new Set(["NEW_CATALOG"])],
|
|
[sourcePath("group"), new Set(["GROUP"])],
|
|
[sourcePath("nested"), new Set(["NESTED"])],
|
|
]),
|
|
);
|
|
});
|
|
|
|
it("accepts official computed definitions while discovering imported catalog groups", () => {
|
|
const actual = collectToolCatalogSources(
|
|
fileURLToPath(
|
|
new URL("../../src/contracts/toolContracts.ts", import.meta.url),
|
|
),
|
|
);
|
|
const official = fileURLToPath(
|
|
new URL("../../src/contracts/officialToolContracts", import.meta.url),
|
|
);
|
|
const definitions = fileURLToPath(
|
|
new URL("../../src/contracts/hopperMemoryContracts", import.meta.url),
|
|
);
|
|
const nested = fileURLToPath(
|
|
new URL(
|
|
"../../src/contracts/browserScenarioToolContracts",
|
|
import.meta.url,
|
|
),
|
|
);
|
|
expect(actual.get(official)).toEqual(new Set(["OFFICIAL_TOOL_CONTRACTS"]));
|
|
expect(actual.has(definitions)).toBe(false);
|
|
expect(actual.get(nested)).toEqual(
|
|
new Set(["BROWSER_SCENARIO_TOOL_CONTRACTS"]),
|
|
);
|
|
});
|
|
|
|
it("propagates source-read and parse failures instead of disabling enforcement", () => {
|
|
const failure = new Error("catalog source unavailable");
|
|
expect(() =>
|
|
collectToolCatalogSources(sourcePath("toolContracts.ts"), () => {
|
|
throw failure;
|
|
}),
|
|
).toThrow(failure);
|
|
expect(() =>
|
|
collectToolCatalogSources(
|
|
sourcePath("toolContracts.ts"),
|
|
() => "export const TOOL_CONTRACTS = [",
|
|
),
|
|
).toThrow();
|
|
});
|
|
|
|
it.each([
|
|
"export const TOOL_CONTRACTS = buildCatalog();",
|
|
"export const OTHER = [];",
|
|
"const alias = alias; export const TOOL_CONTRACTS = [...alias];",
|
|
"export const TOOL_CONTRACTS = [...unresolved];",
|
|
])("refuses unsupported or missing canonical composition: %s", (source) => {
|
|
expect(() =>
|
|
collectToolCatalogSources(sourcePath("toolContracts.ts"), () => source),
|
|
).toThrow();
|
|
});
|
|
});
|