Files
oh-my-opencode/packages/omo-claude/plugin/scripts/sync-components.test.mjs
T
YeonGyu-Kim 234429737a feat(omo-claude): add sync-components vendoring script with --check drift gate
Copies 6 omo-codex components (rules, comment-checker, lsp, ultrawork,
ultragoal, start-work-continuation) into the CC plugin tree and applies a
declarative patch manifest: relax model/turn_id validators, CLAUDE_PLUGIN_ROOT/
DATA env fallbacks, .codex-plugin->.claude-plugin, rules matcher widened to
Write|Edit|MultiEdit, start-work-continuation claude: session prefix. ultragoal
copied verbatim (deep refactor is a later task). --check detects vendor drift.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 13:40:10 +09:00

88 lines
3.2 KiB
JavaScript

import { spawnSync } from "node:child_process";
import { readFile, writeFile } from "node:fs/promises";
import assert from "node:assert/strict";
import { dirname, join } from "node:path";
import { test } from "node:test";
import { fileURLToPath } from "node:url";
import { HANDLED_COMPONENTS, PATCH_MANIFEST, syncComponents } from "./sync-components.mjs";
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
const SCRIPT_PATH = join(SCRIPT_DIR, "sync-components.mjs");
const PLUGIN_ROOT = dirname(SCRIPT_DIR);
const DRIFT_FILE = join(PLUGIN_ROOT, "components", "lsp", "src", "codex-hook.ts");
function runCli(args) {
return spawnSync(process.execPath, [SCRIPT_PATH, ...args], { encoding: "utf8" });
}
test("sync then --check is GREEN (exit 0)", () => {
const sync = runCli([]);
assert.equal(sync.status, 0, `sync failed: ${sync.stderr}`);
const check = runCli(["--check"]);
assert.equal(check.status, 0, `--check should be green after sync but got: ${check.stderr}`);
assert.match(check.stdout, /in sync/);
});
test("--check is RED (nonzero) after hand-drift, then re-sync restores GREEN", async () => {
runCli([]);
const original = await readFile(DRIFT_FILE, "utf8");
try {
await writeFile(DRIFT_FILE, `${original}\n// drift\n`);
const drifted = runCli(["--check"]);
assert.notEqual(drifted.status, 0, "--check must fail after hand-drift");
assert.match(drifted.stderr, /out of sync/);
assert.match(drifted.stderr, /lsp\/src\/codex-hook\.ts/);
} finally {
// Re-sync to restore the vendored tree regardless of assertion outcome.
const restore = runCli([]);
assert.equal(restore.status, 0, `restore sync failed: ${restore.stderr}`);
}
const check = runCli(["--check"]);
assert.equal(check.status, 0, `--check should be green again after re-sync: ${check.stderr}`);
});
test("sync is idempotent (running twice yields no drift)", () => {
assert.equal(runCli([]).status, 0);
assert.equal(runCli([]).status, 0);
assert.equal(runCli(["--check"]).status, 0);
});
test("a single component can be synced and check passes", () => {
const sync = runCli(["rules"]);
assert.equal(sync.status, 0, sync.stderr);
assert.match(sync.stdout, /rules/);
// Re-sync all so the rest of the suite/state is whole.
assert.equal(runCli([]).status, 0);
assert.equal(runCli(["--check"]).status, 0);
});
test("unknown component is rejected", () => {
const result = runCli(["does-not-exist"]);
assert.notEqual(result.status, 0);
assert.match(result.stderr, /unknown component/);
});
test("every required replacement in the manifest matches the source", async () => {
const matched = [];
await syncComponents({ check: true, components: HANDLED_COMPONENTS });
// syncComponents records matches via its internal collector; re-derive by
// running a check pass and asserting no required replacement is unmatched.
const result = await syncComponents({ check: true });
for (const entry of result.matched) {
if (entry.required) {
assert.equal(
entry.matched,
true,
`required replacement did not match: ${entry.component}/${entry.file} :: ${entry.find}`,
);
}
}
assert.ok(result.matched.length >= PATCH_MANIFEST.length, "expected manifest replacements to be exercised");
// matched local kept for clarity; not otherwise used.
void matched;
});