Migrate storage to DogeCloud and expand admin dashboard
This commit is contained in:
@@ -1,25 +1,31 @@
|
||||
package com.yoyuzh.admin;
|
||||
|
||||
import com.yoyuzh.PortalBackendApplication;
|
||||
import com.yoyuzh.admin.AdminMetricsStateRepository;
|
||||
import com.yoyuzh.auth.User;
|
||||
import com.yoyuzh.auth.UserRepository;
|
||||
import com.yoyuzh.files.StoredFile;
|
||||
import com.yoyuzh.files.StoredFileRepository;
|
||||
import com.yoyuzh.transfer.OfflineTransferSessionRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
|
||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@@ -44,9 +50,15 @@ class AdminControllerIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Autowired
|
||||
private StoredFileRepository storedFileRepository;
|
||||
@Autowired
|
||||
private OfflineTransferSessionRepository offlineTransferSessionRepository;
|
||||
@Autowired
|
||||
private AdminMetricsStateRepository adminMetricsStateRepository;
|
||||
|
||||
private User portalUser;
|
||||
private User secondaryUser;
|
||||
@@ -55,14 +67,16 @@ class AdminControllerIntegrationTest {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
offlineTransferSessionRepository.deleteAll();
|
||||
storedFileRepository.deleteAll();
|
||||
userRepository.deleteAll();
|
||||
adminMetricsStateRepository.deleteAll();
|
||||
|
||||
portalUser = new User();
|
||||
portalUser.setUsername("alice");
|
||||
portalUser.setEmail("alice@example.com");
|
||||
portalUser.setPhoneNumber("13800138000");
|
||||
portalUser.setPasswordHash("encoded-password");
|
||||
portalUser.setPasswordHash(passwordEncoder.encode("OriginalA"));
|
||||
portalUser.setCreatedAt(LocalDateTime.now());
|
||||
portalUser = userRepository.save(portalUser);
|
||||
|
||||
@@ -70,7 +84,7 @@ class AdminControllerIntegrationTest {
|
||||
secondaryUser.setUsername("bob");
|
||||
secondaryUser.setEmail("bob@example.com");
|
||||
secondaryUser.setPhoneNumber("13900139000");
|
||||
secondaryUser.setPasswordHash("encoded-password");
|
||||
secondaryUser.setPasswordHash(passwordEncoder.encode("OriginalB"));
|
||||
secondaryUser.setCreatedAt(LocalDateTime.now().minusDays(1));
|
||||
secondaryUser = userRepository.save(secondaryUser);
|
||||
|
||||
@@ -100,6 +114,8 @@ class AdminControllerIntegrationTest {
|
||||
@Test
|
||||
@WithMockUser(username = "admin")
|
||||
void shouldAllowConfiguredAdminToListUsersAndSummary() throws Exception {
|
||||
int currentHour = LocalTime.now().getHour();
|
||||
|
||||
mockMvc.perform(get("/api/admin/users?page=0&size=10"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(0))
|
||||
@@ -107,6 +123,7 @@ class AdminControllerIntegrationTest {
|
||||
.andExpect(jsonPath("$.data.items[0].phoneNumber").value("13800138000"))
|
||||
.andExpect(jsonPath("$.data.items[0].role").value("USER"))
|
||||
.andExpect(jsonPath("$.data.items[0].banned").value(false))
|
||||
.andExpect(jsonPath("$.data.items[0].usedStorageBytes").value(1024L))
|
||||
.andExpect(jsonPath("$.data.items[0].storageQuotaBytes").isNumber())
|
||||
.andExpect(jsonPath("$.data.items[0].maxUploadSizeBytes").isNumber());
|
||||
|
||||
@@ -114,6 +131,16 @@ class AdminControllerIntegrationTest {
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.totalUsers").value(2))
|
||||
.andExpect(jsonPath("$.data.totalFiles").value(2))
|
||||
.andExpect(jsonPath("$.data.totalStorageBytes").value(1280L))
|
||||
.andExpect(jsonPath("$.data.downloadTrafficBytes").value(0L))
|
||||
.andExpect(jsonPath("$.data.requestCount", greaterThanOrEqualTo(1)))
|
||||
.andExpect(jsonPath("$.data.requestTimeline.length()").value(24))
|
||||
.andExpect(jsonPath("$.data.requestTimeline[" + currentHour + "].hour").value(currentHour))
|
||||
.andExpect(jsonPath("$.data.requestTimeline[" + currentHour + "].label").value(String.format("%02d:00", currentHour)))
|
||||
.andExpect(jsonPath("$.data.requestTimeline[" + currentHour + "].requestCount", greaterThanOrEqualTo(1)))
|
||||
.andExpect(jsonPath("$.data.transferUsageBytes").value(0L))
|
||||
.andExpect(jsonPath("$.data.offlineTransferStorageBytes").value(0L))
|
||||
.andExpect(jsonPath("$.data.offlineTransferStorageLimitBytes").isNumber())
|
||||
.andExpect(jsonPath("$.data.inviteCode").isNotEmpty());
|
||||
}
|
||||
|
||||
@@ -150,7 +177,7 @@ class AdminControllerIntegrationTest {
|
||||
mockMvc.perform(put("/api/admin/users/{userId}/password", portalUser.getId())
|
||||
.contentType("application/json")
|
||||
.content("""
|
||||
{"newPassword":"AdminSetPass1!"}
|
||||
{"newPassword":"AdminPass"}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.id").value(portalUser.getId()));
|
||||
@@ -173,11 +200,81 @@ class AdminControllerIntegrationTest {
|
||||
.andExpect(jsonPath("$.data.id").value(portalUser.getId()))
|
||||
.andExpect(jsonPath("$.data.maxUploadSizeBytes").value(10485760L));
|
||||
|
||||
mockMvc.perform(patch("/api/admin/settings/offline-transfer-storage-limit")
|
||||
.contentType("application/json")
|
||||
.content("""
|
||||
{"offlineTransferStorageLimitBytes":2147483648}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.offlineTransferStorageLimitBytes").value(2147483648L));
|
||||
|
||||
mockMvc.perform(post("/api/admin/users/{userId}/password/reset", secondaryUser.getId()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.temporaryPassword").isNotEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "admin")
|
||||
void shouldInvalidateOldPasswordAfterAdminPasswordUpdate() throws Exception {
|
||||
mockMvc.perform(put("/api/admin/users/{userId}/password", portalUser.getId())
|
||||
.contentType("application/json")
|
||||
.content("""
|
||||
{"newPassword":"AdminPass"}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.id").value(portalUser.getId()));
|
||||
|
||||
mockMvc.perform(post("/api/auth/login")
|
||||
.contentType("application/json")
|
||||
.content("""
|
||||
{
|
||||
"username": "alice",
|
||||
"password": "OriginalA"
|
||||
}
|
||||
"""))
|
||||
.andExpect(status().isUnauthorized())
|
||||
.andExpect(jsonPath("$.msg").value("用户名或密码错误"));
|
||||
|
||||
mockMvc.perform(post("/api/auth/login")
|
||||
.contentType("application/json")
|
||||
.content("""
|
||||
{
|
||||
"username": "alice",
|
||||
"password": "AdminPass"
|
||||
}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.user.username").value("alice"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExposeTrafficAndTransferMetricsInSummary() throws Exception {
|
||||
mockMvc.perform(get("/api/files/download/{fileId}/url", storedFile.getId())
|
||||
.with(user("alice")))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.url").value("/api/files/download/" + storedFile.getId()));
|
||||
|
||||
mockMvc.perform(post("/api/transfer/sessions")
|
||||
.with(user("alice"))
|
||||
.contentType("application/json")
|
||||
.content("""
|
||||
{
|
||||
"mode": "OFFLINE",
|
||||
"files": [
|
||||
{"name": "offline.txt", "relativePath": "资料/offline.txt", "size": 13, "contentType": "text/plain"}
|
||||
]
|
||||
}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.mode").value("OFFLINE"));
|
||||
|
||||
mockMvc.perform(get("/api/admin/summary").with(user("admin")))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.downloadTrafficBytes").value(1024L))
|
||||
.andExpect(jsonPath("$.data.transferUsageBytes").value(13L))
|
||||
.andExpect(jsonPath("$.data.requestCount", greaterThanOrEqualTo(2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "admin")
|
||||
void shouldAllowConfiguredAdminToListAndDeleteFiles() throws Exception {
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.yoyuzh.admin;
|
||||
|
||||
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 java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.util.Optional;
|
||||
|
||||
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;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AdminMetricsServiceTest {
|
||||
|
||||
@Mock
|
||||
private AdminMetricsStateRepository adminMetricsStateRepository;
|
||||
@Mock
|
||||
private AdminRequestTimelinePointRepository adminRequestTimelinePointRepository;
|
||||
|
||||
private AdminMetricsService adminMetricsService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
adminMetricsService = new AdminMetricsService(adminMetricsStateRepository, adminRequestTimelinePointRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResetDailyRequestCountWhenSnapshotReadsPreviousDayState() {
|
||||
AdminMetricsState state = new AdminMetricsState();
|
||||
state.setId(1L);
|
||||
state.setRequestCount(42L);
|
||||
state.setRequestCountDate(LocalDate.now().minusDays(1));
|
||||
state.setOfflineTransferStorageLimitBytes(20L * 1024 * 1024 * 1024);
|
||||
|
||||
when(adminMetricsStateRepository.findById(1L)).thenReturn(Optional.of(state));
|
||||
when(adminMetricsStateRepository.save(any(AdminMetricsState.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
when(adminRequestTimelinePointRepository.findAllByMetricDateOrderByHourAsc(LocalDate.now())).thenReturn(java.util.List.of());
|
||||
|
||||
AdminMetricsSnapshot snapshot = adminMetricsService.getSnapshot();
|
||||
|
||||
assertThat(snapshot.requestCount()).isZero();
|
||||
assertThat(state.getRequestCount()).isZero();
|
||||
assertThat(state.getRequestCountDate()).isEqualTo(LocalDate.now());
|
||||
assertThat(snapshot.requestTimeline()).hasSize(24);
|
||||
assertThat(snapshot.requestTimeline().get(0)).isEqualTo(new AdminRequestTimelinePoint(0, "00:00", 0L));
|
||||
verify(adminMetricsStateRepository).save(state);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldStartNewDayRequestCountAtOneWhenIncrementingPreviousDayState() {
|
||||
AdminMetricsState state = new AdminMetricsState();
|
||||
state.setId(1L);
|
||||
state.setRequestCount(42L);
|
||||
state.setRequestCountDate(LocalDate.now().minusDays(1));
|
||||
state.setOfflineTransferStorageLimitBytes(20L * 1024 * 1024 * 1024);
|
||||
|
||||
when(adminMetricsStateRepository.findByIdForUpdate(1L)).thenReturn(Optional.of(state));
|
||||
when(adminMetricsStateRepository.save(any(AdminMetricsState.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
when(adminRequestTimelinePointRepository.findByMetricDateAndHourForUpdate(LocalDate.now(), LocalTime.now().getHour()))
|
||||
.thenReturn(Optional.empty());
|
||||
when(adminRequestTimelinePointRepository.save(any(AdminRequestTimelinePointEntity.class)))
|
||||
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||
when(adminRequestTimelinePointRepository.saveAndFlush(any(AdminRequestTimelinePointEntity.class)))
|
||||
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
adminMetricsService.incrementRequestCount();
|
||||
|
||||
assertThat(state.getRequestCount()).isEqualTo(1L);
|
||||
assertThat(state.getRequestCountDate()).isEqualTo(LocalDate.now());
|
||||
verify(adminMetricsStateRepository).save(state);
|
||||
verify(adminRequestTimelinePointRepository).save(any(AdminRequestTimelinePointEntity.class));
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import com.yoyuzh.common.PageResponse;
|
||||
import com.yoyuzh.files.FileService;
|
||||
import com.yoyuzh.files.StoredFile;
|
||||
import com.yoyuzh.files.StoredFileRepository;
|
||||
import com.yoyuzh.transfer.OfflineTransferSessionRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -48,6 +49,10 @@ class AdminServiceTest {
|
||||
private RefreshTokenService refreshTokenService;
|
||||
@Mock
|
||||
private RegistrationInviteService registrationInviteService;
|
||||
@Mock
|
||||
private OfflineTransferSessionRepository offlineTransferSessionRepository;
|
||||
@Mock
|
||||
private AdminMetricsService adminMetricsService;
|
||||
|
||||
private AdminService adminService;
|
||||
|
||||
@@ -55,7 +60,8 @@ class AdminServiceTest {
|
||||
void setUp() {
|
||||
adminService = new AdminService(
|
||||
userRepository, storedFileRepository, fileService,
|
||||
passwordEncoder, refreshTokenService, registrationInviteService);
|
||||
passwordEncoder, refreshTokenService, registrationInviteService,
|
||||
offlineTransferSessionRepository, adminMetricsService);
|
||||
}
|
||||
|
||||
// --- getSummary ---
|
||||
@@ -64,12 +70,34 @@ class AdminServiceTest {
|
||||
void shouldReturnSummaryWithCountsAndInviteCode() {
|
||||
when(userRepository.count()).thenReturn(5L);
|
||||
when(storedFileRepository.count()).thenReturn(42L);
|
||||
when(storedFileRepository.sumAllFileSize()).thenReturn(8192L);
|
||||
when(adminMetricsService.getSnapshot()).thenReturn(new AdminMetricsSnapshot(
|
||||
0L,
|
||||
0L,
|
||||
0L,
|
||||
20L * 1024 * 1024 * 1024,
|
||||
List.of(
|
||||
new AdminRequestTimelinePoint(0, "00:00", 0L),
|
||||
new AdminRequestTimelinePoint(1, "01:00", 3L)
|
||||
)
|
||||
));
|
||||
when(offlineTransferSessionRepository.sumUploadedFileSizeByExpiresAtAfter(any())).thenReturn(0L);
|
||||
when(registrationInviteService.getCurrentInviteCode()).thenReturn("INV-001");
|
||||
|
||||
AdminSummaryResponse summary = adminService.getSummary();
|
||||
|
||||
assertThat(summary.totalUsers()).isEqualTo(5L);
|
||||
assertThat(summary.totalFiles()).isEqualTo(42L);
|
||||
assertThat(summary.totalStorageBytes()).isEqualTo(8192L);
|
||||
assertThat(summary.downloadTrafficBytes()).isZero();
|
||||
assertThat(summary.requestCount()).isZero();
|
||||
assertThat(summary.transferUsageBytes()).isZero();
|
||||
assertThat(summary.offlineTransferStorageBytes()).isZero();
|
||||
assertThat(summary.offlineTransferStorageLimitBytes()).isGreaterThan(0L);
|
||||
assertThat(summary.requestTimeline()).containsExactly(
|
||||
new AdminRequestTimelinePoint(0, "00:00", 0L),
|
||||
new AdminRequestTimelinePoint(1, "01:00", 3L)
|
||||
);
|
||||
assertThat(summary.inviteCode()).isEqualTo("INV-001");
|
||||
}
|
||||
|
||||
@@ -80,11 +108,13 @@ class AdminServiceTest {
|
||||
User user = createUser(1L, "alice", "alice@example.com");
|
||||
when(userRepository.searchByUsernameOrEmail(anyString(), any()))
|
||||
.thenReturn(new PageImpl<>(List.of(user)));
|
||||
when(storedFileRepository.sumFileSizeByUserId(1L)).thenReturn(2048L);
|
||||
|
||||
PageResponse<AdminUserResponse> response = adminService.listUsers(0, 10, "alice");
|
||||
|
||||
assertThat(response.items()).hasSize(1);
|
||||
assertThat(response.items().get(0).username()).isEqualTo("alice");
|
||||
assertThat(response.items().get(0).usedStorageBytes()).isEqualTo(2048L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -205,7 +235,7 @@ class AdminServiceTest {
|
||||
void shouldRejectWeakPasswordWhenUpdating() {
|
||||
assertThatThrownBy(() -> adminService.updateUserPassword(1L, "weakpass"))
|
||||
.isInstanceOf(BusinessException.class)
|
||||
.hasMessageContaining("密码至少10位");
|
||||
.hasMessageContaining("密码至少8位");
|
||||
verify(userRepository, never()).findById(any());
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ class AuthControllerValidationTest {
|
||||
"""))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.code").value(1000))
|
||||
.andExpect(jsonPath("$.msg").value("密码至少10位,且必须包含大写字母、小写字母、数字和特殊字符"));
|
||||
.andExpect(jsonPath("$.msg").value("密码至少8位,且必须包含大写字母"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -14,49 +14,34 @@ class PasswordPolicyTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectPasswordShorterThanTenCharacters() {
|
||||
assertThat(PasswordPolicy.isStrong("Abc1!defg")).isFalse(); // 9 chars
|
||||
void shouldRejectPasswordShorterThanEightCharacters() {
|
||||
assertThat(PasswordPolicy.isStrong("Abcdefg")).isFalse(); // 7 chars
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAcceptPasswordWithExactlyTenCharacters() {
|
||||
assertThat(PasswordPolicy.isStrong("Abcdefg1!x")).isTrue(); // 10 chars
|
||||
void shouldAcceptPasswordWithExactlyEightCharacters() {
|
||||
assertThat(PasswordPolicy.isStrong("Abcdefgh")).isTrue(); // 8 chars
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectPasswordMissingUppercase() {
|
||||
assertThat(PasswordPolicy.isStrong("abcdefg1!x")).isFalse();
|
||||
assertThat(PasswordPolicy.isStrong("abcdefgh")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectPasswordMissingLowercase() {
|
||||
assertThat(PasswordPolicy.isStrong("ABCDEFG1!X")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectPasswordMissingDigit() {
|
||||
assertThat(PasswordPolicy.isStrong("Abcdefgh!x")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectPasswordMissingSpecialCharacter() {
|
||||
assertThat(PasswordPolicy.isStrong("Abcdefg12x")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAcceptStrongPasswordWithAllRequirements() {
|
||||
assertThat(PasswordPolicy.isStrong("MyP@ssw0rd!")).isTrue();
|
||||
void shouldAcceptPasswordThatOnlyNeedsUppercaseAndLength() {
|
||||
assertThat(PasswordPolicy.isStrong("ABCDEFGH")).isTrue();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"", "short", "nouppercase1!", "NOLOWERCASE1!", "NoSpecialChar1", "NoDigit!AbcXyz"})
|
||||
@ValueSource(strings = {"", "short", "noupper", "abcdefghi"})
|
||||
void shouldRejectWeakPasswords(String password) {
|
||||
assertThat(PasswordPolicy.isStrong(password)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAcceptLongPasswordWithAllRequirements() {
|
||||
assertThat(PasswordPolicy.isStrong("MyV3ryStr0ng&SecureP@ssword2024!")).isTrue();
|
||||
void shouldAcceptLongPasswordWithUppercase() {
|
||||
assertThat(PasswordPolicy.isStrong("MyVerySimplePassword")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -26,7 +26,7 @@ class RegisterRequestValidationTest {
|
||||
|
||||
assertThat(violations)
|
||||
.extracting(violation -> violation.getMessage())
|
||||
.contains("密码至少10位,且必须包含大写字母、小写字母、数字和特殊字符");
|
||||
.contains("密码至少8位,且必须包含大写字母");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -35,8 +35,8 @@ class RegisterRequestValidationTest {
|
||||
"alice",
|
||||
"alice@example.com",
|
||||
"13800138000",
|
||||
"StrongPass1!",
|
||||
"StrongPass1!",
|
||||
"Abcdefgh",
|
||||
"Abcdefgh",
|
||||
"invite-code"
|
||||
);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.yoyuzh.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.yoyuzh.admin.ApiRequestMetricsFilter;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
@@ -23,6 +24,7 @@ class SecurityConfigTest {
|
||||
corsProperties.setAllowedOrigins(java.util.List.of("https://yoyuzh.xyz"));
|
||||
|
||||
SecurityConfig securityConfig = new SecurityConfig(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new ObjectMapper(),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.yoyuzh.files;
|
||||
|
||||
import com.yoyuzh.admin.AdminMetricsService;
|
||||
import com.yoyuzh.auth.User;
|
||||
import com.yoyuzh.common.BusinessException;
|
||||
import com.yoyuzh.config.FileStorageProperties;
|
||||
@@ -34,6 +35,8 @@ class FileServiceEdgeCaseTest {
|
||||
private FileContentStorage fileContentStorage;
|
||||
@Mock
|
||||
private FileShareLinkRepository fileShareLinkRepository;
|
||||
@Mock
|
||||
private AdminMetricsService adminMetricsService;
|
||||
|
||||
private FileService fileService;
|
||||
|
||||
@@ -41,7 +44,7 @@ class FileServiceEdgeCaseTest {
|
||||
void setUp() {
|
||||
FileStorageProperties properties = new FileStorageProperties();
|
||||
properties.setMaxFileSize(500L * 1024 * 1024);
|
||||
fileService = new FileService(storedFileRepository, fileContentStorage, fileShareLinkRepository, properties);
|
||||
fileService = new FileService(storedFileRepository, fileContentStorage, fileShareLinkRepository, adminMetricsService, properties);
|
||||
}
|
||||
|
||||
// --- normalizeDirectoryPath edge cases ---
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.yoyuzh.files;
|
||||
|
||||
import com.yoyuzh.admin.AdminMetricsService;
|
||||
import com.yoyuzh.auth.User;
|
||||
import com.yoyuzh.common.BusinessException;
|
||||
import com.yoyuzh.config.FileStorageProperties;
|
||||
@@ -46,6 +47,8 @@ class FileServiceTest {
|
||||
|
||||
@Mock
|
||||
private FileShareLinkRepository fileShareLinkRepository;
|
||||
@Mock
|
||||
private AdminMetricsService adminMetricsService;
|
||||
|
||||
private FileService fileService;
|
||||
|
||||
@@ -53,7 +56,7 @@ class FileServiceTest {
|
||||
void setUp() {
|
||||
FileStorageProperties properties = new FileStorageProperties();
|
||||
properties.setMaxFileSize(500L * 1024 * 1024);
|
||||
fileService = new FileService(storedFileRepository, fileContentStorage, fileShareLinkRepository, properties);
|
||||
fileService = new FileService(storedFileRepository, fileContentStorage, fileShareLinkRepository, adminMetricsService, properties);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user