Mercurial
annotate mrjunejune/test/conversation_api_test.js @ 266:efaf4c63cc94
fix clean production bundle staging
Recreate deployment staging before copying the Bazel bundle and verify required inference runtime paths before promoting it.
Co-authored-by: Copilot <[email protected]>
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Fri, 07 Aug 2026 12:52:30 -0700 |
| parents | 056790c4fb0d |
| children |
| rev | line source |
|---|---|
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
1 const assert = require('node:assert/strict'); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
2 const fs = require('node:fs'); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
3 const net = require('node:net'); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
4 const os = require('node:os'); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
5 const path = require('node:path'); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
6 const { spawn } = require('node:child_process'); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
7 |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
8 const RUNFILES = process.env.JS_BINARY__RUNFILES; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
9 const WORKSPACE = process.env.JS_BINARY__WORKSPACE; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
10 const runfilesWorkspace = path.join(RUNFILES, WORKSPACE); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
11 |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
12 function findFreePort() { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
13 return new Promise((resolve, reject) => { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
14 const socket = net.createServer(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
15 socket.once('error', reject); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
16 socket.listen(0, '127.0.0.1', () => { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
17 const address = socket.address(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
18 socket.close(error => { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
19 if (error) reject(error); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
20 else resolve(String(address.port)); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
21 }); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
22 }); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
23 }); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
24 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
25 |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
26 async function waitForServer(child, baseUrl, logs) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
27 const deadline = Date.now() + 15000; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
28 while (Date.now() < deadline) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
29 if (child.exitCode !== null) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
30 throw new Error(`Server exited with ${child.exitCode}\n${logs.join('')}`); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
31 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
32 try { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
33 const response = await fetch(baseUrl); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
34 if (response.ok) return; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
35 } catch { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
36 // Keep waiting for startup. |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
37 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
38 await new Promise(resolve => setTimeout(resolve, 100)); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
39 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
40 throw new Error(`Server startup timed out\n${logs.join('')}`); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
41 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
42 |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
43 async function stopProcess(child) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
44 if (!child || child.exitCode !== null) return; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
45 child.kill('SIGTERM'); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
46 await new Promise(resolve => { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
47 const timer = setTimeout(() => { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
48 if (child.exitCode === null) child.kill('SIGKILL'); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
49 }, 3000); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
50 child.once('exit', () => { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
51 clearTimeout(timer); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
52 resolve(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
53 }); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
54 }); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
55 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
56 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
57 /* ------------------------------------------------------------------ */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
58 /* Cookie jar: tracks Set-Cookie headers across responses */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
59 /* ------------------------------------------------------------------ */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
60 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
61 class CookieJar { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
62 constructor() { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
63 this._cookies = new Map(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
64 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
65 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
66 /** Update jar from a Set-Cookie header value (single directive). */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
67 updateFromDirective(directive) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
68 if (!directive) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
69 const firstPart = directive.split(';')[0].trim(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
70 const eqIdx = firstPart.indexOf('='); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
71 if (eqIdx < 0) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
72 const name = firstPart.slice(0, eqIdx).trim(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
73 const value = firstPart.slice(eqIdx + 1).trim(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
74 if (!name) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
75 // A max-age of -1 or a "deleted" value clears the cookie. |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
76 if (/max-age\s*=\s*-?0/i.test(directive) || value === '' || value.toLowerCase() === 'deleted') { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
77 this._cookies.delete(name); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
78 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
79 this._cookies.set(name, value); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
80 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
81 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
82 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
83 /** Update jar from a response (handles multiple Set-Cookie headers). */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
84 updateFromResponse(response) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
85 let setCookies; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
86 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
87 setCookies = response.headers.getSetCookie(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
88 } catch { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
89 // Fallback for older Node versions |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
90 const raw = response.headers.get('set-cookie') || ''; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
91 setCookies = raw ? raw.split(',\n').map(s => s.trim()).filter(Boolean) : []; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
92 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
93 for (const directive of setCookies) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
94 this.updateFromDirective(directive); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
95 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
96 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
97 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
98 get header() { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
99 return [...this._cookies.entries()].map(([k, v]) => `${k}=${v}`).join('; '); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
100 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
101 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
102 clone() { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
103 const jar = new CookieJar(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
104 for (const [k, v] of this._cookies) jar._cookies.set(k, v); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
105 return jar; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
106 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
107 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
108 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
109 /* ------------------------------------------------------------------ */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
110 /* Session bootstrap: GET /api/auth/session → csrfToken + cookie jar */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
111 /* ------------------------------------------------------------------ */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
112 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
113 async function bootstrapSession(baseUrl, jar) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
114 const headers = {}; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
115 if (jar.header) headers['Cookie'] = jar.header; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
116 const response = await fetch(`${baseUrl}/api/auth/session`, { headers }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
117 assert.equal(response.status, 200); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
118 jar.updateFromResponse(response); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
119 const data = await response.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
120 assert.ok(data.csrfToken, 'session must return csrfToken'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
121 return data.csrfToken; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
122 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
123 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
124 /* ------------------------------------------------------------------ */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
125 /* Fetch helper: adds Cookie + X-CSRF-Token headers */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
126 /* ------------------------------------------------------------------ */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
127 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
128 async function authedFetch(baseUrl, jar, csrfToken, path, options = {}) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
129 const headers = { ...(options.headers || {}) }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
130 if (jar.header) headers['Cookie'] = jar.header; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
131 if (csrfToken) headers['X-CSRF-Token'] = csrfToken; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
132 const response = await fetch(`${baseUrl}${path}`, { ...options, headers }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
133 jar.updateFromResponse(response); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
134 return response; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
135 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
136 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
137 /* ------------------------------------------------------------------ */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
138 /* Main test suite */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
139 /* ------------------------------------------------------------------ */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
140 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
141 (async () => { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
142 assert.ok(RUNFILES); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
143 assert.ok(WORKSPACE); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
144 const serverBinary = path.join( |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
145 runfilesWorkspace, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
146 'mrjunejune/mrjunejune_server', |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
147 ); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
148 const fakeSidecar = path.join( |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
149 runfilesWorkspace, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
150 'mrjunejune/test/inference_bridge_fake_sidecar', |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
151 ); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
152 const tempDirectory = fs.mkdtempSync( |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
153 path.join(os.tmpdir(), 'mrjunejune-conversation-api-'), |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
154 ); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
155 const databasePath = path.join(tempDirectory, 'conversations.db'); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
156 const port = await findFreePort(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
157 const baseUrl = `http://127.0.0.1:${port}`; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
158 const logs = []; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
159 let server; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
160 |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
161 try { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
162 server = spawn(serverBinary, [], { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
163 cwd: runfilesWorkspace, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
164 env: { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
165 ...process.env, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
166 MRJUNEJUNE_PORT: port, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
167 MRJUNEJUNE_DB_PATH: databasePath, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
168 MRJUNEJUNE_INFERENCE_SIDECAR_PATH: fakeSidecar, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
169 MRJUNEJUNE_COPILOT_CLI_PATH: fakeSidecar, |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
170 MRJUNEJUNE_ALLOW_GUEST_INFERENCE: '1', |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
171 }, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
172 stdio: ['ignore', 'pipe', 'pipe'], |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
173 }); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
174 server.stdout.on('data', chunk => logs.push(chunk.toString())); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
175 server.stderr.on('data', chunk => logs.push(chunk.toString())); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
176 await waitForServer(server, baseUrl, logs); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
177 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
178 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
179 // Inference health check |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
180 // ---------------------------------------------------------------- |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
181 let response = await fetch(`${baseUrl}/api/inference/health`); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
182 assert.equal(response.status, 200); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
183 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
184 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
185 // Task 2: mutation without any session returns 401 (no guest created) |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
186 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
187 response = await fetch(`${baseUrl}/api/conversations`, { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
188 method: 'POST', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
189 headers: { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
190 'Content-Type': 'application/json', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
191 Origin: baseUrl, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
192 'X-CSRF-Token': 'some-token', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
193 }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
194 body: JSON.stringify({ title: 'No session' }), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
195 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
196 assert.equal(response.status, 401, 'mutation without session must return 401'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
197 // Ensure no Set-Cookie guest header was emitted (no guest row written) |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
198 const setCookieNoSession = response.headers.get('set-cookie') || ''; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
199 assert.ok( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
200 !setCookieNoSession.includes('mjj_guest'), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
201 'mutation without session must not set guest cookie', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
202 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
203 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
204 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
205 // Guest 1: bootstrap session |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
206 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
207 const jar1 = new CookieJar(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
208 const csrf1 = await bootstrapSession(baseUrl, jar1); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
209 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
210 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
211 // CSRF rejection: missing X-CSRF-Token header |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
212 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
213 response = await fetch(`${baseUrl}/api/conversations`, { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
214 method: 'POST', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
215 headers: { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
216 'Content-Type': 'application/json', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
217 Origin: baseUrl, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
218 Cookie: jar1.header, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
219 }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
220 body: JSON.stringify({ title: 'Blocked' }), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
221 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
222 assert.equal(response.status, 403); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
223 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
224 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
225 // CSRF rejection: wrong Origin |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
226 // ---------------------------------------------------------------- |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
227 response = await fetch(`${baseUrl}/api/conversations`, { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
228 method: 'POST', |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
229 headers: { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
230 'Content-Type': 'application/json', |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
231 Origin: 'https://attacker.invalid', |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
232 'X-CSRF-Token': csrf1, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
233 Cookie: jar1.header, |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
234 }, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
235 body: JSON.stringify({ title: 'Blocked' }), |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
236 }); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
237 assert.equal(response.status, 403); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
238 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
239 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
240 // Guest 1 creates a conversation |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
241 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
242 response = await authedFetch(baseUrl, jar1, csrf1, '/api/conversations', { |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
243 method: 'POST', |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
244 headers: { 'Content-Type': 'application/json', Origin: baseUrl }, |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
245 body: JSON.stringify({ title: 'First quest' }), |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
246 }); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
247 assert.equal(response.status, 201); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
248 const created = await response.json(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
249 assert.match(created.id, /^[0-9a-f-]{36}$/); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
250 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
251 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
252 // Guest 1 gets the conversation |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
253 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
254 response = await authedFetch(baseUrl, jar1, null, `/api/conversations/${created.id}`); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
255 assert.equal(response.status, 200); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
256 let conversation = await response.json(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
257 assert.equal(conversation.title, 'First quest'); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
258 assert.deepEqual(conversation.turns, []); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
259 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
260 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
261 // Guest 1 renames it |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
262 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
263 response = await authedFetch( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
264 baseUrl, jar1, csrf1, `/api/conversations/${created.id}`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
265 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
266 method: 'PATCH', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
267 headers: { 'Content-Type': 'application/json', Origin: baseUrl }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
268 body: JSON.stringify({ title: 'Renamed quest' }), |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
269 }, |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
270 ); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
271 assert.equal(response.status, 200); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
272 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
273 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
274 // Guest 2: a separate identity |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
275 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
276 const jar2 = new CookieJar(); // fresh — no cookies |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
277 const csrf2 = await bootstrapSession(baseUrl, jar2); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
278 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
279 // Guest 2 cannot see Guest 1's conversation |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
280 response = await authedFetch(baseUrl, jar2, null, `/api/conversations/${created.id}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
281 assert.equal(response.status, 404, 'Guest 2 must not see Guest 1 conversation'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
282 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
283 // Guest 2 cannot rename Guest 1's conversation |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
284 response = await authedFetch( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
285 baseUrl, jar2, csrf2, `/api/conversations/${created.id}`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
286 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
287 method: 'PATCH', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
288 headers: { 'Content-Type': 'application/json', Origin: baseUrl }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
289 body: JSON.stringify({ title: 'Hijacked' }), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
290 }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
291 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
292 assert.equal(response.status, 404, 'Guest 2 rename must fail'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
293 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
294 // Guest 2 cannot delete Guest 1's conversation |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
295 response = await authedFetch( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
296 baseUrl, jar2, csrf2, `/api/conversations/${created.id}`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
297 { method: 'DELETE', headers: { Origin: baseUrl } }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
298 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
299 assert.equal(response.status, 404, 'Guest 2 delete must fail'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
300 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
301 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
302 // Listing: Guest 1 sees their own conversations; Guest 2 sees none |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
303 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
304 response = await authedFetch(baseUrl, jar1, null, '/api/conversations'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
305 assert.equal(response.status, 200); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
306 let listData = await response.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
307 assert.ok(Array.isArray(listData.conversations)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
308 assert.equal(listData.conversations.length, 1); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
309 assert.equal(listData.conversations[0].id, created.id); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
310 assert.equal(listData.conversations[0].title, 'Renamed quest'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
311 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
312 response = await authedFetch(baseUrl, jar2, null, '/api/conversations'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
313 assert.equal(response.status, 200); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
314 listData = await response.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
315 assert.equal(listData.conversations.length, 0); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
316 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
317 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
318 // Pagination: create more conversations and paginate |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
319 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
320 const ids = [created.id]; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
321 for (let i = 0; i < 4; i++) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
322 const r = await authedFetch( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
323 baseUrl, jar1, csrf1, '/api/conversations', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
324 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
325 method: 'POST', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
326 headers: { 'Content-Type': 'application/json', Origin: baseUrl }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
327 body: JSON.stringify({ title: `Chat ${i + 2}` }), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
328 }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
329 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
330 assert.equal(r.status, 201); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
331 const c = await r.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
332 ids.push(c.id); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
333 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
334 // Page 1 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
335 response = await authedFetch(baseUrl, jar1, null, '/api/conversations?limit=3'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
336 assert.equal(response.status, 200); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
337 const page1 = await response.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
338 assert.equal(page1.conversations.length, 3); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
339 assert.ok(page1.cursor, 'cursor should be present'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
340 // Page 2 using cursor |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
341 response = await authedFetch( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
342 baseUrl, jar1, null, `/api/conversations?cursor=${page1.cursor}&limit=3`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
343 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
344 assert.equal(response.status, 200); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
345 const page2 = await response.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
346 assert.ok(page2.conversations.length >= 1 && page2.conversations.length <= 2); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
347 // IDs must not overlap between pages |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
348 const page1Ids = new Set(page1.conversations.map(c => c.id)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
349 const page2Ids = page2.conversations.map(c => c.id); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
350 for (const id of page2Ids) assert.ok(!page1Ids.has(id), 'page 2 must not repeat page 1 IDs'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
351 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
352 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
353 // Streaming turn (existing mock inference path) |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
354 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
355 response = await authedFetch( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
356 baseUrl, jar1, csrf1, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
357 `/api/conversations/${created.id}/turns`, |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
358 { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
359 method: 'POST', |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
360 headers: { 'Content-Type': 'application/json', Origin: baseUrl }, |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
361 body: JSON.stringify({ prompt: 'Hello Epi' }), |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
362 }, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
363 ); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
364 assert.equal(response.status, 200); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
365 assert.match( |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
366 response.headers.get('content-type') || '', |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
367 /^text\/event-stream/, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
368 ); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
369 const stream = await response.text(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
370 assert.match(stream, /event: turn\.accepted/); |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
371 assert.match(stream, /event: tool\.started/); |
|
265
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
372 assert.match( |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
373 stream, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
374 /"tool":\{"name":"mock-search","prompt_profile":"public_visitor"\}/, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
375 ); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
376 assert.match(stream, /event: assistant\.delta/); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
377 assert.match(stream, /hello \\"traveler\\"\\\\path/); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
378 assert.match(stream, /event: assistant\.completed/); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
379 assert.match(stream, /event: assistant\.usage/); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
380 assert.match(stream, /event: turn\.done/); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
381 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
382 // Guest 2 cannot start a turn on Guest 1's conversation |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
383 response = await authedFetch( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
384 baseUrl, jar2, csrf2, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
385 `/api/conversations/${created.id}/turns`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
386 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
387 method: 'POST', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
388 headers: { 'Content-Type': 'application/json', Origin: baseUrl }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
389 body: JSON.stringify({ prompt: 'Intrude' }), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
390 }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
391 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
392 assert.equal(response.status, 404, 'Guest 2 must not turn on Guest 1 conversation'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
393 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
394 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
395 // Get after turn |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
396 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
397 response = await authedFetch(baseUrl, jar1, null, `/api/conversations/${created.id}`); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
398 conversation = await response.json(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
399 assert.equal(conversation.title, 'Renamed quest'); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
400 assert.equal(conversation.turns.length, 2); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
401 assert.equal(conversation.turns[0].content, 'Hello Epi'); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
402 assert.equal(conversation.turns[1].content, 'hello traveler'); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
403 assert.equal(conversation.turns[1].input_tokens, 7); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
404 assert.equal(conversation.turns[1].output_tokens, 3); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
405 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
406 // Listing should show last_message_preview |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
407 response = await authedFetch(baseUrl, jar1, null, '/api/conversations'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
408 listData = await response.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
409 const listedConv = listData.conversations.find(c => c.id === created.id); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
410 assert.ok(listedConv, 'created conv must appear in listing'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
411 assert.ok(typeof listedConv.turn_count === 'number'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
412 assert.ok(typeof listedConv.last_message_preview === 'string'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
413 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
414 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
415 // Delete |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
416 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
417 response = await authedFetch( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
418 baseUrl, jar1, csrf1, `/api/conversations/${created.id}`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
419 { method: 'DELETE', headers: { Origin: baseUrl } }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
420 ); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
421 assert.equal(response.status, 204); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
422 response = await authedFetch(baseUrl, jar1, null, `/api/conversations/${created.id}`); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
423 assert.equal(response.status, 404); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
424 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
425 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
426 // Claim: POST /api/conversations/claim (body-based, ID not in URL) |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
427 // Uses a dedicated server with a pre-bootstrapped admin user. |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
428 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
429 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
430 const claimPort = await findFreePort(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
431 const claimBase = `http://127.0.0.1:${claimPort}`; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
432 const claimTmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claim-test-')); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
433 const claimDb = path.join(claimTmpDir, 'claim.db'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
434 const claimLogs = []; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
435 // Pre-computed scrypt hash of "TestPass123!" |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
436 const claimHash = |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
437 'zenbu-scrypt$v=1$N=32768$r=8$p=1$cd915bb57924094c3c53be9e8e05fcf4$' + |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
438 '15998394fca01b498124ca96c6da4e78f271b1d52043a5344e1a5ee7f92c5332'; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
439 const claimServer = spawn(serverBinary, [], { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
440 cwd: runfilesWorkspace, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
441 env: { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
442 ...process.env, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
443 MRJUNEJUNE_PORT: claimPort, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
444 MRJUNEJUNE_DB_PATH: claimDb, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
445 MRJUNEJUNE_INFERENCE_SIDECAR_PATH: fakeSidecar, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
446 MRJUNEJUNE_COPILOT_CLI_PATH: fakeSidecar, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
447 AUTH_BOOTSTRAP_USERNAME: 'claimadmin', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
448 AUTH_BOOTSTRAP_PASSWORD_HASH: claimHash, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
449 }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
450 stdio: ['ignore', 'pipe', 'pipe'], |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
451 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
452 claimServer.stdout.on('data', c => claimLogs.push(c.toString())); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
453 claimServer.stderr.on('data', c => claimLogs.push(c.toString())); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
454 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
455 await waitForServer(claimServer, claimBase, claimLogs); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
456 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
457 // Guest creates a conversation |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
458 const guestJarC = new CookieJar(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
459 const guestCsrfC = await bootstrapSession(claimBase, guestJarC); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
460 const guestConvR = await authedFetch(claimBase, guestJarC, guestCsrfC, '/api/conversations', { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
461 method: 'POST', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
462 headers: { 'Content-Type': 'application/json', Origin: claimBase }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
463 body: JSON.stringify({ title: 'Legacy conv' }), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
464 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
465 assert.equal(guestConvR.status, 201); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
466 const guestConv = await guestConvR.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
467 const legacyId = guestConv.id; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
468 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
469 // Old /:id/claim route must be gone (404) |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
470 const oldRouteR = await authedFetch(claimBase, guestJarC, guestCsrfC, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
471 `/api/conversations/${legacyId}/claim`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
472 { method: 'POST', headers: { Origin: claimBase } }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
473 assert.ok( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
474 oldRouteR.status === 404 || oldRouteR.status === 405, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
475 `Old claim route must be absent, got ${oldRouteR.status}`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
476 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
477 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
478 // Guest claim attempt must be 403 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
479 const guestClaimR = await authedFetch(claimBase, guestJarC, guestCsrfC, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
480 '/api/conversations/claim', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
481 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
482 method: 'POST', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
483 headers: { 'Content-Type': 'application/json', Origin: claimBase }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
484 body: JSON.stringify({ conversationId: legacyId }), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
485 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
486 assert.equal(guestClaimR.status, 403, 'Guest must not claim'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
487 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
488 // Log in as the bootstrapped admin user |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
489 const userJarC = new CookieJar(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
490 const userCsrfC1 = await bootstrapSession(claimBase, userJarC); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
491 const loginR = await authedFetch(claimBase, userJarC, userCsrfC1, '/api/auth/login', { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
492 method: 'POST', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
493 headers: { 'Content-Type': 'application/json', Origin: claimBase }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
494 body: JSON.stringify({ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
495 username: 'claimadmin', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
496 password: 'TestPass123!', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
497 csrfToken: userCsrfC1, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
498 }), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
499 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
500 assert.equal(loginR.status, 200, `Login must succeed, got ${loginR.status}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
501 const userCsrfC2 = await bootstrapSession(claimBase, userJarC); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
502 |
|
265
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
503 const assertTurnProfile = async (jar, csrf, expectedProfile, title) => { |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
504 const createResponse = await authedFetch( |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
505 claimBase, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
506 jar, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
507 csrf, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
508 '/api/conversations', |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
509 { |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
510 method: 'POST', |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
511 headers: { 'Content-Type': 'application/json', Origin: claimBase }, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
512 body: JSON.stringify({ title }), |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
513 }, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
514 ); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
515 assert.equal(createResponse.status, 201); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
516 const createdConversation = await createResponse.json(); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
517 const turnResponse = await authedFetch( |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
518 claimBase, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
519 jar, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
520 csrf, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
521 `/api/conversations/${createdConversation.id}/turns`, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
522 { |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
523 method: 'POST', |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
524 headers: { 'Content-Type': 'application/json', Origin: claimBase }, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
525 body: JSON.stringify({ prompt: 'profile check' }), |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
526 }, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
527 ); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
528 assert.equal(turnResponse.status, 200); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
529 assert.match( |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
530 await turnResponse.text(), |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
531 new RegExp(`"prompt_profile":"${expectedProfile}"`), |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
532 ); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
533 }; |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
534 |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
535 await assertTurnProfile( |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
536 userJarC, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
537 userCsrfC2, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
538 'june_admin', |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
539 'Admin profile', |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
540 ); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
541 |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
542 const createMemberR = await authedFetch( |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
543 claimBase, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
544 userJarC, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
545 userCsrfC2, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
546 '/api/admin/users', |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
547 { |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
548 method: 'POST', |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
549 headers: { 'Content-Type': 'application/json', Origin: claimBase }, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
550 body: JSON.stringify({ |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
551 username: 'invitedfriend', |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
552 temporaryPassword: 'InvitedTempPass123!', |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
553 role: 'member', |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
554 }), |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
555 }, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
556 ); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
557 assert.equal(createMemberR.status, 201); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
558 |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
559 const memberJar = new CookieJar(); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
560 const memberGuestCsrf = await bootstrapSession(claimBase, memberJar); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
561 const memberLoginR = await authedFetch( |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
562 claimBase, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
563 memberJar, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
564 memberGuestCsrf, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
565 '/api/auth/login', |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
566 { |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
567 method: 'POST', |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
568 headers: { 'Content-Type': 'application/json', Origin: claimBase }, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
569 body: JSON.stringify({ |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
570 username: 'invitedfriend', |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
571 password: 'InvitedTempPass123!', |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
572 csrfToken: memberGuestCsrf, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
573 }), |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
574 }, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
575 ); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
576 assert.equal(memberLoginR.status, 200); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
577 const memberLoginData = await memberLoginR.json(); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
578 assert.equal(memberLoginData.mustChangePassword, true); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
579 |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
580 const memberPasswordCsrf = await bootstrapSession(claimBase, memberJar); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
581 const memberPasswordR = await authedFetch( |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
582 claimBase, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
583 memberJar, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
584 memberPasswordCsrf, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
585 '/api/auth/password', |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
586 { |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
587 method: 'POST', |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
588 headers: { 'Content-Type': 'application/json', Origin: claimBase }, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
589 body: JSON.stringify({ |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
590 currentPassword: 'InvitedTempPass123!', |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
591 newPassword: 'InvitedPermanentPass123!', |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
592 csrfToken: memberPasswordCsrf, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
593 }), |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
594 }, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
595 ); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
596 assert.equal(memberPasswordR.status, 200); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
597 const memberCsrf = await bootstrapSession(claimBase, memberJar); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
598 await assertTurnProfile( |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
599 memberJar, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
600 memberCsrf, |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
601 'invited_friend', |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
602 'Invited profile', |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
603 ); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
604 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
605 // A user cannot claim a conversation owned by a different guest. |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
606 const claimR = await authedFetch(claimBase, userJarC, userCsrfC2, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
607 '/api/conversations/claim', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
608 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
609 method: 'POST', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
610 headers: { 'Content-Type': 'application/json', Origin: claimBase }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
611 body: JSON.stringify({ conversationId: legacyId }), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
612 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
613 assert.equal(claimR.status, 409, `Guest-owned claim must fail, got ${claimR.status}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
614 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
615 // The conversation remains isolated from the unrelated user. |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
616 const ownedR = await authedFetch(claimBase, userJarC, null, `/api/conversations/${legacyId}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
617 assert.equal(ownedR.status, 404, 'User must not see another guest conversation'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
618 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
619 // Missing conversationId returns 400 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
620 const badR = await authedFetch(claimBase, userJarC, userCsrfC2, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
621 '/api/conversations/claim', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
622 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
623 method: 'POST', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
624 headers: { 'Content-Type': 'application/json', Origin: claimBase }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
625 body: JSON.stringify({}), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
626 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
627 assert.equal(badR.status, 400, 'Missing ID must return 400'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
628 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
629 } finally { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
630 await stopProcess(claimServer); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
631 fs.rmSync(claimTmpDir, { recursive: true, force: true }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
632 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
633 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
634 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
635 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
636 // Guest quota: session endpoint returns quota object for guest |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
637 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
638 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
639 const jarQ = new CookieJar(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
640 const csrfQ = await bootstrapSession(baseUrl, jarQ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
641 // Session endpoint must return a quota object (not null) for guests |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
642 const sessionR = await authedFetch(baseUrl, jarQ, null, '/api/auth/session'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
643 assert.equal(sessionR.status, 200); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
644 const sessionData = await sessionR.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
645 assert.equal(sessionData.kind, 'guest'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
646 assert.ok(sessionData.quota !== null && typeof sessionData.quota === 'object', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
647 'guest session must have non-null quota'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
648 assert.ok(typeof sessionData.quota.turnsLimit === 'number', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
649 'quota.turnsLimit must be a number'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
650 assert.ok(typeof sessionData.quota.turnsUsed === 'number', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
651 'quota.turnsUsed must be a number'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
652 assert.ok(typeof sessionData.quota.turnsRemaining === 'number', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
653 'quota.turnsRemaining must be a number'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
654 assert.ok(typeof sessionData.quota.outputTokensLimit === 'number', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
655 'quota.outputTokensLimit must be a number'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
656 assert.ok(typeof sessionData.quota.outputTokensUsed === 'number', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
657 'quota.outputTokensUsed must be a number'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
658 assert.ok(typeof sessionData.quota.outputTokensReserved === 'number', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
659 'quota.outputTokensReserved must be a number'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
660 assert.ok(typeof sessionData.quota.outputTokensRemaining === 'number', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
661 'quota.outputTokensRemaining must be a number'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
662 assert.ok(typeof sessionData.quota.resetsAt === 'number', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
663 'quota.resetsAt must be a number (unix timestamp)'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
664 assert.ok(sessionData.quota.resetsAt > Date.now() / 1000, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
665 'quota.resetsAt must be in the future'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
666 assert.equal(sessionData.quota.turnsUsed, 0, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
667 'fresh guest must have 0 turns used'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
668 assert.equal(sessionData.quota.turnsLimit, 10, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
669 'default turns limit must be 10'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
670 assert.equal(sessionData.quota.turnsRemaining, 10, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
671 'fresh guest must have full turns remaining'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
672 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
673 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
674 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
675 // Guest quota: quota decrements after a successful turn |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
676 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
677 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
678 const jarQ2 = new CookieJar(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
679 const csrfQ2 = await bootstrapSession(baseUrl, jarQ2); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
680 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
681 // Create a conversation |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
682 const convR = await authedFetch(baseUrl, jarQ2, csrfQ2, '/api/conversations', { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
683 method: 'POST', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
684 headers: { 'Content-Type': 'application/json', Origin: baseUrl }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
685 body: JSON.stringify({ title: 'Quota test' }), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
686 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
687 assert.equal(convR.status, 201); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
688 const convData = await convR.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
689 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
690 // Do a turn |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
691 const turnR = await authedFetch( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
692 baseUrl, jarQ2, csrfQ2, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
693 `/api/conversations/${convData.id}/turns`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
694 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
695 method: 'POST', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
696 headers: { 'Content-Type': 'application/json', Origin: baseUrl }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
697 body: JSON.stringify({ prompt: 'quota turn' }), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
698 }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
699 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
700 assert.equal(turnR.status, 200, 'quota turn must succeed'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
701 // Drain the stream |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
702 await turnR.text(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
703 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
704 // Session must now show 1 turn used |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
705 const sessR2 = await authedFetch(baseUrl, jarQ2, null, '/api/auth/session'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
706 assert.equal(sessR2.status, 200); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
707 const sessData2 = await sessR2.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
708 assert.ok(sessData2.quota !== null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
709 assert.equal(sessData2.quota.turnsUsed, 1, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
710 'turnsUsed must be 1 after one successful turn'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
711 assert.equal(sessData2.quota.turnsRemaining, 9, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
712 'turnsRemaining must be 9 after one turn'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
713 assert.ok(sessData2.quota.outputTokensUsed > 0, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
714 'outputTokensUsed must be > 0 after a turn'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
715 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
716 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
717 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
718 // Guest quota: 429 when turns are exhausted (limit=1 via env) |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
719 // ---------------------------------------------------------------- |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
720 // Note: The main server is started with default limit (10 turns). This test |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
721 // exhausts the remaining turns by doing 9 more turns on a fresh guest, then |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
722 // verifies the next attempt returns 429. |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
723 // We use a separate small-limit server for this test to keep the suite fast. |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
724 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
725 const portQ = await findFreePort(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
726 const tempDirQ = fs.mkdtempSync( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
727 path.join(os.tmpdir(), 'quota-limit-'), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
728 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
729 const dbQ = path.join(tempDirQ, 'q.db'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
730 const logsQ = []; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
731 const serverQ = spawn(serverBinary, [], { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
732 cwd: runfilesWorkspace, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
733 env: { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
734 ...process.env, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
735 MRJUNEJUNE_PORT: portQ, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
736 MRJUNEJUNE_DB_PATH: dbQ, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
737 MRJUNEJUNE_INFERENCE_SIDECAR_PATH: fakeSidecar, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
738 MRJUNEJUNE_COPILOT_CLI_PATH: fakeSidecar, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
739 MRJUNEJUNE_ALLOW_GUEST_INFERENCE: '1', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
740 AUTH_GUEST_DAILY_TURNS: '1', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
741 AUTH_GUEST_DAILY_OUTPUT_TOKENS: '10000', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
742 AUTH_GUEST_REQUEST_OUTPUT_TOKENS: '100', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
743 }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
744 stdio: ['ignore', 'pipe', 'pipe'], |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
745 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
746 serverQ.stdout.on('data', c => logsQ.push(c.toString())); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
747 serverQ.stderr.on('data', c => logsQ.push(c.toString())); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
748 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
749 await waitForServer(serverQ, `http://127.0.0.1:${portQ}`, logsQ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
750 const baseQ = `http://127.0.0.1:${portQ}`; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
751 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
752 const jarL = new CookieJar(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
753 const csrfL = await bootstrapSession(baseQ, jarL); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
754 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
755 // Create a conversation |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
756 const convL = await authedFetch(baseQ, jarL, csrfL, '/api/conversations', { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
757 method: 'POST', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
758 headers: { 'Content-Type': 'application/json', Origin: baseQ }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
759 body: JSON.stringify({ title: 'Limit test' }), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
760 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
761 assert.equal(convL.status, 201); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
762 const convLData = await convL.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
763 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
764 // First turn must succeed (limit=1) |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
765 const turn1 = await authedFetch( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
766 baseQ, jarL, csrfL, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
767 `/api/conversations/${convLData.id}/turns`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
768 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
769 method: 'POST', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
770 headers: { 'Content-Type': 'application/json', Origin: baseQ }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
771 body: JSON.stringify({ prompt: 'hello' }), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
772 }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
773 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
774 assert.equal(turn1.status, 200, 'first turn within limit must succeed'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
775 await turn1.text(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
776 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
777 // Create a second conversation for second turn |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
778 const convL2 = await authedFetch(baseQ, jarL, csrfL, '/api/conversations', { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
779 method: 'POST', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
780 headers: { 'Content-Type': 'application/json', Origin: baseQ }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
781 body: JSON.stringify({ title: 'Limit test 2' }), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
782 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
783 assert.equal(convL2.status, 201); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
784 const convL2Data = await convL2.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
785 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
786 // Second turn must be rejected with 429 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
787 const turn2 = await authedFetch( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
788 baseQ, jarL, csrfL, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
789 `/api/conversations/${convL2Data.id}/turns`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
790 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
791 method: 'POST', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
792 headers: { 'Content-Type': 'application/json', Origin: baseQ }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
793 body: JSON.stringify({ prompt: 'should fail' }), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
794 }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
795 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
796 assert.equal(turn2.status, 429, 'second turn must be 429 when turns exhausted'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
797 const errorData = await turn2.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
798 assert.ok(errorData.error, '429 must have error field'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
799 assert.equal(errorData.error.code, 'guest_quota_turns_exhausted', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
800 '429 code must be guest_quota_turns_exhausted'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
801 assert.ok(errorData.error.quota, '429 must include quota details'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
802 assert.equal(errorData.error.quota.turnsRemaining, 0, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
803 'turnsRemaining must be 0 in 429 payload'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
804 assert.ok(typeof errorData.error.quota.resetsAt === 'number', |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
805 '429 quota must include resetsAt'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
806 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
807 // Verify no turn was persisted for the rejected request |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
808 const convL2Get = await authedFetch(baseQ, jarL, null, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
809 `/api/conversations/${convL2Data.id}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
810 const convL2Detail = await convL2Get.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
811 assert.equal(convL2Detail.turns.length, 0, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
812 'no turn must be persisted when quota is denied'); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
813 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
814 } finally { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
815 await stopProcess(serverQ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
816 fs.rmSync(tempDirQ, { recursive: true, force: true }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
817 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
818 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
261
diff
changeset
|
819 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
820 } finally { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
821 await stopProcess(server); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
822 fs.rmSync(tempDirectory, { recursive: true, force: true }); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
823 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
824 })().catch(error => { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
825 console.error(error.stack || error); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
826 process.exitCode = 1; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
diff
changeset
|
827 }); |