Files
oh-my-opencode/packages/omo-codex/plugin/components/rules/test/finder.test.ts
T
2026-05-28 13:58:12 +09:00

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",
]);
});
});