import assert from 'node:assert/strict'; import test from 'node:test'; import { createDownloadConversationPackageArtifactHandler, createDownloadConversationPackageManifestHandler, createGetConversationPackageHandler, } from './mindspace-conversation-package-routes.mjs'; function createResponseRecorder() { return { statusCode: 200, headersSent: false, body: null, status(code) { this.statusCode = code; return this; }, json(payload) { this.body = payload; this.headersSent = true; return this; }, headers: {}, set(key, value) { this.headers[key.toLowerCase()] = value; return this; }, setHeader(key, value) { this.headers[key.toLowerCase()] = value; return this; }, send(payload) { this.body = payload; this.headersSent = true; return this; }, }; } test('GET conversation package returns manifest for owned sessions', async () => { const hookCalls = []; const handler = createGetConversationPackageHandler({ registry: { async readManifestForSession(input) { assert.deepEqual(input, { userId: 'user-1', sessionId: 'session-1' }); return { packageId: 'cp_session-1', artifacts: [{ artifactId: 'ca_1', kind: 'public_html' }] }; }, }, userAuth: { async ownsSession(userId, sessionId) { assert.equal(userId, 'user-1'); assert.equal(sessionId, 'session-1'); return true; }, }, beforeReadManifest(input) { hookCalls.push({ userId: input.userId, sessionId: input.sessionId, req: input.req, }); }, }); const req = { currentUser: { id: 'user-1' }, params: { sessionId: 'session-1' } }; const res = createResponseRecorder(); await handler(req, res); assert.equal(res.statusCode, 200); assert.deepEqual(res.body, { data: { packageId: 'cp_session-1', artifacts: [{ artifactId: 'ca_1', kind: 'public_html' }] }, }); assert.deepEqual(hookCalls, [{ userId: 'user-1', sessionId: 'session-1', req }]); }); test('GET conversation package blocks sessions owned by another user', async () => { const handler = createGetConversationPackageHandler({ registry: { async readManifestForSession() { assert.fail('registry should not be called for unauthorized sessions'); }, }, userAuth: { async ownsSession() { return false; }, }, beforeReadManifest() { assert.fail('beforeReadManifest should not run for unauthorized sessions'); }, }); const res = createResponseRecorder(); await handler({ currentUser: { id: 'user-1' }, params: { sessionId: 'session-2' } }, res); assert.equal(res.statusCode, 403); assert.deepEqual(res.body, { message: '无权访问该会话' }); }); test('GET conversation package returns 404 when package has not been recorded', async () => { const handler = createGetConversationPackageHandler({ registry: { async readManifestForSession() { return null; }, }, userAuth: { async ownsSession() { return true; }, }, }); const res = createResponseRecorder(); await handler({ currentUser: { id: 'user-1' }, params: { sessionId: 'session-empty' } }, res); assert.equal(res.statusCode, 404); assert.deepEqual(res.body, { message: '对话包不存在' }); }); test('GET conversation package respects MindSpace feature gates', async () => { const handler = createGetConversationPackageHandler({ registry: {}, userAuth: { async ownsSession() { assert.fail('user ownership should not be checked when feature gate rejects'); }, }, ensureMindSpaceEnabled(res) { res.status(403).json({ message: 'MindSpace 已关闭' }); return false; }, }); const res = createResponseRecorder(); await handler({ currentUser: { id: 'user-1' }, params: { sessionId: 'session-1' } }, res); assert.equal(res.statusCode, 403); assert.deepEqual(res.body, { message: 'MindSpace 已关闭' }); }); test('GET conversation package manifest downloads owned package metadata', async () => { const handler = createDownloadConversationPackageManifestHandler({ registry: { async readManifestForSession(input) { assert.deepEqual(input, { userId: 'user-1', sessionId: 'session-1' }); return { packageId: 'cp_session-1', sessionId: 'session-1', displayFolderName: '旅行计划', artifacts: [], }; }, }, userAuth: { async ownsSession(userId, sessionId) { assert.equal(userId, 'user-1'); assert.equal(sessionId, 'session-1'); return true; }, }, }); const res = createResponseRecorder(); await handler( { currentUser: { id: 'user-1' }, params: { sessionId: 'session-1' }, requestId: 'req-1' }, res, ); assert.equal(res.statusCode, 200); assert.equal(res.headers['content-type'], 'application/json; charset=utf-8'); assert.match( res.headers['content-disposition'], /%E6%97%85%E8%A1%8C%E8%AE%A1%E5%88%92-manifest\.json/, ); assert.equal(res.headers['cache-control'], 'private, no-store'); assert.equal(res.headers['x-request-id'], 'req-1'); assert.deepEqual(JSON.parse(res.body), { packageId: 'cp_session-1', sessionId: 'session-1', displayFolderName: '旅行计划', artifacts: [], }); }); test('GET conversation package manifest blocks unauthorized sessions', async () => { const handler = createDownloadConversationPackageManifestHandler({ registry: { async readManifestForSession() { assert.fail('registry should not be called for unauthorized manifest downloads'); }, }, userAuth: { async ownsSession() { return false; }, }, }); const res = createResponseRecorder(); await handler({ currentUser: { id: 'user-1' }, params: { sessionId: 'session-2' } }, res); assert.equal(res.statusCode, 403); assert.deepEqual(res.body, { message: '无权访问该会话' }); }); test('GET conversation package artifact downloads owned private objects', async () => { const handler = createDownloadConversationPackageArtifactHandler({ registry: { async readArtifactObject(input) { assert.deepEqual(input, { userId: 'user-1', sessionId: 'session-1', artifactId: 'ca_docx' }); return { artifact: { id: 'ca_docx', displayName: '研究报告.docx', mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', }, body: Buffer.from('docx-body'), }; }, }, userAuth: { async ownsSession() { return true; }, }, }); const res = createResponseRecorder(); await handler( { currentUser: { id: 'user-1' }, params: { sessionId: 'session-1', artifactId: 'ca_docx' }, requestId: 'req-1', }, res, ); assert.equal(res.statusCode, 200); assert.equal(res.body.toString('utf8'), 'docx-body'); assert.equal( res.headers['content-type'], 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', ); assert.match(res.headers['content-disposition'], /filename\*=/); assert.equal(res.headers['x-request-id'], 'req-1'); }); test('GET conversation package artifact blocks unauthorized sessions before storage access', async () => { const handler = createDownloadConversationPackageArtifactHandler({ registry: { async readArtifactObject() { assert.fail('registry should not be called for unauthorized artifact downloads'); }, }, userAuth: { async ownsSession() { return false; }, }, }); const res = createResponseRecorder(); await handler({ currentUser: { id: 'user-1' }, params: { sessionId: 'session-2', artifactId: 'ca_docx' } }, res); assert.equal(res.statusCode, 403); assert.deepEqual(res.body, { message: '无权访问该会话' }); }); test('GET conversation package validates ownership through sessionAccess when provided', async () => { const validated = []; const handler = createGetConversationPackageHandler({ registry: { async readManifestForSession() { return { packageId: 'cp_session-broker', sessionId: 'session-broker', artifacts: [] }; }, }, userAuth: {}, sessionAccess: { async validateOwnership(userId, sessionId) { validated.push({ userId, sessionId }); return false; }, }, }); const res = createResponseRecorder(); await handler({ currentUser: { id: 'user-1' }, params: { sessionId: 'session-broker' } }, res); assert.deepEqual(validated, [{ userId: 'user-1', sessionId: 'session-broker' }]); assert.equal(res.statusCode, 403); });