import { describe, test, expect, vi, beforeEach } from 'vitest'; import { listDocuments, getDocument, saveDocument, deleteDocument, uploadDocument, search } from '../../src/api/client'; beforeEach(() => { vi.restoreAllMocks(); }); test('listDocuments calls GET /api/documents with the path query param and returns parsed JSON', async () => { const mockResponse = { folders: ['产品文档'], files: [{ key: '产品文档/架构设计.md', name: '架构设计.md', size: 512, modifiedAt: '2026-08-01T10:00:00Z' }], }; global.fetch = vi.fn().mockResolvedValue({ ok: true, json: () => Promise.resolve(mockResponse), }) as unknown as typeof fetch; const result = await listDocuments('产品文档'); expect(global.fetch).toHaveBeenCalledWith(`/api/documents?path=${encodeURIComponent('产品文档')}`); expect(result).toEqual(mockResponse); }); test('listDocuments throws when the response is not ok', async () => { global.fetch = vi.fn().mockResolvedValue({ ok: false, status: 500 }) as unknown as typeof fetch; await expect(listDocuments('')).rejects.toThrow('Failed to list documents: 500'); }); test('getDocument calls GET /api/documents/{key} preserving slashes in the key', async () => { const mockResponse = { key: '产品文档/架构设计.md', content: '# 架构设计', contentType: 'text/markdown' }; global.fetch = vi.fn().mockResolvedValue({ ok: true, json: () => Promise.resolve(mockResponse), }) as unknown as typeof fetch; const result = await getDocument('产品文档/架构设计.md'); expect(global.fetch).toHaveBeenCalledWith( `/api/documents/${encodeURIComponent('产品文档')}/${encodeURIComponent('架构设计.md')}` ); expect(result).toEqual(mockResponse); }); test('saveDocument calls PUT /api/documents/{key} with content and contentType', async () => { const mockResponse = { key: '产品文档/架构设计.md', updatedAt: '2026-08-01T10:05:00Z' }; global.fetch = vi.fn().mockResolvedValue({ ok: true, json: () => Promise.resolve(mockResponse), }) as unknown as typeof fetch; const result = await saveDocument('产品文档/架构设计.md', '# 新内容', 'text/markdown'); expect(global.fetch).toHaveBeenCalledWith( `/api/documents/${encodeURIComponent('产品文档')}/${encodeURIComponent('架构设计.md')}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ content: '# 新内容', contentType: 'text/markdown' }), } ); expect(result).toEqual(mockResponse); }); test('deleteDocument calls DELETE /api/documents/{key} and resolves with no value', async () => { global.fetch = vi.fn().mockResolvedValue({ ok: true, status: 204 }) as unknown as typeof fetch; await expect(deleteDocument('产品文档/架构设计.md')).resolves.toBeUndefined(); expect(global.fetch).toHaveBeenCalledWith( `/api/documents/${encodeURIComponent('产品文档')}/${encodeURIComponent('架构设计.md')}`, { method: 'DELETE' } ); }); test('uploadDocument posts a multipart form with file and path fields', async () => { const mockResponse = { key: '产品文档/新文档.md', name: '新文档.md', size: 42 }; global.fetch = vi.fn().mockResolvedValue({ ok: true, json: () => Promise.resolve(mockResponse), }) as unknown as typeof fetch; const file = new File(['# 新文档'], '新文档.md', { type: 'text/markdown' }); const result = await uploadDocument(file, '产品文档'); expect(global.fetch).toHaveBeenCalledTimes(1); const [url, init] = (global.fetch as ReturnType).mock.calls[0]; expect(url).toBe('/api/upload'); expect(init.method).toBe('POST'); const body = init.body as FormData; expect(body.get('file')).toBe(file); expect(body.get('path')).toBe('产品文档'); expect(result).toEqual(mockResponse); }); test('search calls GET /api/search with the q query param and returns parsed JSON', async () => { const mockResponse = { results: [ { key: '产品文档/架构设计.md', title: '架构设计.md', path: '产品文档', snippet: '...检索...' }, ], }; global.fetch = vi.fn().mockResolvedValue({ ok: true, json: () => Promise.resolve(mockResponse), }) as unknown as typeof fetch; const result = await search('检索'); expect(global.fetch).toHaveBeenCalledWith(`/api/search?q=${encodeURIComponent('检索')}`); expect(result).toEqual(mockResponse); });