Files
memind/mindspace-asset-agent.test.mjs
john 32fb2cdeaf feat: chat uploads, vision turn isolation, and MindSpace agent improvements
Add chat file/image upload UX, attachment proxying, vision thumbnails, and per-turn image scoping so agents only use the current upload. Extend MindSpace asset context, billing token state, OA/scenario verify scripts, and related runtime config.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-11 00:23:01 +08:00

157 lines
5.0 KiB
JavaScript

import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import test from 'node:test';
import {
buildSelectedAssetDeleteInstructions,
buildSelectedAssetReadInstructions,
createAssetAgentService,
} from './mindspace-asset-agent.mjs';
test('buildSelectedAssetReadInstructions uses agent download proxy', () => {
const lines = buildSelectedAssetReadInstructions({
selectedAssets: [
{
id: 'asset-9',
displayName: 'sales.xlsx',
mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
},
],
h5ApiBase: 'http://127.0.0.1:8081',
agentSessionId: 'session-1',
});
const text = lines.join('\n');
assert.match(text, /mindspace_asset_download/);
assert.match(text, /禁止.*assets\/:id\/download/);
assert.match(text, /asset-9/);
assert.match(text, /session-1/);
assert.match(text, /数据表汇总校验/);
assert.match(text, /SUM/);
});
test('buildSelectedAssetReadInstructions skips download probe when workspace path exists', () => {
const lines = buildSelectedAssetReadInstructions({
selectedAssets: [
{
id: 'asset-9',
displayName: 'sales.xlsx',
mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
workspaceRelativePath: 'oa/sales.xlsx',
},
],
h5ApiBase: 'http://127.0.0.1:8081',
agentSessionId: 'session-1',
});
const text = lines.join('\n');
assert.match(text, /已落盘路径/);
assert.match(text, /read_file oa\/sales\.xlsx/);
assert.match(text, /禁止.*list_dir/);
assert.match(text, /禁止.*重复调用 mindspace_asset_download/);
});
test('buildSelectedAssetDeleteInstructions includes agent delete curl', () => {
const lines = buildSelectedAssetDeleteInstructions({
selectedAssets: [
{
id: 'asset-9',
displayName: 'sales.xlsx',
mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
},
],
h5ApiBase: 'http://127.0.0.1:8081',
agentSessionId: 'session-1',
});
const text = lines.join('\n');
assert.match(text, /完整读写删改权限/);
assert.match(text, /mindspace_asset_delete/);
assert.match(text, /asset-9/);
assert.match(text, /session-1/);
assert.match(text, /不要让用户自己去界面点删除/);
});
test('applyAgentDelete requires confirmation and valid session', async () => {
const service = createAssetAgentService({
assetService: {
deleteAsset: async () => ({ deleted: true }),
},
resolveUserIdForAgentSession: async (sessionId) =>
sessionId === 'session-1' ? 'user-1' : null,
resolveWorkspaceRoot: async () => '/tmp/workspace',
});
await assert.rejects(
() => service.applyAgentDelete({ session_id: 'session-1', asset_id: 'asset-9' }),
(error) => error.code === 'confirmation_required',
);
const result = await service.applyAgentDelete({
session_id: 'session-1',
asset_id: 'asset-9',
confirmed: true,
});
assert.equal(result.deleted, true);
assert.equal(result.userId, 'user-1');
});
test('applyAgentDownload materializes asset into workspace oa/', async () => {
const workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'asset-agent-'));
const sourcePath = path.join(workspaceRoot, 'source.xlsx');
await fs.writeFile(sourcePath, 'excel-bytes');
const service = createAssetAgentService({
assetService: {
readAsset: async () => ({
asset: {
id: 'asset-9',
categoryCode: 'oa',
filename: 'sales.xlsx',
displayName: 'sales.xlsx',
mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
},
path: sourcePath,
}),
},
resolveUserIdForAgentSession: async () => 'user-1',
resolveWorkspaceRoot: async () => workspaceRoot,
});
const result = await service.applyAgentDownload({
session_id: 'session-1',
asset_id: 'asset-9',
});
assert.equal(result.relativePath, 'oa/sales.xlsx');
const copied = await fs.readFile(path.join(workspaceRoot, 'oa/sales.xlsx'), 'utf8');
assert.equal(copied, 'excel-bytes');
});
test('materializeAssetsForSession accepts explicit userId', async () => {
const workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'asset-agent-'));
const sourcePath = path.join(workspaceRoot, 'source.csv');
await fs.writeFile(sourcePath, 'a,b\n1,2');
const service = createAssetAgentService({
assetService: {
readAsset: async (_userId, assetId) => ({
asset: {
id: assetId,
categoryCode: 'oa',
filename: 'data.csv',
displayName: 'data.csv',
mimeType: 'text/csv',
},
path: sourcePath,
}),
},
resolveUserIdForAgentSession: async () => null,
resolveWorkspaceRoot: async () => workspaceRoot,
});
const result = await service.materializeAssetsForSession({
userId: 'user-1',
assetIds: ['asset-a'],
});
assert.equal(result.materialized.length, 1);
assert.equal(result.materialized[0].relativePath, 'oa/data.csv');
});