mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-04-25 08:26:22 +02:00
Add clearer app docs plus targeted desktop, CLI, web, and worker tests so cross-surface regressions are caught earlier and the repo is easier to navigate.
131 lines
3.8 KiB
JavaScript
131 lines
3.8 KiB
JavaScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import { createApp } from "../dist/server.js";
|
|
|
|
test("message endpoint creates a message and returns its id", async () => {
|
|
const calls = [];
|
|
const app = createApp({
|
|
createMessage: async (runId, message) => {
|
|
calls.push({ runId, message });
|
|
return "msg-123";
|
|
},
|
|
authorizePermission: async () => {},
|
|
replyToHumanInputRequest: async () => {},
|
|
stop: async () => {},
|
|
subscribeToEvents: async () => () => {},
|
|
});
|
|
|
|
const response = await app.request("/runs/run-1/messages/new", {
|
|
method: "POST",
|
|
headers: { "content-type": "application/json" },
|
|
body: JSON.stringify({ message: "hello" }),
|
|
});
|
|
|
|
assert.equal(response.status, 200);
|
|
assert.deepEqual(await response.json(), { messageId: "msg-123" });
|
|
assert.deepEqual(calls, [{ runId: "run-1", message: "hello" }]);
|
|
});
|
|
|
|
test("permission endpoint validates payload and calls dependency", async () => {
|
|
const calls = [];
|
|
const app = createApp({
|
|
createMessage: async () => "unused",
|
|
authorizePermission: async (runId, payload) => {
|
|
calls.push({ runId, payload });
|
|
},
|
|
replyToHumanInputRequest: async () => {},
|
|
stop: async () => {},
|
|
subscribeToEvents: async () => () => {},
|
|
});
|
|
|
|
const response = await app.request("/runs/run-2/permissions/authorize", {
|
|
method: "POST",
|
|
headers: { "content-type": "application/json" },
|
|
body: JSON.stringify({
|
|
subflow: ["child"],
|
|
toolCallId: "tool-1",
|
|
response: "approve",
|
|
}),
|
|
});
|
|
|
|
assert.equal(response.status, 200);
|
|
assert.deepEqual(await response.json(), { success: true });
|
|
assert.deepEqual(calls, [{
|
|
runId: "run-2",
|
|
payload: {
|
|
subflow: ["child"],
|
|
toolCallId: "tool-1",
|
|
response: "approve",
|
|
},
|
|
}]);
|
|
});
|
|
|
|
test("invalid message payload returns a validation error", async () => {
|
|
const app = createApp({
|
|
createMessage: async () => "unused",
|
|
authorizePermission: async () => {},
|
|
replyToHumanInputRequest: async () => {},
|
|
stop: async () => {},
|
|
subscribeToEvents: async () => () => {},
|
|
});
|
|
|
|
const response = await app.request("/runs/run-1/messages/new", {
|
|
method: "POST",
|
|
headers: { "content-type": "application/json" },
|
|
body: JSON.stringify({}),
|
|
});
|
|
|
|
assert.equal(response.status, 400);
|
|
});
|
|
|
|
test("openapi endpoint is exposed", async () => {
|
|
const app = createApp({
|
|
createMessage: async () => "unused",
|
|
authorizePermission: async () => {},
|
|
replyToHumanInputRequest: async () => {},
|
|
stop: async () => {},
|
|
subscribeToEvents: async () => () => {},
|
|
});
|
|
|
|
const response = await app.request("/openapi.json");
|
|
const body = await response.json();
|
|
|
|
assert.equal(response.status, 200);
|
|
assert.equal(body.info.title, "Hono");
|
|
assert.ok(body.paths["/runs/{runId}/messages/new"]);
|
|
});
|
|
|
|
test("stream endpoint emits SSE payloads and unsubscribes on cancel", async () => {
|
|
let listener;
|
|
let unsubscribed = false;
|
|
const app = createApp({
|
|
createMessage: async () => "unused",
|
|
authorizePermission: async () => {},
|
|
replyToHumanInputRequest: async () => {},
|
|
stop: async () => {},
|
|
subscribeToEvents: async (fn) => {
|
|
listener = fn;
|
|
return () => {
|
|
unsubscribed = true;
|
|
};
|
|
},
|
|
});
|
|
|
|
const response = await app.request("/stream");
|
|
assert.equal(response.status, 200);
|
|
assert.equal(response.headers.get("content-type"), "text/event-stream");
|
|
|
|
await listener({ type: "message", data: { hello: "world" } });
|
|
|
|
const reader = response.body.getReader();
|
|
const chunk = await reader.read();
|
|
const text = new TextDecoder().decode(chunk.value);
|
|
|
|
assert.match(text, /event: message/);
|
|
assert.match(text, /"hello":"world"/);
|
|
|
|
await reader.cancel();
|
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
assert.equal(unsubscribed, true);
|
|
});
|