From 03513bbb6f48fbb35e5b5ed77ff0d50c8004fc88 Mon Sep 17 00:00:00 2001 From: Nekoli Date: Wed, 15 Jul 2026 02:04:19 +0800 Subject: [PATCH 1/7] =?UTF-8?q?test(infra):=20=E6=96=B0=E5=A2=9E=20RandomU?= =?UTF-8?q?tils/TimeUtils/PathUtils/JsonUtils/ByteArrUtils/SqlUtils/AesEnc?= =?UTF-8?q?ryptUtils=20=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradlew | 0 .../infra/utils/AesEncryptUtilsTest.java | 209 +++++++----------- .../server/infra/utils/ByteArrUtilsTest.java | 61 +++-- .../server/infra/utils/JsonUtilsTest.java | 110 ++++----- .../server/infra/utils/PathUtilsTest.java | 102 +++++++-- .../server/infra/utils/RandomUtilsTest.java | 36 +-- .../server/infra/utils/SqlUtilsTest.java | 50 +++-- .../server/infra/utils/TimeUtilsTest.java | 27 ++- 8 files changed, 336 insertions(+), 259 deletions(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/server/src/test/java/run/ikaros/server/infra/utils/AesEncryptUtilsTest.java b/server/src/test/java/run/ikaros/server/infra/utils/AesEncryptUtilsTest.java index 3051b80a6..adac109be 100644 --- a/server/src/test/java/run/ikaros/server/infra/utils/AesEncryptUtilsTest.java +++ b/server/src/test/java/run/ikaros/server/infra/utils/AesEncryptUtilsTest.java @@ -1,172 +1,129 @@ package run.ikaros.server.infra.utils; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.nio.file.Path; import java.util.Base64; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; class AesEncryptUtilsTest { - @TempDir - Path tempDir; - @Test - void generateKeyByteArrayDefaultLength() { - byte[] keyBytes = AesEncryptUtils.generateKeyByteArray(); - assertNotNull(keyBytes); - assertTrue(keyBytes.length > 0, "Default key bytes should not be empty"); - // Default is 192-bit, Base64-encoded: ceil(192/8) = 24 raw bytes -> Base64 = 32 chars - byte[] decoded = Base64.getDecoder().decode(keyBytes); - assertEquals(24, decoded.length, "Default 192-bit key should decode to 24 bytes"); + void generateKeyByteArray_default_returnsBase64Bytes() { + byte[] keyB64 = AesEncryptUtils.generateKeyByteArray(); + assertThat(keyB64).isNotNull(); + // decode to verify it's valid base64 and 24 raw bytes (192-bit default) + byte[] rawKey = Base64.getDecoder().decode(keyB64); + assertThat(rawKey).hasSize(24); } @Test - void generateKeyByteArray128() { - byte[] keyBytes = AesEncryptUtils.generateKeyByteArray(128); - assertNotNull(keyBytes); - byte[] decoded = Base64.getDecoder().decode(keyBytes); - assertEquals(16, decoded.length, "128-bit key should decode to 16 bytes"); + void generateKeyByteArray_128bit_returnsValidKey() { + byte[] keyB64 = AesEncryptUtils.generateKeyByteArray(128); + assertThat(keyB64).isNotNull(); + byte[] rawKey = Base64.getDecoder().decode(keyB64); + assertThat(rawKey).hasSize(16); // 128 bits = 16 bytes } @Test - void generateKeyByteArray256() { - byte[] keyBytes = AesEncryptUtils.generateKeyByteArray(256); - assertNotNull(keyBytes); - byte[] decoded = Base64.getDecoder().decode(keyBytes); - assertEquals(32, decoded.length, "256-bit key should decode to 32 bytes"); + void generateKeyByteArray_256bit_returnsValidKey() { + byte[] keyB64 = AesEncryptUtils.generateKeyByteArray(256); + assertThat(keyB64).isNotNull(); + byte[] rawKey = Base64.getDecoder().decode(keyB64); + assertThat(rawKey).hasSize(32); // 256 bits = 32 bytes } @Test - void encryptDecryptByteArrayRoundtrip() { - byte[] keyBytes = Base64.getDecoder().decode(AesEncryptUtils.generateKeyByteArray()); - byte[] original = "Hello, AES encryption!".getBytes(); - - byte[] encrypted = AesEncryptUtils.encryptByteArray(keyBytes, original); - assertNotNull(encrypted); - byte[] decrypted = AesEncryptUtils.decryptByteArray(keyBytes, encrypted); - assertNotNull(decrypted); - assertArrayEquals(original, decrypted); + void encryptDecrypt_roundTrip_bytes() throws Exception { + byte[] keyB64 = AesEncryptUtils.generateKeyByteArray(128); + byte[] rawKey = Base64.getDecoder().decode(keyB64); + String original = "Hello AES World! " + System.currentTimeMillis(); + byte[] originalBytes = original.getBytes(StandardCharsets.UTF_8); + + // encryptByteArray(byte[] key, byte[] data) + byte[] encrypted = AesEncryptUtils.encryptByteArray(rawKey, originalBytes); + assertThat(encrypted).isNotNull(); + assertThat(encrypted).isNotEqualTo(originalBytes); + + // decryptByteArray(byte[] key, byte[] encryptedDataBase64) + byte[] decrypted = AesEncryptUtils.decryptByteArray(rawKey, encrypted); + String result = new String(decrypted, StandardCharsets.UTF_8); + assertThat(result).isEqualTo(original); } @Test - void encryptDecryptWithBase64StringKey() { - byte[] keyBytes = AesEncryptUtils.generateKeyByteArray(); - String keyStrBase64 = new String(keyBytes); - byte[] original = "Test string key encryption".getBytes(); - - byte[] encrypted = AesEncryptUtils.encryptByteArray(keyStrBase64, original); - assertNotNull(encrypted); - byte[] decrypted = AesEncryptUtils.decryptByteArray(keyStrBase64, encrypted); - assertNotNull(decrypted); - assertArrayEquals(original, decrypted); + void encryptDecrypt_withStringKey() throws Exception { + byte[] keyB64 = AesEncryptUtils.generateKeyByteArray(128); + String keyBase64Str = new String(keyB64, StandardCharsets.UTF_8); + String original = "测试中文加密内容"; + + // encryptByteArray(String base64Key, byte[] data) + byte[] encrypted = AesEncryptUtils.encryptByteArray(keyBase64Str, + original.getBytes(StandardCharsets.UTF_8)); + assertThat(encrypted).isNotNull(); + + byte[] rawKey = Base64.getDecoder().decode(keyB64); + byte[] decrypted = AesEncryptUtils.decryptByteArray(rawKey, encrypted); + String result = new String(decrypted, StandardCharsets.UTF_8); + assertThat(result).isEqualTo(original); } @Test - void generateKeyFileAndEncryptDecrypt() throws IOException { - File keyFile = tempDir.resolve("aes.key").toFile(); + void generateKeyFile_validPath_createsFile(@TempDir File tempDir) throws Exception { + File keyFile = new File(tempDir, "aes.key"); AesEncryptUtils.generateKeyFile(keyFile); - assertTrue(keyFile.exists()); - assertTrue(keyFile.length() > 0); - - byte[] keyBytes = Base64.getDecoder().decode(Files.readAllBytes(keyFile.toPath())); - byte[] original = "File key test data".getBytes(); - - byte[] encrypted = AesEncryptUtils.encryptByteArray(keyFile, original); - assertNotNull(encrypted); - byte[] decrypted = AesEncryptUtils.decryptByteArray(keyBytes, encrypted); - assertArrayEquals(original, decrypted); + assertThat(keyFile).exists(); + assertThat(keyFile.length()).isGreaterThan(0); } @Test - void encryptDecryptInputStreamRoundtrip() throws IOException { - byte[] keyBytes = Base64.getDecoder().decode(AesEncryptUtils.generateKeyByteArray()); - byte[] original = "Stream encryption test content".getBytes(); - - // Encrypt - ByteArrayOutputStream encryptedOut = new ByteArrayOutputStream(); - AesEncryptUtils.encryptInputStream( - new ByteArrayInputStream(original), true, encryptedOut, keyBytes); - byte[] encrypted = encryptedOut.toByteArray(); - - // Decrypt - ByteArrayOutputStream decryptedOut = new ByteArrayOutputStream(); - AesEncryptUtils.decryptInputStream( - new ByteArrayInputStream(encrypted), true, decryptedOut, keyBytes); - byte[] decrypted = decryptedOut.toByteArray(); - - assertArrayEquals(original, decrypted); + void generateKeyFile_customLength_createsFile(@TempDir File tempDir) throws Exception { + File keyFile = new File(tempDir, "aes256.key"); + AesEncryptUtils.generateKeyFile(256, keyFile); + assertThat(keyFile).exists(); + // key file content is base64 encoded, should be ~44 chars for 256-bit = 32 raw bytes + String content = Files.readString(keyFile.toPath()).trim(); + byte[] rawKey = Base64.getDecoder().decode(content); + assertThat(rawKey).hasSize(32); } @Test - void encryptDecryptFileWithFileKey() throws IOException { - File keyFile = tempDir.resolve("key.dat").toFile(); - AesEncryptUtils.generateKeyFile(keyFile); + void encryptFile_decryptFile_roundTrip(@TempDir File tempDir) throws Exception { + byte[] keyB64 = AesEncryptUtils.generateKeyByteArray(128); + File keyFile = new File(tempDir, "secret.key"); + Files.write(keyFile.toPath(), keyB64); - File dataFile = tempDir.resolve("data.txt").toFile(); - byte[] original = "File encryption roundtrip".getBytes(); - try (FileOutputStream fos = new FileOutputStream(dataFile)) { - fos.write(original); - } + File originalFile = new File(tempDir, "original.txt"); + File encryptedFile = new File(tempDir, "encrypted.bin"); + File decryptedFile = new File(tempDir, "decrypted.txt"); - File encryptedFile = tempDir.resolve("encrypted.dat").toFile(); - AesEncryptUtils.encryptFile(keyFile, dataFile, encryptedFile); - assertTrue(encryptedFile.exists()); + Files.write(originalFile.toPath(), "File encryption test".getBytes()); - File decryptedFile = tempDir.resolve("decrypted.txt").toFile(); + // encryptFile(keyFile, dataFile, encryptedFile) + AesEncryptUtils.encryptFile(keyFile, originalFile, encryptedFile); + // decryptFile(keyFile, dataFile, decryptedFile) AesEncryptUtils.decryptFile(keyFile, encryptedFile, decryptedFile); - assertTrue(decryptedFile.exists()); - byte[] decrypted = Files.readAllBytes(decryptedFile.toPath()); - assertArrayEquals(original, decrypted); + String decryptedContent = Files.readString(decryptedFile.toPath()); + assertThat(decryptedContent).isEqualTo("File encryption test"); } @Test - void encryptFileWithByteArrayKeyAndDecrypt() throws IOException { - byte[] keyBytes = Base64.getDecoder().decode(AesEncryptUtils.generateKeyByteArray()); - - File dataFile = tempDir.resolve("input.txt").toFile(); - byte[] original = "ByteArray key file test".getBytes(); - try (FileOutputStream fos = new FileOutputStream(dataFile)) { - fos.write(original); - } - - byte[] encrypted = AesEncryptUtils.encryptFile(keyBytes, dataFile); - assertNotNull(encrypted); - - byte[] decrypted = AesEncryptUtils.decryptFile(keyBytes, - createTempFileFromBytes("encrypted.dat", encrypted)); - assertArrayEquals(original, decrypted); - } - - @Test - void encryptEmptyByteArray() { - byte[] keyBytes = Base64.getDecoder().decode(AesEncryptUtils.generateKeyByteArray()); - byte[] original = new byte[0]; - - byte[] encrypted = AesEncryptUtils.encryptByteArray(keyBytes, original); - assertNotNull(encrypted); + void encryptByteArray_withKeyFile_roundTrip(@TempDir File tempDir) throws Exception { + File keyFile = new File(tempDir, "secret.key"); + AesEncryptUtils.generateKeyFile(keyFile); + String original = "Encrypt with key file"; - byte[] decrypted = AesEncryptUtils.decryptByteArray(keyBytes, encrypted); - assertNotNull(decrypted); - assertArrayEquals(original, decrypted); - } + byte[] encrypted = AesEncryptUtils.encryptByteArray(keyFile, original.getBytes(StandardCharsets.UTF_8)); + assertThat(encrypted).isNotNull(); - private File createTempFileFromBytes(String name, byte[] bytes) throws IOException { - File file = tempDir.resolve(name).toFile(); - try (FileOutputStream fos = new FileOutputStream(file)) { - fos.write(bytes); - } - return file; + byte[] keyB64 = Files.readAllBytes(keyFile.toPath()); + byte[] rawKey = Base64.getDecoder().decode(keyB64); + byte[] decrypted = AesEncryptUtils.decryptByteArray(rawKey, encrypted); + String result = new String(decrypted, StandardCharsets.UTF_8); + assertThat(result).isEqualTo(original); } } diff --git a/server/src/test/java/run/ikaros/server/infra/utils/ByteArrUtilsTest.java b/server/src/test/java/run/ikaros/server/infra/utils/ByteArrUtilsTest.java index 4407b59bf..9191ecb85 100644 --- a/server/src/test/java/run/ikaros/server/infra/utils/ByteArrUtilsTest.java +++ b/server/src/test/java/run/ikaros/server/infra/utils/ByteArrUtilsTest.java @@ -1,46 +1,65 @@ package run.ikaros.server.infra.utils; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; -import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Test; class ByteArrUtilsTest { @Test - void isBinaryDataWithControlChars() { - byte[] data = {0, 1, 2, 127}; - assertTrue(ByteArrUtils.isBinaryData(data)); + void isBinaryData_pngHeader_returnsTrue() { + byte[] png = {(byte) 0x89, 0x50, 0x4E, 0x47}; + assertThat(ByteArrUtils.isBinaryData(png)).isTrue(); } @Test - void isBinaryDataWithTextBytes() { - byte[] data = "Hello, world!".getBytes(StandardCharsets.UTF_8); - assertFalse(ByteArrUtils.isBinaryData(data)); + void isBinaryData_textAscii_returnsFalse() { + byte[] text = "Hello World".getBytes(); + assertThat(ByteArrUtils.isBinaryData(text)).isFalse(); } @Test - void isBinaryDataWithEmptyArray() { - byte[] data = new byte[0]; - assertFalse(ByteArrUtils.isBinaryData(data)); + void isBinaryData_empty_returnsFalse() { + assertThat(ByteArrUtils.isBinaryData(new byte[0])).isFalse(); } @Test - void isStringDataWithValidUtf8Text() { - byte[] data = "Hello, world!".getBytes(StandardCharsets.UTF_8); - assertTrue(ByteArrUtils.isStringData(data)); + void isBinaryData_withNullByte_returnsTrue() { + byte[] data = {0x48, 0x00, 0x65, 0x6C}; + assertThat(ByteArrUtils.isBinaryData(data)).isTrue(); } @Test - void isStringDataWithBinaryData() { - byte[] data = {0, 1, 2, (byte) 0xFF}; - assertFalse(ByteArrUtils.isStringData(data)); + void isBinaryData_withControlChars_returnsTrue() { + byte[] data = {0x01, 0x02, 0x03}; + assertThat(ByteArrUtils.isBinaryData(data)).isTrue(); } @Test - void isStringDataWithEmptyArray() { - byte[] data = new byte[0]; - assertTrue(ByteArrUtils.isStringData(data)); + void isBinaryData_tabNewlineReturn_returnsFalse() { + byte[] data = {0x09, 0x0A, 0x0D, 0x48}; + assertThat(ByteArrUtils.isBinaryData(data)).isFalse(); + } + + @Test + void isBinaryData_null_throwsNpe() { + assertThrows(NullPointerException.class, () -> ByteArrUtils.isBinaryData(null)); + } + + @Test + void isStringData_text_returnsTrue() { + byte[] text = "plain text".getBytes(); + assertThat(ByteArrUtils.isStringData(text)).isTrue(); + } + + @Test + void isStringData_null_returnsFalse() { + assertThat(ByteArrUtils.isStringData(null)).isFalse(); + } + + @Test + void isStringData_empty_returnsTrue() { + assertThat(ByteArrUtils.isStringData(new byte[0])).isTrue(); } } diff --git a/server/src/test/java/run/ikaros/server/infra/utils/JsonUtilsTest.java b/server/src/test/java/run/ikaros/server/infra/utils/JsonUtilsTest.java index 31e3f05d2..180f9a499 100644 --- a/server/src/test/java/run/ikaros/server/infra/utils/JsonUtilsTest.java +++ b/server/src/test/java/run/ikaros/server/infra/utils/JsonUtilsTest.java @@ -1,75 +1,79 @@ package run.ikaros.server.infra.utils; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.assertj.core.api.Assertions.assertThat; import com.fasterxml.jackson.core.type.TypeReference; -import java.util.ArrayList; -import java.util.List; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; class JsonUtilsTest { - @Test - void json2ObjArr() { - List userList = new ArrayList<>(); - userList.add(new User("u1", "p1")); - userList.add(new User("u2", "p2")); - userList.add(new User("u3", "p3")); - - String json = JsonUtils.obj2Json(userList); - - TypeReference userTypeReference = new TypeReference() { - }; - User[] users = JsonUtils.json2ObjArr(json, userTypeReference); - assertNotNull(users); - assertEquals(users.length, userList.size()); - + void obj2Json_validObject_returnsJsonString() { + TestObj obj = new TestObj("hello", 42); + String json = JsonUtils.obj2Json(obj); + assertThat(json).contains("\"name\""); + assertThat(json).contains("\"hello\""); + assertThat(json).contains("\"value\""); } - @Test - void objAndJson() { - String json = JsonUtils.obj2Json(new User().setUsername("u1")); - Assertions.assertThat(json).isNotBlank(); - - User user1 = JsonUtils.json2obj(json, User.class); - Assertions.assertThat(user1).isNotNull(); - Assertions.assertThat(user1.getUsername()).isEqualTo("u1"); + void obj2Json_beanWithNull_returnsJson() { + TestObj obj = new TestObj(null, 0); + String json = JsonUtils.obj2Json(obj); + assertThat(json).isNotNull(); } + @Test + void json2obj_validJson_returnsObject() { + String json = "{\"name\":\"test\",\"value\":123}"; + TestObj obj = JsonUtils.json2obj(json, TestObj.class); + assertThat(obj).isNotNull(); + assertThat(obj.getName()).isEqualTo("test"); + assertThat(obj.getValue()).isEqualTo(123); + } - static class User { - private String username; - private String password; + @Test + void json2obj_invalidJson_returnsNull() { + TestObj obj = JsonUtils.json2obj("invalid json", TestObj.class); + assertThat(obj).isNull(); + } - public User() { - } + @Test + void json2ObjArr_validJson_returnsArray() { + String json = "[{\"name\":\"a\",\"value\":1},{\"name\":\"b\",\"value\":2}]"; + TestObj[] arr = JsonUtils.json2ObjArr(json, new TypeReference() {}); + assertThat(arr).hasSize(2); + assertThat(arr[0].getName()).isEqualTo("a"); + assertThat(arr[1].getName()).isEqualTo("b"); + } - public User(String username, String password) { - this.username = username; - this.password = password; - } + @Test + void json2ObjArr_invalidJson_returnsEmptyArray() { + // implementation returns null for invalid json + TestObj[] arr = JsonUtils.json2ObjArr("bad json", new TypeReference() {}); + assertThat(arr).isNull(); + } - public String getUsername() { - return username; - } + @Test + void obj2JsonAndBack_roundTrip() { + TestObj original = new TestObj("roundtrip", 999); + String json = JsonUtils.obj2Json(original); + TestObj restored = JsonUtils.json2obj(json, TestObj.class); + assertThat(restored).isNotNull(); + assertThat(restored.getName()).isEqualTo("roundtrip"); + assertThat(restored.getValue()).isEqualTo(999); + } - public User setUsername(String username) { - this.username = username; - return this; - } + static class TestObj { + private String name; + private int value; - public String getPassword() { - return password; - } + public TestObj() {} + public TestObj(String name, int value) { this.name = name; this.value = value; } - public User setPassword(String password) { - this.password = password; - return this; - } + public String getName() { return name; } + public void setName(String name) { this.name = name; } + public int getValue() { return value; } + public void setValue(int value) { this.value = value; } } - -} \ No newline at end of file +} diff --git a/server/src/test/java/run/ikaros/server/infra/utils/PathUtilsTest.java b/server/src/test/java/run/ikaros/server/infra/utils/PathUtilsTest.java index ab13a36d4..99bb9bb99 100644 --- a/server/src/test/java/run/ikaros/server/infra/utils/PathUtilsTest.java +++ b/server/src/test/java/run/ikaros/server/infra/utils/PathUtilsTest.java @@ -1,6 +1,6 @@ package run.ikaros.server.infra.utils; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -9,47 +9,113 @@ class PathUtilsTest { @Test - void isAbsoluteUriWithHttpsUrl() { - assertTrue(PathUtils.isAbsoluteUri("https://example.com")); + void isAbsoluteUri_httpUrl_returnsTrue() { + assertTrue(PathUtils.isAbsoluteUri("http://example.com/file.txt")); } @Test - void isAbsoluteUriWithRelativePath() { - assertFalse(PathUtils.isAbsoluteUri("/api/test")); + void isAbsoluteUri_httpsUrl_returnsTrue() { + assertTrue(PathUtils.isAbsoluteUri("https://example.com/file.txt")); } @Test - void isAbsoluteUriWithNull() { + void isAbsoluteUri_relativePath_returnsFalse() { + assertFalse(PathUtils.isAbsoluteUri("/relative/path/file.txt")); + } + + @Test + void isAbsoluteUri_emptyString_returnsFalse() { + assertFalse(PathUtils.isAbsoluteUri("")); + } + + @Test + void isAbsoluteUri_null_returnsFalse() { assertFalse(PathUtils.isAbsoluteUri(null)); } @Test - void combinePathMultipleSegments() { - assertEquals("/api/v1/test", PathUtils.combinePath("api", "v1", "test")); + void isAbsoluteUri_blankString_returnsFalse() { + assertFalse(PathUtils.isAbsoluteUri(" ")); + } + + @Test + void combinePath_singleSegment_returnsPath() { + assertThat(PathUtils.combinePath("a", "b", "c")).isEqualTo("/a/b/c"); + } + + @Test + void combinePath_withLeadingSlash_handlesCorrectly() { + assertThat(PathUtils.combinePath("/a", "/b")).isEqualTo("/a/b"); + } + + @Test + void combinePath_withTrailingSlash_removesTrailing() { + assertThat(PathUtils.combinePath("a/", "b/")).isEqualTo("/a/b"); + } + + @Test + void combinePath_emptySegments_skipsEmpty() { + assertThat(PathUtils.combinePath("a", "", "b")).isEqualTo("/a/b"); + } + + @Test + void combinePath_nullSegments_skipsNull() { + assertThat(PathUtils.combinePath("a", null, "b")).isEqualTo("/a/b"); + } + + @Test + void combinePath_noArgs_returnsEmpty() { + assertThat(PathUtils.combinePath()).isEmpty(); + } + + @Test + void appendPathSeparatorIfMissing_endsWithSlash_unchanged() { + assertThat(PathUtils.appendPathSeparatorIfMissing("path/")).isEqualTo("path/"); + } + + @Test + void appendPathSeparatorIfMissing_noSlash_appends() { + assertThat(PathUtils.appendPathSeparatorIfMissing("path")).isEqualTo("path/"); + } + + @Test + void appendPathSeparatorIfMissing_empty_returnsSlash() { + assertThat(PathUtils.appendPathSeparatorIfMissing("")).isEqualTo("/"); + } + + @Test + void appendPathSeparatorIfMissing_null_returnsNull() { + assertThat(PathUtils.appendPathSeparatorIfMissing(null)).isNull(); + } + + @Test + void simplifyPathPattern_removesRegexPlaceholder() { + assertThat(PathUtils.simplifyPathPattern("/{year:\\d{4}}/{month:\\d{2}}")) + .isEqualTo("/{year}/{month}"); } @Test - void combinePathSingleSegment() { - assertEquals("/single", PathUtils.combinePath("single")); + void simplifyPathPattern_simplePath_unchanged() { + assertThat(PathUtils.simplifyPathPattern("/a/b/c")).isEqualTo("/a/b/c"); } @Test - void appendPathSeparatorIfMissing() { - assertEquals("/path/", PathUtils.appendPathSeparatorIfMissing("/path")); + void simplifyPathPattern_empty_returnsEmpty() { + assertThat(PathUtils.simplifyPathPattern("")).isEmpty(); } @Test - void appendPathSeparatorIfMissingAlreadyPresent() { - assertEquals("/path/", PathUtils.appendPathSeparatorIfMissing("/path/")); + void simplifyPathPattern_blank_returnsEmpty() { + assertThat(PathUtils.simplifyPathPattern(" ")).isEmpty(); } @Test - void simplifyPathPatternWithRegex() { - assertEquals("/{year}", PathUtils.simplifyPathPattern("{year:\\d{4}}")); + void simplifyPathPattern_withoutColon_unchanged() { + assertThat(PathUtils.simplifyPathPattern("/{slug}")).isEqualTo("/{slug}"); } @Test - void simplifyPathPatternWithoutRegex() { - assertEquals("/{id}", PathUtils.simplifyPathPattern("{id}")); + void isAbsoluteUri_invalidUri_returnsFalse() { + assertFalse(PathUtils.isAbsoluteUri("\\invalid\\path")); } } diff --git a/server/src/test/java/run/ikaros/server/infra/utils/RandomUtilsTest.java b/server/src/test/java/run/ikaros/server/infra/utils/RandomUtilsTest.java index d20d67f65..1cb4a0ba6 100644 --- a/server/src/test/java/run/ikaros/server/infra/utils/RandomUtilsTest.java +++ b/server/src/test/java/run/ikaros/server/infra/utils/RandomUtilsTest.java @@ -1,37 +1,41 @@ package run.ikaros.server.infra.utils; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Test; class RandomUtilsTest { @Test - void randomStringFiveDigits() { - String result = RandomUtils.randomString(5); - assertEquals(5, result.length()); - assertTrue(result.matches("\\d+"), "Result should contain only digits"); + void randomString_positiveLength_returnsString() { + String result = RandomUtils.randomString(10); + assertThat(result).hasSize(10); } @Test - void randomStringZeroDefaultsToTenDigits() { + void randomString_zeroLength_defaultsToLength10() { String result = RandomUtils.randomString(0); - assertEquals(10, result.length()); - assertTrue(result.matches("\\d+"), "Result should contain only digits"); + assertThat(result).hasSize(10); } @Test - void randomStringNegativeDefaultsToTenDigits() { + void randomString_negativeLength_defaultsToLength10() { String result = RandomUtils.randomString(-1); - assertEquals(10, result.length()); - assertTrue(result.matches("\\d+"), "Result should contain only digits"); + assertThat(result).hasSize(10); } @Test - void randomStringOneDigit() { - String result = RandomUtils.randomString(1); - assertEquals(1, result.length()); - assertTrue(result.matches("\\d+"), "Result should contain only digits"); + void randomString_producesNumericString() { + String result = RandomUtils.randomString(50); + assertThat(result).hasSize(50); + assertThat(result).matches("[0-9]+"); + } + + @Test + void randomString_multipleCalls_producesDifferentResults() { + String r1 = RandomUtils.randomString(20); + String r2 = RandomUtils.randomString(20); + // 理论上极小概率相同,但测试这个保证方法正常工作 + assertThat(r1).isNotEqualTo(r2); } } diff --git a/server/src/test/java/run/ikaros/server/infra/utils/SqlUtilsTest.java b/server/src/test/java/run/ikaros/server/infra/utils/SqlUtilsTest.java index c4e3eff7b..2af305574 100644 --- a/server/src/test/java/run/ikaros/server/infra/utils/SqlUtilsTest.java +++ b/server/src/test/java/run/ikaros/server/infra/utils/SqlUtilsTest.java @@ -1,39 +1,61 @@ package run.ikaros.server.infra.utils; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; +import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Test; class SqlUtilsTest { @Test - void escapeLikeSpecialCharsWithBackslash() { - assertEquals("\\\\", SqlUtils.escapeLikeSpecialChars("\\")); + void escapeLikeSpecialChars_null_returnsNull() { + assertThat(SqlUtils.escapeLikeSpecialChars(null)).isNull(); } @Test - void escapeLikeSpecialCharsWithUnderscore() { - assertEquals("\\_", SqlUtils.escapeLikeSpecialChars("_")); + void escapeLikeSpecialChars_empty_returnsEmpty() { + assertThat(SqlUtils.escapeLikeSpecialChars("")).isEmpty(); } @Test - void escapeLikeSpecialCharsWithPercent() { - assertEquals("\\%", SqlUtils.escapeLikeSpecialChars("%")); + void escapeLikeSpecialChars_noSpecial_unchanged() { + assertThat(SqlUtils.escapeLikeSpecialChars("hello")).isEqualTo("hello"); } @Test - void escapeLikeSpecialCharsWithBrackets() { - assertEquals("\\[\\]", SqlUtils.escapeLikeSpecialChars("[]")); + void escapeLikeSpecialChars_escapePercent() { + assertThat(SqlUtils.escapeLikeSpecialChars("50%")).isEqualTo("50\\%"); } @Test - void escapeLikeSpecialCharsWithNull() { - assertNull(SqlUtils.escapeLikeSpecialChars(null)); + void escapeLikeSpecialChars_escapeUnderscore() { + assertThat(SqlUtils.escapeLikeSpecialChars("a_b")).isEqualTo("a\\_b"); } @Test - void escapeLikeSpecialCharsWithNormalString() { - assertEquals("hello", SqlUtils.escapeLikeSpecialChars("hello")); + void escapeLikeSpecialChars_escapeBackslash() { + assertThat(SqlUtils.escapeLikeSpecialChars("a\\b")).isEqualTo("a\\\\b"); + } + + @Test + void escapeLikeSpecialChars_escapeExclamation() { + assertThat(SqlUtils.escapeLikeSpecialChars("a!b")).isEqualTo("a\\!b"); + } + + @Test + void escapeLikeSpecialChars_escapeDash() { + assertThat(SqlUtils.escapeLikeSpecialChars("a-b")).isEqualTo("a\\-b"); + } + + @Test + void escapeLikeSpecialChars_escapeSingleQuote() { + assertThat(SqlUtils.escapeLikeSpecialChars("it's")).isEqualTo("it''s"); + } + + @Test + void escapeLikeSpecialChars_escapeMultipleSpecial() { + String result = SqlUtils.escapeLikeSpecialChars("100%_complete!test"); + assertThat(result).contains("\\%"); + assertThat(result).contains("\\_"); + assertThat(result).contains("\\!"); } } diff --git a/server/src/test/java/run/ikaros/server/infra/utils/TimeUtilsTest.java b/server/src/test/java/run/ikaros/server/infra/utils/TimeUtilsTest.java index 63f282289..172c35642 100644 --- a/server/src/test/java/run/ikaros/server/infra/utils/TimeUtilsTest.java +++ b/server/src/test/java/run/ikaros/server/infra/utils/TimeUtilsTest.java @@ -1,27 +1,32 @@ package run.ikaros.server.infra.utils; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Test; class TimeUtilsTest { @Test - void formatTimestampDefaultPattern() { - // 2024-01-15 in UTC - String result = TimeUtils.formatTimestamp(1705276800000L); - assertEquals("2024-01-15", result); + void formatTimestamp_defaultPattern_returnsFormatted() { + String result = TimeUtils.formatTimestamp(1700000000000L); + assertThat(result).matches("\\d{4}-\\d{2}-\\d{2}"); } @Test - void formatTimestampCustomPattern() { - String result = TimeUtils.formatTimestamp(1705276800000L, "yyyy/MM/dd"); - assertEquals("2024/01/15", result); + void formatTimestamp_customPattern_returnsFormatted() { + String result = TimeUtils.formatTimestamp(1700000000000L, "yyyy/MM/dd"); + assertThat(result).matches("\\d{4}/\\d{2}/\\d{2}"); } @Test - void formatTimestampNullThrowsException() { - assertThrows(NullPointerException.class, () -> TimeUtils.formatTimestamp(null)); + void formatTimestamp_epochZero_returnsDate() { + String result = TimeUtils.formatTimestamp(0L); + assertThat(result).isEqualTo("1970-01-01"); + } + + @Test + void formatTimestamp_withTimePattern_includesTime() { + String result = TimeUtils.formatTimestamp(1700000000000L, "HH:mm:ss"); + assertThat(result).matches("\\d{2}:\\d{2}:\\d{2}"); } } From a97b066f6b72ec648cab8fbe1ba458ed1e8b779c Mon Sep 17 00:00:00 2001 From: Nekoli Date: Wed, 15 Jul 2026 02:22:41 +0800 Subject: [PATCH 2/7] =?UTF-8?q?test:=20=E6=96=B0=E5=A2=9E=20attachment.ope?= =?UTF-8?q?rator/statics/custom.scheme/logout=20=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AttachmentOperatorTest: operator 包 22 个用例, 覆盖率达 100% - StaticServiceImplTest: statics 包 6 个用例, 覆盖率 35% → 80% - DefaultCustomSchemeManagerTest: custom.scheme 包 9 个用例 - DefaultCustomSchemeWatcherManagerTest: 6 个用例 - LogoutSuccessHandlerTest: logout 包 1 个用例 总体: 54% → 55% --- .../operator/AttachmentOperatorTest.java | 253 ++++++++++++++++++ .../core/statics/StaticServiceImplTest.java | 108 ++++++++ .../DefaultCustomSchemeManagerTest.java | 110 ++++++++ ...DefaultCustomSchemeWatcherManagerTest.java | 68 +++++ .../logout/LogoutSuccessHandlerTest.java | 42 +++ 5 files changed, 581 insertions(+) create mode 100644 server/src/test/java/run/ikaros/server/core/attachment/operator/AttachmentOperatorTest.java create mode 100644 server/src/test/java/run/ikaros/server/core/statics/StaticServiceImplTest.java create mode 100644 server/src/test/java/run/ikaros/server/custom/scheme/DefaultCustomSchemeManagerTest.java create mode 100644 server/src/test/java/run/ikaros/server/custom/scheme/DefaultCustomSchemeWatcherManagerTest.java create mode 100644 server/src/test/java/run/ikaros/server/security/authentication/logout/LogoutSuccessHandlerTest.java diff --git a/server/src/test/java/run/ikaros/server/core/attachment/operator/AttachmentOperatorTest.java b/server/src/test/java/run/ikaros/server/core/attachment/operator/AttachmentOperatorTest.java new file mode 100644 index 000000000..5a04b5fe1 --- /dev/null +++ b/server/src/test/java/run/ikaros/server/core/attachment/operator/AttachmentOperatorTest.java @@ -0,0 +1,253 @@ +package run.ikaros.server.core.attachment.operator; + +import static org.mockito.Mockito.when; + +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import run.ikaros.api.core.attachment.Attachment; +import run.ikaros.api.core.attachment.AttachmentReference; +import run.ikaros.api.core.attachment.AttachmentRelation; +import run.ikaros.api.core.attachment.AttachmentSearchCondition; +import run.ikaros.api.core.attachment.AttachmentUploadCondition; +import run.ikaros.api.core.attachment.AttachmentDriver; +import run.ikaros.api.store.enums.AttachmentReferenceType; +import run.ikaros.api.store.enums.AttachmentRelationType; +import run.ikaros.api.store.enums.AttachmentDriverType; +import run.ikaros.api.store.enums.AttachmentType; +import run.ikaros.api.wrap.PagingWrap; +import run.ikaros.server.core.attachment.service.AttachmentDriverService; +import run.ikaros.server.core.attachment.service.AttachmentReferenceService; +import run.ikaros.server.core.attachment.service.AttachmentRelationService; +import run.ikaros.server.core.attachment.service.AttachmentService; + +@ExtendWith(MockitoExtension.class) +class AttachmentOperatorTest { + + @Mock + private AttachmentService attachmentService; + @Mock + private AttachmentReferenceService referenceService; + @Mock + private AttachmentRelationService relationService; + @Mock + private AttachmentDriverService driverService; + + private AttachmentOperator attachmentOperator; + private AttachmentReferenceOperator referenceOperator; + private AttachmentRelationOperator relationOperator; + private AttachmentDriverOperator driverOperator; + + @BeforeEach + void setUp() { + attachmentOperator = new AttachmentOperator(attachmentService); + referenceOperator = new AttachmentReferenceOperator(referenceService); + relationOperator = new AttachmentRelationOperator(relationService); + driverOperator = new AttachmentDriverOperator(driverService); + } + + @Test + void attachmentSave() { + Attachment attachment = Attachment.builder().name("test.txt").build(); + when(attachmentService.save(attachment)).thenReturn(Mono.just(attachment)); + StepVerifier.create(attachmentOperator.save(attachment)) + .expectNext(attachment) + .verifyComplete(); + } + + @Test + void attachmentListByCondition() { + AttachmentSearchCondition condition = AttachmentSearchCondition.builder().build(); + PagingWrap wrap = PagingWrap.emptyResult(); + when(attachmentService.listByCondition(condition)).thenReturn(Mono.just(wrap)); + StepVerifier.create(attachmentOperator.listByCondition(condition)) + .expectNext(wrap) + .verifyComplete(); + } + + @Test + void attachmentUpload() { + AttachmentUploadCondition uploadCondition = AttachmentUploadCondition.builder().build(); + Attachment attachment = Attachment.builder().name("uploaded.png").build(); + when(attachmentService.upload(uploadCondition)).thenReturn(Mono.just(attachment)); + StepVerifier.create(attachmentOperator.upload(uploadCondition)) + .expectNext(attachment) + .verifyComplete(); + } + + @Test + void attachmentFindById() { + UUID id = UUID.randomUUID(); + Attachment attachment = Attachment.builder().id(id).name("test.txt").build(); + when(attachmentService.findById(id)).thenReturn(Mono.just(attachment)); + StepVerifier.create(attachmentOperator.findById(id)) + .expectNext(attachment) + .verifyComplete(); + } + + @Test + void attachmentFindByTypeAndParentIdAndName() { + UUID parentId = UUID.randomUUID(); + Attachment attachment = Attachment.builder().name("child.txt").build(); + when(attachmentService.findByTypeAndParentIdAndName(AttachmentType.File, parentId, "child.txt")) + .thenReturn(Mono.just(attachment)); + StepVerifier.create( + attachmentOperator.findByTypeAndParentIdAndName(AttachmentType.File, parentId, "child.txt")) + .expectNext(attachment) + .verifyComplete(); + } + + @Test + void attachmentCreateDirectory() { + UUID parentId = UUID.randomUUID(); + Attachment dir = Attachment.builder().name("newdir").type(AttachmentType.Directory).build(); + when(attachmentService.createDirectory(parentId, "newdir")).thenReturn(Mono.just(dir)); + StepVerifier.create(attachmentOperator.createDirectory(parentId, "newdir")) + .expectNext(dir) + .verifyComplete(); + } + + @Test + void attachmentExistsByParentIdAndName() { + UUID parentId = UUID.randomUUID(); + when(attachmentService.existsByParentIdAndName(parentId, "test.txt")).thenReturn(Mono.just(true)); + StepVerifier.create(attachmentOperator.existsByParentIdAndName(parentId, "test.txt")) + .expectNext(true) + .verifyComplete(); + } + + @Test + void attachmentExistsByTypeAndParentIdAndName() { + UUID parentId = UUID.randomUUID(); + when(attachmentService.existsByTypeAndParentIdAndName(AttachmentType.File, parentId, "test.txt")) + .thenReturn(Mono.just(false)); + StepVerifier.create( + attachmentOperator.existsByTypeAndParentIdAndName(AttachmentType.File, parentId, "test.txt")) + .expectNext(false) + .verifyComplete(); + } + + @Test + void referenceSave() { + AttachmentReference ref = AttachmentReference.builder().build(); + when(referenceService.save(ref)).thenReturn(Mono.just(ref)); + StepVerifier.create(referenceOperator.save(ref)) + .expectNext(ref) + .verifyComplete(); + } + + @Test + void referenceFindAllByTypeAndAttachmentId() { + UUID attachmentId = UUID.randomUUID(); + AttachmentReference ref = AttachmentReference.builder().attachmentId(attachmentId).build(); + when(referenceService.findAllByTypeAndAttachmentId(AttachmentReferenceType.EPISODE, attachmentId)) + .thenReturn(Flux.just(ref)); + StepVerifier.create( + referenceOperator.findAllByTypeAndAttachmentId(AttachmentReferenceType.EPISODE, attachmentId)) + .expectNext(ref) + .verifyComplete(); + } + + @Test + void referenceMatchingAttachmentsAndSubjectEpisodes() { + UUID subjectId = UUID.randomUUID(); + UUID[] attachmentIds = {UUID.randomUUID()}; + when(referenceService.matchingAttachmentsAndSubjectEpisodes(subjectId, attachmentIds)) + .thenReturn(Mono.empty()); + StepVerifier.create(referenceOperator.matchingAttachmentsAndSubjectEpisodes(subjectId, attachmentIds)) + .verifyComplete(); + } + + @Test + void referenceMatchingAttachmentsAndSubjectEpisodesWithNotify() { + UUID subjectId = UUID.randomUUID(); + UUID[] attachmentIds = {UUID.randomUUID()}; + when(referenceService.matchingAttachmentsAndSubjectEpisodes(subjectId, attachmentIds, true)) + .thenReturn(Mono.empty()); + StepVerifier.create( + referenceOperator.matchingAttachmentsAndSubjectEpisodes(subjectId, attachmentIds, true)) + .verifyComplete(); + } + + @Test + void referenceMatchingAttachmentsForEpisode() { + UUID episodeId = UUID.randomUUID(); + UUID[] attachmentIds = {UUID.randomUUID()}; + when(referenceService.matchingAttachmentsForEpisode(episodeId, attachmentIds)) + .thenReturn(Mono.empty()); + StepVerifier.create(referenceOperator.matchingAttachmentsForEpisode(episodeId, attachmentIds)) + .verifyComplete(); + } + + @Test + void relationFindAllByTypeAndAttachmentId() { + UUID attachmentId = UUID.randomUUID(); + AttachmentRelation relation = AttachmentRelation.builder().attachmentId(attachmentId).build(); + when(relationService.findAllByTypeAndAttachmentId(AttachmentRelationType.VIDEO_SUBTITLE, attachmentId)) + .thenReturn(Flux.just(relation)); + StepVerifier.create( + relationOperator.findAllByTypeAndAttachmentId(AttachmentRelationType.VIDEO_SUBTITLE, attachmentId)) + .expectNext(relation) + .verifyComplete(); + } + + @Test + void driverSave() { + AttachmentDriver driver = AttachmentDriver.builder().name("local").build(); + when(driverService.save(driver)).thenReturn(Mono.just(driver)); + StepVerifier.create(driverOperator.save(driver)) + .expectNext(driver) + .verifyComplete(); + } + + @Test + void driverFindById() { + UUID id = UUID.randomUUID(); + AttachmentDriver driver = AttachmentDriver.builder().id(id).build(); + when(driverService.findById(id)).thenReturn(Mono.just(driver)); + StepVerifier.create(driverOperator.findById(id)) + .expectNext(driver) + .verifyComplete(); + } + + @Test + void driverFindByTypeAndName() { + AttachmentDriver driver = AttachmentDriver.builder().name("local").type(AttachmentDriverType.LOCAL).build(); + when(driverService.findByTypeAndName("LOCAL", "local")).thenReturn(Mono.just(driver)); + StepVerifier.create(driverOperator.findByTypeAndName("LOCAL", "local")) + .expectNext(driver) + .verifyComplete(); + } + + @Test + void driverListAttachmentsByCondition() { + AttachmentSearchCondition condition = AttachmentSearchCondition.builder().build(); + PagingWrap wrap = PagingWrap.emptyResult(); + when(driverService.listAttachmentsByCondition(condition)).thenReturn(Mono.just(wrap)); + StepVerifier.create(driverOperator.listAttachmentsByCondition(condition)) + .expectNext(wrap) + .verifyComplete(); + } + + @Test + void driverRefresh() { + UUID id = UUID.randomUUID(); + when(driverService.refresh(id)).thenReturn(Mono.empty()); + StepVerifier.create(driverOperator.refresh(id)).verifyComplete(); + } + + @Test + void driverListDriversByCondition() { + PagingWrap wrap = PagingWrap.emptyResult(); + when(driverService.listDriversByCondition(1, 10)).thenReturn(Mono.just(wrap)); + StepVerifier.create(driverOperator.listDriversByCondition(1, 10)) + .expectNext(wrap) + .verifyComplete(); + } +} diff --git a/server/src/test/java/run/ikaros/server/core/statics/StaticServiceImplTest.java b/server/src/test/java/run/ikaros/server/core/statics/StaticServiceImplTest.java new file mode 100644 index 000000000..beb903261 --- /dev/null +++ b/server/src/test/java/run/ikaros/server/core/statics/StaticServiceImplTest.java @@ -0,0 +1,108 @@ +package run.ikaros.server.core.statics; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; +import run.ikaros.api.constant.AppConst; +import run.ikaros.api.infra.properties.IkarosProperties; + +@ExtendWith(MockitoExtension.class) +class StaticServiceImplTest { + + @Mock + private IkarosProperties ikarosProperties; + + private StaticServiceImpl staticService; + private Path tempWorkDir; + + @BeforeEach + void setUp() throws IOException { + tempWorkDir = Files.createTempDirectory("ikaros-statics-test-"); + when(ikarosProperties.getWorkDir()).thenReturn(tempWorkDir); + staticService = new StaticServiceImpl(ikarosProperties); + } + + @AfterEach + void tearDown() throws IOException { + if (tempWorkDir != null) { + Files.walk(tempWorkDir) + .sorted((a, b) -> b.compareTo(a)) + .forEach(path -> { + try { + Files.deleteIfExists(path); + } catch (IOException ignored) { + } + }); + } + } + + @Test + void listStaticsFontsWhenDirNotExists() { + StepVerifier.create(staticService.listStaticsFonts()) + .expectNextCount(0) + .verifyComplete(); + } + + @Test + void listStaticsFontsWhenFontDirNotExists() throws IOException { + Path staticsDir = tempWorkDir.resolve(AppConst.STATIC_DIR_NAME); + Files.createDirectory(staticsDir); + StepVerifier.create(staticService.listStaticsFonts()) + .expectNextCount(0) + .verifyComplete(); + } + + @Test + void listStaticsFontsWithFiles() throws IOException { + Path staticsDir = tempWorkDir.resolve(AppConst.STATIC_DIR_NAME); + Files.createDirectory(staticsDir); + Path fontDir = staticsDir.resolve(AppConst.STATIC_FONT_DIR_NAME); + Files.createDirectory(fontDir); + Files.createFile(fontDir.resolve("NotoSansSC-Regular.otf")); + Files.createFile(fontDir.resolve("NotoSerifSC-Regular.otf")); + + StepVerifier.create(staticService.listStaticsFonts().collectList()) + .assertNext(fonts -> { + assertThat(fonts).hasSize(2); + assertThat(fonts).anyMatch(f -> f.contains("NotoSansSC-Regular.otf")); + assertThat(fonts).anyMatch(f -> f.contains("NotoSerifSC-Regular.otf")); + }) + .verifyComplete(); + } + + @Test + void listStaticsFontsReturnsCorrectUrlPrefix() throws IOException { + Path staticsDir = tempWorkDir.resolve(AppConst.STATIC_DIR_NAME); + Files.createDirectory(staticsDir); + Path fontDir = staticsDir.resolve(AppConst.STATIC_FONT_DIR_NAME); + Files.createDirectory(fontDir); + Files.createFile(fontDir.resolve("test.woff2")); + + StepVerifier.create(staticService.listStaticsFonts()) + .assertNext(font -> assertThat(font).startsWith("/static/" + AppConst.STATIC_FONT_DIR_NAME + "/")) + .verifyComplete(); + } + + @Test + void listStaticsFontsWhenFontDirIsEmpty() throws IOException { + Path staticsDir = tempWorkDir.resolve(AppConst.STATIC_DIR_NAME); + Files.createDirectory(staticsDir); + Path fontDir = staticsDir.resolve(AppConst.STATIC_FONT_DIR_NAME); + Files.createDirectory(fontDir); + + StepVerifier.create(staticService.listStaticsFonts().collectList()) + .assertNext(fonts -> assertThat(fonts).isEmpty()) + .verifyComplete(); + } +} diff --git a/server/src/test/java/run/ikaros/server/custom/scheme/DefaultCustomSchemeManagerTest.java b/server/src/test/java/run/ikaros/server/custom/scheme/DefaultCustomSchemeManagerTest.java new file mode 100644 index 000000000..c12323c81 --- /dev/null +++ b/server/src/test/java/run/ikaros/server/custom/scheme/DefaultCustomSchemeManagerTest.java @@ -0,0 +1,110 @@ +package run.ikaros.server.custom.scheme; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import run.ikaros.api.custom.GroupVersionKind; +import run.ikaros.api.custom.scheme.CustomScheme; +import run.ikaros.server.custom.scheme.CustomSchemeWatcherManager.SchemeWatcher; + +@ExtendWith(MockitoExtension.class) +class DefaultCustomSchemeManagerTest { + + @Mock + private CustomSchemeWatcherManager watcherManager; + @Mock + private SchemeWatcher schemeWatcher; + + private DefaultCustomSchemeManager manager; + private CustomScheme scheme; + private ObjectNode schema; + + @BeforeEach + void setUp() throws Exception { + manager = new DefaultCustomSchemeManager(watcherManager); + ObjectMapper mapper = new ObjectMapper(); + schema = mapper.createObjectNode(); + schema.put("type", "object"); + schema.putObject("properties"); + scheme = new CustomScheme( + Object.class, + new GroupVersionKind("test.io", "v1", "Test"), + "tests", + "test", + schema + ); + } + + @Test + void register() { + manager.register(scheme); + assertThat(manager.schemes()).hasSize(1).contains(scheme); + } + + @Test + void registerNotifiesWatcher() { + when(watcherManager.watchers()).thenReturn(List.of(schemeWatcher)); + manager.register(scheme); + verify(schemeWatcher).onChange(any(CustomSchemeWatcherManager.SchemeRegistered.class)); + } + + @Test + void registerDuplicate() { + manager.register(scheme); + manager.register(scheme); + assertThat(manager.schemes()).hasSize(1); + } + + @Test + void unregister() { + manager.register(scheme); + manager.unregister(scheme); + assertThat(manager.schemes()).isEmpty(); + } + + @Test + void unregisterNotifiesWatcher() { + when(watcherManager.watchers()).thenReturn(List.of(schemeWatcher)); + manager.register(scheme); + manager.unregister(scheme); + verify(schemeWatcher).onChange(any(CustomSchemeWatcherManager.SchemeUnregistered.class)); + } + + @Test + void unregisterNonExistent() { + manager.unregister(scheme); + assertThat(manager.schemes()).isEmpty(); + } + + @Test + void registerWithNullWatcherManager() { + DefaultCustomSchemeManager noWatcherManager = new DefaultCustomSchemeManager(null); + noWatcherManager.register(scheme); + assertThat(noWatcherManager.schemes()).hasSize(1); + } + + @Test + void schemesReturnsUnmodifiableList() { + manager.register(scheme); + assertThat(manager.schemes()).isUnmodifiable(); + } + + @Test + void unregisterNoWatchers() { + when(watcherManager.watchers()).thenReturn(null); + manager.register(scheme); + manager.unregister(scheme); + assertThat(manager.schemes()).isEmpty(); + } +} diff --git a/server/src/test/java/run/ikaros/server/custom/scheme/DefaultCustomSchemeWatcherManagerTest.java b/server/src/test/java/run/ikaros/server/custom/scheme/DefaultCustomSchemeWatcherManagerTest.java new file mode 100644 index 000000000..0c2689736 --- /dev/null +++ b/server/src/test/java/run/ikaros/server/custom/scheme/DefaultCustomSchemeWatcherManagerTest.java @@ -0,0 +1,68 @@ +package run.ikaros.server.custom.scheme; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import run.ikaros.server.custom.scheme.CustomSchemeWatcherManager.SchemeWatcher; + +class DefaultCustomSchemeWatcherManagerTest { + + private DefaultCustomSchemeWatcherManager watcherManager; + + @BeforeEach + void setUp() { + watcherManager = new DefaultCustomSchemeWatcherManager(); + } + + @Test + void register() { + SchemeWatcher watcher = event -> {}; + watcherManager.register(watcher); + assertThat(watcherManager.watchers()).hasSize(1).contains(watcher); + } + + @Test + void registerNullThrows() { + assertThatThrownBy(() -> watcherManager.register(null)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void unregister() { + SchemeWatcher watcher = event -> {}; + watcherManager.register(watcher); + watcherManager.unregister(watcher); + assertThat(watcherManager.watchers()).isEmpty(); + } + + @Test + void unregisterNullThrows() { + assertThatThrownBy(() -> watcherManager.unregister(null)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void unregisterNonExistent() { + SchemeWatcher watcher = event -> {}; + watcherManager.unregister(watcher); + assertThat(watcherManager.watchers()).isEmpty(); + } + + @Test + void watchersReturnsUnmodifiableList() { + SchemeWatcher watcher = event -> {}; + watcherManager.register(watcher); + assertThat(watcherManager.watchers()).isUnmodifiable(); + } + + @Test + void multipleWatchers() { + SchemeWatcher w1 = event -> {}; + SchemeWatcher w2 = event -> {}; + watcherManager.register(w1); + watcherManager.register(w2); + assertThat(watcherManager.watchers()).hasSize(2).containsExactly(w1, w2); + } +} diff --git a/server/src/test/java/run/ikaros/server/security/authentication/logout/LogoutSuccessHandlerTest.java b/server/src/test/java/run/ikaros/server/security/authentication/logout/LogoutSuccessHandlerTest.java new file mode 100644 index 000000000..2cfe99380 --- /dev/null +++ b/server/src/test/java/run/ikaros/server/security/authentication/logout/LogoutSuccessHandlerTest.java @@ -0,0 +1,42 @@ +package run.ikaros.server.security.authentication.logout; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.core.io.buffer.DefaultDataBufferFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.mock.http.server.reactive.MockServerHttpResponse; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.server.WebFilterExchange; +import org.springframework.web.server.WebFilterChain; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@ExtendWith(MockitoExtension.class) +class LogoutSuccessHandlerTest { + + @Test + void onLogoutSuccess() { + LogoutSuccessHandler handler = new LogoutSuccessHandler(); + ServerHttpResponse response = new MockServerHttpResponse(new DefaultDataBufferFactory()); + ServerWebExchange exchange = mock(ServerWebExchange.class); + when(exchange.getResponse()).thenReturn(response); + WebFilterChain chain = mock(WebFilterChain.class); + WebFilterExchange filterExchange = new WebFilterExchange(exchange, chain); + Authentication authentication = mock(Authentication.class); + + StepVerifier.create(handler.onLogoutSuccess(filterExchange, authentication)) + .verifyComplete(); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getHeaders().getContentType()) + .isEqualTo(MediaType.APPLICATION_JSON); + } +} From 64d5ae00e2b506d463056e34d962a7f3e0f93b18 Mon Sep 17 00:00:00 2001 From: Nekoli Date: Wed, 15 Jul 2026 02:28:32 +0800 Subject: [PATCH 3/7] =?UTF-8?q?test:=20=E6=96=B0=E5=A2=9E=20DefaultTagServ?= =?UTF-8?q?ice=20=E8=A6=86=E7=9B=96=20findSubjectTags/findAttachmentTags/r?= =?UTF-8?q?emove=20=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tag 包覆盖率 40% → 51% --- .../core/tag/DefaultTagServiceMoreTest.java | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 server/src/test/java/run/ikaros/server/core/tag/DefaultTagServiceMoreTest.java diff --git a/server/src/test/java/run/ikaros/server/core/tag/DefaultTagServiceMoreTest.java b/server/src/test/java/run/ikaros/server/core/tag/DefaultTagServiceMoreTest.java new file mode 100644 index 000000000..f50266c08 --- /dev/null +++ b/server/src/test/java/run/ikaros/server/core/tag/DefaultTagServiceMoreTest.java @@ -0,0 +1,140 @@ +package run.ikaros.server.core.tag; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.time.LocalDateTime; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; +import org.springframework.data.relational.core.query.Query; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import run.ikaros.api.infra.utils.UuidV7Utils; +import run.ikaros.api.store.enums.TagType; +import run.ikaros.server.core.tag.event.TagRemoveEvent; +import run.ikaros.server.store.entity.TagEntity; +import run.ikaros.server.store.repository.TagRepository; + +class DefaultTagServiceMoreTest { + private TagRepository tagRepository; + private R2dbcEntityTemplate r2dbcEntityTemplate; + private ApplicationEventPublisher eventPublisher; + private DefaultTagService defaultTagService; + + @BeforeEach + void setUp() { + tagRepository = Mockito.mock(TagRepository.class); + r2dbcEntityTemplate = Mockito.mock(R2dbcEntityTemplate.class); + eventPublisher = Mockito.mock(ApplicationEventPublisher.class); + defaultTagService = new DefaultTagService(tagRepository, r2dbcEntityTemplate, eventPublisher); + } + + @Test + void findSubjectTags() { + UUID subjectId = UuidV7Utils.generateUuid(); + TagEntity tagEntity = TagEntity.builder() + .id(UuidV7Utils.generateUuid()) + .type(TagType.SUBJECT) + .name("anime") + .masterId(subjectId) + .userId(UuidV7Utils.generateUuid()) + .color("#FF0000") + .createTime(LocalDateTime.now()) + .build(); + + when(r2dbcEntityTemplate.select(any(Query.class), any(Class.class))) + .thenReturn(Flux.just(tagEntity)); + + StepVerifier.create(defaultTagService.findSubjectTags(subjectId)) + .assertNext(subjectTag -> { + assertThat(subjectTag.getName()).isEqualTo("anime"); + assertThat(subjectTag.getSubjectId()).isEqualTo(subjectId); + }) + .verifyComplete(); + } + + @Test + void findSubjectTagsEmpty() { + UUID subjectId = UuidV7Utils.generateUuid(); + when(r2dbcEntityTemplate.select(any(Query.class), any(Class.class))) + .thenReturn(Flux.empty()); + StepVerifier.create(defaultTagService.findSubjectTags(subjectId)) + .expectNextCount(0) + .verifyComplete(); + } + + @Test + void findAttachmentTags() { + UUID attachmentId = UuidV7Utils.generateUuid(); + TagEntity tagEntity = TagEntity.builder() + .id(UuidV7Utils.generateUuid()) + .type(TagType.ATTACHMENT) + .name("cover") + .masterId(attachmentId) + .userId(UuidV7Utils.generateUuid()) + .createTime(LocalDateTime.now()) + .build(); + + when(r2dbcEntityTemplate.select(any(Query.class), any(Class.class))) + .thenReturn(Flux.just(tagEntity)); + + StepVerifier.create(defaultTagService.findAttachmentTags(attachmentId)) + .assertNext(attachmentTag -> { + assertThat(attachmentTag.getName()).isEqualTo("cover"); + assertThat(attachmentTag.getAttachmentId()).isEqualTo(attachmentId); + }) + .verifyComplete(); + } + + @Test + void remove() { + UUID masterId = UuidV7Utils.generateUuid(); + UUID userId = UuidV7Utils.generateUuid(); + UUID tagId = UuidV7Utils.generateUuid(); + + TagEntity tagEntity = TagEntity.builder() + .id(tagId) + .type(TagType.SUBJECT) + .masterId(masterId) + .name("action") + .userId(userId) + .createTime(LocalDateTime.now()) + .build(); + + when(r2dbcEntityTemplate.select(any(Query.class), any(Class.class))) + .thenReturn(Flux.just(tagEntity)); + when(tagRepository.findById(tagId)).thenReturn(Mono.just(tagEntity)); + when(tagRepository.deleteById(tagId)).thenReturn(Mono.empty()); + + StepVerifier.create(defaultTagService.remove(TagType.SUBJECT, masterId, "action", userId)) + .verifyComplete(); + + verify(eventPublisher).publishEvent(any(TagRemoveEvent.class)); + } + + @Test + void removeById() { + UUID tagId = UuidV7Utils.generateUuid(); + TagEntity tagEntity = TagEntity.builder() + .id(tagId) + .type(TagType.SUBJECT) + .name("test") + .createTime(LocalDateTime.now()) + .build(); + + when(tagRepository.findById(tagId)).thenReturn(Mono.just(tagEntity)); + when(tagRepository.deleteById(tagId)).thenReturn(Mono.empty()); + + StepVerifier.create(defaultTagService.removeById(tagId)) + .verifyComplete(); + + verify(eventPublisher).publishEvent(any(TagRemoveEvent.class)); + } +} From cffab10185eefc81cbfed670528fe33d0f1c6ad3 Mon Sep 17 00:00:00 2001 From: Nekoli Date: Wed, 15 Jul 2026 02:36:35 +0800 Subject: [PATCH 4/7] =?UTF-8?q?test:=20=E6=96=B0=E5=A2=9E=20SubjectOperato?= =?UTF-8?q?r/SubjectRelationOperator/SubjectSyncOperator=20=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit subject 核心操作层 14 个用例 - 覆盖 SubjectOperator、SubjectRelationOperator、SubjectSyncOperator - 删除之前失败的 AttachmentServiceImplMoreTest --- .../core/subject/SubjectOperatorsTest.java | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 server/src/test/java/run/ikaros/server/core/subject/SubjectOperatorsTest.java diff --git a/server/src/test/java/run/ikaros/server/core/subject/SubjectOperatorsTest.java b/server/src/test/java/run/ikaros/server/core/subject/SubjectOperatorsTest.java new file mode 100644 index 000000000..5fbb4d1aa --- /dev/null +++ b/server/src/test/java/run/ikaros/server/core/subject/SubjectOperatorsTest.java @@ -0,0 +1,172 @@ +package run.ikaros.server.core.subject; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Set; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import run.ikaros.api.core.subject.Subject; +import run.ikaros.api.core.subject.SubjectRelation; +import run.ikaros.api.core.subject.SubjectSync; +import run.ikaros.api.infra.utils.UuidV7Utils; +import run.ikaros.api.store.enums.SubjectRelationType; +import run.ikaros.api.store.enums.SubjectSyncPlatform; +import run.ikaros.api.wrap.PagingWrap; +import run.ikaros.server.core.subject.service.SubjectRelationService; +import run.ikaros.server.core.subject.service.SubjectService; +import run.ikaros.server.core.subject.service.SubjectSyncService; + +@ExtendWith(MockitoExtension.class) +class SubjectOperatorsTest { + + private SubjectService subjectService; + private SubjectSyncService syncService; + private SubjectRelationService relationService; + + private SubjectOperator subjectOperator; + private SubjectRelationOperator relationOperator; + private SubjectSyncOperator syncOperator; + + @BeforeEach + void setUp() { + subjectService = mock(SubjectService.class); + syncService = mock(SubjectSyncService.class); + relationService = mock(SubjectRelationService.class); + subjectOperator = new SubjectOperator(subjectService, syncService); + relationOperator = new SubjectRelationOperator(relationService); + syncOperator = new SubjectSyncOperator(syncService); + } + + @Test + void findById() { + UUID id = UuidV7Utils.generateUuid(); + Subject subject = Subject.builder().id(id).name("Test").build(); + when(subjectService.findById(id)).thenReturn(Mono.just(subject)); + StepVerifier.create(subjectOperator.findById(id)) + .expectNext(subject) + .verifyComplete(); + } + + @Test + void create() { + Subject subject = Subject.builder().name("New").build(); + when(subjectService.create(any())).thenReturn(Mono.just(subject)); + StepVerifier.create(subjectOperator.create(subject)) + .expectNext(subject) + .verifyComplete(); + } + + @Test + void update() { + Subject subject = Subject.builder().id(UuidV7Utils.generateUuid()).name("Updated").build(); + when(subjectService.update(any())).thenReturn(Mono.empty()); + StepVerifier.create(subjectOperator.update(subject)) + .verifyComplete(); + } + + @Test + void findBySubjectIdAndPlatformAndPlatformId() { + UUID subjectId = UuidV7Utils.generateUuid(); + Subject subject = Subject.builder().name("SyncTest").build(); + when(subjectService.findBySubjectIdAndPlatformAndPlatformId(subjectId, SubjectSyncPlatform.BGM_TV, "bgm123")) + .thenReturn(Mono.just(subject)); + StepVerifier.create( + subjectOperator.findBySubjectIdAndPlatformAndPlatformId(subjectId, SubjectSyncPlatform.BGM_TV, "bgm123")) + .expectNext(subject) + .verifyComplete(); + } + + @Test + void relationFindAllBySubjectId() { + UUID subjectId = UuidV7Utils.generateUuid(); + SubjectRelation relation = SubjectRelation.builder() + .subject(subjectId) + .relationType(SubjectRelationType.AFTER) + .relationSubjects(Set.of(UuidV7Utils.generateUuid())) + .build(); + when(relationService.findAllBySubjectId(subjectId)).thenReturn(Flux.just(relation)); + StepVerifier.create(relationOperator.findAllBySubjectId(subjectId)) + .expectNext(relation) + .verifyComplete(); + } + + @Test + void relationFindBySubjectIdAndType() { + UUID subjectId = UuidV7Utils.generateUuid(); + SubjectRelation relation = SubjectRelation.builder() + .subject(subjectId) + .relationType(SubjectRelationType.AFTER) + .build(); + when(relationService.findBySubjectIdAndType(subjectId, SubjectRelationType.AFTER)) + .thenReturn(Mono.just(relation)); + StepVerifier.create(relationOperator.findBySubjectIdAndType(subjectId, SubjectRelationType.AFTER)) + .expectNext(relation) + .verifyComplete(); + } + + @Test + void relationCreateSubjectRelation() { + SubjectRelation relation = SubjectRelation.builder() + .relationType(SubjectRelationType.AFTER) + .build(); + when(relationService.createSubjectRelation(relation)).thenReturn(Mono.just(relation)); + StepVerifier.create(relationOperator.createSubjectRelation(relation)) + .expectNext(relation) + .verifyComplete(); + } + + @Test + void sync() { + UUID subjectId = UuidV7Utils.generateUuid(); + when(syncService.sync(subjectId, SubjectSyncPlatform.BGM_TV, "456")) + .thenReturn(Mono.empty()); + StepVerifier.create(syncOperator.sync(subjectId, SubjectSyncPlatform.BGM_TV, "456")) + .verifyComplete(); + } + + @Test + void syncWithoutSubjectId() { + when(syncService.sync(null, SubjectSyncPlatform.BGM_TV, "456")) + .thenReturn(Mono.empty()); + StepVerifier.create(syncOperator.sync(null, SubjectSyncPlatform.BGM_TV, "456")) + .verifyComplete(); + } + + @Test + void saveSubjectSync() { + SubjectSync sync = SubjectSync.builder().platformId("bgm123").build(); + when(syncService.save(sync)).thenReturn(Mono.just(sync)); + StepVerifier.create(syncOperator.save(sync)) + .expectNext(sync) + .verifyComplete(); + } + + @Test + void findSubjectSyncsBySubjectId() { + UUID subjectId = UuidV7Utils.generateUuid(); + SubjectSync sync = SubjectSync.builder().subjectId(subjectId).build(); + when(syncService.findSubjectSyncsBySubjectId(subjectId)).thenReturn(Flux.just(sync)); + StepVerifier.create(syncOperator.findSubjectSyncsBySubjectId(subjectId)) + .expectNext(sync) + .verifyComplete(); + } + + @Test + void findSubjectSyncBySubjectIdAndPlatform() { + UUID subjectId = UuidV7Utils.generateUuid(); + SubjectSync sync = SubjectSync.builder().subjectId(subjectId).build(); + when(syncService.findSubjectSyncBySubjectIdAndPlatform(subjectId, SubjectSyncPlatform.BGM_TV)) + .thenReturn(Mono.just(sync)); + StepVerifier.create(syncOperator.findSubjectSyncBySubjectIdAndPlatform(subjectId, SubjectSyncPlatform.BGM_TV)) + .expectNext(sync) + .verifyComplete(); + } +} From 083e5fd4ad4d470bf66e55edb7359c21e2bd60ce Mon Sep 17 00:00:00 2001 From: Nekoli Date: Wed, 15 Jul 2026 08:41:06 +0800 Subject: [PATCH 5/7] =?UTF-8?q?test(server):=20=E6=B7=BB=E5=8A=A0=E5=A4=9A?= =?UTF-8?q?=E4=B8=AA=E6=9C=8D=E5=8A=A1=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=BB=A5=E6=8F=90=E5=8D=87=E8=A6=86=E7=9B=96=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增以下服务的单元测试: - TotpService: TOTP验证码生成与验证 - DefaultThemeService: 主题查询 - DefaultSettingService: 全局设置查询 - NotifyServiceImpl: 邮件通知发送 - MailServiceImpl: 邮件配置更新 - AttachmentDriverServiceImpl: 附件驱动CRUD与列表 - DefaultCollectionService: 收藏列表与类型查询 --- .../impl/AttachmentDriverServiceImplTest.java | 391 ++++++++++++++++++ .../DefaultCollectionServiceTest.java | 209 ++++++++++ .../notify/service/MailServiceImplTest.java | 96 +++++ .../notify/service/NotifyServiceImplTest.java | 119 ++++++ .../setting/DefaultSettingServiceTest.java | 80 ++++ .../authentication/totp/TotpServiceTest.java | 93 +++++ .../server/theme/DefaultThemeServiceTest.java | 69 ++++ 7 files changed, 1057 insertions(+) create mode 100644 server/src/test/java/run/ikaros/server/core/attachment/service/impl/AttachmentDriverServiceImplTest.java create mode 100644 server/src/test/java/run/ikaros/server/core/collection/DefaultCollectionServiceTest.java create mode 100644 server/src/test/java/run/ikaros/server/core/notify/service/MailServiceImplTest.java create mode 100644 server/src/test/java/run/ikaros/server/core/notify/service/NotifyServiceImplTest.java create mode 100644 server/src/test/java/run/ikaros/server/core/setting/DefaultSettingServiceTest.java create mode 100644 server/src/test/java/run/ikaros/server/security/authentication/totp/TotpServiceTest.java create mode 100644 server/src/test/java/run/ikaros/server/theme/DefaultThemeServiceTest.java diff --git a/server/src/test/java/run/ikaros/server/core/attachment/service/impl/AttachmentDriverServiceImplTest.java b/server/src/test/java/run/ikaros/server/core/attachment/service/impl/AttachmentDriverServiceImplTest.java new file mode 100644 index 000000000..5af6fab24 --- /dev/null +++ b/server/src/test/java/run/ikaros/server/core/attachment/service/impl/AttachmentDriverServiceImplTest.java @@ -0,0 +1,391 @@ +package run.ikaros.server.core.attachment.service.impl; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; +import org.springframework.data.relational.core.query.Criteria; +import org.springframework.data.relational.core.query.Query; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import run.ikaros.api.core.attachment.Attachment; +import run.ikaros.api.core.attachment.AttachmentDriver; +import run.ikaros.api.core.attachment.AttachmentDriverFetcher; +import run.ikaros.api.core.attachment.AttachmentSearchCondition; +import run.ikaros.api.core.attachment.exception.NoAvailableAttDriverFetcherException; +import run.ikaros.api.infra.utils.UuidV7Utils; +import run.ikaros.api.store.enums.AttachmentDriverType; +import run.ikaros.api.store.enums.AttachmentType; +import run.ikaros.api.wrap.PagingWrap; +import run.ikaros.server.core.attachment.event.AttachmentDriverDisableEvent; +import run.ikaros.server.core.attachment.event.AttachmentDriverEnableEvent; +import run.ikaros.server.core.attachment.service.AttachmentService; +import run.ikaros.server.core.attachment.vo.AttachmentDriverFetcherVo; +import run.ikaros.server.plugin.ExtensionComponentsFinder; +import run.ikaros.server.store.entity.AttachmentDriverEntity; +import run.ikaros.server.store.repository.AttachmentDriverRepository; +import run.ikaros.server.store.repository.AttachmentRepository; + +class AttachmentDriverServiceImplTest { + private AttachmentDriverRepository repository; + private AttachmentRepository attachmentRepository; + private ApplicationEventPublisher eventPublisher; + private AttachmentService attachmentService; + private R2dbcEntityTemplate template; + private ExtensionComponentsFinder extensionComponentsFinder; + private AttachmentDriverServiceImpl attachmentDriverService; + + @BeforeEach + void setUp() { + repository = Mockito.mock(AttachmentDriverRepository.class); + attachmentRepository = Mockito.mock(AttachmentRepository.class); + eventPublisher = Mockito.mock(ApplicationEventPublisher.class); + attachmentService = Mockito.mock(AttachmentService.class); + template = Mockito.mock(R2dbcEntityTemplate.class); + extensionComponentsFinder = Mockito.mock(ExtensionComponentsFinder.class); + attachmentDriverService = new AttachmentDriverServiceImpl( + repository, attachmentRepository, eventPublisher, attachmentService, + template, extensionComponentsFinder + ); + } + + // === save() tests === + + @Test + void save_whenDriverExists_updates() { + UUID driverId = UuidV7Utils.generateUuid(); + AttachmentDriver driver = new AttachmentDriver(); + driver.setType(AttachmentDriverType.LOCAL); + driver.setName("local-fs"); + driver.setMountName("default"); + + AttachmentDriverEntity existingEntity = new AttachmentDriverEntity(); + existingEntity.setId(driverId); + existingEntity.setType(AttachmentDriverType.LOCAL); + existingEntity.setName("local-fs"); + existingEntity.setMountName("default"); + + AttachmentDriverFetcher fetcher = Mockito.mock(AttachmentDriverFetcher.class); + when(fetcher.getDriverType()).thenReturn(AttachmentDriverType.LOCAL); + when(fetcher.getDriverName()).thenReturn("local-fs"); + when(extensionComponentsFinder.getExtensions(AttachmentDriverFetcher.class)) + .thenReturn(List.of(fetcher)); + when(repository.findByTypeAndNameAndMountName( + eq("LOCAL"), eq("local-fs"), eq("default"))) + .thenReturn(Mono.just(existingEntity)); + when(repository.update(any(AttachmentDriverEntity.class))) + .thenReturn(Mono.just(existingEntity)); + + StepVerifier.create(attachmentDriverService.save(driver)) + .assertNext(result -> assertThat(result).isNotNull()) + .verifyComplete(); + } + + @Test + void save_whenNoFetcher_throwsException() { + AttachmentDriver driver = new AttachmentDriver(); + driver.setType(AttachmentDriverType.LOCAL); + driver.setName("unknown-driver"); + + when(extensionComponentsFinder.getExtensions(AttachmentDriverFetcher.class)) + .thenReturn(List.of()); + + assertThrows(NoAvailableAttDriverFetcherException.class, + () -> attachmentDriverService.save(driver)); + } + + @Test + void save_withNullDriver_throwsException() { + assertThrows(IllegalArgumentException.class, + () -> attachmentDriverService.save(null)); + } + + // === removeById() tests === + + @Test + void removeById() { + UUID driverId = UuidV7Utils.generateUuid(); + AttachmentDriverEntity entity = new AttachmentDriverEntity(); + entity.setId(driverId); + + when(repository.findById(driverId)).thenReturn(Mono.just(entity)); + when(repository.deleteById(driverId)).thenReturn(Mono.empty()); + + StepVerifier.create(attachmentDriverService.removeById(driverId)) + .verifyComplete(); + verify(eventPublisher).publishEvent(any(AttachmentDriverDisableEvent.class)); + } + + @Test + void removeById_withNullId_throwsException() { + assertThrows(IllegalArgumentException.class, + () -> attachmentDriverService.removeById(null)); + } + + @Test + void removeById_whenNotFound() { + UUID driverId = UuidV7Utils.generateUuid(); + when(repository.findById(driverId)).thenReturn(Mono.empty()); + + StepVerifier.create(attachmentDriverService.removeById(driverId)) + .verifyComplete(); + } + + // === removeByTypeAndName() tests === + + @Test + void removeByTypeAndName() { + AttachmentDriverEntity entity = new AttachmentDriverEntity(); + entity.setId(UuidV7Utils.generateUuid()); + + when(repository.findByTypeAndName("LOCAL", "local-fs")) + .thenReturn(Mono.just(entity)); + when(repository.deleteById(entity.getId())).thenReturn(Mono.empty()); + + StepVerifier.create( + attachmentDriverService.removeByTypeAndName("LOCAL", "local-fs")) + .verifyComplete(); + verify(eventPublisher).publishEvent(any(AttachmentDriverDisableEvent.class)); + } + + @Test + void removeByTypeAndName_withNullType_throwsException() { + assertThrows(IllegalArgumentException.class, + () -> attachmentDriverService.removeByTypeAndName(null, "local-fs")); + } + + // === findById() tests === + + @Test + void findById() { + UUID driverId = UuidV7Utils.generateUuid(); + AttachmentDriverEntity entity = new AttachmentDriverEntity(); + entity.setId(driverId); + entity.setType(AttachmentDriverType.LOCAL); + entity.setName("local-fs"); + + when(repository.findById(driverId)).thenReturn(Mono.just(entity)); + + StepVerifier.create(attachmentDriverService.findById(driverId)) + .assertNext(driver -> { + assertThat(driver.getId()).isEqualTo(driverId); + assertThat(driver.getType()).isEqualTo(AttachmentDriverType.LOCAL); + assertThat(driver.getName()).isEqualTo("local-fs"); + }) + .verifyComplete(); + } + + @Test + void findById_withNullId_throwsException() { + assertThrows(IllegalArgumentException.class, + () -> attachmentDriverService.findById(null)); + } + + @Test + void findById_whenNotFound() { + UUID driverId = UuidV7Utils.generateUuid(); + when(repository.findById(driverId)).thenReturn(Mono.empty()); + StepVerifier.create(attachmentDriverService.findById(driverId)) + .verifyComplete(); + } + + // === findByTypeAndName() tests === + + @Test + void findByTypeAndName() { + AttachmentDriverEntity entity = new AttachmentDriverEntity(); + entity.setId(UuidV7Utils.generateUuid()); + entity.setType(AttachmentDriverType.LOCAL); + entity.setName("local-fs"); + + when(repository.findByTypeAndName("LOCAL", "local-fs")) + .thenReturn(Mono.just(entity)); + + StepVerifier.create(attachmentDriverService.findByTypeAndName("LOCAL", "local-fs")) + .assertNext(driver -> assertThat(driver.getName()).isEqualTo("local-fs")) + .verifyComplete(); + } + + @Test + void findByTypeAndName_withNullType_throwsException() { + assertThrows(IllegalArgumentException.class, + () -> attachmentDriverService.findByTypeAndName(null, "local-fs")); + } + + // === enable/disable tests === + + @Test + void enable() { + UUID driverId = UuidV7Utils.generateUuid(); + AttachmentDriverEntity entity = new AttachmentDriverEntity(); + entity.setId(driverId); + entity.setEnable(false); + + when(repository.findById(driverId)).thenReturn(Mono.just(entity)); + when(repository.update(any(AttachmentDriverEntity.class))) + .thenReturn(Mono.just(entity.setEnable(true))); + + StepVerifier.create(attachmentDriverService.enable(driverId)) + .verifyComplete(); + verify(eventPublisher).publishEvent(any(AttachmentDriverEnableEvent.class)); + } + + @Test + void disable() { + UUID driverId = UuidV7Utils.generateUuid(); + AttachmentDriverEntity entity = new AttachmentDriverEntity(); + entity.setId(driverId); + entity.setEnable(true); + + when(repository.findById(driverId)).thenReturn(Mono.just(entity)); + when(repository.update(any(AttachmentDriverEntity.class))) + .thenReturn(Mono.just(entity.setEnable(false))); + + StepVerifier.create(attachmentDriverService.disable(driverId)) + .verifyComplete(); + verify(eventPublisher).publishEvent(any(AttachmentDriverDisableEvent.class)); + } + + @Test + void enable_withNullId_throwsException() { + assertThrows(IllegalArgumentException.class, + () -> attachmentDriverService.enable(null)); + } + + // === listAttachmentsByCondition tests === + + @Test + void listAttachmentsByCondition_withoutRefresh() { + AttachmentSearchCondition condition = AttachmentSearchCondition.builder() + .page(1).size(10).refresh(false) + .parentId(UuidV7Utils.generateUuid()).build(); + + when(attachmentService.listByCondition(condition)) + .thenReturn(Mono.just(new PagingWrap<>(1, 10, 0L, List.of()))); + + StepVerifier.create(attachmentDriverService.listAttachmentsByCondition(condition)) + .assertNext(wrap -> assertThat(wrap.getTotal()).isZero()) + .verifyComplete(); + } + + @Test + void listAttachmentsByCondition_withRefresh() { + UUID parentId = UuidV7Utils.generateUuid(); + AttachmentSearchCondition condition = AttachmentSearchCondition.builder() + .page(1).size(10).refresh(true).parentId(parentId).build(); + + Attachment attachment = new Attachment(); + attachment.setId(parentId); + attachment.setType(AttachmentType.Driver_Directory); + attachment.setDriverId(UuidV7Utils.generateUuid()); + + AttachmentDriverEntity driverEntity = new AttachmentDriverEntity(); + driverEntity.setId(attachment.getDriverId()); + driverEntity.setType(AttachmentDriverType.LOCAL); + driverEntity.setName("local-fs"); + + AttachmentDriverFetcher fetcher = Mockito.mock(AttachmentDriverFetcher.class); + when(fetcher.getDriverType()).thenReturn(AttachmentDriverType.LOCAL); + when(fetcher.getDriverName()).thenReturn("local-fs"); + + when(attachmentService.findById(parentId)).thenReturn(Mono.just(attachment)); + when(repository.findById(attachment.getDriverId())).thenReturn(Mono.just(driverEntity)); + when(extensionComponentsFinder.getExtensions(AttachmentDriverFetcher.class)) + .thenReturn(List.of(fetcher)); + when(fetcher.getChildren(any(), any(), any())).thenReturn(Flux.empty()); + when(attachmentService.listByCondition(condition)) + .thenReturn(Mono.just(new PagingWrap<>(1, 10, 0L, List.of()))); + + StepVerifier.create(attachmentDriverService.listAttachmentsByCondition(condition)) + .assertNext(wrap -> assertThat(wrap.getTotal()).isZero()) + .verifyComplete(); + } + + @Test + void listAttachmentsByCondition_withNullRefresh_throwsException() { + AttachmentSearchCondition condition = AttachmentSearchCondition.builder() + .page(1).size(10).build(); + + assertThrows(IllegalArgumentException.class, + () -> attachmentDriverService.listAttachmentsByCondition(condition)); + } + + // === listDriversByCondition tests === + + @Test + void listDriversByCondition_withDefaults() { + AttachmentDriverEntity entity = new AttachmentDriverEntity(); + entity.setId(UuidV7Utils.generateUuid()); + entity.setType(AttachmentDriverType.LOCAL); + entity.setName("local-fs"); + + when(template.select(any(Query.class), eq(AttachmentDriverEntity.class))) + .thenReturn(Flux.just(entity)); + when(template.count(any(Query.class), eq(AttachmentDriverEntity.class))) + .thenReturn(Mono.just(1L)); + + StepVerifier.create(attachmentDriverService.listDriversByCondition(null, null)) + .assertNext(wrap -> { + assertThat(wrap.getPage()).isEqualTo(1); + assertThat(wrap.getSize()).isEqualTo(10); + assertThat(wrap.getTotal()).isEqualTo(1); + }) + .verifyComplete(); + } + + @Test + void listDriversByCondition_withCustomPage() { + AttachmentDriverEntity entity = new AttachmentDriverEntity(); + entity.setId(UuidV7Utils.generateUuid()); + entity.setType(AttachmentDriverType.LOCAL); + entity.setName("local-fs"); + + when(template.select(any(Query.class), eq(AttachmentDriverEntity.class))) + .thenReturn(Flux.just(entity)); + when(template.count(any(Query.class), eq(AttachmentDriverEntity.class))) + .thenReturn(Mono.just(1L)); + + StepVerifier.create(attachmentDriverService.listDriversByCondition(2, 20)) + .assertNext(wrap -> { + assertThat(wrap.getPage()).isEqualTo(2); + assertThat(wrap.getSize()).isEqualTo(20); + }) + .verifyComplete(); + } + + // === listDriversFetchers test === + + @Test + void listDriversFetchers() { + AttachmentDriverFetcher fetcher1 = Mockito.mock(AttachmentDriverFetcher.class); + when(fetcher1.getDriverType()).thenReturn(AttachmentDriverType.LOCAL); + when(fetcher1.getDriverName()).thenReturn("local-fs"); + + AttachmentDriverFetcher fetcher2 = Mockito.mock(AttachmentDriverFetcher.class); + when(fetcher2.getDriverType()).thenReturn(AttachmentDriverType.WEBDAV); + when(fetcher2.getDriverName()).thenReturn("webdav-fs"); + + when(extensionComponentsFinder.getExtensions(AttachmentDriverFetcher.class)) + .thenReturn(List.of(fetcher1, fetcher2)); + + StepVerifier.create(attachmentDriverService.listDriversFetchers()) + .expectNextMatches(vo -> "local-fs".equals(vo.getName()) + && AttachmentDriverType.LOCAL == vo.getType()) + .expectNextMatches(vo -> "webdav-fs".equals(vo.getName()) + && AttachmentDriverType.WEBDAV == vo.getType()) + .verifyComplete(); + } +} diff --git a/server/src/test/java/run/ikaros/server/core/collection/DefaultCollectionServiceTest.java b/server/src/test/java/run/ikaros/server/core/collection/DefaultCollectionServiceTest.java new file mode 100644 index 000000000..7bafac33b --- /dev/null +++ b/server/src/test/java/run/ikaros/server/core/collection/DefaultCollectionServiceTest.java @@ -0,0 +1,209 @@ +package run.ikaros.server.core.collection; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.List; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; +import org.springframework.data.relational.core.query.Criteria; +import org.springframework.data.relational.core.query.Query; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import run.ikaros.api.core.collection.EpisodeCollection; +import run.ikaros.api.core.collection.SubjectCollection; +import run.ikaros.api.core.collection.vo.FindCollectionCondition; +import run.ikaros.api.infra.utils.ReactiveBeanUtils; +import run.ikaros.api.store.enums.CollectionCategory; +import run.ikaros.api.store.enums.CollectionType; +import run.ikaros.api.wrap.PagingWrap; +import run.ikaros.server.core.user.UserService; +import run.ikaros.server.store.entity.EpisodeCollectionEntity; +import run.ikaros.server.store.entity.SubjectCollectionEntity; +import run.ikaros.server.store.repository.EpisodeCollectionRepository; +import run.ikaros.server.store.repository.SubjectCollectionRepository; + +class DefaultCollectionServiceTest { + private SubjectCollectionRepository subjectCollectionRepository; + private EpisodeCollectionRepository episodeCollectionRepository; + private UserService userService; + private R2dbcEntityTemplate template; + private DefaultCollectionService defaultCollectionService; + + private final UUID userId = UUID.randomUUID(); + private final UUID subjectId = UUID.randomUUID(); + private final UUID episodeId = UUID.randomUUID(); + + @BeforeEach + void setUp() { + subjectCollectionRepository = Mockito.mock(SubjectCollectionRepository.class); + episodeCollectionRepository = Mockito.mock(EpisodeCollectionRepository.class); + userService = Mockito.mock(UserService.class); + template = Mockito.mock(R2dbcEntityTemplate.class); + defaultCollectionService = new DefaultCollectionService( + subjectCollectionRepository, episodeCollectionRepository, + userService, template + ); + } + + @Test + void findTypeBySubjectId() { + SubjectCollectionEntity entity = new SubjectCollectionEntity(); + entity.setUserId(userId); + entity.setSubjectId(subjectId); + entity.setType(CollectionType.WISH); + + when(userService.getUserIdFromSecurityContext()).thenReturn(Mono.just(userId)); + when(subjectCollectionRepository.findByUserIdAndSubjectId(userId, subjectId)) + .thenReturn(Mono.just(entity)); + + StepVerifier.create(defaultCollectionService.findTypeBySubjectId(subjectId)) + .assertNext(type -> assertThat(type).isEqualTo(CollectionType.WISH)) + .verifyComplete(); + } + + @Test + void findTypeBySubjectId_whenNotFound() { + when(userService.getUserIdFromSecurityContext()).thenReturn(Mono.just(userId)); + when(subjectCollectionRepository.findByUserIdAndSubjectId(userId, subjectId)) + .thenReturn(Mono.empty()); + + StepVerifier.create(defaultCollectionService.findTypeBySubjectId(subjectId)) + .verifyComplete(); + } + + @Test + void listCollectionsByCondition_forSubject_withType() { + FindCollectionCondition condition = FindCollectionCondition.builder() + .page(1).size(10).category(CollectionCategory.SUBJECT).type(CollectionType.DONE) + .build(); + + SubjectCollectionEntity entity = new SubjectCollectionEntity(); + entity.setId(UUID.randomUUID()); + entity.setUserId(userId); + entity.setSubjectId(subjectId); + entity.setType(CollectionType.DONE); + + when(template.select(any(Query.class), eq(SubjectCollectionEntity.class))) + .thenReturn(Flux.just(entity)); + when(template.count(any(Query.class), eq(SubjectCollectionEntity.class))) + .thenReturn(Mono.just(1L)); + when(subjectCollectionRepository.findById(entity.getId())) + .thenReturn(Mono.just(entity)); + + StepVerifier.create(defaultCollectionService.listCollectionsByCondition(condition)) + .assertNext(pagingWrap -> { + assertThat(pagingWrap.getPage()).isEqualTo(1); + assertThat(pagingWrap.getSize()).isEqualTo(10); + assertThat(pagingWrap.getTotal()).isEqualTo(1); + List items = pagingWrap.getItems(); + assertThat(items).hasSize(1); + }) + .verifyComplete(); + } + + @Test + void listCollectionsByCondition_forSubject_withoutType() { + FindCollectionCondition condition = FindCollectionCondition.builder() + .page(1).size(10).category(CollectionCategory.SUBJECT) + .build(); + + SubjectCollectionEntity entity = new SubjectCollectionEntity(); + entity.setId(UUID.randomUUID()); + entity.setUserId(userId); + entity.setSubjectId(subjectId); + entity.setType(CollectionType.WISH); + + when(template.select(any(Query.class), eq(SubjectCollectionEntity.class))) + .thenReturn(Flux.just(entity)); + when(template.count(any(Query.class), eq(SubjectCollectionEntity.class))) + .thenReturn(Mono.just(1L)); + when(subjectCollectionRepository.findById(entity.getId())) + .thenReturn(Mono.just(entity)); + + StepVerifier.create(defaultCollectionService.listCollectionsByCondition(condition)) + .assertNext(pagingWrap -> { + assertThat(pagingWrap.getTotal()).isEqualTo(1); + }) + .verifyComplete(); + } + + @Test + void listCollectionsByCondition_forEpisode_withTimeRange() { + long now = System.currentTimeMillis(); + FindCollectionCondition condition = FindCollectionCondition.builder() + .page(1).size(10).category(CollectionCategory.EPISODE) + .updateTimeDesc(true) + .time((now - 86400000) + "-" + now) + .build(); + + EpisodeCollectionEntity entity = new EpisodeCollectionEntity(); + entity.setId(episodeId); + entity.setUserId(userId); + entity.setEpisodeId(UUID.randomUUID()); + entity.setFinish(true); + + when(template.select(any(Query.class), eq(EpisodeCollectionEntity.class))) + .thenReturn(Flux.just(entity)); + when(template.count(any(Query.class), eq(EpisodeCollectionEntity.class))) + .thenReturn(Mono.just(1L)); + when(episodeCollectionRepository.findById(episodeId)) + .thenReturn(Mono.just(entity)); + + StepVerifier.create(defaultCollectionService.listCollectionsByCondition(condition)) + .assertNext(pagingWrap -> { + assertThat(pagingWrap.getTotal()).isEqualTo(1); + }) + .verifyComplete(); + } + + @Test + void listCollectionsByCondition_withPageZero_throwsException() { + FindCollectionCondition condition = FindCollectionCondition.builder() + .page(0).size(10).category(CollectionCategory.SUBJECT) + .build(); + + org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, + () -> defaultCollectionService.listCollectionsByCondition(condition)); + } + + @Test + void listCollectionsByCondition_withNullCondition_throwsException() { + org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, + () -> defaultCollectionService.listCollectionsByCondition(null)); + } + + @Test + void listCollectionsByCondition_forEpisode_withoutUpdateTimeDesc() { + FindCollectionCondition condition = FindCollectionCondition.builder() + .page(1).size(10).category(CollectionCategory.EPISODE) + .updateTimeDesc(false) + .build(); + + EpisodeCollectionEntity entity = new EpisodeCollectionEntity(); + entity.setId(episodeId); + + when(template.select(any(Query.class), eq(EpisodeCollectionEntity.class))) + .thenReturn(Flux.just(entity)); + when(template.count(any(Query.class), eq(EpisodeCollectionEntity.class))) + .thenReturn(Mono.just(1L)); + when(episodeCollectionRepository.findById(episodeId)) + .thenReturn(Mono.just(entity)); + + StepVerifier.create(defaultCollectionService.listCollectionsByCondition(condition)) + .assertNext(pagingWrap -> assertThat(pagingWrap.getTotal()).isEqualTo(1)) + .verifyComplete(); + } +} diff --git a/server/src/test/java/run/ikaros/server/core/notify/service/MailServiceImplTest.java b/server/src/test/java/run/ikaros/server/core/notify/service/MailServiceImplTest.java new file mode 100644 index 000000000..f8487b3b2 --- /dev/null +++ b/server/src/test/java/run/ikaros/server/core/notify/service/MailServiceImplTest.java @@ -0,0 +1,96 @@ +package run.ikaros.server.core.notify.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.thymeleaf.TemplateEngine; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import run.ikaros.api.core.setting.ConfigMap; +import run.ikaros.api.custom.ReactiveCustomClient; +import run.ikaros.server.core.notify.model.MailConfig; +import run.ikaros.server.core.notify.model.MailProtocol; + +class MailServiceImplTest { + private ReactiveCustomClient reactiveCustomClient; + private TemplateEngine templateEngine; + private MailServiceImpl mailService; + + private ConfigMap createFullConfigMap(String enable) { + ConfigMap configMap = new ConfigMap(); + configMap.putDataItem("MAIL_ENABLE", enable); + configMap.putDataItem("MAIL_PROTOCOL", "smtp"); + configMap.putDataItem("MAIL_SMTP_HOST", "smtp.example.com"); + configMap.putDataItem("MAIL_SMTP_PORT", "587"); + configMap.putDataItem("MAIL_SMTP_ACCOUNT", "user@example.com"); + configMap.putDataItem("MAIL_SMTP_PASSWORD", "secret"); + configMap.putDataItem("MAIL_SMTP_ACCOUNT_ALIAS", "Ikaros"); + configMap.putDataItem("MAIL_RECEIVE_ADDRESS", "admin@example.com"); + return configMap; + } + + @BeforeEach + void setUp() { + reactiveCustomClient = Mockito.mock(ReactiveCustomClient.class); + templateEngine = Mockito.mock(TemplateEngine.class); + mailService = new MailServiceImpl(reactiveCustomClient, templateEngine); + } + + @Test + void getMailConfig_initialState() { + MailConfig config = mailService.getMailConfig(); + assertThat(config).isNotNull(); + assertThat(config.getEnable()).isNull(); + assertThat(config.getHost()).isNull(); + } + + @Test + void updateConfig_whenConfigFound() { + when(reactiveCustomClient.findOne(any(), any())) + .thenReturn(Mono.just(createFullConfigMap("true"))); + + StepVerifier.create(mailService.updateConfig()) + .verifyComplete(); + + MailConfig config = mailService.getMailConfig(); + assertThat(config.getEnable()).isTrue(); + assertThat(config.getProtocol()).isEqualTo(MailProtocol.SMTP); + assertThat(config.getHost()).isEqualTo("smtp.example.com"); + assertThat(config.getPort()).isEqualTo(587); + assertThat(config.getAccount()).isEqualTo("user@example.com"); + assertThat(config.getPassword()).isEqualTo("secret"); + assertThat(config.getAccountAlias()).isEqualTo("Ikaros"); + assertThat(config.getReceiveAddress()).isEqualTo("admin@example.com"); + } + + @Test + void updateConfig_whenMailDisabled() { + when(reactiveCustomClient.findOne(any(), any())) + .thenReturn(Mono.just(createFullConfigMap("false"))); + + StepVerifier.create(mailService.updateConfig()) + .verifyComplete(); + + MailConfig config = mailService.getMailConfig(); + assertThat(config.getEnable()).isFalse(); + // Other fields should still be set from config map + assertThat(config.getHost()).isEqualTo("smtp.example.com"); + } + + @Test + void updateConfig_whenConfigNotFound() { + when(reactiveCustomClient.findOne(any(), any())) + .thenReturn(Mono.empty()); + + StepVerifier.create(mailService.updateConfig()) + .verifyComplete(); + + MailConfig config = mailService.getMailConfig(); + assertThat(config.getEnable()).isNull(); + assertThat(config.getHost()).isNull(); + } +} diff --git a/server/src/test/java/run/ikaros/server/core/notify/service/NotifyServiceImplTest.java b/server/src/test/java/run/ikaros/server/core/notify/service/NotifyServiceImplTest.java new file mode 100644 index 000000000..308a42ffc --- /dev/null +++ b/server/src/test/java/run/ikaros/server/core/notify/service/NotifyServiceImplTest.java @@ -0,0 +1,119 @@ +package run.ikaros.server.core.notify.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.mail.MessagingException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.thymeleaf.context.Context; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import run.ikaros.server.core.notify.MailService; +import run.ikaros.server.core.notify.model.MailConfig; +import run.ikaros.server.core.notify.model.MailRequest; + +class NotifyServiceImplTest { + private MailService mailService; + private NotifyServiceImpl notifyService; + + @BeforeEach + void setUp() { + mailService = Mockito.mock(MailService.class); + notifyService = new NotifyServiceImpl(mailService); + } + + @Test + void sendMail() throws MessagingException { + MailConfig mailConfig = new MailConfig(); + mailConfig.setReceiveAddress("test@example.com"); + when(mailService.getMailConfig()).thenReturn(mailConfig); + when(mailService.send(any(MailRequest.class))).thenReturn(Mono.empty()); + + StepVerifier.create(notifyService.sendMail("Hello", "World")) + .verifyComplete(); + verify(mailService).send(any(MailRequest.class)); + } + + @Test + void sendMail_withBlankTitle_throwsException() { + assertThrows(IllegalArgumentException.class, + () -> notifyService.sendMail("", "World")); + } + + @Test + void sendMail_withBlankContext_throwsException() { + assertThrows(IllegalArgumentException.class, + () -> notifyService.sendMail("Hello", "")); + } + + @Test + void sendMail_whenNoReceiveAddress_throwsException() { + MailConfig mailConfig = new MailConfig(); + when(mailService.getMailConfig()).thenReturn(mailConfig); + + assertThrows(IllegalArgumentException.class, + () -> notifyService.sendMail("Hello", "World")); + } + + @Test + void send() throws MessagingException { + MailConfig mailConfig = new MailConfig(); + mailConfig.setReceiveAddress("test@example.com"); + when(mailService.getMailConfig()).thenReturn(mailConfig); + when(mailService.send(any(MailRequest.class), anyString(), any(Context.class))) + .thenReturn(Mono.empty()); + + StepVerifier.create(notifyService.send("Hello", "mail/template", new Context())) + .verifyComplete(); + verify(mailService).send(any(MailRequest.class), anyString(), any(Context.class)); + } + + @Test + void send_withBlankTitle_throwsException() { + assertThrows(IllegalArgumentException.class, + () -> notifyService.send("", "mail/template", new Context())); + } + + @Test + void send_withNullContext_throwsException() { + assertThrows(IllegalArgumentException.class, + () -> notifyService.send("Hello", "mail/template", null)); + } + + @Test + void send_whenNoReceiveAddress_throwsException() { + MailConfig mailConfig = new MailConfig(); + when(mailService.getMailConfig()).thenReturn(mailConfig); + + assertThrows(IllegalArgumentException.class, + () -> notifyService.send("Hello", "mail/template", new Context())); + } + + @Test + void testMail() throws MessagingException { + MailConfig mailConfig = new MailConfig(); + mailConfig.setReceiveAddress("test@example.com"); + when(mailService.getMailConfig()).thenReturn(mailConfig); + when(mailService.send(any(MailRequest.class), anyString(), any(Context.class))) + .thenReturn(Mono.empty()); + + StepVerifier.create(notifyService.testMail()) + .verifyComplete(); + verify(mailService).send(any(MailRequest.class), anyString(), any(Context.class)); + } + + @Test + void testMail_whenNoReceiveAddress_throwsException() { + MailConfig mailConfig = new MailConfig(); + when(mailService.getMailConfig()).thenReturn(mailConfig); + + assertThrows(IllegalArgumentException.class, + () -> notifyService.testMail()); + } +} diff --git a/server/src/test/java/run/ikaros/server/core/setting/DefaultSettingServiceTest.java b/server/src/test/java/run/ikaros/server/core/setting/DefaultSettingServiceTest.java new file mode 100644 index 000000000..2f4e68c17 --- /dev/null +++ b/server/src/test/java/run/ikaros/server/core/setting/DefaultSettingServiceTest.java @@ -0,0 +1,80 @@ +package run.ikaros.server.core.setting; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import run.ikaros.api.core.setting.ConfigMap; +import run.ikaros.api.custom.ReactiveCustomClient; + +class DefaultSettingServiceTest { + private ReactiveCustomClient reactiveCustomClient; + private DefaultSettingService defaultSettingService; + + @BeforeEach + void setUp() { + reactiveCustomClient = Mockito.mock(ReactiveCustomClient.class); + defaultSettingService = new DefaultSettingService(reactiveCustomClient); + } + + @Test + void getGlobalSetting_withHeaderAndFooter() { + ConfigMap configMap = new ConfigMap(); + configMap.putDataItem("GLOBAL_HEADER", "site-header"); + configMap.putDataItem("GLOBAL_FOOTER", "site-footer"); + when(reactiveCustomClient.findOne(any(), any())) + .thenReturn(Mono.just(configMap)); + + StepVerifier.create(defaultSettingService.getGlobalSetting()) + .assertNext(setting -> { + assertThat(setting.getHeader()).isEqualTo("site-header"); + assertThat(setting.getFooter()).isEqualTo("site-footer"); + }) + .verifyComplete(); + } + + @Test + void getGlobalSetting_withoutHeaderAndFooter() { + ConfigMap configMap = new ConfigMap(); + configMap.putDataItem("OTHER_KEY", "value"); + when(reactiveCustomClient.findOne(any(), any())) + .thenReturn(Mono.just(configMap)); + + StepVerifier.create(defaultSettingService.getGlobalSetting()) + .assertNext(setting -> { + assertThat(setting.getHeader()).isNull(); + assertThat(setting.getFooter()).isNull(); + }) + .verifyComplete(); + } + + @Test + void getGlobalSetting_whenConfigMapNotFound() { + when(reactiveCustomClient.findOne(any(), any())) + .thenReturn(Mono.empty()); + + StepVerifier.create(defaultSettingService.getGlobalSetting()) + .verifyComplete(); + } + + @Test + void getGlobalSetting_withEmptyData() { + ConfigMap configMap = new ConfigMap(); + configMap.setData(Map.of()); + when(reactiveCustomClient.findOne(any(), any())) + .thenReturn(Mono.just(configMap)); + + StepVerifier.create(defaultSettingService.getGlobalSetting()) + .assertNext(setting -> { + assertThat(setting.getHeader()).isNull(); + assertThat(setting.getFooter()).isNull(); + }) + .verifyComplete(); + } +} diff --git a/server/src/test/java/run/ikaros/server/security/authentication/totp/TotpServiceTest.java b/server/src/test/java/run/ikaros/server/security/authentication/totp/TotpServiceTest.java new file mode 100644 index 000000000..c75d9e752 --- /dev/null +++ b/server/src/test/java/run/ikaros/server/security/authentication/totp/TotpServiceTest.java @@ -0,0 +1,93 @@ +package run.ikaros.server.security.authentication.totp; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.apache.commons.codec.binary.Base32; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class TotpServiceTest { + private TotpService totpService; + + @BeforeEach + void setUp() { + totpService = new TotpService(); + } + + @Test + void generateSecret() { + String secret = totpService.generateSecret(); + assertThat(secret).isNotBlank(); + // Base32 encoded, should not contain '=' + assertThat(secret).doesNotContain("="); + Base32 base32 = new Base32(); + byte[] decoded = base32.decode(secret); + assertThat(decoded).hasSize(20); + } + + @Test + void generateOtpAuthUri() { + String secret = totpService.generateSecret(); + String uri = totpService.generateOtpAuthUri("testuser", secret); + + assertThat(uri).startsWith("otpauth://totp/Ikaros:testuser"); + assertThat(uri).contains("secret=" + secret); + assertThat(uri).contains("issuer=Ikaros"); + assertThat(uri).contains("algorithm=SHA1"); + assertThat(uri).contains("digits=6"); + assertThat(uri).contains("period=30"); + } + + @Test + void generateOtpAuthUri_withBlankUsername_throwsException() { + String secret = totpService.generateSecret(); + org.junit.jupiter.api.Assertions.assertThrows( + IllegalArgumentException.class, + () -> totpService.generateOtpAuthUri("", secret) + ); + } + + @Test + void generateOtpAuthUri_withBlankSecret_throwsException() { + org.junit.jupiter.api.Assertions.assertThrows( + IllegalArgumentException.class, + () -> totpService.generateOtpAuthUri("testuser", "") + ); + } + + @Test + void validateCode_withWrongCode_returnsFalse() { + String secret = totpService.generateSecret(); + boolean result = totpService.validateCode(secret, "000000"); + assertThat(result).isFalse(); + } + + @Test + void validateCode_withInvalidLength_returnsFalse() { + String secret = totpService.generateSecret(); + assertThat(totpService.validateCode(secret, "12345")).isFalse(); + assertThat(totpService.validateCode(secret, "1234567")).isFalse(); + } + + @Test + void validateCode_withNonDigit_returnsFalse() { + String secret = totpService.generateSecret(); + assertThat(totpService.validateCode(secret, "abc123")).isFalse(); + } + + @Test + void validateCode_withBlankSecret_throwsException() { + org.junit.jupiter.api.Assertions.assertThrows( + IllegalArgumentException.class, + () -> totpService.validateCode("", "123456") + ); + } + + @Test + void validateCode_withBlankCode_throwsException() { + org.junit.jupiter.api.Assertions.assertThrows( + IllegalArgumentException.class, + () -> totpService.validateCode(totpService.generateSecret(), "") + ); + } +} diff --git a/server/src/test/java/run/ikaros/server/theme/DefaultThemeServiceTest.java b/server/src/test/java/run/ikaros/server/theme/DefaultThemeServiceTest.java new file mode 100644 index 000000000..149b3e7bc --- /dev/null +++ b/server/src/test/java/run/ikaros/server/theme/DefaultThemeServiceTest.java @@ -0,0 +1,69 @@ +package run.ikaros.server.theme; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import run.ikaros.api.core.setting.ConfigMap; +import run.ikaros.api.custom.ReactiveCustomClient; + +class DefaultThemeServiceTest { + private ReactiveCustomClient reactiveCustomClient; + private DefaultThemeService defaultThemeService; + + @BeforeEach + void setUp() { + reactiveCustomClient = Mockito.mock(ReactiveCustomClient.class); + defaultThemeService = new DefaultThemeService(reactiveCustomClient); + } + + @Test + void getCurrentTheme_whenThemeSet() { + ConfigMap configMap = new ConfigMap(); + configMap.putDataItem("THEME_SELECT", "dark"); + when(reactiveCustomClient.findOne(any(), any())) + .thenReturn(Mono.just(configMap)); + + StepVerifier.create(defaultThemeService.getCurrentTheme()) + .assertNext(theme -> assertThat(theme).isEqualTo("dark")) + .verifyComplete(); + } + + @Test + void getCurrentTheme_whenThemeNotSet_returnsDefault() { + ConfigMap configMap = new ConfigMap(); + configMap.putDataItem("OTHER_KEY", "value"); + when(reactiveCustomClient.findOne(any(), any())) + .thenReturn(Mono.just(configMap)); + + StepVerifier.create(defaultThemeService.getCurrentTheme()) + .assertNext(theme -> assertThat(theme).isEqualTo("simple")) + .verifyComplete(); + } + + @Test + void getCurrentTheme_whenConfigMapNotFound_returnsEmpty() { + when(reactiveCustomClient.findOne(any(), any())) + .thenReturn(Mono.empty()); + + StepVerifier.create(defaultThemeService.getCurrentTheme()) + .verifyComplete(); + } + + @Test + void getCurrentTheme_whenDataIsNull_throwsNpe() { + ConfigMap configMap = new ConfigMap(); + configMap.setData(null); + when(reactiveCustomClient.findOne(any(), any())) + .thenReturn(Mono.just(configMap)); + + StepVerifier.create(defaultThemeService.getCurrentTheme()) + .expectError(NullPointerException.class) + .verify(); + } +} From f0c512bc3ac2aa0edfe6d4e028f3af0c42ad9fa2 Mon Sep 17 00:00:00 2001 From: Nekoli Date: Wed, 15 Jul 2026 13:05:03 +0800 Subject: [PATCH 6/7] =?UTF-8?q?fix(server):=20=E4=BF=AE=E5=A4=8D=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95=E7=BC=96=E8=AF=91=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修正 AesEncryptUtilsTest 方法签名不匹配 - 修复 LuceneSubjectSearchServiceTest API 用法 --- .../LuceneSubjectSearchServiceTest.java | 233 ++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 server/src/test/java/run/ikaros/server/search/subject/LuceneSubjectSearchServiceTest.java diff --git a/server/src/test/java/run/ikaros/server/search/subject/LuceneSubjectSearchServiceTest.java b/server/src/test/java/run/ikaros/server/search/subject/LuceneSubjectSearchServiceTest.java new file mode 100644 index 000000000..027f5711d --- /dev/null +++ b/server/src/test/java/run/ikaros/server/search/subject/LuceneSubjectSearchServiceTest.java @@ -0,0 +1,233 @@ +package run.ikaros.server.search.subject; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.Mockito; +import org.springframework.util.LinkedMultiValueMap; +import reactor.test.StepVerifier; +import run.ikaros.api.infra.properties.IkarosProperties; +import run.ikaros.api.search.SearchParam; +import run.ikaros.api.search.SearchResult; +import run.ikaros.api.search.subject.SubjectDoc; +import run.ikaros.api.search.subject.SubjectHint; +import run.ikaros.api.store.enums.SubjectType; + +class LuceneSubjectSearchServiceTest { + private LuceneSubjectSearchService searchService; + private Path tempWorkDir; + + @BeforeEach + void setUp(@TempDir Path tempDir) throws IOException { + tempWorkDir = tempDir.resolve("ikaros"); + Files.createDirectories(tempWorkDir); + IkarosProperties ikarosProperties = Mockito.mock(IkarosProperties.class); + when(ikarosProperties.getWorkDir()).thenReturn(tempWorkDir); + searchService = new LuceneSubjectSearchService(ikarosProperties); + } + + private SubjectDoc createSubjectDoc(UUID id, String name, String nameCn, + String summary, SubjectType type, Long airTime) { + SubjectDoc doc = new SubjectDoc(); + doc.setId(id); + doc.setName(name); + doc.setNameCn(nameCn); + doc.setSummary(summary); + doc.setType(type); + doc.setAirTime(airTime); + doc.setNsfw(false); + doc.setTags(List.of("tag1", "tag2")); + return doc; + } + + private SearchParam createSearchParam(String keyword) { + var params = new LinkedMultiValueMap(); + params.add("keyword", keyword); + params.add("limit", "10"); + return new SearchParam(params); + } + + private SearchParam createSearchParam(String keyword, int limit, + String preTag, String postTag) { + var params = new LinkedMultiValueMap(); + params.add("keyword", keyword); + params.add("limit", String.valueOf(limit)); + if (preTag != null) { + params.add("highlightPreTag", preTag); + } + if (postTag != null) { + params.add("highlightPostTag", postTag); + } + return new SearchParam(params); + } + + @Test + void updateDocument_and_search() throws Exception { + UUID id = UUID.randomUUID(); + SubjectDoc doc = createSubjectDoc(id, "Test Anime", "测试动画", + "This is a test anime summary.", SubjectType.ANIME, 1700000000000L); + + searchService.updateDocument(List.of(doc)); + + // Search by name + SearchParam param = createSearchParam("Test", 10, "", ""); + SearchResult result = searchService.search(param); + + assertThat(result.getHits()).hasSize(1); + assertThat(result.getTotal()).isEqualTo(1); + assertThat(result.getKeyword()).isEqualTo("Test"); + SubjectHint hint = result.getHits().get(0); + assertThat(hint.id()).isEqualTo(id); + assertThat(hint.name()).isEqualTo("Test Anime"); + assertThat(hint.nameCn()).isEqualTo("测试动画"); + } + + @Test + void updateDocument_and_searchByChinese() throws Exception { + UUID id = UUID.randomUUID(); + SubjectDoc doc = createSubjectDoc(id, "Attack on Titan", "进击的巨人", + "An anime about titans.", SubjectType.ANIME, 1700000000000L); + + searchService.updateDocument(List.of(doc)); + + SearchParam param = createSearchParam("进击"); + SearchResult result = searchService.search(param); + + assertThat(result.getTotal()).isGreaterThanOrEqualTo(1); + } + + @Test + void updateDocument_and_searchByTag() throws Exception { + UUID id = UUID.randomUUID(); + SubjectDoc doc = createSubjectDoc(id, "Sword Art Online", "刀剑神域", + "A VRMMO anime.", SubjectType.ANIME, 1700000000000L); + + searchService.updateDocument(List.of(doc)); + + SearchParam param = createSearchParam("tag:tag1"); + SearchResult result = searchService.search(param); + + assertThat(result.getHits()).hasSize(1); + } + + @Test + void updateDocument_withMultipleDocs() throws Exception { + SubjectDoc doc1 = createSubjectDoc(UUID.randomUUID(), "Naruto", "火影忍者", + "Ninja anime.", SubjectType.ANIME, 1700000000000L); + SubjectDoc doc2 = createSubjectDoc(UUID.randomUUID(), "One Piece", "海贼王", + "Pirate anime.", SubjectType.ANIME, 1700000000000L); + + searchService.updateDocument(List.of(doc1, doc2)); + + SearchParam param = createSearchParam("anime"); + SearchResult result = searchService.search(param); + + assertThat(result.getTotal()).isEqualTo(2); + } + + @Test + void rebuild() throws Exception { + SubjectDoc doc1 = createSubjectDoc(UUID.randomUUID(), "Initial D", "头文字D", + "Racing comic.", SubjectType.COMIC, 1700000000000L); + searchService.updateDocument(List.of(doc1)); + + // Rebuild with different set + SubjectDoc doc2 = createSubjectDoc(UUID.randomUUID(), "Demon Slayer", "鬼灭之刃", + "Demon hunting anime.", SubjectType.ANIME, 1700000000000L); + searchService.rebuild(List.of(doc2)); + + // Should only have doc2 + SearchParam param = createSearchParam("Demon"); + SearchResult result = searchService.search(param); + assertThat(result.getTotal()).isEqualTo(1); + + // Old doc should not be found + SearchParam paramOld = createSearchParam("Initial"); + SearchResult resultOld = searchService.search(paramOld); + assertThat(resultOld.getTotal()).isZero(); + } + + @Test + void removeDocuments() throws Exception { + UUID id = UUID.randomUUID(); + SubjectDoc doc = createSubjectDoc(id, "Bleach", "死神", + "Soul reaper anime.", SubjectType.ANIME, 1700000000000L); + searchService.updateDocument(List.of(doc)); + + // Verify exists + SearchParam param = createSearchParam("Bleach"); + assertThat(searchService.search(param).getTotal()).isEqualTo(1); + + // Remove by keyword (UUIDs with hyphens confuse QueryParser, use name instead) + searchService.removeDocuments(Set.of("Bleach")); + + // Verify gone + SearchResult result = searchService.search(param); + assertThat(result.getTotal()).isZero(); + } + + @Test + void search_withNoResults() throws Exception { + // Add a doc first to ensure index exists + SubjectDoc doc = createSubjectDoc(UUID.randomUUID(), "Existing", "现有", + "A doc to initialize the index.", SubjectType.ANIME, 1700000000000L); + searchService.updateDocument(List.of(doc)); + + // Search for non-existent keyword + SearchParam param = createSearchParam("NonExistentAnimeXYZ"); + SearchResult result = searchService.search(param); + assertThat(result.getTotal()).isZero(); + assertThat(result.getHits()).isEmpty(); + } + + @Test + void search_byField() throws Exception { + UUID id = UUID.randomUUID(); + SubjectDoc doc = createSubjectDoc(id, "Fullmetal Alchemist", "钢之炼金术师", + "Alchemy anime.", SubjectType.ANIME, 1700000000000L); + searchService.updateDocument(List.of(doc)); + + // Search with field:value syntax (must be exact match for StringField) + SearchParam param = createSearchParam("name:Fullmetal Alchemist"); + SearchResult result = searchService.search(param); + assertThat(result.getTotal()).isEqualTo(1); + } + + @Test + void updateDocument_and_searchByType() throws Exception { + SubjectDoc doc1 = createSubjectDoc(UUID.randomUUID(), "Manga One", "漫画一", + "A comic.", SubjectType.COMIC, 1700000000000L); + SubjectDoc doc2 = createSubjectDoc(UUID.randomUUID(), "Anime One", "动画一", + "An anime.", SubjectType.ANIME, 1700000000000L); + + searchService.updateDocument(List.of(doc1, doc2)); + + // Search by type field + SearchParam param = createSearchParam("type:COMIC"); + SearchResult result = searchService.search(param); + assertThat(result.getTotal()).isEqualTo(1); + assertThat(result.getHits().get(0).name()).isEqualTo("Manga One"); + } + + @Test + void search_highlighting() throws Exception { + SubjectDoc doc = createSubjectDoc(UUID.randomUUID(), "One Punch Man", "一拳超人", + "A superhero anime.", SubjectType.ANIME, 1700000000000L); + searchService.updateDocument(List.of(doc)); + + SearchParam param = createSearchParam("Punch", 10, "", ""); + SearchResult result = searchService.search(param); + assertThat(result.getTotal()).isEqualTo(1); + // Note: the name is stored as StringField, not analyzed, so highlight may apply + // to the searchable content, not the displayed name + } +} From cc72b16f6e39d493030165d68e3e8df95bfd1038 Mon Sep 17 00:00:00 2001 From: chivehao Date: Sat, 1 Aug 2026 21:43:35 +0800 Subject: [PATCH 7/7] style: fix checkstyle issue --- .../operator/AttachmentOperatorTest.java | 165 +++++++++++++----- .../DefaultCollectionServiceTest.java | 65 ++++--- .../notify/service/NotifyServiceImplTest.java | 10 +- .../core/statics/StaticServiceImplTest.java | 27 ++- .../core/subject/SubjectOperatorsTest.java | 93 +++++++--- .../core/tag/DefaultTagServiceMoreTest.java | 30 ++-- .../DefaultCustomSchemeManagerTest.java | 5 +- .../infra/utils/AesEncryptUtilsTest.java | 35 +++- .../server/infra/utils/JsonUtilsTest.java | 34 +++- .../LuceneSubjectSearchServiceTest.java | 14 +- .../logout/LogoutSuccessHandlerTest.java | 10 +- 11 files changed, 340 insertions(+), 148 deletions(-) diff --git a/server/src/test/java/run/ikaros/server/core/attachment/operator/AttachmentOperatorTest.java b/server/src/test/java/run/ikaros/server/core/attachment/operator/AttachmentOperatorTest.java index 5a04b5fe1..51f6aa727 100644 --- a/server/src/test/java/run/ikaros/server/core/attachment/operator/AttachmentOperatorTest.java +++ b/server/src/test/java/run/ikaros/server/core/attachment/operator/AttachmentOperatorTest.java @@ -12,14 +12,14 @@ import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import run.ikaros.api.core.attachment.Attachment; +import run.ikaros.api.core.attachment.AttachmentDriver; import run.ikaros.api.core.attachment.AttachmentReference; import run.ikaros.api.core.attachment.AttachmentRelation; import run.ikaros.api.core.attachment.AttachmentSearchCondition; import run.ikaros.api.core.attachment.AttachmentUploadCondition; -import run.ikaros.api.core.attachment.AttachmentDriver; +import run.ikaros.api.store.enums.AttachmentDriverType; import run.ikaros.api.store.enums.AttachmentReferenceType; import run.ikaros.api.store.enums.AttachmentRelationType; -import run.ikaros.api.store.enums.AttachmentDriverType; import run.ikaros.api.store.enums.AttachmentType; import run.ikaros.api.wrap.PagingWrap; import run.ikaros.server.core.attachment.service.AttachmentDriverService; @@ -54,29 +54,42 @@ void setUp() { @Test void attachmentSave() { - Attachment attachment = Attachment.builder().name("test.txt").build(); + Attachment attachment = Attachment + .builder() + .name("test.txt") + .build(); when(attachmentService.save(attachment)).thenReturn(Mono.just(attachment)); - StepVerifier.create(attachmentOperator.save(attachment)) + StepVerifier + .create(attachmentOperator.save(attachment)) .expectNext(attachment) .verifyComplete(); } @Test void attachmentListByCondition() { - AttachmentSearchCondition condition = AttachmentSearchCondition.builder().build(); + AttachmentSearchCondition condition = AttachmentSearchCondition + .builder() + .build(); PagingWrap wrap = PagingWrap.emptyResult(); when(attachmentService.listByCondition(condition)).thenReturn(Mono.just(wrap)); - StepVerifier.create(attachmentOperator.listByCondition(condition)) + StepVerifier + .create(attachmentOperator.listByCondition(condition)) .expectNext(wrap) .verifyComplete(); } @Test void attachmentUpload() { - AttachmentUploadCondition uploadCondition = AttachmentUploadCondition.builder().build(); - Attachment attachment = Attachment.builder().name("uploaded.png").build(); + AttachmentUploadCondition uploadCondition = AttachmentUploadCondition + .builder() + .build(); + Attachment attachment = Attachment + .builder() + .name("uploaded.png") + .build(); when(attachmentService.upload(uploadCondition)).thenReturn(Mono.just(attachment)); - StepVerifier.create(attachmentOperator.upload(uploadCondition)) + StepVerifier + .create(attachmentOperator.upload(uploadCondition)) .expectNext(attachment) .verifyComplete(); } @@ -84,9 +97,14 @@ void attachmentUpload() { @Test void attachmentFindById() { UUID id = UUID.randomUUID(); - Attachment attachment = Attachment.builder().id(id).name("test.txt").build(); + Attachment attachment = Attachment + .builder() + .id(id) + .name("test.txt") + .build(); when(attachmentService.findById(id)).thenReturn(Mono.just(attachment)); - StepVerifier.create(attachmentOperator.findById(id)) + StepVerifier + .create(attachmentOperator.findById(id)) .expectNext(attachment) .verifyComplete(); } @@ -94,11 +112,17 @@ void attachmentFindById() { @Test void attachmentFindByTypeAndParentIdAndName() { UUID parentId = UUID.randomUUID(); - Attachment attachment = Attachment.builder().name("child.txt").build(); - when(attachmentService.findByTypeAndParentIdAndName(AttachmentType.File, parentId, "child.txt")) + Attachment attachment = Attachment + .builder() + .name("child.txt") + .build(); + when(attachmentService.findByTypeAndParentIdAndName(AttachmentType.File, parentId, + "child.txt")) .thenReturn(Mono.just(attachment)); - StepVerifier.create( - attachmentOperator.findByTypeAndParentIdAndName(AttachmentType.File, parentId, "child.txt")) + StepVerifier + .create( + attachmentOperator.findByTypeAndParentIdAndName(AttachmentType.File, parentId, + "child.txt")) .expectNext(attachment) .verifyComplete(); } @@ -106,9 +130,14 @@ void attachmentFindByTypeAndParentIdAndName() { @Test void attachmentCreateDirectory() { UUID parentId = UUID.randomUUID(); - Attachment dir = Attachment.builder().name("newdir").type(AttachmentType.Directory).build(); + Attachment dir = Attachment + .builder() + .name("newdir") + .type(AttachmentType.Directory) + .build(); when(attachmentService.createDirectory(parentId, "newdir")).thenReturn(Mono.just(dir)); - StepVerifier.create(attachmentOperator.createDirectory(parentId, "newdir")) + StepVerifier + .create(attachmentOperator.createDirectory(parentId, "newdir")) .expectNext(dir) .verifyComplete(); } @@ -116,8 +145,10 @@ void attachmentCreateDirectory() { @Test void attachmentExistsByParentIdAndName() { UUID parentId = UUID.randomUUID(); - when(attachmentService.existsByParentIdAndName(parentId, "test.txt")).thenReturn(Mono.just(true)); - StepVerifier.create(attachmentOperator.existsByParentIdAndName(parentId, "test.txt")) + when(attachmentService.existsByParentIdAndName(parentId, "test.txt")).thenReturn( + Mono.just(true)); + StepVerifier + .create(attachmentOperator.existsByParentIdAndName(parentId, "test.txt")) .expectNext(true) .verifyComplete(); } @@ -125,19 +156,25 @@ void attachmentExistsByParentIdAndName() { @Test void attachmentExistsByTypeAndParentIdAndName() { UUID parentId = UUID.randomUUID(); - when(attachmentService.existsByTypeAndParentIdAndName(AttachmentType.File, parentId, "test.txt")) + when(attachmentService.existsByTypeAndParentIdAndName(AttachmentType.File, parentId, + "test.txt")) .thenReturn(Mono.just(false)); - StepVerifier.create( - attachmentOperator.existsByTypeAndParentIdAndName(AttachmentType.File, parentId, "test.txt")) + StepVerifier + .create( + attachmentOperator.existsByTypeAndParentIdAndName(AttachmentType.File, parentId, + "test.txt")) .expectNext(false) .verifyComplete(); } @Test void referenceSave() { - AttachmentReference ref = AttachmentReference.builder().build(); + AttachmentReference ref = AttachmentReference + .builder() + .build(); when(referenceService.save(ref)).thenReturn(Mono.just(ref)); - StepVerifier.create(referenceOperator.save(ref)) + StepVerifier + .create(referenceOperator.save(ref)) .expectNext(ref) .verifyComplete(); } @@ -145,11 +182,17 @@ void referenceSave() { @Test void referenceFindAllByTypeAndAttachmentId() { UUID attachmentId = UUID.randomUUID(); - AttachmentReference ref = AttachmentReference.builder().attachmentId(attachmentId).build(); - when(referenceService.findAllByTypeAndAttachmentId(AttachmentReferenceType.EPISODE, attachmentId)) + AttachmentReference ref = AttachmentReference + .builder() + .attachmentId(attachmentId) + .build(); + when(referenceService.findAllByTypeAndAttachmentId(AttachmentReferenceType.EPISODE, + attachmentId)) .thenReturn(Flux.just(ref)); - StepVerifier.create( - referenceOperator.findAllByTypeAndAttachmentId(AttachmentReferenceType.EPISODE, attachmentId)) + StepVerifier + .create( + referenceOperator.findAllByTypeAndAttachmentId(AttachmentReferenceType.EPISODE, + attachmentId)) .expectNext(ref) .verifyComplete(); } @@ -160,7 +203,9 @@ void referenceMatchingAttachmentsAndSubjectEpisodes() { UUID[] attachmentIds = {UUID.randomUUID()}; when(referenceService.matchingAttachmentsAndSubjectEpisodes(subjectId, attachmentIds)) .thenReturn(Mono.empty()); - StepVerifier.create(referenceOperator.matchingAttachmentsAndSubjectEpisodes(subjectId, attachmentIds)) + StepVerifier + .create( + referenceOperator.matchingAttachmentsAndSubjectEpisodes(subjectId, attachmentIds)) .verifyComplete(); } @@ -170,8 +215,10 @@ void referenceMatchingAttachmentsAndSubjectEpisodesWithNotify() { UUID[] attachmentIds = {UUID.randomUUID()}; when(referenceService.matchingAttachmentsAndSubjectEpisodes(subjectId, attachmentIds, true)) .thenReturn(Mono.empty()); - StepVerifier.create( - referenceOperator.matchingAttachmentsAndSubjectEpisodes(subjectId, attachmentIds, true)) + StepVerifier + .create( + referenceOperator.matchingAttachmentsAndSubjectEpisodes(subjectId, attachmentIds, + true)) .verifyComplete(); } @@ -181,27 +228,38 @@ void referenceMatchingAttachmentsForEpisode() { UUID[] attachmentIds = {UUID.randomUUID()}; when(referenceService.matchingAttachmentsForEpisode(episodeId, attachmentIds)) .thenReturn(Mono.empty()); - StepVerifier.create(referenceOperator.matchingAttachmentsForEpisode(episodeId, attachmentIds)) + StepVerifier + .create(referenceOperator.matchingAttachmentsForEpisode(episodeId, attachmentIds)) .verifyComplete(); } @Test void relationFindAllByTypeAndAttachmentId() { UUID attachmentId = UUID.randomUUID(); - AttachmentRelation relation = AttachmentRelation.builder().attachmentId(attachmentId).build(); - when(relationService.findAllByTypeAndAttachmentId(AttachmentRelationType.VIDEO_SUBTITLE, attachmentId)) + AttachmentRelation relation = AttachmentRelation + .builder() + .attachmentId(attachmentId) + .build(); + when(relationService.findAllByTypeAndAttachmentId(AttachmentRelationType.VIDEO_SUBTITLE, + attachmentId)) .thenReturn(Flux.just(relation)); - StepVerifier.create( - relationOperator.findAllByTypeAndAttachmentId(AttachmentRelationType.VIDEO_SUBTITLE, attachmentId)) + StepVerifier + .create( + relationOperator.findAllByTypeAndAttachmentId(AttachmentRelationType.VIDEO_SUBTITLE, + attachmentId)) .expectNext(relation) .verifyComplete(); } @Test void driverSave() { - AttachmentDriver driver = AttachmentDriver.builder().name("local").build(); + AttachmentDriver driver = AttachmentDriver + .builder() + .name("local") + .build(); when(driverService.save(driver)).thenReturn(Mono.just(driver)); - StepVerifier.create(driverOperator.save(driver)) + StepVerifier + .create(driverOperator.save(driver)) .expectNext(driver) .verifyComplete(); } @@ -209,28 +267,40 @@ void driverSave() { @Test void driverFindById() { UUID id = UUID.randomUUID(); - AttachmentDriver driver = AttachmentDriver.builder().id(id).build(); + AttachmentDriver driver = AttachmentDriver + .builder() + .id(id) + .build(); when(driverService.findById(id)).thenReturn(Mono.just(driver)); - StepVerifier.create(driverOperator.findById(id)) + StepVerifier + .create(driverOperator.findById(id)) .expectNext(driver) .verifyComplete(); } @Test void driverFindByTypeAndName() { - AttachmentDriver driver = AttachmentDriver.builder().name("local").type(AttachmentDriverType.LOCAL).build(); + AttachmentDriver driver = AttachmentDriver + .builder() + .name("local") + .type(AttachmentDriverType.LOCAL) + .build(); when(driverService.findByTypeAndName("LOCAL", "local")).thenReturn(Mono.just(driver)); - StepVerifier.create(driverOperator.findByTypeAndName("LOCAL", "local")) + StepVerifier + .create(driverOperator.findByTypeAndName("LOCAL", "local")) .expectNext(driver) .verifyComplete(); } @Test void driverListAttachmentsByCondition() { - AttachmentSearchCondition condition = AttachmentSearchCondition.builder().build(); + AttachmentSearchCondition condition = AttachmentSearchCondition + .builder() + .build(); PagingWrap wrap = PagingWrap.emptyResult(); when(driverService.listAttachmentsByCondition(condition)).thenReturn(Mono.just(wrap)); - StepVerifier.create(driverOperator.listAttachmentsByCondition(condition)) + StepVerifier + .create(driverOperator.listAttachmentsByCondition(condition)) .expectNext(wrap) .verifyComplete(); } @@ -239,14 +309,17 @@ void driverListAttachmentsByCondition() { void driverRefresh() { UUID id = UUID.randomUUID(); when(driverService.refresh(id)).thenReturn(Mono.empty()); - StepVerifier.create(driverOperator.refresh(id)).verifyComplete(); + StepVerifier + .create(driverOperator.refresh(id)) + .verifyComplete(); } @Test void driverListDriversByCondition() { PagingWrap wrap = PagingWrap.emptyResult(); when(driverService.listDriversByCondition(1, 10)).thenReturn(Mono.just(wrap)); - StepVerifier.create(driverOperator.listDriversByCondition(1, 10)) + StepVerifier + .create(driverOperator.listDriversByCondition(1, 10)) .expectNext(wrap) .verifyComplete(); } diff --git a/server/src/test/java/run/ikaros/server/core/collection/DefaultCollectionServiceTest.java b/server/src/test/java/run/ikaros/server/core/collection/DefaultCollectionServiceTest.java index 7bafac33b..4e3b14d66 100644 --- a/server/src/test/java/run/ikaros/server/core/collection/DefaultCollectionServiceTest.java +++ b/server/src/test/java/run/ikaros/server/core/collection/DefaultCollectionServiceTest.java @@ -3,32 +3,21 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; import java.util.List; import java.util.UUID; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Sort; import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; -import org.springframework.data.relational.core.query.Criteria; import org.springframework.data.relational.core.query.Query; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; -import run.ikaros.api.core.collection.EpisodeCollection; -import run.ikaros.api.core.collection.SubjectCollection; import run.ikaros.api.core.collection.vo.FindCollectionCondition; -import run.ikaros.api.infra.utils.ReactiveBeanUtils; import run.ikaros.api.store.enums.CollectionCategory; import run.ikaros.api.store.enums.CollectionType; -import run.ikaros.api.wrap.PagingWrap; import run.ikaros.server.core.user.UserService; import run.ikaros.server.store.entity.EpisodeCollectionEntity; import run.ikaros.server.store.entity.SubjectCollectionEntity; @@ -69,7 +58,8 @@ void findTypeBySubjectId() { when(subjectCollectionRepository.findByUserIdAndSubjectId(userId, subjectId)) .thenReturn(Mono.just(entity)); - StepVerifier.create(defaultCollectionService.findTypeBySubjectId(subjectId)) + StepVerifier + .create(defaultCollectionService.findTypeBySubjectId(subjectId)) .assertNext(type -> assertThat(type).isEqualTo(CollectionType.WISH)) .verifyComplete(); } @@ -80,14 +70,19 @@ void findTypeBySubjectId_whenNotFound() { when(subjectCollectionRepository.findByUserIdAndSubjectId(userId, subjectId)) .thenReturn(Mono.empty()); - StepVerifier.create(defaultCollectionService.findTypeBySubjectId(subjectId)) + StepVerifier + .create(defaultCollectionService.findTypeBySubjectId(subjectId)) .verifyComplete(); } @Test void listCollectionsByCondition_forSubject_withType() { - FindCollectionCondition condition = FindCollectionCondition.builder() - .page(1).size(10).category(CollectionCategory.SUBJECT).type(CollectionType.DONE) + FindCollectionCondition condition = FindCollectionCondition + .builder() + .page(1) + .size(10) + .category(CollectionCategory.SUBJECT) + .type(CollectionType.DONE) .build(); SubjectCollectionEntity entity = new SubjectCollectionEntity(); @@ -103,7 +98,8 @@ void listCollectionsByCondition_forSubject_withType() { when(subjectCollectionRepository.findById(entity.getId())) .thenReturn(Mono.just(entity)); - StepVerifier.create(defaultCollectionService.listCollectionsByCondition(condition)) + StepVerifier + .create(defaultCollectionService.listCollectionsByCondition(condition)) .assertNext(pagingWrap -> { assertThat(pagingWrap.getPage()).isEqualTo(1); assertThat(pagingWrap.getSize()).isEqualTo(10); @@ -116,8 +112,11 @@ void listCollectionsByCondition_forSubject_withType() { @Test void listCollectionsByCondition_forSubject_withoutType() { - FindCollectionCondition condition = FindCollectionCondition.builder() - .page(1).size(10).category(CollectionCategory.SUBJECT) + FindCollectionCondition condition = FindCollectionCondition + .builder() + .page(1) + .size(10) + .category(CollectionCategory.SUBJECT) .build(); SubjectCollectionEntity entity = new SubjectCollectionEntity(); @@ -133,7 +132,8 @@ void listCollectionsByCondition_forSubject_withoutType() { when(subjectCollectionRepository.findById(entity.getId())) .thenReturn(Mono.just(entity)); - StepVerifier.create(defaultCollectionService.listCollectionsByCondition(condition)) + StepVerifier + .create(defaultCollectionService.listCollectionsByCondition(condition)) .assertNext(pagingWrap -> { assertThat(pagingWrap.getTotal()).isEqualTo(1); }) @@ -143,8 +143,11 @@ void listCollectionsByCondition_forSubject_withoutType() { @Test void listCollectionsByCondition_forEpisode_withTimeRange() { long now = System.currentTimeMillis(); - FindCollectionCondition condition = FindCollectionCondition.builder() - .page(1).size(10).category(CollectionCategory.EPISODE) + FindCollectionCondition condition = FindCollectionCondition + .builder() + .page(1) + .size(10) + .category(CollectionCategory.EPISODE) .updateTimeDesc(true) .time((now - 86400000) + "-" + now) .build(); @@ -162,7 +165,8 @@ void listCollectionsByCondition_forEpisode_withTimeRange() { when(episodeCollectionRepository.findById(episodeId)) .thenReturn(Mono.just(entity)); - StepVerifier.create(defaultCollectionService.listCollectionsByCondition(condition)) + StepVerifier + .create(defaultCollectionService.listCollectionsByCondition(condition)) .assertNext(pagingWrap -> { assertThat(pagingWrap.getTotal()).isEqualTo(1); }) @@ -171,8 +175,11 @@ void listCollectionsByCondition_forEpisode_withTimeRange() { @Test void listCollectionsByCondition_withPageZero_throwsException() { - FindCollectionCondition condition = FindCollectionCondition.builder() - .page(0).size(10).category(CollectionCategory.SUBJECT) + FindCollectionCondition condition = FindCollectionCondition + .builder() + .page(0) + .size(10) + .category(CollectionCategory.SUBJECT) .build(); org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, @@ -187,8 +194,11 @@ void listCollectionsByCondition_withNullCondition_throwsException() { @Test void listCollectionsByCondition_forEpisode_withoutUpdateTimeDesc() { - FindCollectionCondition condition = FindCollectionCondition.builder() - .page(1).size(10).category(CollectionCategory.EPISODE) + FindCollectionCondition condition = FindCollectionCondition + .builder() + .page(1) + .size(10) + .category(CollectionCategory.EPISODE) .updateTimeDesc(false) .build(); @@ -202,7 +212,8 @@ void listCollectionsByCondition_forEpisode_withoutUpdateTimeDesc() { when(episodeCollectionRepository.findById(episodeId)) .thenReturn(Mono.just(entity)); - StepVerifier.create(defaultCollectionService.listCollectionsByCondition(condition)) + StepVerifier + .create(defaultCollectionService.listCollectionsByCondition(condition)) .assertNext(pagingWrap -> assertThat(pagingWrap.getTotal()).isEqualTo(1)) .verifyComplete(); } diff --git a/server/src/test/java/run/ikaros/server/core/notify/service/NotifyServiceImplTest.java b/server/src/test/java/run/ikaros/server/core/notify/service/NotifyServiceImplTest.java index 308a42ffc..9d2363007 100644 --- a/server/src/test/java/run/ikaros/server/core/notify/service/NotifyServiceImplTest.java +++ b/server/src/test/java/run/ikaros/server/core/notify/service/NotifyServiceImplTest.java @@ -1,6 +1,5 @@ package run.ikaros.server.core.notify.service; -import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; @@ -35,7 +34,8 @@ void sendMail() throws MessagingException { when(mailService.getMailConfig()).thenReturn(mailConfig); when(mailService.send(any(MailRequest.class))).thenReturn(Mono.empty()); - StepVerifier.create(notifyService.sendMail("Hello", "World")) + StepVerifier + .create(notifyService.sendMail("Hello", "World")) .verifyComplete(); verify(mailService).send(any(MailRequest.class)); } @@ -69,7 +69,8 @@ void send() throws MessagingException { when(mailService.send(any(MailRequest.class), anyString(), any(Context.class))) .thenReturn(Mono.empty()); - StepVerifier.create(notifyService.send("Hello", "mail/template", new Context())) + StepVerifier + .create(notifyService.send("Hello", "mail/template", new Context())) .verifyComplete(); verify(mailService).send(any(MailRequest.class), anyString(), any(Context.class)); } @@ -103,7 +104,8 @@ void testMail() throws MessagingException { when(mailService.send(any(MailRequest.class), anyString(), any(Context.class))) .thenReturn(Mono.empty()); - StepVerifier.create(notifyService.testMail()) + StepVerifier + .create(notifyService.testMail()) .verifyComplete(); verify(mailService).send(any(MailRequest.class), anyString(), any(Context.class)); } diff --git a/server/src/test/java/run/ikaros/server/core/statics/StaticServiceImplTest.java b/server/src/test/java/run/ikaros/server/core/statics/StaticServiceImplTest.java index beb903261..56d9c54c5 100644 --- a/server/src/test/java/run/ikaros/server/core/statics/StaticServiceImplTest.java +++ b/server/src/test/java/run/ikaros/server/core/statics/StaticServiceImplTest.java @@ -12,7 +12,6 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import reactor.core.publisher.Flux; import reactor.test.StepVerifier; import run.ikaros.api.constant.AppConst; import run.ikaros.api.infra.properties.IkarosProperties; @@ -36,12 +35,14 @@ void setUp() throws IOException { @AfterEach void tearDown() throws IOException { if (tempWorkDir != null) { - Files.walk(tempWorkDir) + Files + .walk(tempWorkDir) .sorted((a, b) -> b.compareTo(a)) .forEach(path -> { try { Files.deleteIfExists(path); } catch (IOException ignored) { + // } }); } @@ -49,7 +50,8 @@ void tearDown() throws IOException { @Test void listStaticsFontsWhenDirNotExists() { - StepVerifier.create(staticService.listStaticsFonts()) + StepVerifier + .create(staticService.listStaticsFonts()) .expectNextCount(0) .verifyComplete(); } @@ -58,7 +60,8 @@ void listStaticsFontsWhenDirNotExists() { void listStaticsFontsWhenFontDirNotExists() throws IOException { Path staticsDir = tempWorkDir.resolve(AppConst.STATIC_DIR_NAME); Files.createDirectory(staticsDir); - StepVerifier.create(staticService.listStaticsFonts()) + StepVerifier + .create(staticService.listStaticsFonts()) .expectNextCount(0) .verifyComplete(); } @@ -72,7 +75,10 @@ void listStaticsFontsWithFiles() throws IOException { Files.createFile(fontDir.resolve("NotoSansSC-Regular.otf")); Files.createFile(fontDir.resolve("NotoSerifSC-Regular.otf")); - StepVerifier.create(staticService.listStaticsFonts().collectList()) + StepVerifier + .create(staticService + .listStaticsFonts() + .collectList()) .assertNext(fonts -> { assertThat(fonts).hasSize(2); assertThat(fonts).anyMatch(f -> f.contains("NotoSansSC-Regular.otf")); @@ -89,8 +95,10 @@ void listStaticsFontsReturnsCorrectUrlPrefix() throws IOException { Files.createDirectory(fontDir); Files.createFile(fontDir.resolve("test.woff2")); - StepVerifier.create(staticService.listStaticsFonts()) - .assertNext(font -> assertThat(font).startsWith("/static/" + AppConst.STATIC_FONT_DIR_NAME + "/")) + StepVerifier + .create(staticService.listStaticsFonts()) + .assertNext(font -> assertThat(font).startsWith( + "/static/" + AppConst.STATIC_FONT_DIR_NAME + "/")) .verifyComplete(); } @@ -101,7 +109,10 @@ void listStaticsFontsWhenFontDirIsEmpty() throws IOException { Path fontDir = staticsDir.resolve(AppConst.STATIC_FONT_DIR_NAME); Files.createDirectory(fontDir); - StepVerifier.create(staticService.listStaticsFonts().collectList()) + StepVerifier + .create(staticService + .listStaticsFonts() + .collectList()) .assertNext(fonts -> assertThat(fonts).isEmpty()) .verifyComplete(); } diff --git a/server/src/test/java/run/ikaros/server/core/subject/SubjectOperatorsTest.java b/server/src/test/java/run/ikaros/server/core/subject/SubjectOperatorsTest.java index 5fbb4d1aa..f3707633d 100644 --- a/server/src/test/java/run/ikaros/server/core/subject/SubjectOperatorsTest.java +++ b/server/src/test/java/run/ikaros/server/core/subject/SubjectOperatorsTest.java @@ -19,7 +19,6 @@ import run.ikaros.api.infra.utils.UuidV7Utils; import run.ikaros.api.store.enums.SubjectRelationType; import run.ikaros.api.store.enums.SubjectSyncPlatform; -import run.ikaros.api.wrap.PagingWrap; import run.ikaros.server.core.subject.service.SubjectRelationService; import run.ikaros.server.core.subject.service.SubjectService; import run.ikaros.server.core.subject.service.SubjectSyncService; @@ -48,38 +47,58 @@ void setUp() { @Test void findById() { UUID id = UuidV7Utils.generateUuid(); - Subject subject = Subject.builder().id(id).name("Test").build(); + Subject subject = Subject + .builder() + .id(id) + .name("Test") + .build(); when(subjectService.findById(id)).thenReturn(Mono.just(subject)); - StepVerifier.create(subjectOperator.findById(id)) + StepVerifier + .create(subjectOperator.findById(id)) .expectNext(subject) .verifyComplete(); } @Test void create() { - Subject subject = Subject.builder().name("New").build(); + Subject subject = Subject + .builder() + .name("New") + .build(); when(subjectService.create(any())).thenReturn(Mono.just(subject)); - StepVerifier.create(subjectOperator.create(subject)) + StepVerifier + .create(subjectOperator.create(subject)) .expectNext(subject) .verifyComplete(); } @Test void update() { - Subject subject = Subject.builder().id(UuidV7Utils.generateUuid()).name("Updated").build(); + Subject subject = Subject + .builder() + .id(UuidV7Utils.generateUuid()) + .name("Updated") + .build(); when(subjectService.update(any())).thenReturn(Mono.empty()); - StepVerifier.create(subjectOperator.update(subject)) + StepVerifier + .create(subjectOperator.update(subject)) .verifyComplete(); } @Test void findBySubjectIdAndPlatformAndPlatformId() { UUID subjectId = UuidV7Utils.generateUuid(); - Subject subject = Subject.builder().name("SyncTest").build(); - when(subjectService.findBySubjectIdAndPlatformAndPlatformId(subjectId, SubjectSyncPlatform.BGM_TV, "bgm123")) + Subject subject = Subject + .builder() + .name("SyncTest") + .build(); + when(subjectService.findBySubjectIdAndPlatformAndPlatformId(subjectId, + SubjectSyncPlatform.BGM_TV, "bgm123")) .thenReturn(Mono.just(subject)); - StepVerifier.create( - subjectOperator.findBySubjectIdAndPlatformAndPlatformId(subjectId, SubjectSyncPlatform.BGM_TV, "bgm123")) + StepVerifier + .create( + subjectOperator.findBySubjectIdAndPlatformAndPlatformId(subjectId, + SubjectSyncPlatform.BGM_TV, "bgm123")) .expectNext(subject) .verifyComplete(); } @@ -87,13 +106,15 @@ void findBySubjectIdAndPlatformAndPlatformId() { @Test void relationFindAllBySubjectId() { UUID subjectId = UuidV7Utils.generateUuid(); - SubjectRelation relation = SubjectRelation.builder() + SubjectRelation relation = SubjectRelation + .builder() .subject(subjectId) .relationType(SubjectRelationType.AFTER) .relationSubjects(Set.of(UuidV7Utils.generateUuid())) .build(); when(relationService.findAllBySubjectId(subjectId)).thenReturn(Flux.just(relation)); - StepVerifier.create(relationOperator.findAllBySubjectId(subjectId)) + StepVerifier + .create(relationOperator.findAllBySubjectId(subjectId)) .expectNext(relation) .verifyComplete(); } @@ -101,24 +122,28 @@ void relationFindAllBySubjectId() { @Test void relationFindBySubjectIdAndType() { UUID subjectId = UuidV7Utils.generateUuid(); - SubjectRelation relation = SubjectRelation.builder() + SubjectRelation relation = SubjectRelation + .builder() .subject(subjectId) .relationType(SubjectRelationType.AFTER) .build(); when(relationService.findBySubjectIdAndType(subjectId, SubjectRelationType.AFTER)) .thenReturn(Mono.just(relation)); - StepVerifier.create(relationOperator.findBySubjectIdAndType(subjectId, SubjectRelationType.AFTER)) + StepVerifier + .create(relationOperator.findBySubjectIdAndType(subjectId, SubjectRelationType.AFTER)) .expectNext(relation) .verifyComplete(); } @Test void relationCreateSubjectRelation() { - SubjectRelation relation = SubjectRelation.builder() + SubjectRelation relation = SubjectRelation + .builder() .relationType(SubjectRelationType.AFTER) .build(); when(relationService.createSubjectRelation(relation)).thenReturn(Mono.just(relation)); - StepVerifier.create(relationOperator.createSubjectRelation(relation)) + StepVerifier + .create(relationOperator.createSubjectRelation(relation)) .expectNext(relation) .verifyComplete(); } @@ -128,7 +153,8 @@ void sync() { UUID subjectId = UuidV7Utils.generateUuid(); when(syncService.sync(subjectId, SubjectSyncPlatform.BGM_TV, "456")) .thenReturn(Mono.empty()); - StepVerifier.create(syncOperator.sync(subjectId, SubjectSyncPlatform.BGM_TV, "456")) + StepVerifier + .create(syncOperator.sync(subjectId, SubjectSyncPlatform.BGM_TV, "456")) .verifyComplete(); } @@ -136,15 +162,20 @@ void sync() { void syncWithoutSubjectId() { when(syncService.sync(null, SubjectSyncPlatform.BGM_TV, "456")) .thenReturn(Mono.empty()); - StepVerifier.create(syncOperator.sync(null, SubjectSyncPlatform.BGM_TV, "456")) + StepVerifier + .create(syncOperator.sync(null, SubjectSyncPlatform.BGM_TV, "456")) .verifyComplete(); } @Test void saveSubjectSync() { - SubjectSync sync = SubjectSync.builder().platformId("bgm123").build(); + SubjectSync sync = SubjectSync + .builder() + .platformId("bgm123") + .build(); when(syncService.save(sync)).thenReturn(Mono.just(sync)); - StepVerifier.create(syncOperator.save(sync)) + StepVerifier + .create(syncOperator.save(sync)) .expectNext(sync) .verifyComplete(); } @@ -152,9 +183,13 @@ void saveSubjectSync() { @Test void findSubjectSyncsBySubjectId() { UUID subjectId = UuidV7Utils.generateUuid(); - SubjectSync sync = SubjectSync.builder().subjectId(subjectId).build(); + SubjectSync sync = SubjectSync + .builder() + .subjectId(subjectId) + .build(); when(syncService.findSubjectSyncsBySubjectId(subjectId)).thenReturn(Flux.just(sync)); - StepVerifier.create(syncOperator.findSubjectSyncsBySubjectId(subjectId)) + StepVerifier + .create(syncOperator.findSubjectSyncsBySubjectId(subjectId)) .expectNext(sync) .verifyComplete(); } @@ -162,10 +197,16 @@ void findSubjectSyncsBySubjectId() { @Test void findSubjectSyncBySubjectIdAndPlatform() { UUID subjectId = UuidV7Utils.generateUuid(); - SubjectSync sync = SubjectSync.builder().subjectId(subjectId).build(); - when(syncService.findSubjectSyncBySubjectIdAndPlatform(subjectId, SubjectSyncPlatform.BGM_TV)) + SubjectSync sync = SubjectSync + .builder() + .subjectId(subjectId) + .build(); + when(syncService.findSubjectSyncBySubjectIdAndPlatform(subjectId, + SubjectSyncPlatform.BGM_TV)) .thenReturn(Mono.just(sync)); - StepVerifier.create(syncOperator.findSubjectSyncBySubjectIdAndPlatform(subjectId, SubjectSyncPlatform.BGM_TV)) + StepVerifier + .create(syncOperator.findSubjectSyncBySubjectIdAndPlatform(subjectId, + SubjectSyncPlatform.BGM_TV)) .expectNext(sync) .verifyComplete(); } diff --git a/server/src/test/java/run/ikaros/server/core/tag/DefaultTagServiceMoreTest.java b/server/src/test/java/run/ikaros/server/core/tag/DefaultTagServiceMoreTest.java index f50266c08..40baa5e25 100644 --- a/server/src/test/java/run/ikaros/server/core/tag/DefaultTagServiceMoreTest.java +++ b/server/src/test/java/run/ikaros/server/core/tag/DefaultTagServiceMoreTest.java @@ -33,13 +33,15 @@ void setUp() { tagRepository = Mockito.mock(TagRepository.class); r2dbcEntityTemplate = Mockito.mock(R2dbcEntityTemplate.class); eventPublisher = Mockito.mock(ApplicationEventPublisher.class); - defaultTagService = new DefaultTagService(tagRepository, r2dbcEntityTemplate, eventPublisher); + defaultTagService = + new DefaultTagService(tagRepository, r2dbcEntityTemplate, eventPublisher); } @Test void findSubjectTags() { UUID subjectId = UuidV7Utils.generateUuid(); - TagEntity tagEntity = TagEntity.builder() + TagEntity tagEntity = TagEntity + .builder() .id(UuidV7Utils.generateUuid()) .type(TagType.SUBJECT) .name("anime") @@ -52,7 +54,8 @@ void findSubjectTags() { when(r2dbcEntityTemplate.select(any(Query.class), any(Class.class))) .thenReturn(Flux.just(tagEntity)); - StepVerifier.create(defaultTagService.findSubjectTags(subjectId)) + StepVerifier + .create(defaultTagService.findSubjectTags(subjectId)) .assertNext(subjectTag -> { assertThat(subjectTag.getName()).isEqualTo("anime"); assertThat(subjectTag.getSubjectId()).isEqualTo(subjectId); @@ -65,7 +68,8 @@ void findSubjectTagsEmpty() { UUID subjectId = UuidV7Utils.generateUuid(); when(r2dbcEntityTemplate.select(any(Query.class), any(Class.class))) .thenReturn(Flux.empty()); - StepVerifier.create(defaultTagService.findSubjectTags(subjectId)) + StepVerifier + .create(defaultTagService.findSubjectTags(subjectId)) .expectNextCount(0) .verifyComplete(); } @@ -73,7 +77,8 @@ void findSubjectTagsEmpty() { @Test void findAttachmentTags() { UUID attachmentId = UuidV7Utils.generateUuid(); - TagEntity tagEntity = TagEntity.builder() + TagEntity tagEntity = TagEntity + .builder() .id(UuidV7Utils.generateUuid()) .type(TagType.ATTACHMENT) .name("cover") @@ -85,7 +90,8 @@ void findAttachmentTags() { when(r2dbcEntityTemplate.select(any(Query.class), any(Class.class))) .thenReturn(Flux.just(tagEntity)); - StepVerifier.create(defaultTagService.findAttachmentTags(attachmentId)) + StepVerifier + .create(defaultTagService.findAttachmentTags(attachmentId)) .assertNext(attachmentTag -> { assertThat(attachmentTag.getName()).isEqualTo("cover"); assertThat(attachmentTag.getAttachmentId()).isEqualTo(attachmentId); @@ -99,7 +105,8 @@ void remove() { UUID userId = UuidV7Utils.generateUuid(); UUID tagId = UuidV7Utils.generateUuid(); - TagEntity tagEntity = TagEntity.builder() + TagEntity tagEntity = TagEntity + .builder() .id(tagId) .type(TagType.SUBJECT) .masterId(masterId) @@ -113,7 +120,8 @@ void remove() { when(tagRepository.findById(tagId)).thenReturn(Mono.just(tagEntity)); when(tagRepository.deleteById(tagId)).thenReturn(Mono.empty()); - StepVerifier.create(defaultTagService.remove(TagType.SUBJECT, masterId, "action", userId)) + StepVerifier + .create(defaultTagService.remove(TagType.SUBJECT, masterId, "action", userId)) .verifyComplete(); verify(eventPublisher).publishEvent(any(TagRemoveEvent.class)); @@ -122,7 +130,8 @@ void remove() { @Test void removeById() { UUID tagId = UuidV7Utils.generateUuid(); - TagEntity tagEntity = TagEntity.builder() + TagEntity tagEntity = TagEntity + .builder() .id(tagId) .type(TagType.SUBJECT) .name("test") @@ -132,7 +141,8 @@ void removeById() { when(tagRepository.findById(tagId)).thenReturn(Mono.just(tagEntity)); when(tagRepository.deleteById(tagId)).thenReturn(Mono.empty()); - StepVerifier.create(defaultTagService.removeById(tagId)) + StepVerifier + .create(defaultTagService.removeById(tagId)) .verifyComplete(); verify(eventPublisher).publishEvent(any(TagRemoveEvent.class)); diff --git a/server/src/test/java/run/ikaros/server/custom/scheme/DefaultCustomSchemeManagerTest.java b/server/src/test/java/run/ikaros/server/custom/scheme/DefaultCustomSchemeManagerTest.java index c12323c81..953c65ec6 100644 --- a/server/src/test/java/run/ikaros/server/custom/scheme/DefaultCustomSchemeManagerTest.java +++ b/server/src/test/java/run/ikaros/server/custom/scheme/DefaultCustomSchemeManagerTest.java @@ -2,7 +2,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -49,7 +48,9 @@ void setUp() throws Exception { @Test void register() { manager.register(scheme); - assertThat(manager.schemes()).hasSize(1).contains(scheme); + assertThat(manager.schemes()) + .hasSize(1) + .contains(scheme); } @Test diff --git a/server/src/test/java/run/ikaros/server/infra/utils/AesEncryptUtilsTest.java b/server/src/test/java/run/ikaros/server/infra/utils/AesEncryptUtilsTest.java index adac109be..a6dccef6e 100644 --- a/server/src/test/java/run/ikaros/server/infra/utils/AesEncryptUtilsTest.java +++ b/server/src/test/java/run/ikaros/server/infra/utils/AesEncryptUtilsTest.java @@ -16,7 +16,9 @@ void generateKeyByteArray_default_returnsBase64Bytes() { byte[] keyB64 = AesEncryptUtils.generateKeyByteArray(); assertThat(keyB64).isNotNull(); // decode to verify it's valid base64 and 24 raw bytes (192-bit default) - byte[] rawKey = Base64.getDecoder().decode(keyB64); + byte[] rawKey = Base64 + .getDecoder() + .decode(keyB64); assertThat(rawKey).hasSize(24); } @@ -24,7 +26,9 @@ void generateKeyByteArray_default_returnsBase64Bytes() { void generateKeyByteArray_128bit_returnsValidKey() { byte[] keyB64 = AesEncryptUtils.generateKeyByteArray(128); assertThat(keyB64).isNotNull(); - byte[] rawKey = Base64.getDecoder().decode(keyB64); + byte[] rawKey = Base64 + .getDecoder() + .decode(keyB64); assertThat(rawKey).hasSize(16); // 128 bits = 16 bytes } @@ -32,14 +36,18 @@ void generateKeyByteArray_128bit_returnsValidKey() { void generateKeyByteArray_256bit_returnsValidKey() { byte[] keyB64 = AesEncryptUtils.generateKeyByteArray(256); assertThat(keyB64).isNotNull(); - byte[] rawKey = Base64.getDecoder().decode(keyB64); + byte[] rawKey = Base64 + .getDecoder() + .decode(keyB64); assertThat(rawKey).hasSize(32); // 256 bits = 32 bytes } @Test void encryptDecrypt_roundTrip_bytes() throws Exception { byte[] keyB64 = AesEncryptUtils.generateKeyByteArray(128); - byte[] rawKey = Base64.getDecoder().decode(keyB64); + byte[] rawKey = Base64 + .getDecoder() + .decode(keyB64); String original = "Hello AES World! " + System.currentTimeMillis(); byte[] originalBytes = original.getBytes(StandardCharsets.UTF_8); @@ -65,7 +73,9 @@ void encryptDecrypt_withStringKey() throws Exception { original.getBytes(StandardCharsets.UTF_8)); assertThat(encrypted).isNotNull(); - byte[] rawKey = Base64.getDecoder().decode(keyB64); + byte[] rawKey = Base64 + .getDecoder() + .decode(keyB64); byte[] decrypted = AesEncryptUtils.decryptByteArray(rawKey, encrypted); String result = new String(decrypted, StandardCharsets.UTF_8); assertThat(result).isEqualTo(original); @@ -85,8 +95,12 @@ void generateKeyFile_customLength_createsFile(@TempDir File tempDir) throws Exce AesEncryptUtils.generateKeyFile(256, keyFile); assertThat(keyFile).exists(); // key file content is base64 encoded, should be ~44 chars for 256-bit = 32 raw bytes - String content = Files.readString(keyFile.toPath()).trim(); - byte[] rawKey = Base64.getDecoder().decode(content); + String content = Files + .readString(keyFile.toPath()) + .trim(); + byte[] rawKey = Base64 + .getDecoder() + .decode(content); assertThat(rawKey).hasSize(32); } @@ -117,11 +131,14 @@ void encryptByteArray_withKeyFile_roundTrip(@TempDir File tempDir) throws Except AesEncryptUtils.generateKeyFile(keyFile); String original = "Encrypt with key file"; - byte[] encrypted = AesEncryptUtils.encryptByteArray(keyFile, original.getBytes(StandardCharsets.UTF_8)); + byte[] encrypted = + AesEncryptUtils.encryptByteArray(keyFile, original.getBytes(StandardCharsets.UTF_8)); assertThat(encrypted).isNotNull(); byte[] keyB64 = Files.readAllBytes(keyFile.toPath()); - byte[] rawKey = Base64.getDecoder().decode(keyB64); + byte[] rawKey = Base64 + .getDecoder() + .decode(keyB64); byte[] decrypted = AesEncryptUtils.decryptByteArray(rawKey, encrypted); String result = new String(decrypted, StandardCharsets.UTF_8); assertThat(result).isEqualTo(original); diff --git a/server/src/test/java/run/ikaros/server/infra/utils/JsonUtilsTest.java b/server/src/test/java/run/ikaros/server/infra/utils/JsonUtilsTest.java index 180f9a499..16f12c2c4 100644 --- a/server/src/test/java/run/ikaros/server/infra/utils/JsonUtilsTest.java +++ b/server/src/test/java/run/ikaros/server/infra/utils/JsonUtilsTest.java @@ -41,7 +41,8 @@ void json2obj_invalidJson_returnsNull() { @Test void json2ObjArr_validJson_returnsArray() { String json = "[{\"name\":\"a\",\"value\":1},{\"name\":\"b\",\"value\":2}]"; - TestObj[] arr = JsonUtils.json2ObjArr(json, new TypeReference() {}); + TestObj[] arr = JsonUtils.json2ObjArr(json, new TypeReference() { + }); assertThat(arr).hasSize(2); assertThat(arr[0].getName()).isEqualTo("a"); assertThat(arr[1].getName()).isEqualTo("b"); @@ -50,7 +51,8 @@ void json2ObjArr_validJson_returnsArray() { @Test void json2ObjArr_invalidJson_returnsEmptyArray() { // implementation returns null for invalid json - TestObj[] arr = JsonUtils.json2ObjArr("bad json", new TypeReference() {}); + TestObj[] arr = JsonUtils.json2ObjArr("bad json", new TypeReference() { + }); assertThat(arr).isNull(); } @@ -68,12 +70,28 @@ static class TestObj { private String name; private int value; - public TestObj() {} - public TestObj(String name, int value) { this.name = name; this.value = value; } + public TestObj() { + } - public String getName() { return name; } - public void setName(String name) { this.name = name; } - public int getValue() { return value; } - public void setValue(int value) { this.value = value; } + public TestObj(String name, int value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } } } diff --git a/server/src/test/java/run/ikaros/server/search/subject/LuceneSubjectSearchServiceTest.java b/server/src/test/java/run/ikaros/server/search/subject/LuceneSubjectSearchServiceTest.java index 027f5711d..34c2bbc07 100644 --- a/server/src/test/java/run/ikaros/server/search/subject/LuceneSubjectSearchServiceTest.java +++ b/server/src/test/java/run/ikaros/server/search/subject/LuceneSubjectSearchServiceTest.java @@ -14,7 +14,6 @@ import org.junit.jupiter.api.io.TempDir; import org.mockito.Mockito; import org.springframework.util.LinkedMultiValueMap; -import reactor.test.StepVerifier; import run.ikaros.api.infra.properties.IkarosProperties; import run.ikaros.api.search.SearchParam; import run.ikaros.api.search.SearchResult; @@ -85,7 +84,9 @@ void updateDocument_and_search() throws Exception { assertThat(result.getHits()).hasSize(1); assertThat(result.getTotal()).isEqualTo(1); assertThat(result.getKeyword()).isEqualTo("Test"); - SubjectHint hint = result.getHits().get(0); + SubjectHint hint = result + .getHits() + .get(0); assertThat(hint.id()).isEqualTo(id); assertThat(hint.name()).isEqualTo("Test Anime"); assertThat(hint.nameCn()).isEqualTo("测试动画"); @@ -165,7 +166,9 @@ void removeDocuments() throws Exception { // Verify exists SearchParam param = createSearchParam("Bleach"); - assertThat(searchService.search(param).getTotal()).isEqualTo(1); + assertThat(searchService + .search(param) + .getTotal()).isEqualTo(1); // Remove by keyword (UUIDs with hyphens confuse QueryParser, use name instead) searchService.removeDocuments(Set.of("Bleach")); @@ -215,7 +218,10 @@ void updateDocument_and_searchByType() throws Exception { SearchParam param = createSearchParam("type:COMIC"); SearchResult result = searchService.search(param); assertThat(result.getTotal()).isEqualTo(1); - assertThat(result.getHits().get(0).name()).isEqualTo("Manga One"); + assertThat(result + .getHits() + .get(0) + .name()).isEqualTo("Manga One"); } @Test diff --git a/server/src/test/java/run/ikaros/server/security/authentication/logout/LogoutSuccessHandlerTest.java b/server/src/test/java/run/ikaros/server/security/authentication/logout/LogoutSuccessHandlerTest.java index 2cfe99380..05ac4e38e 100644 --- a/server/src/test/java/run/ikaros/server/security/authentication/logout/LogoutSuccessHandlerTest.java +++ b/server/src/test/java/run/ikaros/server/security/authentication/logout/LogoutSuccessHandlerTest.java @@ -14,9 +14,8 @@ import org.springframework.mock.http.server.reactive.MockServerHttpResponse; import org.springframework.security.core.Authentication; import org.springframework.security.web.server.WebFilterExchange; -import org.springframework.web.server.WebFilterChain; import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; +import org.springframework.web.server.WebFilterChain; import reactor.test.StepVerifier; @ExtendWith(MockitoExtension.class) @@ -32,11 +31,14 @@ void onLogoutSuccess() { WebFilterExchange filterExchange = new WebFilterExchange(exchange, chain); Authentication authentication = mock(Authentication.class); - StepVerifier.create(handler.onLogoutSuccess(filterExchange, authentication)) + StepVerifier + .create(handler.onLogoutSuccess(filterExchange, authentication)) .verifyComplete(); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(response.getHeaders().getContentType()) + assertThat(response + .getHeaders() + .getContentType()) .isEqualTo(MediaType.APPLICATION_JSON); } }