Initial agentchat server, skill, and deployment setup
Bun + bun:sqlite message hub for agents on different machines, with a self-served Claude Code skill and installer, podman compose files, and Gitea CI that builds and pushes the container image.
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
process.env.AGENTCHAT_DB = ":memory:";
|
||||
const { handle } = await import("../src/server");
|
||||
|
||||
async function post(path: string, body: unknown): Promise<Response> {
|
||||
return handle(
|
||||
new Request(`http://test${path}`, {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async function getJson<T>(path: string): Promise<T> {
|
||||
const response = await handle(new Request(`http://test${path}`));
|
||||
expect(response.status).toBe(200);
|
||||
return response.json() as Promise<T>;
|
||||
}
|
||||
|
||||
type Message = { id: number; from: string; to: string | null; body: string };
|
||||
|
||||
describe("messages", () => {
|
||||
test("direct message reaches its recipient but not the sender's inbox", async () => {
|
||||
const sent = await post("/api/messages", { from: "desktop", to: "vps", body: "deploy done?" });
|
||||
expect(sent.status).toBe(201);
|
||||
|
||||
const inbox = await getJson<Message[]>("/api/messages?for=vps");
|
||||
expect(inbox.map((m) => m.body)).toContain("deploy done?");
|
||||
|
||||
const senderInbox = await getJson<Message[]>("/api/messages?for=desktop");
|
||||
expect(senderInbox.map((m) => m.body)).not.toContain("deploy done?");
|
||||
});
|
||||
|
||||
test("broadcast (no `to`) reaches other agents", async () => {
|
||||
await post("/api/messages", { from: "desktop", body: "heads up everyone" });
|
||||
|
||||
const inbox = await getJson<Message[]>("/api/messages?for=server");
|
||||
const broadcast = inbox.find((m) => m.body === "heads up everyone");
|
||||
expect(broadcast?.to).toBeNull();
|
||||
});
|
||||
|
||||
test("since filters out already-seen messages", async () => {
|
||||
const first = (await (await post("/api/messages", { from: "a", to: "b", body: "first" })).json()) as Message;
|
||||
await post("/api/messages", { from: "a", to: "b", body: "second" });
|
||||
|
||||
const inbox = await getJson<Message[]>(`/api/messages?for=b&since=${first.id}`);
|
||||
expect(inbox.map((m) => m.body)).toEqual(["second"]);
|
||||
});
|
||||
|
||||
test("rejects missing from/body", async () => {
|
||||
expect((await post("/api/messages", { from: "a" })).status).toBe(400);
|
||||
expect((await post("/api/messages", { body: "hi" })).status).toBe(400);
|
||||
expect((await post("/api/messages", { from: " ", body: " " })).status).toBe(400);
|
||||
});
|
||||
});
|
||||
|
||||
describe("agents", () => {
|
||||
test("senders and readers are tracked", async () => {
|
||||
await post("/api/messages", { from: "desktop", to: "vps", body: "hi" });
|
||||
await getJson<Message[]>("/api/messages?for=laptop");
|
||||
|
||||
const agents = await getJson<{ name: string }[]>("/api/agents");
|
||||
const names = agents.map((agent) => agent.name);
|
||||
expect(names).toContain("desktop");
|
||||
expect(names).toContain("laptop");
|
||||
});
|
||||
|
||||
test("explicit check-in registers an agent", async () => {
|
||||
const response = await post("/api/agents", { name: "pi", machine: "raspberry pi 5" });
|
||||
expect(response.status).toBe(200);
|
||||
expect(((await response.json()) as { machine: string }).machine).toBe("raspberry pi 5");
|
||||
});
|
||||
});
|
||||
|
||||
describe("served assets", () => {
|
||||
test("skill.md templates the requesting host as base URL", async () => {
|
||||
const response = await handle(new Request("http://chat.example.com/skill.md"));
|
||||
const text = await response.text();
|
||||
expect(response.status).toBe(200);
|
||||
expect(text).toContain("http://chat.example.com/api/messages");
|
||||
expect(text).not.toContain("{{BASE_URL}}");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user