Files
oh-my-pi/packages/coding-agent/test/model-registry-command-values.test.ts
T
can1357 b279db1790 test: refactored test suites to eliminate time-based sleeps and polling loops
- Replaced time-based sleeps and polling loops with event-driven promise resolvers and fake timers across agent and tool tests.
- Migrated test suites to share in-memory auth storage and fixtures using lifecycle hooks.
- Updated catalog model definitions, metadata, and configurations.
2026-08-13 19:32:22 +02:00

240 lines
8.2 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, test } from "bun:test";
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import { withAuth } from "@oh-my-pi/pi-ai/auth-retry";
import type { Api, Model } from "@oh-my-pi/pi-ai/types";
import { buildModel } from "@oh-my-pi/pi-catalog/build";
import { ModelRegistry } from "@oh-my-pi/pi-coding-agent/config/model-registry";
import { AuthStorage } from "@oh-my-pi/pi-coding-agent/session/auth-storage";
import { removeSyncWithRetries, Snowflake } from "@oh-my-pi/pi-utils";
function shellQuote(value: string): string {
return `'${value.replaceAll("'", "'\\''")}'`;
}
function stdoutCommand(value: string): string {
if (process.platform !== "win32") return `printf %s ${shellQuote(value)}`;
return `${JSON.stringify(process.execPath)} -e ${JSON.stringify(`process.stdout.write(${JSON.stringify(value)})`)}`;
}
function trackedTokenCommand(tokenFile: string, counterFile: string): string {
if (process.platform !== "win32") {
return `IFS= read -r token < ${shellQuote(tokenFile)}; printf 1 >> ${shellQuote(counterFile)}; [ "$token" = FAIL ] && exit 1; printf %s "$token"`;
}
const script = `const fs=require("node:fs");fs.appendFileSync(${JSON.stringify(counterFile)}, "1");const token=fs.readFileSync(${JSON.stringify(tokenFile)}, "utf8").trim();if(token==="FAIL")process.exit(1);process.stdout.write(token);`;
return `${JSON.stringify(process.execPath)} -e ${JSON.stringify(script)}`;
}
function failedTrackingCommand(counterFile: string): string {
if (process.platform !== "win32") return `printf 1 >> ${shellQuote(counterFile)}; exit 1`;
const script = `const fs=require("node:fs");fs.appendFileSync(${JSON.stringify(counterFile)}, "1");process.exit(1);`;
return `${JSON.stringify(process.execPath)} -e ${JSON.stringify(script)}`;
}
describe("ModelRegistry command-resolved models.yml values", () => {
let tempDir = "";
let authStorage: AuthStorage;
let modelsPath = "";
beforeEach(async () => {
tempDir = path.join(os.tmpdir(), `pi-test-model-command-values-${Snowflake.next()}`);
fs.mkdirSync(tempDir, { recursive: true });
modelsPath = path.join(tempDir, "models.json");
authStorage = await AuthStorage.create(":memory:");
});
afterEach(() => {
authStorage.close();
if (!tempDir || !fs.existsSync(tempDir)) return;
try {
removeSyncWithRetries(tempDir);
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== "EBUSY") throw error;
}
});
test("provider apiKey and headers resolve from command stdout", async () => {
fs.writeFileSync(
modelsPath,
JSON.stringify({
providers: {
anthropic: {
baseUrl: "https://anthropic-proxy.example.com/v1",
apiKey: `!${stdoutCommand("cmd-api-key")}`,
authHeader: true,
headers: { "X-Api-Key": `!${stdoutCommand("cmd-header")}` },
},
},
}),
);
const registry = new ModelRegistry(authStorage, modelsPath);
expect(registry.hasCommandBackedApiKey("anthropic")).toBe(true);
expect(registry.hasCommandBackedApiKey("openai")).toBe(false);
const models = registry.getAll().filter(model => model.provider === "anthropic");
expect(models.length).toBeGreaterThan(1);
for (const model of models) {
expect(model.headers?.Authorization).toBe("Bearer cmd-api-key");
expect(model.headers?.["X-Api-Key"]).toBe("cmd-header");
}
expect(await registry.getApiKey(models[0])).toBe("cmd-api-key");
});
test("modelOverrides headers resolve from command stdout", async () => {
fs.writeFileSync(
modelsPath,
JSON.stringify({
providers: {
"custom-proxy": {
baseUrl: "https://custom-proxy.example.com/v1",
api: "openai-completions",
apiKey: `!${stdoutCommand("cmd-api-key")}`,
authHeader: true,
models: [{ id: "custom-model", name: "Custom Model" }],
modelOverrides: {
"custom-model": { headers: { "X-Model-Key": `!${stdoutCommand("cmd-model-header")}` } },
},
},
},
}),
);
const registry = new ModelRegistry(authStorage, modelsPath);
const model = registry.find("custom-proxy", "custom-model");
expect(model).toBeDefined();
expect(model?.headers?.["X-Model-Key"]).toBe("cmd-model-header");
expect(model?.headers?.Authorization).toBe("Bearer cmd-api-key");
});
test("401 reruns a command-backed API key and updates live auth headers", async () => {
const tokenFile = path.join(tempDir, "token.txt");
const counterFile = path.join(tempDir, "counter.txt");
fs.writeFileSync(tokenFile, "stale-key");
fs.writeFileSync(counterFile, "");
const command = trackedTokenCommand(tokenFile, counterFile);
fs.writeFileSync(
modelsPath,
JSON.stringify({
providers: {
"custom-proxy": {
baseUrl: "https://custom-proxy.example.com/v1",
api: "openai-completions",
apiKey: `!${command}`,
authHeader: true,
models: [{ id: "custom-model", name: "Custom Model" }],
},
},
}),
);
const registry = new ModelRegistry(authStorage, modelsPath);
const model = registry.find("custom-proxy", "custom-model");
if (!model) throw new Error("Expected custom model");
fs.writeFileSync(tokenFile, "fresh-key");
const attemptedKeys: string[] = [];
const result = await withAuth(registry.resolver(model), async key => {
attemptedKeys.push(key);
if (key === "stale-key") {
throw Object.assign(new Error("401 authentication_error"), { status: 401 });
}
if (key === "fresh-key") return "ok";
throw new Error(`Unexpected API key: ${key}`);
});
expect(result).toBe("ok");
expect(attemptedKeys).toEqual(["stale-key", "fresh-key"]);
expect(fs.readFileSync(counterFile, "utf8")).toBe("11");
expect(model.headers?.Authorization).toBe("Bearer fresh-key");
});
test("failed 401 refresh discards the rejected command-backed key", async () => {
const tokenFile = path.join(tempDir, "token.txt");
const counterFile = path.join(tempDir, "counter.txt");
fs.writeFileSync(tokenFile, "stale-key");
fs.writeFileSync(counterFile, "");
const command = trackedTokenCommand(tokenFile, counterFile);
fs.writeFileSync(
modelsPath,
JSON.stringify({
providers: {
"custom-proxy": {
baseUrl: "https://custom-proxy.example.com/v1",
api: "openai-completions",
apiKey: `!${command}`,
authHeader: true,
models: [{ id: "custom-model", name: "Custom Model" }],
},
},
}),
);
const registry = new ModelRegistry(authStorage, modelsPath);
const model = registry.find("custom-proxy", "custom-model");
if (!model) throw new Error("Expected custom model");
fs.writeFileSync(tokenFile, "FAIL");
const refreshed = await registry.resolver(model)({
lastChance: false,
error: Object.assign(new Error("401 authentication_error"), { status: 401 }),
previousKey: "stale-key",
});
expect(refreshed).toBeUndefined();
expect(fs.readFileSync(counterFile, "utf8")).toBe("11");
expect(await registry.getApiKey(model)).toBeUndefined();
expect(model.headers?.Authorization).toBeUndefined();
});
test("resolveCommandConfig caches failed executions so they do not retry", async () => {
const counterFile = path.join(tempDir, "counter.txt");
fs.writeFileSync(counterFile, "");
// Command increments a counter and then fails (exit 1).
const trackingCommand = failedTrackingCommand(counterFile);
fs.writeFileSync(
modelsPath,
JSON.stringify({
providers: {
"custom-proxy": {
baseUrl: "https://custom-proxy.example.com/v1",
api: "openai-completions",
apiKey: `!${trackingCommand}`,
},
},
}),
);
// Init triggers the first command resolution.
const registry = new ModelRegistry(authStorage, modelsPath);
const dummyModel: Model<Api> = buildModel({
id: "foo",
name: "foo",
api: "openai-completions",
provider: "custom-proxy",
baseUrl: "a",
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 4096,
maxTokens: 1024,
});
// Trigger the fallback resolver which also calls resolveConfigValue.
await registry.getApiKey(dummyModel);
// Another call to ensure it hits cache multiple times.
await registry.getApiKey(dummyModel);
// The command should have only run once.
expect(fs.readFileSync(counterFile, "utf8")).toBe("1");
});
});