This repository has been archived on 2026-09-17. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
nixos-config/services/live-intent/tests/server.test.mjs
pijukka c8cfdcd88e host(pinix): add live intent and ereader viewer services
Both run on pinix, published only through the pinix Tailnet namespace and
Traefik's web-internal entrypoint (unbound local-data + Headscale split
DNS for intent./ereader.domain2). Live Intent reads its OpenAI key from
the new pinix-only agenix secret.

ereaderViewer is a path: flake input for now; the repo split plan moves
it into its own repository on cgit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:18:55 +02:00

114 lines
4.9 KiB
JavaScript

import assert from "node:assert/strict";
import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
import { createServer } from "node:http";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import { createApp, sessionConfiguration } from "../server.mjs";
const origin = "https://intent.example.test";
const conversationId = "2026-09-15-123e4567-e89b-12d3-a456-426614174000";
async function withApp(options, run) {
const server = createServer(createApp({ origin, apiKey: "test-key", ...options }));
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
const { port } = server.address();
try {
await run(`http://127.0.0.1:${port}`);
} finally {
await new Promise((resolve) => server.close(resolve));
}
}
function post(url, path, payload, headers = {}) {
return fetch(`${url}${path}`, {
method: "POST",
headers: { Origin: origin, "Content-Type": "application/json", ...headers },
body: JSON.stringify(payload),
});
}
function payload(overrides = {}) {
return {
conversationId,
finalization: "confirmed",
transcript: [{ speaker: "Nutzer", text: "Bitte halte dieses Gespräch fest." }],
...overrides,
};
}
test("creates a Live WebRTC session through the server only", async () => {
let received;
await withApp({
fetchImpl: async (_url, options) => {
received = JSON.parse(options.body);
return Response.json({ session: { id: "live_test" }, transport: { type: "webrtc", sdp: "answer" } }, { status: 201 });
},
}, async (url) => {
const response = await post(url, "/api/session", { sdp: "offer" });
assert.equal(response.status, 201);
assert.equal((await response.json()).session.id, "live_test");
});
assert.equal(received.session.model, "gpt-live-1");
assert.equal(received.transport.type, "webrtc");
assert.equal(received.session.store, false);
});
test("rejects cross-origin state-changing requests", async () => {
await withApp({}, async (url) => {
const response = await fetch(`${url}/api/transcripts`, {
method: "POST",
headers: { Origin: "https://wrong.example", "Content-Type": "application/json" },
body: JSON.stringify(payload()),
});
assert.equal(response.status, 403);
});
});
test("saves a transcript without an API call, retries idempotently, and downloads it", async () => {
const directory = await mkdtemp(join(tmpdir(), "live-intent-test-"));
let calls = 0;
await withApp({ apiKey: "", transcriptsDirectory: directory, fetchImpl: async () => { calls += 1; throw new Error("must not call API"); } }, async (url) => {
const first = await post(url, "/api/transcripts", payload());
assert.equal(first.status, 201);
const saved = await first.json();
assert.equal(saved.created, true);
const retry = await post(url, "/api/transcripts", payload());
assert.equal(retry.status, 200);
assert.equal((await retry.json()).created, false);
const stored = await fetch(`${url}${saved.url}`);
assert.equal(stored.status, 200);
assert.match(await stored.text(), /Session-Abschluss: bestätigt/);
});
assert.equal(calls, 0);
assert.match(await readFile(join(directory, conversationId, "transcript.md"), "utf8"), /Gesprächstranskript/);
});
test("rejects a collision without overwriting the original transcript", async () => {
const directory = await mkdtemp(join(tmpdir(), "live-intent-test-"));
await withApp({ transcriptsDirectory: directory }, async (url) => {
assert.equal((await post(url, "/api/transcripts", payload({ finalization: "unconfirmed" }))).status, 201);
const collision = await post(url, "/api/transcripts", payload({ transcript: [{ speaker: "Nutzer", text: "Anderer Inhalt." }] }));
assert.equal(collision.status, 409);
});
assert.match(await readFile(join(directory, conversationId, "transcript.md"), "utf8"), /Session-Abschluss: unbestätigt/);
});
test("preserves existing intents while storing new transcripts", async () => {
const stateDirectory = await mkdtemp(join(tmpdir(), "live-intent-test-"));
const oldIntent = join(stateDirectory, "intents", "old-conversation", "intent.md");
await mkdir(join(stateDirectory, "intents", "old-conversation"), { recursive: true });
await writeFile(oldIntent, "# Alter Intent\n", "utf8");
await withApp({ transcriptsDirectory: join(stateDirectory, "transcripts") }, async (url) => {
assert.equal((await post(url, "/api/transcripts", payload())).status, 201);
});
assert.equal(await readFile(oldIntent, "utf8"), "# Alter Intent\n");
});
test("session configuration preserves the documented model and in-conversation delegation", () => {
const body = sessionConfiguration({ liveModel: "gpt-live-1", backendModel: "gpt-5.6-luna" }, "offer");
assert.equal(body.session.model, "gpt-live-1");
assert.equal(body.transport.type, "webrtc");
assert.equal(body.session.delegation.type, "responses");
assert.match(body.session.instructions, /kein Intent erzeugt/);
});