mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-04-25 08:26:22 +02:00
strengthen repo verification and runtime coverage
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.
This commit is contained in:
parent
2133d7226f
commit
4239f9f1ef
63 changed files with 3678 additions and 764 deletions
131
apps/cli/test/server.test.mjs
Normal file
131
apps/cli/test/server.test.mjs
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
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);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue