Theo Brownesettings pagef8883d08 months agoHistory
283 lines · 7.7 KB · TypeScript
RawDownload
import { describe, expect, it } from "vitest"; import {  PROVIDER_SEND_TURN_MAX_ATTACHMENTS,  providerCheckpointSchema,  providerEventSchema,  providerGetCheckpointDiffInputSchema,  providerGetCheckpointDiffResultSchema,  providerListCheckpointsInputSchema,  providerListCheckpointsResultSchema,  providerRevertToCheckpointInputSchema,  providerRevertToCheckpointResultSchema,  providerRespondToRequestInputSchema,  providerSendTurnInputSchema,  providerSessionStartInputSchema,} from "./provider"; describe("providerSessionStartInputSchema", () => {  it("defaults to codex with safe policies", () => {    const parsed = providerSessionStartInputSchema.parse({});    expect(parsed.provider).toBe("codex");    expect(parsed.approvalPolicy).toBe("never");    expect(parsed.sandboxMode).toBe("workspace-write");  });   it("accepts optional resumeThreadId", () => {    const parsed = providerSessionStartInputSchema.parse({      resumeThreadId: "thread_123",    });    expect(parsed.resumeThreadId).toBe("thread_123");  });   it("rejects blank resumeThreadId", () => {    expect(() =>      providerSessionStartInputSchema.parse({        resumeThreadId: "   ",      }),    ).toThrow();  });   it("accepts optional codex binary and home path overrides", () => {    const parsed = providerSessionStartInputSchema.parse({      codexBinaryPath: "/opt/codex/bin/codex",      codexHomePath: "/Users/theo/.codex",    });    expect(parsed.codexBinaryPath).toBe("/opt/codex/bin/codex");    expect(parsed.codexHomePath).toBe("/Users/theo/.codex");  });   it("rejects blank codex overrides", () => {    expect(() =>      providerSessionStartInputSchema.parse({        codexBinaryPath: "   ",      }),    ).toThrow();    expect(() =>      providerSessionStartInputSchema.parse({        codexHomePath: "   ",      }),    ).toThrow();  });}); describe("providerSendTurnInputSchema", () => {  it("trims input text and optional model/effort", () => {    const parsed = providerSendTurnInputSchema.parse({      sessionId: "sess_1",      input: "  summarize this repo  ",      model: "  gpt-5.2-codex  ",      effort: "  high  ",    });    expect(parsed.input).toBe("summarize this repo");    expect(parsed.attachments).toEqual([]);    expect(parsed.model).toBe("gpt-5.2-codex");    expect(parsed.effort).toBe("high");  });   it("accepts image-only turns", () => {    const parsed = providerSendTurnInputSchema.parse({      sessionId: "sess_1",      attachments: [        {          type: "image",          name: "diagram.png",          mimeType: "image/png",          sizeBytes: 1_024,          dataUrl: "data:image/png;base64,AAAA",        },      ],    });    expect(parsed.input).toBeUndefined();    expect(parsed.attachments).toHaveLength(1);  });   it("rejects turns with neither text nor attachments", () => {    expect(() =>      providerSendTurnInputSchema.parse({        sessionId: "sess_1",      }),    ).toThrow();  });   it("rejects non-image data urls", () => {    expect(() =>      providerSendTurnInputSchema.parse({        sessionId: "sess_1",        attachments: [          {            type: "image",            name: "not-image.txt",            mimeType: "text/plain",            sizeBytes: 25,            dataUrl: "data:text/plain;base64,QQ==",          },        ],      }),    ).toThrow();  });   it("rejects more than the max attachment count", () => {    expect(() =>      providerSendTurnInputSchema.parse({        sessionId: "sess_1",        attachments: Array.from({ length: PROVIDER_SEND_TURN_MAX_ATTACHMENTS + 1 }, (_, index) => ({          type: "image" as const,          name: `image-${index}.png`,          mimeType: "image/png",          sizeBytes: 1_024,          dataUrl: "data:image/png;base64,AAAA",        })),      }),    ).toThrow();  });}); describe("providerEventSchema", () => {  it("accepts notification events with routing metadata", () => {    const parsed = providerEventSchema.parse({      id: "evt_1",      kind: "notification",      provider: "codex",      sessionId: "sess_1",      createdAt: "2026-01-01T00:00:00.000Z",      method: "item/agentMessage/delta",      threadId: "thr_1",      turnId: "turn_1",      itemId: "item_1",      textDelta: "hi",    });    expect(parsed.method).toBe("item/agentMessage/delta");  });   it("accepts request approval metadata", () => {    const parsed = providerEventSchema.parse({      id: "evt_2",      kind: "request",      provider: "codex",      sessionId: "sess_1",      createdAt: "2026-01-01T00:00:00.000Z",      method: "item/commandExecution/requestApproval",      requestId: "req_123",      requestKind: "command",    });    expect(parsed.requestId).toBe("req_123");    expect(parsed.requestKind).toBe("command");  });}); describe("providerRespondToRequestInputSchema", () => {  it("accepts valid decisions", () => {    const parsed = providerRespondToRequestInputSchema.parse({      sessionId: "sess_1",      requestId: "req_1",      decision: "acceptForSession",    });    expect(parsed.decision).toBe("acceptForSession");  });   it("rejects unknown decisions", () => {    expect(() =>      providerRespondToRequestInputSchema.parse({        sessionId: "sess_1",        requestId: "req_1",        decision: "always",      }),    ).toThrow();  });}); describe("provider checkpoint schemas", () => {  it("accepts list checkpoint inputs", () => {    const parsed = providerListCheckpointsInputSchema.parse({      sessionId: "sess_1",    });    expect(parsed.sessionId).toBe("sess_1");  });   it("accepts checkpoint records and list results", () => {    const checkpoint = providerCheckpointSchema.parse({      id: "turn_1",      turnCount: 1,      messageCount: 2,      label: "Turn 1",      preview: "Summarize this file",      isCurrent: true,    });    expect(checkpoint.turnCount).toBe(1);     const listResult = providerListCheckpointsResultSchema.parse({      threadId: "thr_1",      checkpoints: [checkpoint],    });    expect(listResult.checkpoints).toHaveLength(1);  });   it("accepts revert checkpoint input/result", () => {    const input = providerRevertToCheckpointInputSchema.parse({      sessionId: "sess_1",      turnCount: 2,    });    expect(input.turnCount).toBe(2);     const result = providerRevertToCheckpointResultSchema.parse({      threadId: "thr_1",      turnCount: 2,      messageCount: 4,      rolledBackTurns: 1,      checkpoints: [        {          id: "root",          turnCount: 0,          messageCount: 0,          label: "Start of conversation",          isCurrent: false,        },        {          id: "turn_2",          turnCount: 2,          messageCount: 4,          label: "Turn 2",          isCurrent: true,        },      ],    });    expect(result.rolledBackTurns).toBe(1);  });   it("rejects negative turn counts", () => {    expect(() =>      providerRevertToCheckpointInputSchema.parse({        sessionId: "sess_1",        turnCount: -1,      }),    ).toThrow();  });   it("accepts checkpoint diff input/result", () => {    const input = providerGetCheckpointDiffInputSchema.parse({      sessionId: "sess_1",      fromTurnCount: 1,      toTurnCount: 2,    });    expect(input.fromTurnCount).toBe(1);     const result = providerGetCheckpointDiffResultSchema.parse({      threadId: "thr_1",      fromTurnCount: 1,      toTurnCount: 2,      diff: "diff --git a/src/app.ts b/src/app.ts",    });    expect(result.toTurnCount).toBe(2);  });   it("rejects checkpoint diff ranges where start is after end", () => {    expect(() =>      providerGetCheckpointDiffInputSchema.parse({        sessionId: "sess_1",        fromTurnCount: 3,        toTurnCount: 2,      }),    ).toThrow();  });});