Merge "Merge remote-tracking branch 'origin/master'" into whx
diff --git a/src/main/java/com/example/g8backend/util/TorrentUtil.java b/src/main/java/com/example/g8backend/util/TorrentUtil.java
index e6c1f3b..e8360bc 100644
--- a/src/main/java/com/example/g8backend/util/TorrentUtil.java
+++ b/src/main/java/com/example/g8backend/util/TorrentUtil.java
@@ -54,7 +54,7 @@
}
}
- private static byte[] readBytes(File file) throws IOException {
+ public static byte[] readBytes(File file) throws IOException {
try (InputStream in = new FileInputStream(file)) {
return in.readAllBytes();
}
diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql
new file mode 100644
index 0000000..0907033
--- /dev/null
+++ b/src/main/resources/data.sql
@@ -0,0 +1 @@
+# 后面统一数据库数据用
\ No newline at end of file
diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql
index e88ff07..952313e 100644
--- a/src/main/resources/schema.sql
+++ b/src/main/resources/schema.sql
@@ -41,6 +41,21 @@
FOREIGN KEY (user_id) REFERENCES users(user_id)
);
+CREATE TABLE IF NOT EXISTS `tags`(
+ tag_id INT AUTO_INCREMENT PRIMARY KEY,
+ tag_name VARCHAR(255) NOT NULL UNIQUE,
+ parent_id INT DEFAULT NULL,
+ FOREIGN KEY (parent_id) REFERENCES tags(tag_id)
+);
+
+CREATE TABLE IF NOT EXISTS `post_tag` (
+ post_id INT NOT NULL,
+ tag_id INT NOT NULL,
+ FOREIGN KEY (post_id) REFERENCES posts(post_id),
+ FOREIGN KEY (tag_id) REFERENCES tags(tag_id),
+ PRIMARY KEY (post_id, tag_id)
+);
+
CREATE TABLE IF NOT EXISTS `post_likes` (
user_id INT NOT NULL,
post_id INT NOT NULL,
diff --git a/src/test/java/com/example/g8backend/service/TorrentServiceTest.java b/src/test/java/com/example/g8backend/service/TorrentServiceTest.java
new file mode 100644
index 0000000..5063501
--- /dev/null
+++ b/src/test/java/com/example/g8backend/service/TorrentServiceTest.java
@@ -0,0 +1,158 @@
+package com.example.g8backend.service;
+
+import com.dampcake.bencode.Bencode;
+import com.example.g8backend.entity.Torrent;
+import com.example.g8backend.mapper.TorrentMapper;
+import com.example.g8backend.service.impl.TorrentServiceImpl;
+import com.example.g8backend.util.TorrentUtil;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.io.TempDir;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+public class TorrentServiceTest {
+ @InjectMocks
+ private TorrentServiceImpl torrentService;
+
+ @Mock
+ private TorrentMapper torrentMapper;
+
+ @TempDir
+ Path tempDir;
+
+ private File createTestTorrentFile(Map<String, Object> info) throws Exception {
+ // 1. 构造 Bencode 数据结构
+ Map<String, Object> torrentMap = new HashMap<>();
+ torrentMap.put("announce", "http://localhost:6881/announce");
+ torrentMap.put("info", info);
+
+ // 2. 编码为 Bencode 字节流
+ Bencode bencode = new Bencode();
+ byte[] bencodeData = bencode.encode(torrentMap);
+
+ // 3. 写入临时文件
+ Path torrentPath = tempDir.resolve("dynamic.torrent");
+ Files.write(torrentPath, bencodeData);
+ return torrentPath.toFile();
+ }
+
+ @BeforeEach
+ public void setUp() {
+ torrentService = new TorrentServiceImpl();
+ torrentMapper = Mockito.mock(TorrentMapper.class);
+ // 注入 Mock 对象(反射)
+ try {
+ var field = TorrentServiceImpl.class.getDeclaredField("torrentMapper");
+ field.setAccessible(true);
+ field.set(torrentService, torrentMapper);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ public void testHandleTorrentUpload() throws Exception {
+ Map<String, Object> info = Map.of( "name", "test.txt", "length", 1024);
+ File torrentFile = createTestTorrentFile(info);
+ Long userId = 1L;
+ String passkey = "123456";
+ byte[] mockedBytes = "modified".getBytes();
+ String expectedInfoHash = "modified-info-hash";
+
+ // 注入 Mock 对象(静态方法)
+ try (MockedStatic<TorrentUtil> mockedStatic = Mockito.mockStatic(TorrentUtil.class)){
+ mockedStatic.when(() -> TorrentUtil.injectTracker(any(File.class), any(String.class)))
+ .thenReturn(mockedBytes);
+ mockedStatic.when(() -> TorrentUtil.getInfoHash(any(File.class)))
+ .thenReturn(expectedInfoHash);
+ Torrent torrent = torrentService.handleTorrentUpload(torrentFile, userId, passkey);
+
+ // 验证调用
+ verify(torrentMapper, times(1))
+ .insertTorrent(eq(1L),
+ anyString(),
+ eq("modified-info-hash"),
+ eq(torrentFile.length()/1024.0/1024.0));
+
+ assertEquals(expectedInfoHash, torrent.getInfoHash());
+ assertEquals(userId, torrent.getUserId());
+ assertEquals(torrentFile.length()/1024.0/1024.0, torrent.getFileSize());
+ } finally {
+ if (!torrentFile.delete()) {
+ System.err.println("Failed to delete temporary file: " + torrentFile.getAbsolutePath());
+ }
+ }
+ }
+
+ @Test
+ public void testHandleTorrentDownload() throws Exception {
+ Torrent torrent = new Torrent();
+ torrent.setTorrentName("sample.torrent");
+
+ File fakeTorrentFile = new File("uploaded-torrents/sample.torrent");
+ fakeTorrentFile.getParentFile().mkdirs();
+ byte[] originalBytes = "original-data".getBytes();
+ Files.write(fakeTorrentFile.toPath(), originalBytes);
+
+ String passkey = "testpasskey";
+ byte[] modifiedBytes = "modified-data".getBytes();
+
+ try (MockedStatic<TorrentUtil> mockedStatic = Mockito.mockStatic(TorrentUtil.class)) {
+ // mock injectTracker
+ mockedStatic.when(() -> TorrentUtil.injectTracker(any(File.class), anyString()))
+ .thenReturn(modifiedBytes);
+
+ File downloaded = torrentService.handleTorrentDownload(torrent, passkey);
+
+ assertTrue(downloaded.exists());
+ assertEquals("modified-data", Files.readString(downloaded.toPath()));
+
+ // 清理临时文件
+ downloaded.delete();
+ } finally {
+ fakeTorrentFile.delete(); // 清理原始文件
+ }
+ }
+
+
+ @Test
+ public void testFindByInfoHash() {
+ Torrent mockTorrent = new Torrent();
+ mockTorrent.setInfoHash("abc123");
+ when(torrentMapper.getTorrentByInfoHash("abc123")).thenReturn(mockTorrent);
+
+ Torrent result = torrentService.findByInfoHash("abc123");
+
+ assertEquals("abc123", result.getInfoHash());
+ verify(torrentMapper).getTorrentByInfoHash("abc123");
+ }
+
+ @Test
+ public void testFindByTorrentId() {
+ Torrent mockTorrent = new Torrent();
+ mockTorrent.setTorrentId(42L);
+ when(torrentMapper.getTorrentByTorrentId(42L)).thenReturn(mockTorrent);
+
+ Torrent result = torrentService.findByTorrentId(42L);
+
+ assertEquals(42L, result.getTorrentId());
+ verify(torrentMapper).getTorrentByTorrentId(42L);
+ }
+}
\ No newline at end of file