The function moved to utils/provider-model.ts but the test still imported from commands/agent/run.ts.
127 lines
3.6 KiB
TypeScript
127 lines
3.6 KiB
TypeScript
#!/usr/bin/env npx tsx
|
|
|
|
import assert from "node:assert";
|
|
import {
|
|
resolveStructuredResponseMessage,
|
|
type StructuredResponseTimelineClient,
|
|
} from "../src/commands/agent/run.ts";
|
|
import { resolveProviderAndModel } from "../src/utils/provider-model.ts";
|
|
|
|
type TimelineEntry = {
|
|
item: {
|
|
type: string;
|
|
text?: string;
|
|
};
|
|
};
|
|
|
|
function createClient(options: {
|
|
entries?: TimelineEntry[];
|
|
throwOnFetch?: boolean;
|
|
onFetch?: () => void;
|
|
}): StructuredResponseTimelineClient {
|
|
return {
|
|
fetchAgentTimeline: async () => {
|
|
options.onFetch?.();
|
|
if (options.throwOnFetch) {
|
|
throw new Error("timeline unavailable");
|
|
}
|
|
return {
|
|
entries: options.entries ?? [],
|
|
} as Awaited<ReturnType<StructuredResponseTimelineClient["fetchAgentTimeline"]>>;
|
|
},
|
|
};
|
|
}
|
|
|
|
console.log("=== Run Output Schema Helper Tests ===\n");
|
|
|
|
// Test 1: Direct lastMessage is returned without timeline fetch.
|
|
{
|
|
let fetchCount = 0;
|
|
const client = createClient({ onFetch: () => (fetchCount += 1) });
|
|
const result = await resolveStructuredResponseMessage({
|
|
client,
|
|
agentId: "agent-1",
|
|
lastMessage: ' {"summary":"ok"} ',
|
|
});
|
|
assert.strictEqual(result, '{"summary":"ok"}');
|
|
assert.strictEqual(fetchCount, 0, "should not fetch timeline when lastMessage exists");
|
|
console.log("✓ returns direct lastMessage when present");
|
|
}
|
|
|
|
// Test 2: Falls back to latest assistant message from timeline.
|
|
{
|
|
const client = createClient({
|
|
entries: [
|
|
{ item: { type: "user_message", text: "prompt" } },
|
|
{ item: { type: "assistant_message", text: "" } },
|
|
{ item: { type: "assistant_message", text: ' {"summary":"from timeline"} ' } },
|
|
],
|
|
});
|
|
const result = await resolveStructuredResponseMessage({
|
|
client,
|
|
agentId: "agent-2",
|
|
lastMessage: null,
|
|
});
|
|
assert.strictEqual(result, '{"summary":"from timeline"}');
|
|
console.log("✓ falls back to latest assistant timeline entry");
|
|
}
|
|
|
|
// Test 3: Returns null when timeline has no assistant messages.
|
|
{
|
|
const client = createClient({
|
|
entries: [
|
|
{ item: { type: "user_message", text: "prompt" } },
|
|
{ item: { type: "reasoning", text: "thinking" } },
|
|
],
|
|
});
|
|
const result = await resolveStructuredResponseMessage({
|
|
client,
|
|
agentId: "agent-3",
|
|
lastMessage: null,
|
|
});
|
|
assert.strictEqual(result, null);
|
|
console.log("✓ returns null when no assistant messages exist");
|
|
}
|
|
|
|
// Test 4: Returns null if timeline fetch fails.
|
|
{
|
|
const client = createClient({ throwOnFetch: true });
|
|
const result = await resolveStructuredResponseMessage({
|
|
client,
|
|
agentId: "agent-4",
|
|
lastMessage: null,
|
|
});
|
|
assert.strictEqual(result, null);
|
|
console.log("✓ returns null when timeline fetch throws");
|
|
}
|
|
|
|
// Test 5: Provider/model slash syntax resolves both values.
|
|
{
|
|
const result = resolveProviderAndModel({ provider: "codex/gpt-5.4" });
|
|
assert.deepStrictEqual(result, {
|
|
provider: "codex",
|
|
model: "gpt-5.4",
|
|
});
|
|
console.log("✓ resolves provider/model slash syntax");
|
|
}
|
|
|
|
// Test 6: Explicit matching --model coexists with slash syntax.
|
|
{
|
|
const result = resolveProviderAndModel({ provider: "codex/gpt-5.4", model: "gpt-5.4" });
|
|
assert.deepStrictEqual(result, {
|
|
provider: "codex",
|
|
model: "gpt-5.4",
|
|
});
|
|
console.log("✓ accepts matching explicit model with slash syntax");
|
|
}
|
|
|
|
// Test 7: Conflicting --model is rejected.
|
|
{
|
|
assert.throws(() => resolveProviderAndModel({ provider: "codex/gpt-5.4", model: "gpt-5.5" }), {
|
|
message: "Conflicting model values provided",
|
|
});
|
|
console.log("✓ rejects conflicting explicit model with slash syntax");
|
|
}
|
|
|
|
console.log("\n=== All helper tests passed ===");
|