165 lines
4.9 KiB
TypeScript
165 lines
4.9 KiB
TypeScript
import { describe, expect, it } from "bun:test";
|
|
|
|
import {
|
|
streamPromptResponse,
|
|
type PermissionRequestPayload,
|
|
} from "../../src/routes/chatStream.js";
|
|
import { type OpencodeRuntimeAdapter } from "../../src/runtime/opencode.js";
|
|
|
|
const createEventStream = (events: unknown[]) => ({
|
|
async *[Symbol.asyncIterator]() {
|
|
for (const event of events) {
|
|
yield event;
|
|
}
|
|
},
|
|
});
|
|
|
|
describe("streamPromptResponse", () => {
|
|
it("forwards opencode permission requests as SSE payloads", async () => {
|
|
const runtime = {
|
|
subscribeEvents: async () =>
|
|
createEventStream([
|
|
{
|
|
type: "permission.asked",
|
|
properties: {
|
|
id: "perm-1",
|
|
sessionID: "runtime-session-1",
|
|
permission: "bash",
|
|
patterns: ["rm *"],
|
|
metadata: { command: "rm tmp.txt" },
|
|
always: ["rm *"],
|
|
},
|
|
},
|
|
{
|
|
type: "session.idle",
|
|
properties: {
|
|
sessionID: "runtime-session-1",
|
|
},
|
|
},
|
|
]),
|
|
prompt: async () => undefined,
|
|
messages: async () => [],
|
|
} as unknown as OpencodeRuntimeAdapter;
|
|
const events: Array<{ event: string; data: Record<string, unknown> }> = [];
|
|
|
|
await streamPromptResponse({
|
|
runtime,
|
|
sessionId: "runtime-session-1",
|
|
clientSessionId: "client-session-1",
|
|
message: "delete temp",
|
|
write: (event, data) => events.push({ event, data }),
|
|
});
|
|
|
|
const permissionEvent = events.find((item) => item.event === "permission_request");
|
|
expect(permissionEvent?.data).toMatchObject({
|
|
session_id: "client-session-1",
|
|
request_id: "perm-1",
|
|
permission: "bash",
|
|
patterns: ["rm *"],
|
|
metadata: { command: "rm tmp.txt" },
|
|
always: ["rm *"],
|
|
} satisfies Partial<PermissionRequestPayload>);
|
|
});
|
|
|
|
it("auto replies always when approval mode is always", async () => {
|
|
const replies: Array<Record<string, unknown>> = [];
|
|
const runtime = {
|
|
subscribeEvents: async () =>
|
|
createEventStream([
|
|
{
|
|
type: "permission.asked",
|
|
properties: {
|
|
id: "perm-1",
|
|
sessionID: "runtime-session-1",
|
|
permission: "bash",
|
|
patterns: ["npm test"],
|
|
metadata: { command: "npm test" },
|
|
always: ["npm test"],
|
|
},
|
|
},
|
|
{
|
|
type: "session.idle",
|
|
properties: {
|
|
sessionID: "runtime-session-1",
|
|
},
|
|
},
|
|
]),
|
|
prompt: async () => undefined,
|
|
messages: async () => [],
|
|
replyPermission: async (options: Record<string, unknown>) => {
|
|
replies.push(options);
|
|
},
|
|
} as unknown as OpencodeRuntimeAdapter;
|
|
const events: Array<{ event: string; data: Record<string, unknown> }> = [];
|
|
|
|
await streamPromptResponse({
|
|
runtime,
|
|
sessionId: "runtime-session-1",
|
|
clientSessionId: "client-session-1",
|
|
message: "run tests",
|
|
approvalMode: "always",
|
|
write: (event, data) => events.push({ event, data }),
|
|
});
|
|
|
|
expect(replies).toEqual([
|
|
{
|
|
requestId: "perm-1",
|
|
sessionId: "runtime-session-1",
|
|
reply: "always",
|
|
},
|
|
]);
|
|
expect(events.some((item) => item.event === "permission_request")).toBe(false);
|
|
expect(events.find((item) => item.event === "permission_response")?.data).toEqual({
|
|
session_id: "client-session-1",
|
|
request_id: "perm-1",
|
|
reply: "always",
|
|
});
|
|
});
|
|
|
|
it("forwards opencode v2 permission requests as SSE payloads", async () => {
|
|
const runtime = {
|
|
subscribeEvents: async () =>
|
|
createEventStream([
|
|
{
|
|
type: "permission.v2.asked",
|
|
properties: {
|
|
id: "perm-v2-1",
|
|
sessionID: "runtime-session-1",
|
|
action: "external_directory",
|
|
resources: ["/tmp"],
|
|
save: ["/tmp"],
|
|
metadata: { path: "/tmp" },
|
|
},
|
|
},
|
|
{
|
|
type: "session.idle",
|
|
properties: {
|
|
sessionID: "runtime-session-1",
|
|
},
|
|
},
|
|
]),
|
|
prompt: async () => undefined,
|
|
messages: async () => [],
|
|
} as unknown as OpencodeRuntimeAdapter;
|
|
const events: Array<{ event: string; data: Record<string, unknown> }> = [];
|
|
|
|
await streamPromptResponse({
|
|
runtime,
|
|
sessionId: "runtime-session-1",
|
|
clientSessionId: "client-session-1",
|
|
message: "read /tmp",
|
|
write: (event, data) => events.push({ event, data }),
|
|
});
|
|
|
|
const permissionEvent = events.find((item) => item.event === "permission_request");
|
|
expect(permissionEvent?.data).toMatchObject({
|
|
session_id: "client-session-1",
|
|
request_id: "perm-v2-1",
|
|
permission: "external_directory",
|
|
patterns: ["/tmp"],
|
|
metadata: { path: "/tmp" },
|
|
always: ["/tmp"],
|
|
} satisfies Partial<PermissionRequestPayload>);
|
|
});
|
|
});
|