154 lines
4.3 KiB
TypeScript
154 lines
4.3 KiB
TypeScript
import { afterAll, beforeAll, describe, expect, it, mock } from "bun:test";
|
|
import express, { Router } from "express";
|
|
import type { Server } from "node:http";
|
|
|
|
import { CredentialRefreshCoordinator } from "../../src/auth/credentialRefresh.js";
|
|
import { registerChatInteractionRoutes } from "../../src/routes/chatInteractionRoutes.js";
|
|
import type { ActiveRun } from "../../src/routes/chatUiState.js";
|
|
|
|
describe("chat interaction routes", () => {
|
|
let baseUrl = "";
|
|
let server: Server;
|
|
const replyQuestion = mock(async () => ({ ok: true }));
|
|
const replyPermission = mock(async () => ({ ok: true }));
|
|
|
|
beforeAll(async () => {
|
|
const activeRuns = new Map<string, ActiveRun>();
|
|
activeRuns.set("runtime-session", {
|
|
clientSessionId: "client-session",
|
|
controller: new AbortController(),
|
|
messages: [
|
|
{
|
|
id: "assistant-1",
|
|
role: "assistant",
|
|
permissions: [
|
|
{
|
|
requestId: "permission-1",
|
|
sessionId: "runtime-session",
|
|
permission: "bash",
|
|
patterns: ["npm test"],
|
|
always: ["npm test"],
|
|
createdAt: 1,
|
|
status: "pending",
|
|
},
|
|
],
|
|
questions: [{ requestId: "question-1", status: "pending" }],
|
|
},
|
|
],
|
|
pendingPermissions: new Map([
|
|
[
|
|
"permission-1",
|
|
{
|
|
session_id: "runtime-session",
|
|
request_id: "permission-1",
|
|
permission: "bash",
|
|
patterns: ["npm test"],
|
|
always: ["npm test"],
|
|
created_at: 1,
|
|
},
|
|
],
|
|
]),
|
|
pendingQuestions: new Map([
|
|
[
|
|
"question-1",
|
|
{
|
|
created_at: 1,
|
|
request_id: "question-1",
|
|
session_id: "runtime-session",
|
|
questions: [],
|
|
},
|
|
],
|
|
]),
|
|
status: "running",
|
|
subscribers: new Set(),
|
|
});
|
|
|
|
const router = Router();
|
|
router.use((req, _res, next) => {
|
|
req.agentAuth = {
|
|
accessToken: "access-token",
|
|
userId: "user-1",
|
|
keycloakSub: "keycloak-1",
|
|
username: "tester",
|
|
role: "user",
|
|
isSuperuser: false,
|
|
projectId: "project-1",
|
|
network: "network-1",
|
|
projectRole: "member",
|
|
};
|
|
next();
|
|
});
|
|
registerChatInteractionRoutes(router, {
|
|
activeRuns,
|
|
credentialRefreshCoordinator: new CredentialRefreshCoordinator(),
|
|
runtime: { replyPermission, replyQuestion } as never,
|
|
sessionMetadataStore: {
|
|
get: async () => ({ sessionId: "runtime-session" }),
|
|
} as never,
|
|
sessionUiStateStore: {
|
|
read: async () => null,
|
|
write: async () => undefined,
|
|
} as never,
|
|
});
|
|
|
|
const app = express();
|
|
app.use(express.json());
|
|
app.use(router);
|
|
server = app.listen(0);
|
|
await new Promise<void>((resolve) => server.once("listening", resolve));
|
|
const address = server.address();
|
|
if (!address || typeof address === "string") {
|
|
throw new Error("test server did not expose a TCP port");
|
|
}
|
|
baseUrl = `http://127.0.0.1:${address.port}`;
|
|
});
|
|
|
|
afterAll(() => {
|
|
server.close();
|
|
});
|
|
|
|
it("submits answers to the stable OpenCode question adapter", async () => {
|
|
const response = await fetch(
|
|
`${baseUrl}/sessions/client-session/question-responses`,
|
|
{
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
request_id: "question-1",
|
|
action: "reply",
|
|
answers: [["继续"]],
|
|
}),
|
|
},
|
|
);
|
|
|
|
expect(response.status).toBe(202);
|
|
expect(replyQuestion).toHaveBeenCalledWith({
|
|
requestId: "question-1",
|
|
sessionId: "runtime-session",
|
|
answers: [["继续"]],
|
|
});
|
|
});
|
|
|
|
it("forwards saved permission grants to OpenCode", async () => {
|
|
const response = await fetch(
|
|
`${baseUrl}/sessions/client-session/permission-responses`,
|
|
{
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
request_id: "permission-1",
|
|
reply: "always",
|
|
}),
|
|
},
|
|
);
|
|
|
|
expect(response.status).toBe(202);
|
|
expect(replyPermission).toHaveBeenCalledWith({
|
|
requestId: "permission-1",
|
|
sessionId: "runtime-session",
|
|
reply: "always",
|
|
message: undefined,
|
|
});
|
|
});
|
|
});
|