上传种子以及接管求助帖

Change-Id: Icba99400a2dc4f706c9e184b16fc47cde5ebf12a
diff --git a/src/test/TorrentUpload.test.jsx b/src/test/TorrentUpload.test.jsx
new file mode 100644
index 0000000..482184e
--- /dev/null
+++ b/src/test/TorrentUpload.test.jsx
@@ -0,0 +1,489 @@
+import { describe, it, expect, vi, beforeEach } from 'vitest';
+import { render, screen, fireEvent, waitFor } from '@testing-library/react';
+import { MemoryRouter } from 'react-router-dom';
+import UploadTorrent from '../components/upload';
+import { Form, Select } from 'antd';
+
+// 模拟 axios
+vi.mock('axios');
+
+// 模拟 antd 的 message
+vi.mock('antd', async () => {
+  const actual = await vi.importActual('antd');
+  return {
+    ...actual,
+    message: {
+      success: vi.fn(),
+      error: vi.fn(),
+    },
+  };
+});
+
+describe('UploadTorrent 组件', () => {
+  const mockCategories = [
+    { categoryid: '1', category_name: '电影' },
+    { categoryid: '2', category_name: '剧集' },
+    { categoryid: '3', category_name: '音乐' },
+    { categoryid: '4', category_name: '动漫' },
+    { categoryid: '5', category_name: '游戏' },
+    { categoryid: '6', category_name: '综艺' },
+    { categoryid: '7', category_name: '体育' },
+    { categoryid: '8', category_name: '软件' },
+    { categoryid: '9', category_name: '学习资料' },
+    { categoryid: '10', category_name: '纪录片' },
+    { categoryid: '11', category_name: '音乐' },
+  ];
+
+  beforeEach(() => {
+    vi.clearAllMocks();
+    // 模拟 axios.get 返回分类数据
+    vi.mocked(axios.get).mockResolvedValueOnce({ data: mockCategories });
+  });
+
+  it('应该正确渲染而不崩溃', async () => {
+    render(
+      <MemoryRouter>
+        <UploadTorrent />
+      </MemoryRouter>
+    );
+
+    // 等待分类数据加载
+    await waitFor(() => {
+      expect(screen.getByText('上传种子')).toBeInTheDocument();
+    });
+
+    // 验证一些基本元素是否存在
+    expect(screen.getByText('封面图片')).toBeInTheDocument();
+    expect(screen.getByText('种子文件')).toBeInTheDocument();
+    expect(screen.getByText('标题')).toBeInTheDocument();
+    expect(screen.getByText('描述')).toBeInTheDocument();
+    expect(screen.getByText('分类')).toBeInTheDocument();
+    expect(screen.getByText('上传种子')).toBeInTheDocument();
+  });
+
+  it('应该加载分类数据', async () => {
+    render(
+      <MemoryRouter>
+        <UploadTorrent />
+      </MemoryRouter>
+    );
+
+    // 等待分类数据加载
+    await waitFor(() => {
+      expect(axios.get).toHaveBeenCalledWith('http://localhost:8080/categories');
+    });
+
+    // 验证分类下拉框是否渲染了正确的选项
+    const categorySelect = screen.getByLabelText('分类');
+    expect(categorySelect).toBeInTheDocument();
+
+    // 打开下拉菜单并检查选项
+    fireEvent.mouseDown(categorySelect);
+    mockCategories.forEach(category => {
+      expect(screen.getByText(category.category_name)).toBeInTheDocument();
+    });
+  });
+
+  it('应该在选择分类后显示相应的动态字段', async () => {
+    render(
+      <MemoryRouter>
+        <UploadTorrent />
+      </MemoryRouter>
+    );
+
+    // 等待分类数据加载
+    await waitFor(() => {
+      expect(axios.get).toHaveBeenCalledWith('http://localhost:8080/categories');
+    });
+
+    // 选择"电影"分类
+    const categorySelect = screen.getByLabelText('分类');
+    fireEvent.mouseDown(categorySelect);
+    fireEvent.click(screen.getByText('电影'));
+
+    // 验证电影相关的字段是否显示
+    expect(screen.getByLabelText('字幕/说明')).toBeInTheDocument();
+    expect(screen.getByLabelText('地区')).toBeInTheDocument();
+    expect(screen.getByLabelText('年份')).toBeInTheDocument();
+    expect(screen.getByLabelText('类型')).toBeInTheDocument();
+    expect(screen.getByLabelText('编码格式')).toBeInTheDocument();
+    expect(screen.getByLabelText('分辨率')).toBeInTheDocument();
+  });
+
+  it('应该在选择"剧集"分类后显示相应的动态字段', async () => {
+    render(
+      <MemoryRouter>
+        <UploadTorrent />
+      </MemoryRouter>
+    );
+
+    // 等待分类数据加载
+    await waitFor(() => {
+      expect(axios.get).toHaveBeenCalledWith('http://localhost:8080/categories');
+    });
+
+    // 选择"剧集"分类
+    const categorySelect = screen.getByLabelText('分类');
+    fireEvent.mouseDown(categorySelect);
+    fireEvent.click(screen.getByText('剧集'));
+
+    // 验证剧集相关的字段是否显示
+    expect(screen.getByLabelText('地区')).toBeInTheDocument();
+    expect(screen.getByLabelText('格式')).toBeInTheDocument();
+    expect(screen.getByLabelText('类型')).toBeInTheDocument();
+  });
+
+  it('应该在选择"游戏"分类后显示相应的动态字段', async () => {
+    render(
+      <MemoryRouter>
+        <UploadTorrent />
+      </MemoryRouter>
+    );
+
+    // 等待分类数据加载
+    await waitFor(() => {
+      expect(axios.get).toHaveBeenCalledWith('http://localhost:8080/categories');
+    });
+
+    // 选择"游戏"分类
+    const categorySelect = screen.getByLabelText('分类');
+    fireEvent.mouseDown(categorySelect);
+    fireEvent.click(screen.getByText('游戏'));
+
+    // 验证游戏相关的字段是否显示
+    expect(screen.getByLabelText('平台')).toBeInTheDocument();
+    expect(screen.getByLabelText('类型')).toBeInTheDocument();
+    expect(screen.getByLabelText('语言')).toBeInTheDocument();
+    expect(screen.getByLabelText('数据类型')).toBeInTheDocument();
+  });
+
+  it('应该在选择"综艺"分类后显示相应的动态字段', async () => {
+    render(
+      <MemoryRouter>
+        <UploadTorrent />
+      </MemoryRouter>
+    );
+
+    // 等待分类数据加载
+    await waitFor(() => {
+      expect(axios.get).toHaveBeenCalledWith('http://localhost:8080/categories');
+    });
+
+    // 选择"综艺"分类
+    const categorySelect = screen.getByLabelText('分类');
+    fireEvent.mouseDown(categorySelect);
+    fireEvent.click(screen.getByText('综艺'));
+
+    // 验证综艺相关的字段是否显示
+    expect(screen.getByLabelText('是否大陆综艺')).toBeInTheDocument();
+    expect(screen.getByLabelText('类型')).toBeInTheDocument();
+    expect(screen.getByLabelText('格式')).toBeInTheDocument();
+  });
+
+  it('应该在选择"动漫"分类后显示相应的动态字段', async () => {
+    render(
+      <MemoryRouter>
+        <UploadTorrent />
+      </MemoryRouter>
+    );
+
+    // 等待分类数据加载
+    await waitFor(() => {
+      expect(axios.get).toHaveBeenCalledWith('http://localhost:8080/categories');
+    });
+
+    // 选择"动漫"分类
+    const categorySelect = screen.getByLabelText('分类');
+    fireEvent.mouseDown(categorySelect);
+    fireEvent.click(screen.getByText('动漫'));
+
+    // 验证动漫相关的字段是否显示
+    expect(screen.getByLabelText('类型')).toBeInTheDocument();
+    expect(screen.getByLabelText('格式')).toBeInTheDocument();
+    expect(screen.getByLabelText('分辨率')).toBeInTheDocument();
+  });
+
+  it('应该在选择"学习资料"分类后显示相应的动态字段', async () => {
+    render(
+      <MemoryRouter>
+        <UploadTorrent />
+      </MemoryRouter>
+    );
+
+    // 等待分类数据加载
+    await waitFor(() => {
+      expect(axios.get).toHaveBeenCalledWith('http://localhost:8080/categories');
+    });
+
+    // 选择"学习资料"分类
+    const categorySelect = screen.getByLabelText('分类');
+    fireEvent.mouseDown(categorySelect);
+    fireEvent.click(screen.getByText('学习资料'));
+
+    // 验证学习资料相关的字段是否显示
+    expect(screen.getByLabelText('类型')).toBeInTheDocument();
+    expect(screen.getByLabelText('格式')).toBeInTheDocument();
+  });
+
+  it('应该在选择"纪录片"分类后显示相应的动态字段', async () => {
+    render(
+      <MemoryRouter>
+        <UploadTorrent />
+      </MemoryRouter>
+    );
+
+    // 等待分类数据加载
+    await waitFor(() => {
+      expect(axios.get).toHaveBeenCalledWith('http://localhost:8080/categories');
+    });
+
+    // 选择"纪录片"分类
+    const categorySelect = screen.getByLabelText('分类');
+    fireEvent.mouseDown(categorySelect);
+    fireEvent.click(screen.getByText('纪录片'));
+
+    // 验证纪录片相关的字段是否显示
+    expect(screen.getByLabelText('年份')).toBeInTheDocument();
+    expect(screen.getByLabelText('视频源')).toBeInTheDocument();
+    expect(screen.getByLabelText('格式')).toBeInTheDocument();
+  });
+
+  it('应该在选择"音乐"分类后显示相应的动态字段', async () => {
+    render(
+      <MemoryRouter>
+        <UploadTorrent />
+      </MemoryRouter>
+    );
+
+    // 等待分类数据加载
+    await waitFor(() => {
+      expect(axios.get).toHaveBeenCalledWith('http://localhost:8080/categories');
+    });
+
+    // 选择"音乐"分类
+    const categorySelect = screen.getByLabelText('分类');
+    fireEvent.mouseDown(categorySelect);
+    fireEvent.click(screen.getByText('音乐'));
+
+    // 验证音乐相关的字段是否显示
+    expect(screen.getByLabelText('类型')).toBeInTheDocument();
+    expect(screen.getByLabelText('地区')).toBeInTheDocument();
+    expect(screen.getByLabelText('风格')).toBeInTheDocument();
+    expect(screen.getByLabelText('格式')).toBeInTheDocument();
+  });
+
+  it('应该在选择"软件"分类后显示相应的动态字段', async () => {
+    render(
+      <MemoryRouter>
+        <UploadTorrent />
+      </MemoryRouter>
+    );
+
+    // 等待分类数据加载
+    await waitFor(() => {
+      expect(axios.get).toHaveBeenCalledWith('http://localhost:8080/categories');
+    });
+
+    // 选择"软件"分类
+    const categorySelect = screen.getByLabelText('分类');
+    fireEvent.mouseDown(categorySelect);
+    fireEvent.click(screen.getByText('软件'));
+
+    // 验证软件相关的字段是否显示
+    expect(screen.getByLabelText('平台')).toBeInTheDocument();
+    expect(screen.getByLabelText('类型')).toBeInTheDocument();
+    expect(screen.getByLabelText('格式')).toBeInTheDocument();
+  });
+
+  it('应该在选择"体育"分类后显示相应的动态字段', async () => {
+    render(
+      <MemoryRouter>
+        <UploadTorrent />
+      </MemoryRouter>
+    );
+
+    // 等待分类数据加载
+    await waitFor(() => {
+      expect(axios.get).toHaveBeenCalledWith('http://localhost:8080/categories');
+    });
+
+    // 选择"体育"分类
+    const categorySelect = screen.getByLabelText('分类');
+    fireEvent.mouseDown(categorySelect);
+    fireEvent.click(screen.getByText('体育'));
+
+    // 验证体育相关的字段是否显示
+    expect(screen.getByLabelText('类型')).toBeInTheDocument();
+    expect(screen.getByLabelText('格式')).toBeInTheDocument();
+    expect(screen.getByLabelText('赛事类型')).toBeInTheDocument();
+  });
+
+  it('应该在选择分类后正确设置状态', async () => {
+    render(
+      <MemoryRouter>
+        <UploadTorrent />
+      </MemoryRouter>
+    );
+
+    // 等待分类数据加载
+    await waitFor(() => {
+      expect(axios.get).toHaveBeenCalledWith('http://localhost:8080/categories');
+    });
+
+    // 选择"电影"分类
+    const categorySelect = screen.getByLabelText('分类');
+    fireEvent.mouseDown(categorySelect);
+    fireEvent.click(screen.getByText('电影'));
+
+    // 验证状态是否正确设置
+    // 注意:由于状态是组件内部的,我们需要通过 UI 变化来间接验证
+    // 这里我们验证电影相关的字段是否显示
+    expect(screen.getByLabelText('字幕/说明')).toBeInTheDocument();
+  });
+
+  it('应该在选择种子文件后更新状态', async () => {
+    render(
+      <MemoryRouter>
+        <UploadTorrent />
+      </MemoryRouter>
+    );
+
+    // 等待分类数据加载
+    await waitFor(() => {
+      expect(axios.get).toHaveBeenCalledWith('http://localhost:8080/categories');
+    });
+
+    // 模拟文件选择
+    const fileInput = screen.getByLabelText('种子文件').querySelector('input');
+    const mockFile = new File(['torrent content'], 'test.torrent', { type: 'application/x-bittorrent' });
+    fireEvent.change(fileInput, { target: { files: [mockFile] } });
+
+    // 验证文件是否被设置(通过 UI 变化间接验证)
+    // 由于我们无法直接访问组件状态,我们可以通过表单验证状态来间接验证
+    // 这里我们只是验证文件输入是否正常工作
+    expect(fileInput).toBeInTheDocument();
+  });
+
+  it('应该在选择封面图片后更新状态', async () => {
+    render(
+      <MemoryRouter>
+        <UploadTorrent />
+      </MemoryRouter>
+    );
+
+    // 等待分类数据加载
+    await waitFor(() => {
+      expect(axios.get).toHaveBeenCalledWith('http://localhost:8080/categories');
+    });
+
+    // 模拟图片选择
+    const imageInput = screen.getByLabelText('封面图片').querySelector('input');
+    const mockImage = new File(['image content'], 'test.jpg', { type: 'image/jpeg' });
+    fireEvent.change(imageInput, { target: { files: [mockImage] } });
+
+    // 验证图片是否被设置(通过 UI 变化间接验证)
+    expect(imageInput).toBeInTheDocument();
+  });
+
+  it('应该在表单提交时验证必填字段', async () => {
+    render(
+      <MemoryRouter>
+        <UploadTorrent />
+      </MemoryRouter>
+    );
+
+    // 等待分类数据加载
+    await waitFor(() => {
+      expect(axios.get).toHaveBeenCalledWith('http://localhost:8080/categories');
+    });
+
+    // 直接提交表单而不填写任何字段
+    const submitButton = screen.getByText('上传种子');
+    fireEvent.click(submitButton);
+
+    // 验证错误消息是否显示
+    // 注意:由于我们使用了 antd 的 Form,错误消息可能由 antd 内部处理
+    // 我们可以通过检查是否有错误提示来验证
+    // 这里我们只是验证表单提交被调用
+    // 更详细的验证可能需要更复杂的测试设置
+  });
+
+  it('应该在填写所有必填字段后成功提交表单', async () => {
+    // 模拟 axios.post
+    vi.mocked(axios.post).mockResolvedValueOnce({ data: {} });
+
+    render(
+      <MemoryRouter>
+        <UploadTorrent />
+      </MemoryRouter>
+    );
+
+    // 等待分类数据加载
+    await waitFor(() => {
+      expect(axios.get).toHaveBeenCalledWith('http://localhost:8080/categories');
+    });
+
+    // 选择分类
+    const categorySelect = screen.getByLabelText('分类');
+    fireEvent.mouseDown(categorySelect);
+    fireEvent.click(screen.getByText('电影'));
+
+    // 填写标题
+    const titleInput = screen.getByLabelText('标题');
+    fireEvent.change(titleInput, { target: { value: '测试电影' } });
+
+    // 填写描述
+    const descriptionInput = screen.getByLabelText('描述');
+    fireEvent.change(descriptionInput, { target: { value: '这是一个测试电影的描述' } });
+
+    // 选择封面图片
+    const imageInput = screen.getByLabelText('封面图片').querySelector('input');
+    const mockImage = new File(['image content'], 'test.jpg', { type: 'image/jpeg' });
+    fireEvent.change(imageInput, { target: { files: [mockImage] } });
+
+    // 选择种子文件
+    const fileInput = screen.getByLabelText('种子文件').querySelector('input');
+    const mockFile = new File(['torrent content'], 'test.torrent', { type: 'application/x-bittorrent' });
+    fireEvent.change(fileInput, { target: { files: [mockFile] } });
+
+    // 填写电影相关字段
+    const captionInput = screen.getByLabelText('字幕/说明');
+    fireEvent.change(captionInput, { target: { value: '测试字幕' } });
+
+    const regionSelect = screen.getByLabelText('地区');
+    fireEvent.mouseDown(regionSelect);
+    fireEvent.click(screen.getByText('大陆'));
+
+    const yearInput = screen.getByLabelText('年份');
+    fireEvent.change(yearInput, { target: { value: '2023' } });
+
+    const genreSelect = screen.getByLabelText('类型');
+    fireEvent.mouseDown(genreSelect);
+    fireEvent.click(screen.getByText('动作'));
+
+    const codecFormatSelect = screen.getByLabelText('编码格式');
+    fireEvent.mouseDown(codecFormatSelect);
+    fireEvent.click(screen.getByText('H.264'));
+
+    const resolutionSelect = screen.getByLabelText('分辨率');
+    fireEvent.mouseDown(resolutionSelect);
+    fireEvent.click(screen.getByText('1080p'));
+
+    // 提交表单
+    const submitButton = screen.getByText('上传种子');
+    fireEvent.click(submitButton);
+
+    // 验证 axios.post 是否被调用
+    expect(axios.post).toHaveBeenCalledWith(
+      'http://localhost:8080/torrent/upload',
+      expect.any(FormData),
+      {
+        headers: { 'Content-Type': 'multipart/form-data' },
+        responseType: 'blob',
+      }
+    );
+
+    // 验证成功消息是否显示
+    expect(vi.mocked(message.success)).toHaveBeenCalledWith('上传成功!');
+  });
+});
\ No newline at end of file