/** * read_inbox 必须把列表收窄到**自己那条会话**(2026-09-14 用户报的缺陷)。 * * 「不同 session 的 agent 都可以看到全部邮件」—— 列表按 Agent 列,还按契约把列出的 * 都标已读 ⇒ A 会话的 worker 会标掉 B 会话的未读,而 B 之后按 `?status=unread` * 补投时就再也看不到那封信(静默丢信)。现场实例:另一条会话的来信被我在读自己 * 收件箱的时刻标成已读(mail_reads 里那行的 reader=pi、时间就是那次读取)。 * * 这里只验**接线**(工具把 session_id 传出去了、worker 把邮件会话 id 递进来了); * 服务端语义(读 A 不动 B)由 server/internal/repo/session_scope_test.go 负责。 */ import { test } from 'node:test'; import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; const HERE = dirname(fileURLToPath(import.meta.url)); const tools = readFileSync(join(HERE, '..', 'src', 'tools.mjs'), 'utf8'); const worker = readFileSync(join(HERE, '..', 'src', 'worker.mjs'), 'utf8'); test('read_inbox 的 URL 带上会话收窄', () => { assert.match(tools, /mail\/inbox\?status=[^`]*\$\{inboxScope\(\)\}/, 'URL 必须拼上 inboxScope()'); assert.match(tools, /session_id=\$\{encodeURIComponent\(sid\)\}/, 'inboxScope 要真的带上 session_id'); }); test('worker 把邮件会话 id 递给工具', () => { assert.match(worker, /getMailSessionId: \(\) => mailContext\.sessionID/, '要传闭包而不是快照'); assert.match(worker, /sessionID: msg\.data\?\.session_id/, '会话 id 来自 SSE 事件'); }); test('★ 判据自检:拿没带会话收窄的旧写法喂进来必须判红', () => { const old = '`/mail/inbox?status=${encodeURIComponent(status)}&limit=${params.limit || DEFAULT_INBOX_LIMIT}`'; assert.equal(/mail\/inbox\?status=[^`]*\$\{inboxScope\(\)\}/.test(old), false); });