完成上传下载连接,公告管理与详情页面,求种区页面,轮播图折扣显示,修改部分bug

Change-Id: I86fc294e32911cb3426a8b16f90aca371f975c11
diff --git a/src/api/personal.test.js b/src/api/personal.test.js
new file mode 100644
index 0000000..3a929e8
--- /dev/null
+++ b/src/api/personal.test.js
@@ -0,0 +1,354 @@
+// src/api/__tests__/personal.test.js
+import { 
+  getUserInfo, 
+  formatFileSize, 
+  getDownloadQuota, 
+  getDownloadProgress, 
+  getUserTorrents, 
+  deleteTorrent, 
+  generateInviteCode, 
+  getUserInviteCodes, 
+  exchangeUpload, 
+  updatePassword 
+} from './personal';
+import { api } from './auth';
+
+// 模拟整个 auth 模块
+jest.mock('./auth');
+
+describe('Personal API', () => {
+  beforeEach(() => {
+    // 在每个测试前重置模拟
+    jest.clearAllMocks();
+  });
+
+  describe('getUserInfo', () => {
+    it('should return formatted user info when API call succeeds', async () => {
+      const mockUserData = {
+        username: 'testuser',
+        level: 3,
+        registTime: '2023-01-01T00:00:00Z',
+        magicPoints: 100,
+        upload: 1024,
+        download: 512,
+        shareRate: 2.0
+      };
+
+      api.get.mockResolvedValue({
+        data: {
+          code: 200,
+          data: mockUserData,
+          message: 'success'
+        }
+      });
+
+      const result = await getUserInfo();
+      
+      expect(api.get).toHaveBeenCalledWith('/user/userInfo');
+      expect(result).toEqual({
+        username: 'testuser',
+        level: 3,
+        registTime: '2023-01-01',
+        magicPoints: 100,
+        upload: 1024,
+        download: 512,
+        shareRate: '2.00'
+      });
+    });
+
+    it('should throw error when API call fails', async () => {
+      api.get.mockResolvedValue({
+        data: {
+          code: 500,
+          message: 'Internal server error'
+        }
+      });
+
+      await expect(getUserInfo()).rejects.toThrow('Internal server error');
+    });
+  });
+
+  describe('formatFileSize', () => {
+    it('should format bytes correctly', () => {
+      expect(formatFileSize(500)).toBe('500 B');
+      expect(formatFileSize(1024)).toBe('1.00 KB');
+      expect(formatFileSize(2048)).toBe('2.00 KB');
+      expect(formatFileSize(1024 * 1024)).toBe('1.00 MB');
+      expect(formatFileSize(1024 * 1024 * 2.5)).toBe('2.50 MB');
+      expect(formatFileSize(1024 * 1024 * 1024)).toBe('1.00 GB');
+      expect(formatFileSize(1024 * 1024 * 1024 * 3.7)).toBe('3.70 GB');
+    });
+  });
+
+  describe('getDownloadQuota', () => {
+    it('should return download quota data when API call succeeds', async () => {
+      const mockData = {
+        total: 1073741824, // 1GB in bytes
+        used: 536870912,   // 512MB
+        remaining: 536870912
+      };
+
+      api.get.mockResolvedValue({
+        data: {
+          code: 200,
+          data: mockData,
+          message: 'success'
+        }
+      });
+
+      const result = await getDownloadQuota();
+      
+      expect(api.get).toHaveBeenCalledWith('/user/allowDownload');
+      expect(result).toEqual(mockData);
+    });
+
+    it('should throw error when API call fails', async () => {
+      api.get.mockResolvedValue({
+        data: {
+          code: 403,
+          message: 'Forbidden'
+        }
+      });
+
+      await expect(getDownloadQuota()).rejects.toThrow('Forbidden');
+    });
+  });
+
+  describe('getDownloadProgress', () => {
+    it('should return download progress when API call succeeds', async () => {
+      const mockProgresses = [
+        { id: 1, name: 'file1', progress: 50 },
+        { id: 2, name: 'file2', progress: 75 }
+      ];
+
+      api.get.mockResolvedValue({
+        data: {
+          code: 200,
+          data: { progresses: mockProgresses },
+          message: 'success'
+        }
+      });
+
+      const result = await getDownloadProgress();
+      
+      expect(api.get).toHaveBeenCalledWith('/torrent/getProgress');
+      expect(result).toEqual(mockProgresses);
+    });
+
+    it('should throw error when API call fails', async () => {
+      api.get.mockResolvedValue({
+        data: {
+          code: 404,
+          message: 'Not found'
+        }
+      });
+
+      await expect(getDownloadProgress()).rejects.toThrow('Not found');
+    });
+  });
+
+  describe('getUserTorrents', () => {
+    it('should return user torrents with pagination when API call succeeds', async () => {
+      const mockResponse = {
+        records: [
+          { 
+            torrent: { id: 1, name: 'torrent1', size: 1024 },
+            downloadCount: 10,
+            formattedSize: '1 KB'
+          }
+        ],
+        total: 1
+      };
+
+      api.get.mockResolvedValue({
+        data: {
+          code: 200,
+          data: mockResponse,
+          message: 'success'
+        }
+      });
+
+      const result = await getUserTorrents(1, 5);
+      
+      expect(api.get).toHaveBeenCalledWith('/torrent/get/torrentMyself', {
+        params: { page: 1, size: 5 }
+      });
+      expect(result).toEqual({
+        records: [
+          {
+            id: 1,
+            name: 'torrent1',
+            size: 1024,
+            downloadCount: 10,
+            formattedSize: '1 KB'
+          }
+        ],
+        total: 1
+      });
+    });
+
+    it('should throw error when API call fails', async () => {
+      api.get.mockResolvedValue({
+        data: {
+          code: 500,
+          message: 'Server error'
+        }
+      });
+
+      await expect(getUserTorrents()).rejects.toThrow('Server error');
+    });
+  });
+
+  describe('deleteTorrent', () => {
+    it('should successfully delete torrent when API call succeeds', async () => {
+      const mockResponse = {
+        code: 200,
+        message: 'Deleted successfully'
+      };
+
+      api.delete.mockResolvedValue({
+        data: mockResponse
+      });
+
+      const result = await deleteTorrent(123);
+      
+      expect(api.delete).toHaveBeenCalledWith('/torrent/deleteTorrent/123');
+      expect(result).toEqual(mockResponse);
+    });
+
+    it('should throw error when API call fails', async () => {
+      api.delete.mockResolvedValue({
+        data: {
+          code: 404,
+          message: 'Torrent not found'
+        }
+      });
+
+      await expect(deleteTorrent(123)).rejects.toThrow('Torrent not found');
+    });
+  });
+
+  describe('generateInviteCode', () => {
+    it('should return generated invite code when API call succeeds', async () => {
+      const mockCode = 'ABCD-EFGH-IJKL';
+
+      api.post.mockResolvedValue({
+        data: {
+          code: 200,
+          data: { inviteCode: mockCode },
+          message: 'success'
+        }
+      });
+
+      const result = await generateInviteCode();
+      
+      expect(api.post).toHaveBeenCalledWith('/invitecode/generate');
+      expect(result).toBe(mockCode);
+    });
+
+    it('should throw error when API call fails', async () => {
+      api.post.mockResolvedValue({
+        data: {
+          code: 403,
+          message: 'Permission denied'
+        }
+      });
+
+      await expect(generateInviteCode()).rejects.toThrow('Permission denied');
+    });
+  });
+
+  describe('getUserInviteCodes', () => {
+    it('should return user invite codes when API call succeeds', async () => {
+      const mockCodes = ['CODE1', 'CODE2'];
+
+      api.get.mockResolvedValue({
+        data: {
+          code: 200,
+          data: { inviteCode: mockCodes },
+          message: 'success'
+        }
+      });
+
+      const result = await getUserInviteCodes();
+      
+      expect(api.get).toHaveBeenCalledWith('/invitecode/userInviteCode');
+      expect(result).toEqual(mockCodes);
+    });
+
+    it('should throw error when API call fails', async () => {
+      api.get.mockResolvedValue({
+        data: {
+          code: 500,
+          message: 'Server error'
+        }
+      });
+
+      await expect(getUserInviteCodes()).rejects.toThrow('Server error');
+    });
+  });
+
+  describe('exchangeUpload', () => {
+    it('should successfully exchange magic points for upload when API call succeeds', async () => {
+      const mockResponse = {
+        code: 200,
+        message: 'Exchange successful'
+      };
+
+      api.post.mockResolvedValue({
+        data: mockResponse
+      });
+
+      const result = await exchangeUpload(100);
+      
+      expect(api.post).toHaveBeenCalledWith('/user/exchangeUpload', {
+        magicPoint: 100
+      });
+      expect(result).toEqual(mockResponse);
+    });
+
+    it('should throw error when API call fails', async () => {
+      api.post.mockResolvedValue({
+        data: {
+          code: 400,
+          message: 'Not enough points'
+        }
+      });
+
+      await expect(exchangeUpload(100)).rejects.toThrow('Not enough points');
+    });
+  });
+
+  describe('updatePassword', () => {
+    it('should successfully update password when API call succeeds', async () => {
+      const mockResponse = {
+        code: 200,
+        message: 'Password updated'
+      };
+
+      api.put.mockResolvedValue({
+        data: mockResponse
+      });
+
+      const result = await updatePassword('oldPass', 'newPass');
+      
+      expect(api.put).toHaveBeenCalledWith('/user/password', {
+        oldPassword: 'oldPass',
+        newPassword: 'newPass'
+      });
+      expect(result).toEqual(mockResponse);
+    });
+
+    it('should throw error when API call fails', async () => {
+      api.put.mockResolvedValue({
+        data: {
+          code: 401,
+          message: 'Invalid old password'
+        }
+      });
+
+      await expect(updatePassword('wrongPass', 'newPass'))
+        .rejects.toThrow('Invalid old password');
+    });
+  });
+});
\ No newline at end of file