package com.mosquito.project.dto; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.validation.ConstraintViolation; import jakarta.validation.Validation; import jakarta.validation.Validator; import jakarta.validation.ValidatorFactory; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Nested; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.NullAndEmptySource; import org.junit.jupiter.params.provider.ValueSource; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * UseApiKeyRequest DTO测试 */ @DisplayName("UseApiKeyRequest DTO测试") class UseApiKeyRequestTest { private Validator validator; private UseApiKeyRequest request; private ObjectMapper objectMapper; @BeforeEach void setUp() { ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); validator = factory.getValidator(); request = new UseApiKeyRequest(); objectMapper = new ObjectMapper(); } @Nested @DisplayName("验证测试") class ValidationTests { @Test @DisplayName("有效的API密钥应该通过验证") void shouldPassValidation_WhenValidApiKey() { // Given request.setApiKey("valid-api-key-123"); // When Set> violations = validator.validate(request); // Then assertThat(violations).isEmpty(); } @ParameterizedTest @NullAndEmptySource @ValueSource(strings = {" ", "\t", "\n", "\r"}) @DisplayName("无效API密钥应该失败验证") void shouldFailValidation_WhenInvalidApiKey(String apiKey) { // Given request.setApiKey(apiKey); // When Set> violations = validator.validate(request); // Then assertThat(violations).isNotEmpty(); assertThat(violations).hasSize(1); ConstraintViolation violation = violations.iterator().next(); assertThat(violation.getPropertyPath().toString()).isEqualTo("apiKey"); assertThat(violation.getMessage()).isEqualTo("API密钥不能为空"); } @Test @DisplayName("包含空格的API密钥应该通过验证") void shouldPassValidation_WhenApiKeyContainsWhitespace() { // Given request.setApiKey("key with spaces"); // When Set> violations = validator.validate(request); // Then assertThat(violations).isEmpty(); } @Test @DisplayName("特殊字符API密钥应该通过验证") void shouldPassValidation_WhenApiKeyContainsSpecialChars() { // Given request.setApiKey("key-!@#$%^&*()_+"); // When Set> violations = validator.validate(request); // Then assertThat(violations).isEmpty(); } @Test @DisplayName("长API密钥应该通过验证") void shouldPassValidation_WhenApiKeyIsLong() { // Given StringBuilder sb = new StringBuilder(); for (int i = 0; i < 1000; i++) { sb.append("k"); } request.setApiKey(sb.toString()); // When Set> violations = validator.validate(request); // Then assertThat(violations).isEmpty(); } @Test @DisplayName("Unicode字符API密钥应该通过验证") void shouldPassValidation_WhenApiKeyContainsUnicode() { // Given request.setApiKey("密钥-中文-🔑"); // When Set> violations = validator.validate(request); // Then assertThat(violations).isEmpty(); } } @Nested @DisplayName("Getter和Setter测试") class GetterSetterTests { @Test @DisplayName("apiKey字段的getter和setter应该正常工作") void shouldWorkCorrectly_ApiKeyGetterSetter() { // Given String apiKey = "test-api-key"; // When request.setApiKey(apiKey); // Then assertThat(request.getApiKey()).isEqualTo(apiKey); } @Test @DisplayName("多次设置apiKey应该正确更新") void shouldUpdateCorrectly_WhenSettingApiKeyMultipleTimes() { // Given request.setApiKey("key-1"); assertThat(request.getApiKey()).isEqualTo("key-1"); // When request.setApiKey("key-2"); // Then assertThat(request.getApiKey()).isEqualTo("key-2"); } @Test @DisplayName("设置null值应该正确处理") void shouldHandleNullValues_WhenSettingFields() { // Given request.setApiKey("test-key"); assertThat(request.getApiKey()).isNotNull(); // When request.setApiKey(null); // Then assertThat(request.getApiKey()).isNull(); } @Test @DisplayName("设置空字符串应该正确处理") void shouldHandleEmptyString_WhenSettingFields() { // Given request.setApiKey("test-key"); // When request.setApiKey(""); // Then assertThat(request.getApiKey()).isEmpty(); } } @Nested @DisplayName("边界值测试") class BoundaryTests { @ParameterizedTest @ValueSource(strings = { "a", // 单字符 "AB", // 双字符 "0123456789", // 数字 "key-with-dashes", // 带横线 "key_with_underscores", // 带下划线 "key.with.dots", // 带点 "key:with:colons", // 带冒号 "key/with/slashes", // 带斜杠 "key+with+plus", // 带加号 "key=with=equals", // 带等号 "key?with?question", // 带问号 "key&with&ersand", // 带& }) @DisplayName("各种格式API密钥应该正确处理") void shouldHandleVariousFormats(String apiKey) { // When request.setApiKey(apiKey); // Then assertThat(request.getApiKey()).isEqualTo(apiKey); } @Test @DisplayName("Unicode字符API密钥应该正确处理") void shouldHandleUnicodeCharacters() { // Given String[] unicodeKeys = { "密钥-中文测试", "ключ-русский", "キー-日本語", "🔑-emoji-test", "مفتاح-عربي", "🔐🔑🛡️-多emoji" }; for (String key : unicodeKeys) { // When request.setApiKey(key); // Then assertThat(request.getApiKey()).isEqualTo(key); } } @Test @DisplayName("极大长度API密钥应该正确处理") void shouldHandleExtremelyLongKey() { // Given StringBuilder sb = new StringBuilder(); for (int i = 0; i < 10000; i++) { sb.append("A"); } String extremelyLongKey = sb.toString(); // When request.setApiKey(extremelyLongKey); // Then assertThat(request.getApiKey()).hasSize(10000); assertThat(request.getApiKey()).isEqualTo(extremelyLongKey); } @Test @DisplayName("JSON特殊字符API密钥应该正确处理") void shouldHandleJsonSpecialCharacters() { // Given String jsonSpecialKey = "key{with}[brackets]\"quotes\"'apostrophe'"; // When request.setApiKey(jsonSpecialKey); // Then assertThat(request.getApiKey()).isEqualTo(jsonSpecialKey); } @Test @DisplayName("包含换行符API密钥应该正确处理") void shouldHandleNewlines() { // Given String keyWithNewlines = "line1\nline2\r\nline3\t"; // When request.setApiKey(keyWithNewlines); // Then assertThat(request.getApiKey()).isEqualTo(keyWithNewlines); } @Test @DisplayName("空白字符组合API密钥应该正确处理") void shouldHandleWhitespaceCombinations() { // Given String[] whitespaceKeys = { "key with spaces", "key\twith\ttabs", " leading-spaces", "trailing-spaces ", " both-spaces " }; for (String key : whitespaceKeys) { // When request.setApiKey(key); // Then assertThat(request.getApiKey()).isEqualTo(key); } } } @Nested @DisplayName("JSON序列化测试") class JsonSerializationTests { @Test @DisplayName("完整对象应该正确序列化为JSON") void shouldSerializeCorrectly_CompleteObject() throws JsonProcessingException { // Given request.setApiKey("test-api-key-123"); // When String json = objectMapper.writeValueAsString(request); // Then assertThat(json).isNotNull(); assertThat(json).contains("\"apiKey\":\"test-api-key-123\""); } @Test @DisplayName("null值应该正确序列化为JSON") void shouldSerializeCorrectly_WithNullValue() throws JsonProcessingException { // Given request.setApiKey(null); // When String json = objectMapper.writeValueAsString(request); // Then assertThat(json).isNotNull(); assertThat(json).contains("\"apiKey\":null"); } @Test @DisplayName("空字符串应该正确序列化为JSON") void shouldSerializeCorrectly_WithEmptyString() throws JsonProcessingException { // Given request.setApiKey(""); // When String json = objectMapper.writeValueAsString(request); // Then assertThat(json).contains("\"apiKey\":\"\""); } @Test @DisplayName("特殊字符应该正确序列化为JSON") void shouldSerializeCorrectly_WithSpecialCharacters() throws JsonProcessingException { // Given request.setApiKey("key-🔑-测试"); // When String json = objectMapper.writeValueAsString(request); // Then assertThat(json).isNotNull(); // 验证反序列化后值相同 UseApiKeyRequest deserialized = objectMapper.readValue(json, UseApiKeyRequest.class); assertThat(deserialized.getApiKey()).isEqualTo("key-🔑-测试"); } @Test @DisplayName("JSON转义字符应该正确序列化") void shouldSerializeCorrectly_WithJsonEscapes() throws JsonProcessingException { // Given String keyWithEscapes = "line1\nline2\t\"quoted\""; request.setApiKey(keyWithEscapes); // When String json = objectMapper.writeValueAsString(request); // Then assertThat(json).isNotNull(); // 验证反序列化后值相同 UseApiKeyRequest deserialized = objectMapper.readValue(json, UseApiKeyRequest.class); assertThat(deserialized.getApiKey()).isEqualTo(keyWithEscapes); } } @Nested @DisplayName("JSON反序列化测试") class JsonDeserializationTests { @Test @DisplayName("完整JSON应该正确反序列化") void shouldDeserializeCorrectly_CompleteJson() throws JsonProcessingException { // Given String json = "{\"apiKey\":\"test-api-key-12345\"}"; // When UseApiKeyRequest deserialized = objectMapper.readValue(json, UseApiKeyRequest.class); // Then assertThat(deserialized.getApiKey()).isEqualTo("test-api-key-12345"); } @Test @DisplayName("null值JSON应该正确反序列化") void shouldDeserializeCorrectly_WithNullValue() throws JsonProcessingException { // Given String json = "{\"apiKey\":null}"; // When UseApiKeyRequest deserialized = objectMapper.readValue(json, UseApiKeyRequest.class); // Then assertThat(deserialized.getApiKey()).isNull(); } @Test @DisplayName("空字符串JSON应该正确反序列化") void shouldDeserializeCorrectly_WithEmptyString() throws JsonProcessingException { // Given String json = "{\"apiKey\":\"\"}"; // When UseApiKeyRequest deserialized = objectMapper.readValue(json, UseApiKeyRequest.class); // Then assertThat(deserialized.getApiKey()).isEmpty(); } @Test @DisplayName("空对象JSON应该正确反序列化") void shouldDeserializeCorrectly_EmptyObjectJson() throws JsonProcessingException { // Given String json = "{}"; // When UseApiKeyRequest deserialized = objectMapper.readValue(json, UseApiKeyRequest.class); // Then assertThat(deserialized.getApiKey()).isNull(); } @Test @DisplayName("特殊字符JSON应该正确反序列化") void shouldDeserializeCorrectly_WithSpecialCharacters() throws JsonProcessingException { // Given String json = "{\"apiKey\":\"key-🔑-测试\"}"; // When UseApiKeyRequest deserialized = objectMapper.readValue(json, UseApiKeyRequest.class); // Then assertThat(deserialized.getApiKey()).isEqualTo("key-🔑-测试"); } @Test @DisplayName("JSON格式错误应该抛出异常") void shouldThrowException_WhenJsonIsMalformed() { // Given String malformedJson = "{\"apiKey\":\"test\""; // 缺少闭合括号 // When & Then assertThatThrownBy(() -> objectMapper.readValue(malformedJson, UseApiKeyRequest.class)) .isInstanceOf(JsonProcessingException.class); } } @Nested @DisplayName("对象行为测试") class ObjectBehaviorTests { @Test @DisplayName("两个相同apiKey的请求应该相等") void shouldBeEqual_WhenSameApiKey() { // Given UseApiKeyRequest request1 = new UseApiKeyRequest(); UseApiKeyRequest request2 = new UseApiKeyRequest(); request1.setApiKey("same-key"); request2.setApiKey("same-key"); // Then assertThat(request1.getApiKey()).isEqualTo(request2.getApiKey()); } @Test @DisplayName("两个不同apiKey的请求应该不相等") void shouldNotBeEqual_WhenDifferentApiKey() { // Given UseApiKeyRequest request1 = new UseApiKeyRequest(); UseApiKeyRequest request2 = new UseApiKeyRequest(); request1.setApiKey("key-1"); request2.setApiKey("key-2"); // Then assertThat(request1.getApiKey()).isNotEqualTo(request2.getApiKey()); } @Test @DisplayName("多次调用getter应该返回相同值") void shouldReturnSameValue_WhenCallingGetterMultipleTimes() { // Given request.setApiKey("consistent-key"); // When & Then assertThat(request.getApiKey()).isEqualTo("consistent-key"); assertThat(request.getApiKey()).isEqualTo("consistent-key"); assertThat(request.getApiKey()).isEqualTo("consistent-key"); } } @Nested @DisplayName("并发安全测试") class ConcurrencyTests { @Test @DisplayName("多线程并发操作应该是安全的") void shouldBeThreadSafe_ConcurrentOperations() throws InterruptedException { // Given int threadCount = 10; Thread[] threads = new Thread[threadCount]; boolean[] results = new boolean[threadCount]; // When for (int i = 0; i < threadCount; i++) { final int threadIndex = i; threads[i] = new Thread(() -> { try { UseApiKeyRequest localRequest = new UseApiKeyRequest(); localRequest.setApiKey("key-" + threadIndex); // 验证getter assertThat(localRequest.getApiKey()).isEqualTo("key-" + threadIndex); // 验证验证器 Set> violations = validator.validate(localRequest); assertThat(violations).isEmpty(); results[threadIndex] = true; } catch (Exception e) { results[threadIndex] = false; } }); } // 启动所有线程 for (Thread thread : threads) { thread.start(); } // 等待所有线程完成 for (Thread thread : threads) { thread.join(); } // Then for (int i = 0; i < threadCount; i++) { assertThat(results[i]).isTrue(); } } } }