Files
rea/tests/evaluation/positionalToolContractRule.test.ts
T

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();
});
});