import assert from 'node:assert/strict'; import test from 'node:test'; import { attachPortalMindSpaceAgentOperationRoutes } from './portal-mindspace-agent-operation-routes.mjs'; function createRouterRecorder() { const routes = new Map(); return { routes, post(path, handler) { routes.set(`POST ${path}`, handler); }, }; } function createResponseRecorder() { return { statusCode: 200, body: undefined, status(code) { this.statusCode = code; return this; }, json(body) { this.body = body; return this; }, }; } function createRequest(overrides = {}) { return { body: {}, ip: '127.0.0.1', ...overrides, }; } function createDependencies(overrides = {}) { const calls = { data: [], errors: [], mapped: [], }; return { calls, dependencies: { sendData(res, req, data, status = 200) { calls.data.push({ res, req, data, status }); return res.status(status).json({ data }); }, sendError(res, req, status, code, message) { calls.errors.push({ res, req, status, code, message }); return res.status(status).json({ error: { code, message } }); }, handleMindSpaceError(res, req, error) { calls.mapped.push({ res, req, error }); return res.status(418).json({ mapped: error.message }); }, ...overrides, }, }; } test('MindSpace agent operation module preserves route inventory and order', () => { const api = createRouterRecorder(); attachPortalMindSpaceAgentOperationRoutes(api); assert.deepEqual([...api.routes.keys()], [ 'POST /agent/mindspace_page_patch', 'POST /agent/mindspace_asset_delete', 'POST /agent/mindspace_asset_download', ]); }); test('page patch preserves payload projection and unavailable response', async () => { const api = createRouterRecorder(); const dependencies = createDependencies({ getMindSpacePageLiveEdit: () => ({ async applyAgentPatch(body) { assert.deepEqual(body, { page_id: 'page-1', patch: 'title' }); return { page: { id: 'page-1', title: 'Updated', summary: 'Summary', versionNo: 3, updatedAt: '2026-07-23T00:00:00Z', }, liveRevision: 7, internal: 'hidden', }; }, }), }); attachPortalMindSpaceAgentOperationRoutes(api, dependencies.dependencies); const res = createResponseRecorder(); await api.routes.get('POST /agent/mindspace_page_patch')( createRequest({ body: { page_id: 'page-1', patch: 'title' } }), res, ); assert.deepEqual(res.body, { data: { pageId: 'page-1', title: 'Updated', summary: 'Summary', versionNo: 3, updatedAt: '2026-07-23T00:00:00Z', liveRevision: 7, }, }); const unavailableApi = createRouterRecorder(); attachPortalMindSpaceAgentOperationRoutes(unavailableApi); const unavailableRes = createResponseRecorder(); await unavailableApi.routes.get('POST /agent/mindspace_page_patch')( createRequest(), unavailableRes, ); assert.equal(unavailableRes.statusCode, 503); assert.deepEqual(unavailableRes.body, { message: 'MindSpace 未启用' }); }); test('page patch preserves expected and fallback error mapping', async () => { for (const [code, expectedStatus] of [ ['forbidden', 403], ['page_binding_mismatch', 403], ['invalid_request', 400], ]) { const api = createRouterRecorder(); const failure = Object.assign(new Error(code), { code }); const dependencies = createDependencies({ getMindSpacePageLiveEdit: () => ({ async applyAgentPatch() { throw failure; }, }), }); attachPortalMindSpaceAgentOperationRoutes(api, dependencies.dependencies); await api.routes.get('POST /agent/mindspace_page_patch')( createRequest(), createResponseRecorder(), ); assert.equal(dependencies.calls.errors[0].status, expectedStatus); assert.equal(dependencies.calls.errors[0].code, code); } const api = createRouterRecorder(); const failure = new Error('patch failed'); const dependencies = createDependencies({ getMindSpacePageLiveEdit: () => ({ async applyAgentPatch() { throw failure; }, }), }); attachPortalMindSpaceAgentOperationRoutes(api, dependencies.dependencies); await api.routes.get('POST /agent/mindspace_page_patch')( createRequest(), createResponseRecorder(), ); assert.equal(dependencies.calls.mapped[0].error, failure); }); test('asset delete preserves body, audit, response, and error mapping', async () => { const audit = []; const api = createRouterRecorder(); const dependencies = createDependencies({ getMindSpaceAssetAgent: () => ({ async applyAgentDelete(body) { assert.deepEqual(body, { asset_id: 'asset-1', confirm: true }); return { userId: 'user-1', assetId: 'asset-1', deleted: true }; }, }), getMindSpaceAudit: () => ({ async write(entry) { audit.push(entry); }, }), }); attachPortalMindSpaceAgentOperationRoutes(api, dependencies.dependencies); const req = createRequest({ body: { asset_id: 'asset-1', confirm: true }, ip: '10.0.0.8', }); const res = createResponseRecorder(); await api.routes.get('POST /agent/mindspace_asset_delete')(req, res); assert.deepEqual(audit, [{ userId: 'user-1', action: 'asset.delete', objectType: 'asset', objectId: 'asset-1', ip: '10.0.0.8', metadata: { via: 'agent' }, }]); assert.deepEqual(dependencies.calls.data[0].data, { userId: 'user-1', assetId: 'asset-1', deleted: true, }); for (const [code, expectedStatus] of [ ['forbidden', 403], ['invalid_request', 400], ['confirmation_required', 400], ]) { const failureApi = createRouterRecorder(); const failureDependencies = createDependencies({ getMindSpaceAssetAgent: () => ({ async applyAgentDelete() { throw Object.assign(new Error(code), { code }); }, }), }); attachPortalMindSpaceAgentOperationRoutes( failureApi, failureDependencies.dependencies, ); await failureApi.routes.get('POST /agent/mindspace_asset_delete')( createRequest(), createResponseRecorder(), ); assert.equal(failureDependencies.calls.errors[0].status, expectedStatus); } }); test('asset download preserves audit metadata and error mapping', async () => { const audit = []; const api = createRouterRecorder(); const dependencies = createDependencies({ getMindSpaceAssetAgent: () => ({ async applyAgentDownload(body) { assert.deepEqual(body, { asset_id: 'asset-2' }); return { userId: 'user-2', assetId: 'asset-2', relativePath: 'downloads/report.docx', }; }, }), getMindSpaceAudit: () => ({ async write(entry) { audit.push(entry); }, }), }); attachPortalMindSpaceAgentOperationRoutes(api, dependencies.dependencies); await api.routes.get('POST /agent/mindspace_asset_download')( createRequest({ body: { asset_id: 'asset-2' }, ip: '10.0.0.9', }), createResponseRecorder(), ); assert.deepEqual(audit[0], { userId: 'user-2', action: 'asset.download', objectType: 'asset', objectId: 'asset-2', ip: '10.0.0.9', metadata: { via: 'agent', relativePath: 'downloads/report.docx', }, }); for (const [code, expectedStatus] of [ ['forbidden', 403], ['invalid_request', 400], ['asset_not_found', 400], ]) { const failureApi = createRouterRecorder(); const failureDependencies = createDependencies({ getMindSpaceAssetAgent: () => ({ async applyAgentDownload() { throw Object.assign(new Error(code), { code }); }, }), }); attachPortalMindSpaceAgentOperationRoutes( failureApi, failureDependencies.dependencies, ); await failureApi.routes.get('POST /agent/mindspace_asset_download')( createRequest(), createResponseRecorder(), ); assert.equal(failureDependencies.calls.errors[0].status, expectedStatus); } });