repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
extensions/rtk/__tests__/harness.test.ts
Rawimport {
chmodSync,
mkdtempSync,
readdirSync,
readFileSync,
rmSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { createTestSession, type TestSession } from "../../../test/harness";
let t: TestSession | undefined;
let oldPath = "";
function rtkPath(): string {
return path.resolve(import.meta.dirname, "../index.ts");
}
function installFakeRtk(): string {
const dir = mkdtempSync(path.join(tmpdir(), "pi-ext-rtk-"));
const script = [
"#!/usr/bin/env node",
"const [, , sub, command] = process.argv;",
"if (sub === '--version') { console.log('rtk 0.37.2'); process.exit(0); }",
"if (sub === 'rewrite' && command === 'git status') { console.log('rtk git status'); process.exit(0); }",
"if (sub === 'rewrite' && command === 'find . -name AGENTS.md') { console.log('rtk find . -name AGENTS.md'); process.exit(3); }",
"if (sub === 'rewrite' && command === 'uptime') { process.exit(2); }",
"process.exit(1);",
"",
].join("\n");
const bin = path.join(dir, process.platform === "win32" ? "rtk.cmd" : "rtk");
if (process.platform === "win32") {
writeFileSync(
bin,
`@echo off\nnode "${path.join(dir, "rtk.js")}" %*\n`,
"utf8",
);
writeFileSync(path.join(dir, "rtk.js"), script, "utf8");
} else {
writeFileSync(bin, script, "utf8");
chmodSync(bin, 0o755);
}
return dir;
}
beforeEach(async () => {
oldPath = process.env.PATH ?? "";
process.env.PATH = `${installFakeRtk()}${path.delimiter}${oldPath}`;
t = await createTestSession({ extensions: [rtkPath()] });
});
afterEach(() => {
t?.dispose();
t = undefined;
process.env.PATH = oldPath;
});
describe("rtk harness integration", () => {
it("loads with no tools and no commands", () => {
const [extension] = t?.session.extensionRunner.extensions ?? [];
expect(extension?.path).toBe(rtkPath());
expect([...(extension?.tools.keys() ?? [])]).toEqual([]);
expect([...(extension?.commands.keys() ?? [])]).toEqual([]);
});
it("writes its opt-in lifecycle trace inside the harness sandbox", async () => {
const root = mkdtempSync(path.join(tmpdir(), "pi-ext-rtk-debug-"));
const debug = await createTestSession({
cwd: root,
extensions: [rtkPath()],
env: { PI_RTK_DEBUG: "1" },
});
try {
const runner = debug.session.extensionRunner;
await runner.emit({ type: "session_start", reason: "startup" });
await runner.emitToolCall({
type: "tool_call",
toolName: "bash",
toolCallId: "debug-rewrite",
input: { command: "git status" },
} as never);
await runner.emitToolCall({
type: "tool_call",
toolName: "bash",
toolCallId: "debug-fallback",
input: { command: "ps aux" },
} as never);
await runner.emitToolCall({
type: "tool_call",
toolName: "bash",
toolCallId: "debug-failure",
input: { command: "uptime" },
} as never);
await runner.emit({ type: "session_shutdown", reason: "quit" });
const stateHome = debug.env.XDG_STATE_HOME;
if (!stateHome) throw new Error("missing sandbox state home");
const directory = path.join(stateHome, "pi-ext", "debug", "rtk");
const [file] = readdirSync(directory);
const records = readFileSync(path.join(directory, file), "utf8")
.trim()
.split("\n")
.map((line) => JSON.parse(line));
expect(records.map((record) => record.event)).toEqual(
expect.arrayContaining([
"session.start",
"session.shutdown",
"rtk.rewrite",
]),
);
expect(
records
.filter((record) => record.event === "rtk.rewrite")
.map((record) => record.outcome),
).toEqual(["rewritten", "fallback", "unchanged"]);
expect(
records.find(
(record) =>
record.event === "rtk.rewrite" && record.outcome === "unchanged",
)?.reason,
).toBe("unexpected_exit");
} finally {
debug.dispose();
rmSync(root, { recursive: true, force: true });
}
});
it("mutates bash tool calls before execution", async () => {
const event = {
type: "tool_call",
toolName: "bash",
toolCallId: "1",
input: { command: "git status" },
};
await t?.session.extensionRunner.emitToolCall(event as never);
expect(event.input.command).toBe("rtk git status");
});
it("mutates local fallback bash tool calls before execution", async () => {
const event = {
type: "tool_call",
toolName: "bash",
toolCallId: "2",
input: { command: "ps aux" },
};
await t?.session.extensionRunner.emitToolCall(event as never);
expect(event.input.command).toBe("rtk summary ps aux");
});
it("rewrites find commands without a compatibility probe", async () => {
const event = {
type: "tool_call",
toolName: "bash",
toolCallId: "3",
input: { command: "find . -name AGENTS.md" },
};
await t?.session.extensionRunner.emitToolCall(event as never);
expect(event.input.command).toBe("rtk find . -name AGENTS.md");
});
});