test: aligned full suite with merged sweep contracts

- container stubs gained disposeChildren for the stale-renderer teardown paths
- login-stored API key assertions include the new source provenance field
- bash timeout test covers the zero-disable contract alongside the clamp
- skill keyword steering activates a task tool for the gated workflow notice
This commit is contained in:
can1357
2026-07-08 16:52:28 +02:00
parent e66e2dc129
commit 2e189b6f9e
9 changed files with 30 additions and 11 deletions
@@ -765,8 +765,8 @@ describe("AuthStorage OAuth login upgrade and multi-account coexistence", () =>
});
expect(authStorage.listStoredCredentials("nvidia").map(entry => entry.credential)).toEqual([
{ type: "api_key", key: "nvapi-first" },
{ type: "api_key", key: "nvapi-second" },
{ type: "api_key", key: "nvapi-first", source: "login" },
{ type: "api_key", key: "nvapi-second", source: "login" },
]);
const selectedKeys = new Set<string>();
+1 -1
View File
@@ -55,7 +55,7 @@ describe("openrouter login wiring", () => {
});
const credential = await storage.get("openrouter");
expect(credential).toEqual({ type: "api_key", key: "sk-or-validated" });
expect(credential).toEqual({ type: "api_key", key: "sk-or-validated", source: "login" });
const authCall = fetchCalls.find(call => call.url.includes("/api/v1/auth/key"));
expect(authCall).toBeDefined();
@@ -1,6 +1,6 @@
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
import * as path from "node:path";
import { Agent } from "@oh-my-pi/pi-agent-core";
import { Agent, type AgentTool } from "@oh-my-pi/pi-agent-core";
import type { TextContent } from "@oh-my-pi/pi-ai";
import { AssistantMessageEventStream } from "@oh-my-pi/pi-ai/utils/event-stream";
import { getBundledModel } from "@oh-my-pi/pi-catalog/models";
@@ -17,11 +17,22 @@ import {
import { SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manager";
import { TempDir } from "@oh-my-pi/pi-utils";
import { createAssistantMessage } from "./helpers/agent-session-setup";
import { type } from "arktype";
type ObservedSkillTurn = {
texts: string[];
};
// 4644 gates the workflowz notice on an active `task` tool; keep one active so
// keyword steering exercises the notice path.
const mockTaskTool: AgentTool = {
name: "task",
label: "Task",
description: "Mock task tool",
parameters: type({}),
execute: async () => ({ content: [{ type: "text" as const, text: "ok" }] }),
};
describe("AgentSession skill prompt keyword steering", () => {
let tempDir: TempDir;
let authStorage: AuthStorage | undefined;
@@ -43,7 +54,7 @@ describe("AgentSession skill prompt keyword steering", () => {
initialState: {
model,
systemPrompt: ["Test"],
tools: [],
tools: [mockTaskTool],
messages: [],
},
convertToLlm,
@@ -67,6 +67,7 @@ function createFixture(streamingMessage?: AssistantMessage) {
const clearPinnedError = vi.fn();
const statusContainer = {
clear: vi.fn(),
disposeChildren: vi.fn(),
addChild: vi.fn(),
};
@@ -283,7 +284,7 @@ describe("EventController working loader reconciliation", () => {
} as Extract<AgentSessionEvent, { type: "auto_compaction_end" }>);
expect(loader?.stop).toHaveBeenCalledTimes(1);
expect(ctx.statusContainer.clear).toHaveBeenCalledTimes(1);
expect(ctx.statusContainer.disposeChildren).toHaveBeenCalledTimes(1);
expect(ctx.flushCompactionQueue).toHaveBeenCalledWith({ willRetry: false });
expect(ctx.ensureLoadingAnimation).toHaveBeenCalledTimes(1);
});
@@ -32,6 +32,9 @@ function createContext(options: { terminalProgress?: boolean } = {}) {
clear() {
children.length = 0;
},
disposeChildren() {
children.length = 0;
},
addChild(child: unknown) {
children.push(child);
},
@@ -27,7 +27,7 @@ function createContext() {
retryLoader: undefined,
streamingComponent: undefined,
streamingMessage: undefined,
statusContainer: { clear: vi.fn() },
statusContainer: { clear: vi.fn(), disposeChildren: vi.fn() },
chatContainer: { removeChild: vi.fn() },
flushPendingModelSwitch: vi.fn(async () => {}),
editor: { getText: () => "" },
@@ -12,6 +12,9 @@ function createContainer() {
clear() {
this.children = [];
},
disposeChildren() {
this.children = [];
},
};
}
@@ -62,7 +62,7 @@ function makeCtx(): {
const ctx = {
chatContainer: { clear: vi.fn(), addChild: vi.fn() },
pendingMessagesContainer: { clear: vi.fn() },
pendingMessagesContainer: { clear: vi.fn(), disposeChildren: vi.fn() },
pendingBashComponents: [],
pendingPythonComponents: [],
session: { buildTranscriptSessionContext: transcriptSpy },
@@ -288,7 +288,7 @@ describe("tool schema validation (post-sanitization)", () => {
expect(description).toContain("pr://123");
});
it("bash schema and prompt advertise the timeout clamp", async () => {
it("bash schema and prompt advertise the timeout clamp and zero-disable", async () => {
const session = createTestSession();
session.settings.set("async.enabled", true);
const tools = await createTools(session);
@@ -302,8 +302,9 @@ describe("tool schema validation (post-sanitization)", () => {
expect(timeoutDescription).toContain("clamped");
expect(timeoutDescription).toContain("1-3600");
expect(bashTool.description).toContain("clamped to `1..3600`");
expect(bashTool.description).toContain("does NOT extend the timeout");
expect(timeoutDescription).toContain("0 disables the command deadline");
expect(bashTool.description).toContain("nonzero values are clamped to `1..3600`");
expect(bashTool.description).toContain("does NOT extend a nonzero timeout");
});
it("hidden tools also have valid sanitized schemas", async () => {