test: share acp prompt harness

This commit is contained in:
Peter Steinberger
2026-04-19 00:10:25 +01:00
parent e7343dbfa8
commit 91bb931b0f
3 changed files with 107 additions and 173 deletions

View File

@@ -1,88 +1,9 @@
import type { PromptRequest } from "@agentclientprotocol/sdk";
import { describe, expect, it, vi } from "vitest";
import type { GatewayClient } from "../gateway/client.js";
import type { EventFrame } from "../gateway/protocol/index.js";
import { createInMemorySessionStore } from "./session.js";
import { AcpGatewayAgent } from "./translator.js";
import { createAcpConnection, createAcpGateway } from "./translator.test-helpers.js";
type PendingPromptHarness = {
agent: AcpGatewayAgent;
promptPromise: ReturnType<AcpGatewayAgent["prompt"]>;
runId: string;
};
const DEFAULT_SESSION_ID = "session-1";
const DEFAULT_SESSION_KEY = "agent:main:main";
const DEFAULT_PROMPT_TEXT = "hello";
function createSessionAgentHarness(
request: GatewayClient["request"],
options: { sessionId?: string; sessionKey?: string; cwd?: string } = {},
) {
const sessionId = options.sessionId ?? DEFAULT_SESSION_ID;
const sessionKey = options.sessionKey ?? DEFAULT_SESSION_KEY;
const sessionStore = createInMemorySessionStore();
sessionStore.createSession({
sessionId,
sessionKey,
cwd: options.cwd ?? "/tmp",
});
const agent = new AcpGatewayAgent(createAcpConnection(), createAcpGateway(request), {
sessionStore,
});
return {
agent,
sessionId,
sessionKey,
sessionStore,
};
}
function promptAgent(
agent: AcpGatewayAgent,
sessionId = DEFAULT_SESSION_ID,
text = DEFAULT_PROMPT_TEXT,
) {
return agent.prompt({
sessionId,
prompt: [{ type: "text", text }],
_meta: {},
} as unknown as PromptRequest);
}
async function createPendingPromptHarness(): Promise<PendingPromptHarness> {
let runId: string | undefined;
const request = vi.fn(async (method: string, params?: Record<string, unknown>) => {
if (method === "chat.send") {
runId = params?.idempotencyKey as string | undefined;
return new Promise<never>(() => {});
}
return {};
}) as GatewayClient["request"];
const { agent, sessionId } = createSessionAgentHarness(request);
const promptPromise = promptAgent(agent, sessionId);
await vi.waitFor(() => {
expect(runId).toBeDefined();
});
return {
agent,
promptPromise,
runId: runId!,
};
}
function createChatEvent(payload: Record<string, unknown>): EventFrame {
return {
type: "event",
event: "chat",
payload,
} as EventFrame;
}
import { describe, expect, it } from "vitest";
import {
createChatEvent,
createPendingPromptHarness,
DEFAULT_SESSION_KEY,
} from "./translator.prompt-harness.test-support.js";
describe("acp translator errorKind mapping", () => {
it("maps errorKind: refusal to stopReason: refusal", async () => {

View File

@@ -0,0 +1,94 @@
import type { PromptRequest } from "@agentclientprotocol/sdk";
import { expect, vi } from "vitest";
import type { GatewayClient } from "../gateway/client.js";
import type { EventFrame } from "../gateway/protocol/index.js";
import { createInMemorySessionStore } from "./session.js";
import { AcpGatewayAgent } from "./translator.js";
import { createAcpConnection, createAcpGateway } from "./translator.test-helpers.js";
export type PendingPromptHarness = {
agent: AcpGatewayAgent;
promptPromise: ReturnType<AcpGatewayAgent["prompt"]>;
runId: string;
};
export const DEFAULT_SESSION_ID = "session-1";
export const DEFAULT_SESSION_KEY = "agent:main:main";
export const DEFAULT_PROMPT_TEXT = "hello";
export function createSessionAgentHarness(
request: GatewayClient["request"],
options: { sessionId?: string; sessionKey?: string; cwd?: string } = {},
) {
const sessionId = options.sessionId ?? DEFAULT_SESSION_ID;
const sessionKey = options.sessionKey ?? DEFAULT_SESSION_KEY;
const sessionStore = createInMemorySessionStore();
sessionStore.createSession({
sessionId,
sessionKey,
cwd: options.cwd ?? "/tmp",
});
const agent = new AcpGatewayAgent(createAcpConnection(), createAcpGateway(request), {
sessionStore,
});
return {
agent,
sessionId,
sessionKey,
sessionStore,
};
}
export function promptAgent(
agent: AcpGatewayAgent,
sessionId = DEFAULT_SESSION_ID,
text = DEFAULT_PROMPT_TEXT,
) {
return agent.prompt({
sessionId,
prompt: [{ type: "text", text }],
_meta: {},
} as unknown as PromptRequest);
}
export function observeSettlement(promise: ReturnType<AcpGatewayAgent["prompt"]>) {
const settleSpy = vi.fn();
void promise.then(
(value) => settleSpy({ kind: "resolve", value }),
(error) => settleSpy({ kind: "reject", error }),
);
return settleSpy;
}
export async function createPendingPromptHarness(): Promise<PendingPromptHarness> {
let runId: string | undefined;
const request = vi.fn(async (method: string, params?: Record<string, unknown>) => {
if (method === "chat.send") {
runId = params?.idempotencyKey as string | undefined;
return new Promise<never>(() => {});
}
return {};
}) as GatewayClient["request"];
const { agent, sessionId } = createSessionAgentHarness(request);
const promptPromise = promptAgent(agent, sessionId);
await vi.waitFor(() => {
expect(runId).toBeDefined();
});
return {
agent,
promptPromise,
runId: runId!,
};
}
export function createChatEvent(payload: Record<string, unknown>): EventFrame {
return {
type: "event",
event: "chat",
payload,
} as EventFrame;
}

View File

@@ -1,98 +1,17 @@
import type { PromptRequest } from "@agentclientprotocol/sdk";
import { describe, expect, it, vi } from "vitest";
import type { GatewayClient } from "../gateway/client.js";
import type { EventFrame } from "../gateway/protocol/index.js";
import { createInMemorySessionStore } from "./session.js";
import { AcpGatewayAgent } from "./translator.js";
import {
createChatEvent,
createPendingPromptHarness,
createSessionAgentHarness,
observeSettlement,
promptAgent,
} from "./translator.prompt-harness.test-support.js";
import { createAcpConnection, createAcpGateway } from "./translator.test-helpers.js";
type PendingPromptHarness = {
agent: AcpGatewayAgent;
promptPromise: ReturnType<AcpGatewayAgent["prompt"]>;
runId: string;
};
const DEFAULT_SESSION_ID = "session-1";
const DEFAULT_SESSION_KEY = "agent:main:main";
const DEFAULT_PROMPT_TEXT = "hello";
function createSessionAgentHarness(
request: GatewayClient["request"],
options: { sessionId?: string; sessionKey?: string; cwd?: string } = {},
) {
const sessionId = options.sessionId ?? DEFAULT_SESSION_ID;
const sessionKey = options.sessionKey ?? DEFAULT_SESSION_KEY;
const sessionStore = createInMemorySessionStore();
sessionStore.createSession({
sessionId,
sessionKey,
cwd: options.cwd ?? "/tmp",
});
const agent = new AcpGatewayAgent(createAcpConnection(), createAcpGateway(request), {
sessionStore,
});
return {
agent,
sessionId,
sessionKey,
sessionStore,
};
}
function promptAgent(
agent: AcpGatewayAgent,
sessionId = DEFAULT_SESSION_ID,
text = DEFAULT_PROMPT_TEXT,
) {
return agent.prompt({
sessionId,
prompt: [{ type: "text", text }],
_meta: {},
} as unknown as PromptRequest);
}
function observeSettlement(promise: ReturnType<AcpGatewayAgent["prompt"]>) {
const settleSpy = vi.fn();
void promise.then(
(value) => settleSpy({ kind: "resolve", value }),
(error) => settleSpy({ kind: "reject", error }),
);
return settleSpy;
}
async function createPendingPromptHarness(): Promise<PendingPromptHarness> {
let runId: string | undefined;
const request = vi.fn(async (method: string, params?: Record<string, unknown>) => {
if (method === "chat.send") {
runId = params?.idempotencyKey as string | undefined;
return new Promise<never>(() => {});
}
return {};
}) as GatewayClient["request"];
const { agent, sessionId } = createSessionAgentHarness(request);
const promptPromise = promptAgent(agent, sessionId);
await vi.waitFor(() => {
expect(runId).toBeDefined();
});
return {
agent,
promptPromise,
runId: runId!,
};
}
function createChatEvent(payload: Record<string, unknown>): EventFrame {
return {
type: "event",
event: "chat",
payload,
} as EventFrame;
}
describe("acp translator stop reason mapping", () => {
it("error state resolves as end_turn, not refusal", async () => {
const { agent, promptPromise, runId } = await createPendingPromptHarness();