test: audit existing suite

This commit is contained in:
Federico Jaramillo Martinez
2026-07-03 09:48:20 +02:00
parent 45d9f4360a
commit 1564f1cfc5
37 changed files with 256 additions and 231 deletions
@@ -33,7 +33,7 @@ describe("auth provider options", () => {
expect(isApiKeyLoginProvider("openai", new Set(["openai-codex"]))).toBe(true);
});
it("includes Anthropic in both OAuth and API key login options", () => {
it("builds login options for OAuth-only, dual-auth, and API-key providers", () => {
const options = getLoginProviderOptions(registry());
expect(options).toEqual(expect.arrayContaining([
expect.objectContaining({ id: "anthropic", authType: "oauth" }),
@@ -44,7 +44,7 @@ describe("auth provider options", () => {
expect(options).not.toEqual(expect.arrayContaining([expect.objectContaining({ id: "openai-codex", authType: "api_key" })]));
});
it("returns only stored credentials for logout", () => {
it("returns only currently stored credentials for logout", () => {
expect(getLogoutProviderOptions(registry())).toEqual([
expect.objectContaining({ id: "openai", authType: "api_key" }),
]);
+9 -20
View File
@@ -169,23 +169,6 @@ function emptyArchiveStore(): NonNullable<PiSessionServiceDependencies["archiveS
}
describe("PiSessionService", () => {
it("exposes the session's agent.streamFn for one-off model calls", async () => {
const hub = new CapturingSessionEventHub();
const streamFn = vi.fn();
const fake = fakeRuntime("stream-session", { agent: { streamFn } });
const service = new PiSessionService(hub, {
createAgentRuntime: runtimeCreator(fake.runtime),
sessionManager: sessionGateway([]),
heartbeatIntervalMs: 60_000,
});
await service.start("/workspace");
expect(fake.session.agent.streamFn).toBe(streamFn);
await service.dispose();
});
it("starts sessions through an injected runtime creator", async () => {
const hub = new CapturingSessionEventHub();
const fake = fakeRuntime();
@@ -957,14 +940,21 @@ describe("PiSessionService", () => {
it("rejects malformed prompt text before opening the runtime", async () => {
const fake = fakeRuntime("prompt-session");
let createCalls = 0;
const createAgentRuntime: RuntimeCreator = async () => {
createCalls += 1;
await Promise.resolve();
return fake.runtime;
};
const service = new PiSessionService(new CapturingSessionEventHub(), {
createAgentRuntime: runtimeCreator(fake.runtime),
createAgentRuntime,
sessionManager: sessionGateway([sessionRecord("prompt-session")]),
heartbeatIntervalMs: 60_000,
});
await expect(service.prompt("prompt-session", undefined)).rejects.toThrow("Prompt text is required");
expect(createCalls).toBe(0);
expect(fake.calls.prompt).toEqual([]);
await service.dispose();
});
@@ -1313,7 +1303,7 @@ describe("PiSessionService", () => {
}
it("records the parent, delivers the prompt, and lists the tracked child", async () => {
const { parent, child, service } = subsessionService({ allowed: true, cwd: "/workspace-feature" });
const { child, service } = subsessionService({ allowed: true, cwd: "/workspace-feature" });
await service.start("/workspace"); // bring the parent online so it can be notified
const result = await service.spawnSubsession({ spawningCwd: "/workspace", parentSessionId: "parent-1", parentSessionFile: "/tmp/parent-1.jsonl", prompt: "do the slice", cwd: "/workspace-feature" });
@@ -1323,7 +1313,6 @@ describe("PiSessionService", () => {
await expect(service.listSubsessions("parent-1")).resolves.toEqual([
{ sessionId: "child-1", cwd: "/workspace-feature", status: "idle" },
]);
void parent;
await service.dispose();
});
@@ -66,11 +66,15 @@ describe("SessionCommandService", () => {
await expect(service.run("s1", "/template arg")).resolves.toMatchObject({ type: "done" });
await expect(service.run("s1", "/skill:skill-a arg")).resolves.toMatchObject({ type: "done" });
expect(prompt).toHaveBeenCalledTimes(3);
expect(prompt).toHaveBeenNthCalledWith(1, "s1", "/ext arg");
expect(prompt).toHaveBeenNthCalledWith(2, "s1", "/template arg");
expect(prompt).toHaveBeenNthCalledWith(3, "s1", "/skill:skill-a arg");
});
it("renames sessions and returns updated client session metadata", async () => {
it("renames sessions, publishes the name update, and returns updated client session metadata", async () => {
const active = activeSession();
const service = new SessionCommandService(() => getActive(active), vi.fn(), eventPublisher());
const events = eventPublisher();
const service = new SessionCommandService(() => getActive(active), vi.fn(), events);
await expect(service.run("s1", "/name Useful name")).resolves.toMatchObject({
type: "done",
@@ -78,6 +82,7 @@ describe("SessionCommandService", () => {
session: { id: "s1", cwd: "/work", name: "Useful name", messageCount: 2 },
});
expect(active.runtime.session.setSessionName).toHaveBeenCalledWith("Useful name");
expect(events.publish).toHaveBeenCalledWith("s1", { type: "session.name", sessionId: "s1", name: "Useful name" });
});
it("formats session stats", async () => {
@@ -90,18 +95,22 @@ describe("SessionCommandService", () => {
});
});
it("starts compaction and publishes completion", async () => {
it("starts compaction, updates lifecycle hooks, and publishes completion", async () => {
const active = activeSession();
const events = eventPublisher();
const service = new SessionCommandService(() => getActive(active), vi.fn(), events);
const onCompactionStart = vi.fn();
const onCompactionEnd = vi.fn();
const service = new SessionCommandService(() => getActive(active), vi.fn(), events, { onCompactionStart, onCompactionEnd });
await expect(service.run("s1", "/compact focus on tests")).resolves.toEqual({ type: "done", message: "Compaction started…" });
expect(onCompactionStart).toHaveBeenCalledWith(active.runtime.session);
await vi.waitFor(() => {
expect(events.publish).toHaveBeenCalledWith("s1", {
type: "command.output",
level: "success",
message: "Compaction complete.\nTokens before: 123\n\nshort summary",
});
expect(onCompactionEnd).toHaveBeenCalledWith(active.runtime.session, "success");
});
expect(active.runtime.session.compact).toHaveBeenCalledWith("focus on tests");
});
+2 -11
View File
@@ -9,7 +9,7 @@ const dispatchModel = { provider: "anthropic", id: "claude-sonnet" };
const ctxWithModel = { model: dispatchModel } as ExtensionContext;
describe("createSpawnSessionToolDefinition", () => {
it("passes the spawning cwd and params to the spawn callback and reports success", async () => {
it("passes the spawning cwd, explicit cwd, dispatching model, and prompt to spawn callback", async () => {
const spawn = vi.fn(() => Promise.resolve({ sessionId: "new-1", cwd: "/repos/a-feature" }));
const tool = createSpawnSessionToolDefinition("/repos/a", { spawn });
@@ -20,7 +20,7 @@ describe("createSpawnSessionToolDefinition", () => {
expect(result.content[0]).toMatchObject({ type: "text", text: "Started session new-1 in /repos/a-feature." });
});
it("defaults cwd to undefined so the service falls back to the spawning cwd", async () => {
it("forwards omitted cwd as undefined and omits a missing dispatching model", async () => {
const spawn = vi.fn(() => Promise.resolve({ sessionId: "new-2", cwd: "/repos/a" }));
const tool = createSpawnSessionToolDefinition("/repos/a", { spawn });
@@ -29,15 +29,6 @@ describe("createSpawnSessionToolDefinition", () => {
expect(spawn).toHaveBeenCalledWith({ spawningCwd: "/repos/a", prompt: "continue", cwd: undefined });
});
it("omits the inherited model when the dispatching session has no current model", async () => {
const spawn = vi.fn(() => Promise.resolve({ sessionId: "new-3", cwd: "/repos/a" }));
const tool = createSpawnSessionToolDefinition("/repos/a", { spawn });
await tool.execute("call-3", { prompt: "continue" }, undefined, undefined, ctx);
expect(spawn).toHaveBeenCalledWith({ spawningCwd: "/repos/a", prompt: "continue", cwd: undefined });
});
it("propagates the spawn callback error so the agent loop reports it", async () => {
const spawn = vi.fn(() => Promise.reject(new Error("cwd must be a workspace of this project. Allowed: /repos/a")));
const tool = createSpawnSessionToolDefinition("/repos/a", { spawn });
@@ -81,12 +81,12 @@ describe("buildTranscriptView", () => {
expect(callPart.args).toEqual({ command: "ls" });
});
it("search keeps only matching entries across text and tool names", () => {
const messages = [assistant("the auth flow"), assistant("unrelated"), toolResult("error in auth.ts", "read")];
it("search keeps only entries matching text or tool-call names", () => {
const messages = [assistant("the auth flow"), assistant("unrelated"), toolResult("error in auth.ts", "read"), toolCall("auth-search")];
const view = buildTranscriptView(messages, { search: "auth" });
expect(view.matched).toBe(2);
expect(view.entries.map((entry) => entry.index)).toEqual([0, 2]);
expect(view.matched).toBe(3);
expect(view.entries.map((entry) => entry.index)).toEqual([0, 2, 3]);
});
it("search runs against full content even when maxChars would clip the match away", () => {