103 lines
3.6 KiB
TypeScript
103 lines
3.6 KiB
TypeScript
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
|
|
import { findRuleCandidates } from "../src/rules/finder.js";
|
|
import type { RuleCandidate } from "../src/rules/types.js";
|
|
|
|
const tempDirectories: string[] = [];
|
|
|
|
afterEach(() => {
|
|
for (const directory of tempDirectories.splice(0)) {
|
|
rmSync(directory, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
function makeProject(): { projectRoot: string; homeRoot: string; targetPath: string } {
|
|
const projectRoot = mkdtempSync(join(tmpdir(), "codex-rules-finder-project-"));
|
|
const homeRoot = mkdtempSync(join(tmpdir(), "codex-rules-finder-home-"));
|
|
tempDirectories.push(projectRoot, homeRoot);
|
|
mkdirSync(join(projectRoot, "src", ".omo", "rules"), { recursive: true });
|
|
mkdirSync(join(projectRoot, ".omo", "rules"), { recursive: true });
|
|
mkdirSync(join(homeRoot, ".opencode", "rules"), { recursive: true });
|
|
mkdirSync(join(homeRoot, ".config", "opencode"), { recursive: true });
|
|
writeFileSync(join(projectRoot, "package.json"), JSON.stringify({ name: "fixture" }));
|
|
writeFileSync(join(projectRoot, "AGENTS.md"), "Project rule\n");
|
|
writeFileSync(join(projectRoot, "src", ".omo", "rules", "local.md"), "Local rule\n");
|
|
writeFileSync(join(projectRoot, ".omo", "rules", "root.md"), "Root rule\n");
|
|
writeFileSync(join(homeRoot, ".opencode", "rules", "global.md"), "Global rule\n");
|
|
writeFileSync(join(homeRoot, ".config", "opencode", "AGENTS.md"), "Home rule\n");
|
|
const targetPath = join(projectRoot, "src", "app.ts");
|
|
writeFileSync(targetPath, "export const app = true;\n");
|
|
return { projectRoot, homeRoot, targetPath };
|
|
}
|
|
|
|
function candidateSummary(candidate: RuleCandidate): string {
|
|
return `${candidate.source}:${candidate.distance}:${candidate.relativePath}`;
|
|
}
|
|
|
|
describe("findRuleCandidates", () => {
|
|
it("#given project and user-home rules #when target file is inside project #then candidates keep source distance", () => {
|
|
// given
|
|
const { projectRoot, homeRoot, targetPath } = makeProject();
|
|
|
|
// when
|
|
const candidates = findRuleCandidates({
|
|
projectRoot,
|
|
targetFile: targetPath,
|
|
homeDir: homeRoot,
|
|
disabledSources: new Set(["plugin-bundled"]),
|
|
});
|
|
|
|
// then
|
|
expect(candidates.map(candidateSummary)).toEqual([
|
|
".omo/rules:0:src/.omo/rules/local.md",
|
|
".omo/rules:1:.omo/rules/root.md",
|
|
"AGENTS.md:1:AGENTS.md",
|
|
"~/.opencode/rules:9999:.opencode/rules/global.md",
|
|
"~/.config/opencode/AGENTS.md:9999:.config/opencode/AGENTS.md",
|
|
]);
|
|
});
|
|
|
|
it("#given disabled source #when finding candidates #then matching source is omitted", () => {
|
|
// given
|
|
const { projectRoot, homeRoot, targetPath } = makeProject();
|
|
|
|
// when
|
|
const candidates = findRuleCandidates({
|
|
projectRoot,
|
|
targetFile: targetPath,
|
|
homeDir: homeRoot,
|
|
disabledSources: new Set([".omo/rules", "~/.opencode/rules", "plugin-bundled"]),
|
|
});
|
|
|
|
// then
|
|
expect(candidates.map(candidateSummary)).toEqual([
|
|
"AGENTS.md:1:AGENTS.md",
|
|
"~/.config/opencode/AGENTS.md:9999:.config/opencode/AGENTS.md",
|
|
]);
|
|
});
|
|
|
|
it("#given skip user home #when finding candidates #then only project rules are returned", () => {
|
|
// given
|
|
const { projectRoot, homeRoot, targetPath } = makeProject();
|
|
|
|
// when
|
|
const candidates = findRuleCandidates({
|
|
projectRoot,
|
|
targetFile: targetPath,
|
|
homeDir: homeRoot,
|
|
skipUserHome: true,
|
|
disabledSources: new Set(["plugin-bundled"]),
|
|
});
|
|
|
|
// then
|
|
expect(candidates.map(candidateSummary)).toEqual([
|
|
".omo/rules:0:src/.omo/rules/local.md",
|
|
".omo/rules:1:.omo/rules/root.md",
|
|
"AGENTS.md:1:AGENTS.md",
|
|
]);
|
|
});
|
|
});
|