index.test.ts•17.4 kB
import { describe, it, expect, mock, beforeEach } from "bun:test";
import {
buildOracleArgs,
invokeOracle,
} from "./index.js";
describe("buildOracleArgs", () => {
describe("Codex command construction", () => {
it("should construct codex args with model and reasoning", () => {
const args = buildOracleArgs("codex", "gpt-5.1-codex-mini", "medium", "test prompt");
expect(args[0]).toBe("exec");
expect(args[1]).toBe("--model");
expect(args[2]).toBe("gpt-5.1-codex-mini");
expect(args[3]).toBe("-c");
expect(args[4]).toBe("reasoning_level=medium");
expect(args[5]).toBe("test prompt");
});
it("should construct codex args without reasoning when undefined", () => {
const args = buildOracleArgs("codex", "gpt-5.1-codex-mini", undefined, "test prompt");
expect(args[0]).toBe("exec");
expect(args[1]).toBe("--model");
expect(args[2]).toBe("gpt-5.1-codex-mini");
expect(args[3]).toBe("test prompt");
expect(args).not.toContain("reasoning_level=medium");
});
it("should support custom codex command names", () => {
const args = buildOracleArgs("my-codex", "gpt-5.1", "high", "prompt");
expect(args[0]).toBe("exec");
expect(args[1]).toBe("--model");
});
});
describe("Gemini command construction", () => {
it("should construct gemini args with model", () => {
const args = buildOracleArgs("gemini", "gemini-2.5-pro", undefined, "test prompt");
expect(args[0]).toBe("-p");
expect(args[1]).toBe("--model");
expect(args[2]).toBe("gemini-2.5-pro");
expect(args[3]).toBe("test prompt");
});
it("should not include reasoning_level for gemini even when specified", () => {
const args = buildOracleArgs("gemini", "gemini-2.5-pro", "high", "prompt");
expect(args).not.toContain("reasoning_level=high");
expect(args[0]).toBe("-p");
});
it("should support custom gemini command names", () => {
const args = buildOracleArgs("my-gemini", "gemini-2.5-pro", undefined, "prompt");
expect(args[0]).toBe("-p");
expect(args[1]).toBe("--model");
expect(args[2]).toBe("gemini-2.5-pro");
});
});
describe("Claude command construction", () => {
it("should construct claude args with model", () => {
const args = buildOracleArgs("claude", "opus", undefined, "test prompt");
expect(args[0]).toBe("-p");
expect(args[1]).toBe("--model");
expect(args[2]).toBe("opus");
expect(args[3]).toBe("test prompt");
});
it("should not include reasoning_level for claude even when specified", () => {
const args = buildOracleArgs("claude", "opus", "high", "prompt");
expect(args).not.toContain("reasoning_level=high");
expect(args[0]).toBe("-p");
});
});
describe("Special characters handling", () => {
it("should preserve double quotes in prompt without escaping", () => {
const prompt = 'Say "hello world"';
const args = buildOracleArgs("codex", "gpt-5.1-codex-mini", "medium", prompt);
expect(args[5]).toBe(prompt);
expect(args[5]).toContain('"hello world"');
});
it("should preserve backticks in prompt without escaping", () => {
const prompt = "Use `variable` here";
const args = buildOracleArgs("codex", "gpt-5.1-codex-mini", "medium", prompt);
expect(args[5]).toBe(prompt);
expect(args[5]).toContain("`variable`");
});
it("should preserve dollar signs and parens without escaping", () => {
const prompt = "What about $(test) and $var?";
const args = buildOracleArgs("codex", "gpt-5.1-codex-mini", "medium", prompt);
expect(args[5]).toBe(prompt);
expect(args[5]).toContain("$(test)");
expect(args[5]).toContain("$var");
});
it("should preserve newlines in prompt", () => {
const prompt = "Line 1\nLine 2\nLine 3";
const args = buildOracleArgs("codex", "gpt-5.1-codex-mini", "medium", prompt);
expect(args[5]).toBe(prompt);
expect(args[5]).toContain("\n");
});
it("should preserve mixed special characters", () => {
const prompt = 'Mix: `code`, "string", $(cmd), & and |';
const args = buildOracleArgs("codex", "gpt-5.1-codex-mini", "medium", prompt);
expect(args[5]).toBe(prompt);
expect(args[5]).toContain("`code`");
expect(args[5]).toContain('"string"');
expect(args[5]).toContain("$(cmd)");
});
});
});
describe("invokeOracle", () => {
describe("Return type structure", () => {
it("should return an object with stdout, status, and optional error", () => {
// This tests the function signature and return type
// In real usage, invokeOracle will be mocked in the server logic
const result = invokeOracle("nonexistent-command", []);
expect(typeof result).toBe("object");
expect("stdout" in result).toBe(true);
expect("status" in result).toBe(true);
expect("error" in result).toBe(true);
});
it("should have a numeric status field", () => {
const result = invokeOracle("nonexistent-command", []);
expect(typeof result.status).toBe("number");
});
it("should have an error field when command not found", () => {
const result = invokeOracle("nonexistent-command-xyz", []);
expect(result.error).toBeDefined();
});
});
});
describe("Error messages", () => {
it("should format Error objects correctly", () => {
const error = new Error("Test error message");
const message =
error instanceof Error ? error.message : String(error);
expect(message).toBe("Test error message");
});
it("should format non-Error objects correctly", () => {
const error: unknown = "String error";
const message =
error instanceof Error ? error.message : String(error);
expect(message).toBe("String error");
});
it("should construct oracle error response", () => {
const errorMsg = "Command failed";
const response = {
content: [
{
type: "text",
text: `Error consulting oracle: ${errorMsg}`,
},
],
isError: true,
};
expect(response.isError).toBe(true);
expect(response.content[0].text).toContain("Error consulting oracle");
expect(response.content[0].text).toContain(errorMsg);
});
});
describe("Configuration", () => {
it("should have a default Oracle config constant", () => {
const DEFAULT_ORACLE_CONFIG = {
models: ["gpt-5.1-codex-mini"],
reasoning: "medium",
command: "codex",
};
expect(DEFAULT_ORACLE_CONFIG.models[0]).toBe("gpt-5.1-codex-mini");
expect(DEFAULT_ORACLE_CONFIG.reasoning).toBe("medium");
expect(DEFAULT_ORACLE_CONFIG.command).toBe("codex");
});
it("should accept custom oracle config with single model", () => {
const customConfig = {
model: "gpt-6.0",
reasoning: "high",
command: "custom-codex",
};
expect(customConfig.model).toBe("gpt-6.0");
expect(customConfig.reasoning).toBe("high");
expect(customConfig.command).toBe("custom-codex");
});
it("should accept custom oracle config with multiple models", () => {
const customConfig = {
models: ["gpt-5.1", "gpt-5.1-codex-max"],
reasoning: "high",
command: "codex",
};
expect(customConfig.models).toBeDefined();
expect(customConfig.models.length).toBe(2);
expect(customConfig.models[0]).toBe("gpt-5.1");
expect(customConfig.models[1]).toBe("gpt-5.1-codex-max");
});
it("should allow optional reasoning field", () => {
const config: Record<string, unknown> = {
model: "gpt-5.1",
command: "codex",
};
expect(config.model).toBe("gpt-5.1");
expect(config.reasoning).toBeUndefined();
});
it("should support single oracle config with single model (backward compatible)", () => {
const config = {
oracle: {
model: "gpt-5.1",
command: "codex",
},
};
expect(config.oracle).toBeDefined();
expect(config.oracle.model).toBe("gpt-5.1");
});
it("should support multiple oracles config with single models each", () => {
const config = {
oracles: [
{ model: "gpt-5.1", command: "codex" },
{ model: "gemini-2.5-pro", command: "gemini" },
{ model: "opus", command: "claude" },
],
};
expect(config.oracles).toBeDefined();
expect(config.oracles.length).toBe(3);
expect(config.oracles[0].model).toBe("gpt-5.1");
expect(config.oracles[1].model).toBe("gemini-2.5-pro");
expect(config.oracles[2].model).toBe("opus");
});
it("should support oracle config with multiple models per oracle", () => {
const config = {
oracles: [
{ models: ["gpt-5.1", "gpt-5.1-codex-max"], command: "codex" },
{ models: ["gemini-2.5-pro", "gemini-1.5-pro"], command: "gemini" },
{ models: ["opus", "sonnet"], command: "claude" },
],
};
expect(config.oracles).toBeDefined();
expect(config.oracles.length).toBe(3);
expect(config.oracles[0].models).toEqual(["gpt-5.1", "gpt-5.1-codex-max"]);
expect(config.oracles[1].models).toEqual(["gemini-2.5-pro", "gemini-1.5-pro"]);
expect(config.oracles[2].models).toEqual(["opus", "sonnet"]);
});
it("should support mixed single and multiple models across oracles", () => {
const config = {
oracles: [
{ models: ["gpt-5.1", "gpt-5.1-codex-max"], command: "codex" },
{ model: "gemini-2.5-pro", command: "gemini" },
{ models: ["opus"], command: "claude" },
],
};
expect(config.oracles[0].models).toEqual(["gpt-5.1", "gpt-5.1-codex-max"]);
expect(config.oracles[1].model).toBe("gemini-2.5-pro");
expect(config.oracles[2].models).toEqual(["opus"]);
});
});
describe("Tool descriptions", () => {
it("should generate description with reasoning when specified", () => {
const model = "gpt-5.1-codex-mini";
const command = "codex";
const reasoning = "medium";
const baseDescription = `Consult the oracle (${model}) via ${command} CLI`;
const reasoningPart = reasoning
? ` with ${reasoning}-level reasoning`
: "";
const capabilityPart = ". The oracle provides expert reasoning and analysis for complex problem-solving.";
const usagePart =
" Use when: (1) planning complex tasks with multiple tradeoffs, (2) you are <=90% confident in your approach, (3) you need analysis of architectural decisions or design patterns.";
const description = baseDescription + reasoningPart + capabilityPart + usagePart;
expect(description).toContain(model);
expect(description).toContain(command);
expect(description).toContain(reasoning);
expect(description).toContain("planning complex tasks");
expect(description).toContain("expert reasoning");
});
it("should generate description without reasoning when undefined", () => {
const model = "gpt-5.1-codex-mini";
const command = "codex";
const reasoning = undefined;
const baseDescription = `Consult the oracle (${model}) via ${command} CLI`;
const reasoningPart = reasoning
? ` with ${reasoning}-level reasoning`
: "";
const capabilityPart = ". The oracle provides expert reasoning and analysis for complex problem-solving.";
const usagePart =
" Use when: (1) planning complex tasks with multiple tradeoffs, (2) you are <=90% confident in your approach, (3) you need analysis of architectural decisions or design patterns.";
const description = baseDescription + reasoningPart + capabilityPart + usagePart;
expect(description).not.toContain("reasoning-level");
expect(description).toContain(model);
expect(description).toContain(command);
expect(description).toContain("expert reasoning");
});
});
describe("Type safety", () => {
it("should handle dynamic args as Record<string, unknown>", () => {
const args: Record<string, unknown> = {
prompt: "test prompt",
extra: 123,
};
const prompt = (args as Record<string, unknown>).prompt as string;
expect(typeof prompt).toBe("string");
expect(prompt).toBe("test prompt");
});
it("should handle missing prompt in args", () => {
const args = {} as Record<string, unknown>;
const prompt = (args as Record<string, unknown>).prompt as string;
expect(prompt).toBeUndefined();
});
it("should safely cast different types", () => {
const args: Record<string, unknown> = { value: 42 };
const value = (args.value as unknown) as number;
expect(typeof value).toBe("number");
});
});
describe("Response format", () => {
it("should wrap success response correctly", () => {
const stdout = "Success output";
const response = {
content: [
{
type: "text",
text: stdout,
},
],
};
expect(response.content[0].type).toBe("text");
expect(response.content[0].text).toBe(stdout);
expect((response as Record<string, unknown>).isError).toBeUndefined();
});
it("should wrap error response with isError flag", () => {
const errorText = "Error consulting oracle: test error";
const response = {
content: [
{
type: "text",
text: errorText,
},
],
isError: true,
};
expect(response.isError).toBe(true);
expect(response.content[0].text).toContain("Error consulting oracle");
});
it("should handle multiline responses", () => {
const stdout = "line 1\nline 2\nline 3";
const response = {
content: [
{
type: "text",
text: stdout,
},
],
};
expect(response.content[0].text).toContain("line 1");
expect(response.content[0].text).toContain("line 2");
expect(response.content[0].text).toContain("line 3");
});
});
describe("Unknown tool handling", () => {
it("should return error for unknown tool", () => {
const toolName = "unknown_tool";
const response = {
content: [
{
type: "text",
text: `Unknown tool: ${toolName}`,
},
],
isError: true,
};
expect(response.isError).toBe(true);
expect(response.content[0].text).toContain("Unknown tool");
expect(response.content[0].text).toContain(toolName);
});
});
describe("Fallback behavior", () => {
it("should track errors from multiple failed oracles with single models", () => {
const errors = [
"Oracle 1, model 1 (gpt-5.1): Connection failed",
"Oracle 2, model 1 (gemini-2.5-pro): Timeout",
"Oracle 3, model 1 (opus): Rate limited",
];
const response = {
content: [
{
type: "text",
text: `All oracles failed:\n${errors.join("\n")}`,
},
],
isError: true,
};
expect(response.isError).toBe(true);
expect(response.content[0].text).toContain("All oracles failed");
expect(response.content[0].text).toContain("gpt-5.1");
expect(response.content[0].text).toContain("gemini-2.5-pro");
expect(response.content[0].text).toContain("opus");
});
it("should track errors from multiple models within a single oracle", () => {
const errors = [
"Oracle 1, model 1 (gpt-5.1): Rate limited",
"Oracle 1, model 2 (gpt-5.1-codex-max): Timeout",
"Oracle 2, model 1 (gemini-2.5-pro): Connection failed",
];
const response = {
content: [
{
type: "text",
text: `All oracles failed:\n${errors.join("\n")}`,
},
],
isError: true,
};
expect(response.isError).toBe(true);
expect(response.content[0].text).toContain("Oracle 1, model 1");
expect(response.content[0].text).toContain("Oracle 1, model 2");
expect(response.content[0].text).toContain("Oracle 2, model 1");
expect(response.content[0].text).toContain("gpt-5.1");
expect(response.content[0].text).toContain("gpt-5.1-codex-max");
expect(response.content[0].text).toContain("gemini-2.5-pro");
});
it("should format error message with oracle index, model index, and model name", () => {
const errorMsg = "Oracle 2, model 3 (gemini-1.5-pro): Connection failed";
expect(errorMsg).toContain("Oracle 2");
expect(errorMsg).toContain("model 3");
expect(errorMsg).toContain("gemini-1.5-pro");
expect(errorMsg).toContain("Connection failed");
});
it("should return success on first successful model", () => {
const stdout = "Successfully consulted oracle 1, model 2";
const response = {
content: [
{
type: "text",
text: stdout,
},
],
};
expect(response.content[0].text).toBe(stdout);
expect((response as Record<string, unknown>).isError).toBeUndefined();
});
it("should accumulate all oracle and model errors when all fail", () => {
const errors = [
"Oracle 1, model 1 (gpt-5.1): Error A",
"Oracle 1, model 2 (gpt-5.1-codex-max): Error B",
"Oracle 2, model 1 (gemini-2.5-pro): Error C",
];
const fullErrorMessage = errors.join("\n");
expect(fullErrorMessage).toContain("Oracle 1, model 1");
expect(fullErrorMessage).toContain("Oracle 1, model 2");
expect(fullErrorMessage).toContain("Oracle 2, model 1");
expect(fullErrorMessage).toContain("Error A");
expect(fullErrorMessage).toContain("Error B");
expect(fullErrorMessage).toContain("Error C");
});
});