import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; import { describe, expect, it } from 'vitest'; import { canMutateUserFilesPath } from './user-file-paths'; const root = resolve(__dirname, '..'); describe('user file system frontend wiring', () => { it('only enables mutating file actions inside concrete user-file roots', () => { expect(canMutateUserFilesPath('')).toBe(false); expect(canMutateUserFilesPath('/')).toBe(false); expect(canMutateUserFilesPath('qa-folder')).toBe(false); expect(canMutateUserFilesPath('uploads')).toBe(true); expect(canMutateUserFilesPath('uploads/qa-folder')).toBe(true); expect(canMutateUserFilesPath('outputs/report.md')).toBe(true); expect(canMutateUserFilesPath('shared')).toBe(true); expect(canMutateUserFilesPath('tasks/task-1')).toBe(true); }); it('routes API client helpers to user file endpoints', () => { const apiSource = readFileSync(resolve(root, 'lib/api.ts'), 'utf8'); expect(apiSource).toContain('/api/user-files/browse'); expect(apiSource).toContain('/api/user-files/upload'); expect(apiSource).toContain('/api/user-files/download'); expect(apiSource).toContain('/api/user-files/preview'); expect(apiSource).toContain('/api/user-files/delete'); expect(apiSource).toContain('/api/user-files/mkdir'); }); it('notifies the app shell when API auth is cleared', () => { const apiSource = readFileSync(resolve(root, 'lib/api.ts'), 'utf8'); expect(apiSource).toContain('AUTH_CLEARED_EVENT'); expect(apiSource).toContain("window.dispatchEvent(new CustomEvent(AUTH_CLEARED_EVENT))"); }); it('does not wire the Files page to workspace or MinIO management APIs', () => { const pageSource = readFileSync(resolve(root, 'app/(app)/files/page.tsx'), 'utf8'); expect(pageSource).toContain('browseUserFiles'); expect(pageSource).toContain('uploadUserFile'); expect(pageSource).not.toContain('browseWorkspace'); expect(pageSource).not.toContain('uploadToWorkspace'); expect(pageSource).not.toContain('MinIO'); expect(pageSource).not.toContain('bucket'); expect(pageSource).not.toContain('accessKey'); expect(pageSource).not.toContain('secretKey'); }); it('does not retry user-file loads after an auth failure', () => { const pageSource = readFileSync(resolve(root, 'app/(app)/files/page.tsx'), 'utf8'); expect(pageSource).toContain('isAuthError'); expect(pageSource).toContain('if (isAuthError(err))'); }); it('shows backend upload error details instead of raw JSON payloads', () => { const apiSource = readFileSync(resolve(root, 'lib/api.ts'), 'utf8'); expect(apiSource).toContain('function parseErrorDetail'); expect(apiSource).toContain('throw new Error(`接口错误 ${res.status}: ${parseErrorDetail(text)}`)'); }); });