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