/** * read_inbox 必须收窄到**自己那条会话**(用户:「你还是没修好不同 session agent * 收件箱隔离的问题」)。 * * 缺陷:列表按 Agent 列,且 read_inbox 按契约把列出的都标已读 ⇒ A 会话的回合把 * B 会话的未读标掉 ⇒ B 之后按 `?status=unread` 补投时再也看不到那封信(静默丢信)。 * * 这里只验**接线**(工具把会话 id 传出去了、会话来源是平台上下文而不是模块级变量); * 服务端语义由 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 src = readFileSync(join(HERE, '..', 'src', 'index.ts'), 'utf8'); test('read_inbox 的 URL 会拼上会话收窄', () => { assert.match(src, /mail\/inbox\?status=\$\{status\}&limit=\$\{args\.limit \|\| DEFAULT_INBOX_LIMIT\}\$\{scope\}/); assert.match(src, /session_id=\$\{encodeURIComponent\(mailSessionID\)\}/); }); test('★ 会话来源是平台上下文(并发安全),不是模块级变量', () => { // 工具签名必须接住第二个参数 assert.match(src, /async execute\(args: any, exec\?: any\): Promise/); assert.match(src, /exec\?\.agent\?\.id/, '要从 exec.agent.id 取 DSH 会话'); assert.match(src, /reverseMap\.get\(dshSessionId\)/, '再经 reverseMap 换成邮件会话'); // 反向对照:不能引入"当前会话"这种模块级可变状态来做这件事 const bad = /let\s+currentMailSessionID|let\s+activeMailSession/; assert.ok(!bad.test(src), '不得用模块级"当前会话"变量(同进程多会话会互相覆盖)'); }); test('取不到会话时退回整体收件箱(不猜)', () => { assert.match(src, /if \(!dshSessionId\) return ''/); assert.match(src, /const scope = mailSessionID \? `&session_id=[^`]*` : ''/); }); test('★ 判据自检:不带收窄的旧写法必须判红', () => { const old = '`/mail/inbox?status=${status}&limit=${args.limit || DEFAULT_INBOX_LIMIT}`'; assert.ok(!/\$\{scope\}/.test(old), '旧写法没有 scope ⇒ 会判红'); assert.ok(!/async execute\(args: any, exec\?: any\)/.test('async execute(args: any): Promise {')); });