77 lines
3.3 KiB
TypeScript
77 lines
3.3 KiB
TypeScript
import { HarnessError, InMemoryEventStore } from "@agent-studio/harness";
|
|
import type { FastifyInstance } from "fastify";
|
|
import { vi } from "vitest";
|
|
import { buildApp } from "../src/app.js";
|
|
|
|
export function createTestApp(): {
|
|
app: FastifyInstance;
|
|
session: { startRun: ReturnType<typeof vi.fn>; cancelRun: ReturnType<typeof vi.fn> };
|
|
modelStore: {
|
|
configure: ReturnType<typeof vi.fn>;
|
|
getRedacted: ReturnType<typeof vi.fn>;
|
|
clearCredentials: ReturnType<typeof vi.fn>;
|
|
resolveConnection: ReturnType<typeof vi.fn>;
|
|
};
|
|
attachmentStore: { add: ReturnType<typeof vi.fn>; remove: ReturnType<typeof vi.fn> };
|
|
approvalBroker: { approve: ReturnType<typeof vi.fn>; deny: ReturnType<typeof vi.fn> };
|
|
eventStore: InMemoryEventStore;
|
|
} {
|
|
const session = {
|
|
sessionId: "ses_1",
|
|
workspaceRoot: "/safe/session",
|
|
startRun: vi.fn(() => Promise.resolve({ runId: "run_1" })),
|
|
cancelRun: vi.fn(() => Promise.resolve(true)),
|
|
dispose: vi.fn(() => Promise.resolve()),
|
|
};
|
|
const registry = {
|
|
create: vi.fn(() => Promise.resolve(session)),
|
|
get: vi.fn((sessionId: string) => sessionId === "ses_1" ? session : undefined),
|
|
delete: vi.fn((sessionId: string) => Promise.resolve(sessionId === "ses_1")),
|
|
disposeAll: vi.fn(() => Promise.resolve()),
|
|
};
|
|
const modelStore = {
|
|
configure: vi.fn(() => Promise.resolve()),
|
|
getRedacted: vi.fn(() => ({
|
|
providerId: "training", api: "openai-responses" as const, baseUrl: "https://api.example.com/v1",
|
|
modelId: "training-model", supportsImages: true, configured: true, source: "session" as const, keyHint: "••••cret",
|
|
})),
|
|
clearCredentials: vi.fn(),
|
|
resolveConnection: vi.fn(() => ({
|
|
connection: { providerId: "training", api: "openai-responses" as const, modelId: "training-model", supportsImages: true },
|
|
apiKey: "server-only-secret", source: "session" as const,
|
|
})),
|
|
};
|
|
const attachmentStore = {
|
|
add: vi.fn(async (_sessionId: string, upload: { stream: AsyncIterable<unknown> }) => {
|
|
for await (const _chunk of upload.stream) {
|
|
// Consume the multipart stream like the real attachment store.
|
|
void _chunk;
|
|
}
|
|
return {
|
|
attachmentId: "att_1", sessionId: "ses_1", name: "chart.png", mediaType: "image/png" as const,
|
|
byteSize: 12, width: 4, height: 3, status: "ready" as const,
|
|
};
|
|
}),
|
|
remove: vi.fn(() => Promise.resolve(true)),
|
|
};
|
|
const approvalBroker = {
|
|
approve: vi.fn((approvalId: string) => ({ approvalId, status: "approved" as const })),
|
|
deny: vi.fn((approvalId: string) => ({ approvalId, status: "denied" as const })),
|
|
};
|
|
const eventStore = new InMemoryEventStore();
|
|
const app = buildApp({
|
|
sessionRegistry: registry,
|
|
skillCatalog: { list: () => [{ name: "ads-analysis", description: "Analyze", source: "project", filePath: ".agents/skills/ads-analysis/SKILL.md", diagnostics: [] }] },
|
|
modelStore,
|
|
attachmentStore,
|
|
approvalBroker,
|
|
eventStore,
|
|
testModelConnection: () => Promise.resolve({ status: "deferred", message: "Saved; validation will occur on first run." }),
|
|
});
|
|
return { app, session, modelStore, attachmentStore, approvalBroker, eventStore };
|
|
}
|
|
|
|
export function makeSessionBusy(session: { startRun: ReturnType<typeof vi.fn> }): void {
|
|
session.startRun.mockRejectedValueOnce(new HarnessError("SESSION_BUSY", "Session is busy"));
|
|
}
|