Mercurial
view mrjunejune/test/conversation_api_test.js @ 260:1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
Integrate the production JRPG chat with Seobeo streaming, Deita persistence, and a Bazel-managed Copilot SDK and LiteLLM inference stack.
Co-authored-by: Copilot <[email protected]>
| author | MrJuneJune <mrjunejune@users.noreply.github.com> |
|---|---|
| date | Wed, 05 Aug 2026 09:19:41 -0700 |
| parents | |
| children | b401627fc49e |
line wrap: on
line source
const assert = require('node:assert/strict'); const fs = require('node:fs'); const net = require('node:net'); const os = require('node:os'); const path = require('node:path'); const { spawn } = require('node:child_process'); const RUNFILES = process.env.JS_BINARY__RUNFILES; const WORKSPACE = process.env.JS_BINARY__WORKSPACE; const runfilesWorkspace = path.join(RUNFILES, WORKSPACE); function findFreePort() { return new Promise((resolve, reject) => { const socket = net.createServer(); socket.once('error', reject); socket.listen(0, '127.0.0.1', () => { const address = socket.address(); socket.close(error => { if (error) reject(error); else resolve(String(address.port)); }); }); }); } async function waitForServer(child, baseUrl, logs) { const deadline = Date.now() + 15000; while (Date.now() < deadline) { if (child.exitCode !== null) { throw new Error(`Server exited with ${child.exitCode}\n${logs.join('')}`); } try { const response = await fetch(baseUrl); if (response.ok) return; } catch { // Keep waiting for startup. } await new Promise(resolve => setTimeout(resolve, 100)); } throw new Error(`Server startup timed out\n${logs.join('')}`); } async function stopProcess(child) { if (!child || child.exitCode !== null) return; child.kill('SIGTERM'); await new Promise(resolve => { const timer = setTimeout(() => { if (child.exitCode === null) child.kill('SIGKILL'); }, 3000); child.once('exit', () => { clearTimeout(timer); resolve(); }); }); } (async () => { assert.ok(RUNFILES); assert.ok(WORKSPACE); const serverBinary = path.join( runfilesWorkspace, 'mrjunejune/mrjunejune_server', ); const fakeSidecar = path.join( runfilesWorkspace, 'mrjunejune/test/inference_bridge_fake_sidecar', ); const tempDirectory = fs.mkdtempSync( path.join(os.tmpdir(), 'mrjunejune-conversation-api-'), ); const databasePath = path.join(tempDirectory, 'conversations.db'); const port = await findFreePort(); const baseUrl = `http://127.0.0.1:${port}`; const logs = []; let server; try { server = spawn(serverBinary, [], { cwd: runfilesWorkspace, env: { ...process.env, MRJUNEJUNE_PORT: port, MRJUNEJUNE_DB_PATH: databasePath, MRJUNEJUNE_INFERENCE_SIDECAR_PATH: fakeSidecar, MRJUNEJUNE_COPILOT_CLI_PATH: fakeSidecar, MRJUNEJUNE_ALLOW_ANONYMOUS_INFERENCE: '1', }, stdio: ['ignore', 'pipe', 'pipe'], }); server.stdout.on('data', chunk => logs.push(chunk.toString())); server.stderr.on('data', chunk => logs.push(chunk.toString())); await waitForServer(server, baseUrl, logs); let response = await fetch(`${baseUrl}/api/inference/health`); assert.equal(response.status, 200); response = await fetch(`${baseUrl}/api/conversations`, { method: 'POST', headers: { 'Content-Type': 'application/json', Origin: 'https://attacker.invalid', }, body: JSON.stringify({ title: 'Blocked' }), }); assert.equal(response.status, 403); response = await fetch(`${baseUrl}/api/conversations`, { method: 'POST', headers: { 'Content-Type': 'application/json', Origin: baseUrl, }, body: JSON.stringify({ title: 'First quest' }), }); assert.equal(response.status, 201); const created = await response.json(); assert.match(created.id, /^[0-9a-f-]{36}$/); response = await fetch(`${baseUrl}/api/conversations/${created.id}`); assert.equal(response.status, 200); let conversation = await response.json(); assert.equal(conversation.title, 'First quest'); assert.deepEqual(conversation.turns, []); response = await fetch(`${baseUrl}/api/conversations/${created.id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', Origin: baseUrl, }, body: JSON.stringify({ title: 'Renamed quest' }), }); assert.equal(response.status, 200); response = await fetch( `${baseUrl}/api/conversations/${created.id}/turns`, { method: 'POST', headers: { 'Content-Type': 'application/json', Origin: baseUrl, }, body: JSON.stringify({ prompt: 'Hello Epi' }), }, ); assert.equal(response.status, 200); assert.match( response.headers.get('content-type') || '', /^text\/event-stream/, ); const stream = await response.text(); assert.match(stream, /event: turn\.accepted/); assert.match(stream, /event: assistant\.delta/); assert.match(stream, /hello \\"traveler\\"\\\\path/); assert.match(stream, /event: assistant\.completed/); assert.match(stream, /event: assistant\.usage/); assert.match(stream, /event: turn\.done/); response = await fetch(`${baseUrl}/api/conversations/${created.id}`); conversation = await response.json(); assert.equal(conversation.title, 'Renamed quest'); assert.equal(conversation.turns.length, 2); assert.equal(conversation.turns[0].content, 'Hello Epi'); assert.equal(conversation.turns[1].content, 'hello traveler'); assert.equal(conversation.turns[1].input_tokens, 7); assert.equal(conversation.turns[1].output_tokens, 3); response = await fetch(`${baseUrl}/api/conversations/${created.id}`, { method: 'DELETE', headers: { Origin: baseUrl }, }); assert.equal(response.status, 204); response = await fetch(`${baseUrl}/api/conversations/${created.id}`); assert.equal(response.status, 404); } finally { await stopProcess(server); fs.rmSync(tempDirectory, { recursive: true, force: true }); } })().catch(error => { console.error(error.stack || error); process.exitCode = 1; });