Files
wenzi/src/test/java/com/mosquito/project/dto/UseApiKeyRequestTest.java

563 lines
18 KiB
Java
Raw Normal View History

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<ConstraintViolation<UseApiKeyRequest>> 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<ConstraintViolation<UseApiKeyRequest>> violations = validator.validate(request);
// Then
assertThat(violations).isNotEmpty();
assertThat(violations).hasSize(1);
ConstraintViolation<UseApiKeyRequest> 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<ConstraintViolation<UseApiKeyRequest>> violations = validator.validate(request);
// Then
assertThat(violations).isEmpty();
}
@Test
@DisplayName("特殊字符API密钥应该通过验证")
void shouldPassValidation_WhenApiKeyContainsSpecialChars() {
// Given
request.setApiKey("key-!@#$%^&*()_+");
// When
Set<ConstraintViolation<UseApiKeyRequest>> 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<ConstraintViolation<UseApiKeyRequest>> violations = validator.validate(request);
// Then
assertThat(violations).isEmpty();
}
@Test
@DisplayName("Unicode字符API密钥应该通过验证")
void shouldPassValidation_WhenApiKeyContainsUnicode() {
// Given
request.setApiKey("密钥-中文-🔑");
// When
Set<ConstraintViolation<UseApiKeyRequest>> 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&ampersand", // 带&
})
@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<ConstraintViolation<UseApiKeyRequest>> 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();
}
}
}
}