Files
memind/mindspace-conversation-package-routes.test.mjs
T
john 08feae8bef feat(h5-session): Session Broker、run SSE replay 与 Finish 竞态修复
落地 H5 Session 架构 Patch 1–5(Broker 收口、Router decision、SSE taxonomy、goosed 边界检查),
并新增可选 MEMIND_RUN_STREAM_REPLAY run 事件回放与 H5 假交付 guard;修复 Finish 先于 agent-run
gate 导致 UI 永久 loading 的竞态,接入 verify:h5-session-patches 回归脚本。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-06 14:19:48 +08:00

293 lines
8.6 KiB
JavaScript

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);
});