import assert from 'node:assert/strict'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import test from 'node:test'; import { buildUserSpaceConstraints, ensureUserZoneDirs, mirrorAssetToZone, renderUserSpaceHints, resolveUserWorkspaceRoot, syncUserZonesFromAssets, UPLOAD_ZONE_CODES, } from './user-space.mjs'; const USER_ID = 'a6fb1e97-2b0f-447b-b138-4561d8e5c53e'; test('user workspace is MindSpace/ with zone subdirs', () => { const h5Root = fs.mkdtempSync(path.join(os.tmpdir(), 'h5-')); const workspace = resolveUserWorkspaceRoot(h5Root, { id: USER_ID, username: 'john' }); assert.ok(workspace.endsWith(`${path.sep}MindSpace${path.sep}${USER_ID}`)); ensureUserZoneDirs(workspace); for (const code of UPLOAD_ZONE_CODES) { assert.ok(fs.existsSync(path.join(workspace, code))); } }); test('mirrorAssetToZone copies upload into oa/', () => { const h5Root = fs.mkdtempSync(path.join(os.tmpdir(), 'h5-')); const workspace = resolveUserWorkspaceRoot(h5Root, { id: USER_ID, username: 'john' }); const source = path.join(h5Root, 'source.csv'); fs.writeFileSync(source, 'a,b\n1,2\n'); const dest = mirrorAssetToZone({ workspaceRoot: workspace, categoryCode: 'oa', filename: 'export.csv', sourcePath: source, }); assert.equal(dest, path.join(workspace, 'oa', 'export.csv')); assert.equal(fs.readFileSync(dest, 'utf8'), 'a,b\n1,2\n'); }); test('syncUserZonesFromAssets backfills from canonical storage', async () => { const h5Root = fs.mkdtempSync(path.join(os.tmpdir(), 'h5-')); const storageRoot = path.join(h5Root, 'data', 'mindspace'); const userId = USER_ID; const storageKey = path.posix.join('users', userId, 'assets', 'a1', 'versions', 'v1'); const sourcePath = path.join(storageRoot, storageKey); fs.mkdirSync(path.dirname(sourcePath), { recursive: true }); fs.writeFileSync(sourcePath, 'col\nval\n'); const workspace = resolveUserWorkspaceRoot(h5Root, { id: userId, username: 'john' }); const pool = { async query() { return [ [{ original_filename: '2025-12-06T13-34_export.csv', category_code: 'oa', storage_key: storageKey }], ]; }, }; const result = await syncUserZonesFromAssets(pool, storageRoot, userId, workspace); assert.equal(result.mirrored, 1); assert.ok( fs.existsSync(path.join(workspace, 'oa', '2025-12-06T13-34_export.csv')), ); }); test('user space publishing guidance points to sandbox file tools', () => { const workspaceRoot = `/var/h5/MindSpace/${USER_ID}`; const hints = renderUserSpaceHints({ username: 'john', displayName: 'John', slug: USER_ID, workspaceRoot, }); const constraints = buildUserSpaceConstraints({ username: 'john', displayName: 'John', slug: USER_ID, workspaceRoot, publicBaseUrl: 'https://m.tkmind.cn', }); assert.match(hints, /write_file/); assert.match(hints, /edit_file/); assert.doesNotMatch(hints, /再使用 \\`write\\` 创建/); assert.match(constraints, /write_file/); assert.match(constraints, /edit_file/); assert.doesNotMatch(constraints, /再用 `write` 写入/); });