Files
my-pi/pi-extension-codex-fast-mode/tests/fast-mode.test.ts
T

300 lines
13 KiB
TypeScript

import assert from "node:assert/strict";
import { chmodSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import type { ExtensionAPI, ExtensionCommandContext, ExtensionContext } from "@earendil-works/pi-coding-agent";
import codexFastModeExtension, {
FAST_MODE_SERVICE_TIER,
FAST_MODE_STATE_ENTRY_TYPE,
FAST_MODE_STATUS_KEY,
fastModeArgumentCompletions,
getFastModeConfigPath,
isFastModeEligibleModel,
isPlainObject,
loadGlobalFastModeState,
parseFastModeCommand,
reconstructFastModeState,
saveGlobalFastModeState,
transformFastModeRequest,
type FastModeState,
} from "../index.ts";
type StatusUpdate = { key: string; value: string | undefined };
type Notification = { message: string; level: string };
type Handler = (event: any, ctx: any) => unknown;
function createHarness(options: {
entries?: unknown[];
busy?: boolean;
pending?: boolean;
globalEnabled?: boolean;
saveError?: string;
} = {}) {
const handlers = new Map<string, Handler>();
const commands = new Map<string, any>();
const entries = options.entries ?? [];
const statusUpdates: StatusUpdate[] = [];
const notifications: Notification[] = [];
const appendCalls: Array<{ customType: string; data: unknown }> = [];
const loadCalls: string[] = [];
const saveCalls: Array<{ path: string; state: FastModeState }> = [];
let busy = options.busy ?? false;
let pending = options.pending ?? false;
let globalEnabled = options.globalEnabled ?? false;
const context = {
model: { provider: "openai-codex", api: "openai-codex-responses" },
sessionManager: { getBranch: () => entries },
isIdle: () => !busy,
hasPendingMessages: () => pending,
ui: {
setStatus(key: string, value: string | undefined) {
statusUpdates.push({ key, value });
},
notify(message: string, level: string) {
notifications.push({ message, level });
},
},
} as unknown as ExtensionCommandContext;
codexFastModeExtension({
on(event: string, handler: Handler) {
handlers.set(event, handler);
},
registerCommand(name: string, command: unknown) {
commands.set(name, command);
},
appendEntry(customType: string, data: unknown) {
appendCalls.push({ customType, data });
entries.push({ type: "custom", customType, data });
},
} as unknown as ExtensionAPI, {
configPath: "/test-agent/extensions/pi-extension-codex-fast-mode/config.json",
loadGlobalState(path) {
loadCalls.push(path);
return { state: { enabled: globalEnabled } };
},
saveGlobalState(path, state) {
saveCalls.push({ path, state });
if (options.saveError) return { success: false, error: options.saveError };
globalEnabled = state.enabled;
return { success: true };
},
});
return {
appendCalls,
commands,
context,
entries,
handlers,
loadCalls,
notifications,
saveCalls,
setBusy(value: boolean) { busy = value; },
setPending(value: boolean) { pending = value; },
statusUpdates,
};
}
test("plain-object guard excludes malformed payloads", () => {
assert.equal(isPlainObject({}), true);
assert.equal(isPlainObject(Object.create(null)), true);
assert.equal(isPlainObject([]), false);
assert.equal(isPlainObject(new Date()), false);
assert.equal(isPlainObject(null), false);
assert.equal(isPlainObject("payload"), false);
});
test("provider eligibility is exact", () => {
assert.equal(isFastModeEligibleModel({ provider: "openai-codex", api: "openai-codex-responses" }), true);
assert.equal(isFastModeEligibleModel({ provider: "openai", api: "openai-codex-responses" }), false);
assert.equal(isFastModeEligibleModel({ provider: "openai-codex", api: "openai-responses" }), false);
assert.equal(isFastModeEligibleModel(undefined), false);
});
test("request transformer is isolated, non-mutating, and overwrites only service_tier", () => {
const payload = { model: "gpt-5.6-codex", service_tier: "default", nested: { preserved: true } };
const transformed = transformFastModeRequest(true, { provider: "openai-codex", api: "openai-codex-responses" }, payload);
assert.deepEqual(transformed, {
model: "gpt-5.6-codex",
service_tier: FAST_MODE_SERVICE_TIER,
nested: { preserved: true },
});
assert.notStrictEqual(transformed, payload);
assert.strictEqual(transformed?.nested, payload.nested);
assert.equal(payload.service_tier, "default");
assert.equal(transformFastModeRequest(false, { provider: "openai-codex", api: "openai-codex-responses" }, payload), undefined);
assert.equal(transformFastModeRequest(true, { provider: "openai", api: "openai-codex-responses" }, payload), undefined);
assert.equal(transformFastModeRequest(true, { provider: "openai-codex", api: "openai-codex-responses" }, []), undefined);
});
test("branch reconstruction prefers the latest valid snapshot over the global default", () => {
assert.deepEqual(reconstructFastModeState([], true), { enabled: true });
assert.deepEqual(reconstructFastModeState([
{ type: "custom", customType: FAST_MODE_STATE_ENTRY_TYPE, data: { enabled: true } },
{ type: "custom", customType: "other", data: { enabled: false } },
{ type: "custom", customType: FAST_MODE_STATE_ENTRY_TYPE, data: { enabled: "invalid" } },
{ type: "custom", customType: FAST_MODE_STATE_ENTRY_TYPE, data: { enabled: false } },
], true), { enabled: false });
});
test("global config round-trips atomically with owner-only permissions", () => {
const agentDir = mkdtempSync(join(tmpdir(), "fast-mode-test-"));
const configPath = getFastModeConfigPath(agentDir);
try {
assert.deepEqual(loadGlobalFastModeState(configPath), { state: { enabled: false } });
assert.deepEqual(saveGlobalFastModeState(configPath, { enabled: true }), { success: true });
assert.deepEqual(loadGlobalFastModeState(configPath), { state: { enabled: true } });
assert.deepEqual(JSON.parse(readFileSync(configPath, "utf8")), { enabled: true });
assert.equal(statSync(configPath).mode & 0o777, 0o600);
assert.equal(statSync(join(agentDir, "extensions", "pi-extension-codex-fast-mode")).mode & 0o777, 0o700);
} finally {
rmSync(agentDir, { recursive: true, force: true });
}
});
test("invalid or unreadable global config fails safely to off", () => {
const directory = mkdtempSync(join(tmpdir(), "fast-mode-invalid-"));
const configPath = join(directory, "config.json");
try {
writeFileSync(configPath, "{ not-json", "utf8");
const invalid = loadGlobalFastModeState(configPath);
assert.deepEqual(invalid.state, { enabled: false });
assert.match(invalid.warning ?? "", /Could not read/u);
writeFileSync(configPath, JSON.stringify({ enabled: "yes" }), "utf8");
const wrongType = loadGlobalFastModeState(configPath);
assert.deepEqual(wrongType.state, { enabled: false });
assert.match(wrongType.warning ?? "", /expected/u);
chmodSync(configPath, 0o600);
} finally {
rmSync(directory, { recursive: true, force: true });
}
});
test("command grammar and completions are deterministic", () => {
assert.equal(parseFastModeCommand(""), "toggle");
assert.equal(parseFastModeCommand(" ON "), "on");
assert.equal(parseFastModeCommand("off"), "off");
assert.equal(parseFastModeCommand("status"), "status");
assert.equal(parseFastModeCommand("on now"), "invalid");
assert.deepEqual(fastModeArgumentCompletions("o"), [
{ value: "on", label: "on" },
{ value: "off", label: "off" },
]);
assert.deepEqual(fastModeArgumentCompletions("status"), [{ value: "status", label: "status" }]);
});
test("a new session inherits the global default while a branch snapshot takes precedence", () => {
const inherited = createHarness({ globalEnabled: true });
const inheritedStart = inherited.handlers.get("session_start");
assert.ok(inheritedStart);
inheritedStart!({}, inherited.context as unknown as ExtensionContext);
assert.deepEqual(inherited.statusUpdates.at(-1), { key: FAST_MODE_STATUS_KEY, value: "on" });
const overridden = createHarness({
globalEnabled: true,
entries: [{ type: "custom", customType: FAST_MODE_STATE_ENTRY_TYPE, data: { enabled: false } }],
});
const overriddenStart = overridden.handlers.get("session_start");
assert.ok(overriddenStart);
overriddenStart!({}, overridden.context as unknown as ExtensionContext);
assert.deepEqual(overridden.statusUpdates.at(-1), { key: FAST_MODE_STATUS_KEY, value: "off" });
});
test("extension restores state, publishes status, and persists session and global mutations", async () => {
const harness = createHarness({
entries: [{ type: "custom", customType: FAST_MODE_STATE_ENTRY_TYPE, data: { enabled: true } }],
});
const sessionStart = harness.handlers.get("session_start");
const request = harness.handlers.get("before_provider_request");
const command = harness.commands.get("fast-mode");
assert.ok(sessionStart && request && command);
sessionStart!({}, harness.context as unknown as ExtensionContext);
assert.deepEqual(harness.statusUpdates.at(-1), { key: FAST_MODE_STATUS_KEY, value: "on" });
assert.deepEqual(request!({ payload: { service_tier: "standard", keep: true } }, harness.context), {
service_tier: FAST_MODE_SERVICE_TIER,
keep: true,
});
await command.handler("off", harness.context);
assert.deepEqual(harness.appendCalls, [{ customType: FAST_MODE_STATE_ENTRY_TYPE, data: { enabled: false } }]);
assert.deepEqual(harness.saveCalls.map((call) => call.state), [{ enabled: false }]);
assert.deepEqual(harness.statusUpdates.at(-1), { key: FAST_MODE_STATUS_KEY, value: "off" });
assert.equal(request!({ payload: { keep: true } }, harness.context), undefined);
await command.handler("status", harness.context);
assert.match(harness.notifications.at(-1)?.message ?? "", /Fast mode: off.*New sessions default to off/u);
assert.equal(harness.notifications.at(-1)?.level, "info");
});
test("an explicit no-op mode command still refreshes the global default", async () => {
const harness = createHarness({ globalEnabled: true });
const sessionStart = harness.handlers.get("session_start");
const command = harness.commands.get("fast-mode");
assert.ok(sessionStart && command);
sessionStart!({}, harness.context as unknown as ExtensionContext);
await command.handler("on", harness.context);
assert.equal(harness.appendCalls.length, 0);
assert.deepEqual(harness.saveCalls.map((call) => call.state), [{ enabled: true }]);
});
test("a global save failure leaves the session usable and reports the persistence failure", async () => {
const harness = createHarness({ saveError: "read-only filesystem" });
const sessionStart = harness.handlers.get("session_start");
const command = harness.commands.get("fast-mode");
assert.ok(sessionStart && command);
sessionStart!({}, harness.context as unknown as ExtensionContext);
await command.handler("on", harness.context);
assert.deepEqual(harness.appendCalls, [{ customType: FAST_MODE_STATE_ENTRY_TYPE, data: { enabled: true } }]);
assert.equal(harness.notifications.at(-1)?.level, "warning");
assert.match(harness.notifications.at(-1)?.message ?? "", /could not be saved.*read-only filesystem/u);
});
test("extension rejects every mutation while busy but leaves status readable", async () => {
const harness = createHarness({ busy: true });
const sessionStart = harness.handlers.get("session_start");
const command = harness.commands.get("fast-mode");
assert.ok(sessionStart && command);
sessionStart!({}, harness.context as unknown as ExtensionContext);
await command.handler("on", harness.context);
await command.handler("", harness.context);
await command.handler("off", harness.context);
assert.equal(harness.appendCalls.length, 0);
assert.equal(harness.saveCalls.length, 0);
assert.equal(harness.notifications.filter((item) => item.level === "warning").length, 3);
await command.handler("status", harness.context);
assert.match(harness.notifications.at(-1)?.message ?? "", /Fast mode: off/u);
assert.equal(harness.notifications.at(-1)?.level, "info");
harness.setBusy(false);
harness.setPending(true);
await command.handler("on", harness.context);
assert.equal(harness.appendCalls.length, 0);
assert.equal(harness.saveCalls.length, 0);
});
test("tree navigation reconstructs the active branch state", () => {
const harness = createHarness({
globalEnabled: true,
entries: [{ type: "custom", customType: FAST_MODE_STATE_ENTRY_TYPE, data: { enabled: true } }],
});
const sessionStart = harness.handlers.get("session_start");
const sessionTree = harness.handlers.get("session_tree");
assert.ok(sessionStart && sessionTree);
sessionStart!({}, harness.context as unknown as ExtensionContext);
harness.entries.push({ type: "custom", customType: FAST_MODE_STATE_ENTRY_TYPE, data: { enabled: false } });
sessionTree!({}, harness.context as unknown as ExtensionContext);
assert.deepEqual(harness.statusUpdates.at(-1), { key: FAST_MODE_STATUS_KEY, value: "off" });
});