Files
jason.woltje 068d868a99 lead: commit rocko's untracked evidence (4471 files: queue-124 queue-129 queue-171 queue-178 queue-280 queue-31 queue-84 queue-85 queue-94)
Unclaimed files left in the canonical checkout. Inventory: canonical-cleanup-inventory.md (commit-as-evidence groups). detect-secrets scan clean on 2026-11-02: hits are sha256 pins, git object ids, test sentinels already in tracked tests and unit-file lines.
2026-11-02 15:05:15 -06:00

87 lines
5.6 KiB
Diff

diff --git a/mnt/storage/src/mosaic-stack-rocko-129-r1/packages/webui/tests/chat-browser.test.mjs b/mnt/storage/src/mosaic-stack-rocko-129-r2/packages/webui/tests/chat-browser.test.mjs
index a602883fe..f3eeeca96 100644
--- a/mnt/storage/src/mosaic-stack-rocko-129-r1/packages/webui/tests/chat-browser.test.mjs
+++ b/mnt/storage/src/mosaic-stack-rocko-129-r2/packages/webui/tests/chat-browser.test.mjs
@@ -63,6 +63,9 @@ test('chat browser: list/create named projects and threads, hostile labels inert
assert.equal(await b.evaluate('window.injected'), undefined);
assert.equal(await b.evaluate('!!document.querySelector(".chat-projects img")'), false);
assert.match(await b.evaluate('document.querySelector(".chat-projects").textContent'), /\\u001b|␛/);
+ const projectText = await b.evaluate('document.querySelector(".chat-projects").textContent');
+ assert.doesNotMatch(projectText, /[\u202a-\u202e\u2066-\u2069]/u);
+ assert.match(projectText, /\\u202e/);
assert.equal(await b.evaluate(`!!document.querySelector('#sections a[href="#/chat"]')`), true);
await b.key('k', 'KeyK', 2);
await wait('document.querySelector("#cmdk-dialog").open');
@@ -74,11 +77,13 @@ test('chat browser: list/create named projects and threads, hostile labels inert
await click('#chat-project-form button[type=submit]');
await wait('document.querySelector("#chat-thread-form")');
assert.deepEqual(await b.evaluate('window.__writes[0].body'), { name: 'new project', repo: 'stack' });
+ assert.equal(await b.evaluate('window.__writes[0].path'), '/api/chat/projects');
await set('#chat-thread-name', 'named thread');
await b.evaluate(`window.__answers.push({body:{id:'${A}',instance:'assistant'}}),true`);
await click('#chat-thread-form button[type=submit]');
await wait('document.querySelector("#chat-send-form")');
assert.deepEqual(await b.evaluate('window.__writes[1].body'), { project: P, name: 'named thread' });
+ assert.equal(await b.evaluate('window.__writes[1].path'), '/api/chat/threads');
assert.equal(await b.evaluate('window.__writes.length'), 2);
});
test('chat browser: thread drafts, receipts and cursor history stay isolated; refresh never writes', async t => {
@@ -86,6 +91,9 @@ test('chat browser: thread drafts, receipts and cursor history stay isolated; re
await b.evaluate(`window.__pages.push({rows:[{id:'${M}',body:${JSON.stringify(HOSTILE)},from_role:'human',at:'2026-10-18T00:00:00Z'}],after:'${M}',hasMore:true}),true`);
await route(A);
assert.equal(await b.evaluate('!!document.querySelector(".chat-log img")'), false);
+ const messageText = await b.evaluate('document.querySelector(".chat-log").textContent');
+ assert.doesNotMatch(messageText, /[\u202a-\u202e\u2066-\u2069]/u);
+ assert.match(messageText, /\\u202e/);
await set('#chat-body', 'alpha draft');
await b.evaluate('(()=>{const e=document.querySelector("#chat-body");e.focus();e.setSelectionRange(2,5);return true})()');
await refresh();
@@ -100,9 +108,11 @@ test('chat browser: thread drafts, receipts and cursor history stay isolated; re
assert.doesNotMatch(await b.evaluate('document.querySelector("#chat-receipt").textContent'), /Accepted/);
assert.deepEqual(await b.evaluate('window.__writes.map(x=>x.body)'), [{body:'alpha draft'}]);
assert.equal(await b.evaluate('window.__writes[0].redirect'), 'manual');
+ assert.equal(await b.evaluate('window.__writes[0].path'), `/api/chat/threads/${A}/messages`);
await b.evaluate('window.__answers.push({body:{opened:location.hash.split("/").at(-1)}}),true');
await click('#chat-resume'); await wait('document.querySelector("#chat-receipt")?.textContent.includes("requested")');
assert.equal(await b.evaluate('window.__writes.length'), 2);
+ assert.equal(await b.evaluate('window.__writes[1].path'), `/api/chat/threads/${B}/open`);
});
test('chat browser: uncertain writes retain draft, ownership refusal clears it, read-only cannot send', async t => {
const { b, wait, set, click, route, refresh } = await setup(t);
diff --git a/mnt/storage/src/mosaic-stack-rocko-129-r1/packages/webui/tests/chat.test.mjs b/mnt/storage/src/mosaic-stack-rocko-129-r2/packages/webui/tests/chat.test.mjs
index 699ad9c51..00ee8326a 100644
--- a/mnt/storage/src/mosaic-stack-rocko-129-r1/packages/webui/tests/chat.test.mjs
+++ b/mnt/storage/src/mosaic-stack-rocko-129-r2/packages/webui/tests/chat.test.mjs
@@ -118,6 +118,31 @@ for (const [path, value, verb, args, answer] of posts) {
});
}
+test('chat createThread receipt requires an instance even when its id is valid', async t => {
+ const c = await setup(t);
+ for (const answer of [{ id: T }, { id: T, instance: 'invalid/instance' }, { id: T, instance: 3 }]) {
+ c.state.answer = answer;
+ const before = c.calls.length;
+ const result = await c.get('/api/chat/threads', { method: 'POST', value: { project: P, name: 'discussion' } });
+ assert.equal(result.status, 503);
+ assert.equal(result.data.error, 'outcome-unknown');
+ assert.equal(result.data.sent, 'unknown');
+ assert.equal(c.calls.length, before + 1);
+ }
+});
+
+test('chat write receipts expose only the fields belonging to their operation', async t => {
+ for (const [path, value, , , answer] of posts) {
+ const c = await setup(t);
+ c.state.answer = { owner: 'private owner', capability: 'not-a-capability', body: 'private body', ...answer };
+ const result = await c.get(path, { method: 'POST', value });
+ assert.equal(result.status, 200);
+ const { at, ...receipt } = result.data;
+ assert.equal(typeof at, 'string');
+ assert.deepEqual(receipt, answer);
+ }
+});
+
test('chat validates names, body, project and strict empty open payload', async t => {
const c = await setup(t);
for (const [path, value] of [