feat(server): expose secure harness API
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { createTestApp, makeSessionBusy } from "./test-app.js";
|
||||
|
||||
const apps: Array<ReturnType<typeof createTestApp>["app"]> = [];
|
||||
afterEach(async () => Promise.all(apps.splice(0).map((app) => app.close())));
|
||||
|
||||
describe("HTTP API", () => {
|
||||
it("serves health, skills, and creates a session with request IDs", async () => {
|
||||
const { app } = createTestApp();
|
||||
apps.push(app);
|
||||
const health = await app.inject({ method: "GET", url: "/health" });
|
||||
const skills = await app.inject({ method: "GET", url: "/api/skills" });
|
||||
const created = await app.inject({ method: "POST", url: "/api/sessions" });
|
||||
|
||||
const healthBody = health.json<{ status: string; requestId: string }>();
|
||||
const skillsBody = skills.json<{ skills: Array<{ name: string }>; requestId: string }>();
|
||||
const createdBody = created.json<{ sessionId: string; requestId: string }>();
|
||||
expect(health.statusCode).toBe(200);
|
||||
expect(healthBody.status).toBe("ok");
|
||||
expect(healthBody.requestId).toMatch(/^req_/);
|
||||
expect(skillsBody.skills[0]?.name).toBe("ads-analysis");
|
||||
expect(skillsBody.requestId).toMatch(/^req_/);
|
||||
expect(created.statusCode).toBe(201);
|
||||
expect(createdBody.sessionId).toBe("ses_1");
|
||||
expect(createdBody.requestId).toMatch(/^req_/);
|
||||
});
|
||||
|
||||
it("accepts a run without waiting for completion", async () => {
|
||||
const { app, session } = createTestApp();
|
||||
apps.push(app);
|
||||
const response = await app.inject({ method: "POST", url: "/api/sessions/ses_1/messages", payload: { text: "hello", attachmentIds: [] } });
|
||||
|
||||
expect(response.statusCode).toBe(202);
|
||||
const body = response.json<{ runId: string; requestId: string }>();
|
||||
expect(body.runId).toBe("run_1");
|
||||
expect(body.requestId).toMatch(/^req_/);
|
||||
expect(session.startRun).toHaveBeenCalledWith({ text: "hello", attachmentIds: [] });
|
||||
});
|
||||
|
||||
it("returns safe validation, not-found, and busy errors", async () => {
|
||||
const { app, session } = createTestApp();
|
||||
apps.push(app);
|
||||
const invalid = await app.inject({ method: "POST", url: "/api/sessions/ses_1/messages", payload: { text: "", attachmentIds: [] } });
|
||||
const missing = await app.inject({ method: "POST", url: "/api/sessions/ses_missing/cancel" });
|
||||
makeSessionBusy(session);
|
||||
const busy = await app.inject({ method: "POST", url: "/api/sessions/ses_1/messages", payload: { text: "again" } });
|
||||
|
||||
expect(invalid.statusCode).toBe(400);
|
||||
expect(invalid.json<{ error: { code: string; requestId: string } }>().error).toMatchObject({ code: "VALIDATION_ERROR" });
|
||||
expect(invalid.json<{ error: { requestId: string } }>().error.requestId).toMatch(/^req_/);
|
||||
expect(missing.statusCode).toBe(404);
|
||||
expect(missing.json()).toMatchObject({ error: { code: "SESSION_NOT_FOUND" } });
|
||||
expect(busy.statusCode).toBe(409);
|
||||
expect(busy.body).not.toContain("stack");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user