add backend
This commit is contained in:
105
backend/src/test/java/com/yoyuzh/auth/AuthServiceTest.java
Normal file
105
backend/src/test/java/com/yoyuzh/auth/AuthServiceTest.java
Normal file
@@ -0,0 +1,105 @@
|
||||
package com.yoyuzh.auth;
|
||||
|
||||
import com.yoyuzh.auth.dto.AuthResponse;
|
||||
import com.yoyuzh.auth.dto.LoginRequest;
|
||||
import com.yoyuzh.auth.dto.RegisterRequest;
|
||||
import com.yoyuzh.common.BusinessException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AuthServiceTest {
|
||||
|
||||
@Mock
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Mock
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Mock
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
@Mock
|
||||
private JwtTokenProvider jwtTokenProvider;
|
||||
|
||||
@InjectMocks
|
||||
private AuthService authService;
|
||||
|
||||
@Test
|
||||
void shouldRegisterUserWithEncryptedPassword() {
|
||||
RegisterRequest request = new RegisterRequest("alice", "alice@example.com", "plain-password");
|
||||
when(userRepository.existsByUsername("alice")).thenReturn(false);
|
||||
when(userRepository.existsByEmail("alice@example.com")).thenReturn(false);
|
||||
when(passwordEncoder.encode("plain-password")).thenReturn("encoded-password");
|
||||
when(userRepository.save(any(User.class))).thenAnswer(invocation -> {
|
||||
User user = invocation.getArgument(0);
|
||||
user.setId(1L);
|
||||
user.setCreatedAt(LocalDateTime.now());
|
||||
return user;
|
||||
});
|
||||
when(jwtTokenProvider.generateToken(1L, "alice")).thenReturn("jwt-token");
|
||||
|
||||
AuthResponse response = authService.register(request);
|
||||
|
||||
assertThat(response.token()).isEqualTo("jwt-token");
|
||||
assertThat(response.user().username()).isEqualTo("alice");
|
||||
verify(passwordEncoder).encode("plain-password");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectDuplicateUsernameOnRegister() {
|
||||
RegisterRequest request = new RegisterRequest("alice", "alice@example.com", "plain-password");
|
||||
when(userRepository.existsByUsername("alice")).thenReturn(true);
|
||||
|
||||
assertThatThrownBy(() -> authService.register(request))
|
||||
.isInstanceOf(BusinessException.class)
|
||||
.hasMessageContaining("用户名已存在");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldLoginAndReturnToken() {
|
||||
LoginRequest request = new LoginRequest("alice", "plain-password");
|
||||
User user = new User();
|
||||
user.setId(1L);
|
||||
user.setUsername("alice");
|
||||
user.setEmail("alice@example.com");
|
||||
user.setPasswordHash("encoded-password");
|
||||
user.setCreatedAt(LocalDateTime.now());
|
||||
when(userRepository.findByUsername("alice")).thenReturn(Optional.of(user));
|
||||
when(jwtTokenProvider.generateToken(1L, "alice")).thenReturn("jwt-token");
|
||||
|
||||
AuthResponse response = authService.login(request);
|
||||
|
||||
verify(authenticationManager).authenticate(
|
||||
new UsernamePasswordAuthenticationToken("alice", "plain-password"));
|
||||
assertThat(response.token()).isEqualTo("jwt-token");
|
||||
assertThat(response.user().email()).isEqualTo("alice@example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowBusinessExceptionWhenAuthenticationFails() {
|
||||
LoginRequest request = new LoginRequest("alice", "wrong-password");
|
||||
when(authenticationManager.authenticate(any()))
|
||||
.thenThrow(new BadCredentialsException("bad credentials"));
|
||||
|
||||
assertThatThrownBy(() -> authService.login(request))
|
||||
.isInstanceOf(BusinessException.class)
|
||||
.hasMessageContaining("用户名或密码错误");
|
||||
}
|
||||
}
|
||||
86
backend/src/test/java/com/yoyuzh/cqu/CquDataServiceTest.java
Normal file
86
backend/src/test/java/com/yoyuzh/cqu/CquDataServiceTest.java
Normal file
@@ -0,0 +1,86 @@
|
||||
package com.yoyuzh.cqu;
|
||||
|
||||
import com.yoyuzh.auth.User;
|
||||
import com.yoyuzh.config.CquApiProperties;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.anyList;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class CquDataServiceTest {
|
||||
|
||||
@Mock
|
||||
private CquApiClient cquApiClient;
|
||||
|
||||
@Mock
|
||||
private CourseRepository courseRepository;
|
||||
|
||||
@Mock
|
||||
private GradeRepository gradeRepository;
|
||||
|
||||
@InjectMocks
|
||||
private CquDataService cquDataService;
|
||||
|
||||
@Test
|
||||
void shouldNormalizeScheduleFromRemoteApi() {
|
||||
CquApiProperties properties = new CquApiProperties();
|
||||
properties.setRequireLogin(false);
|
||||
cquDataService = new CquDataService(cquApiClient, courseRepository, gradeRepository, properties);
|
||||
when(cquApiClient.fetchSchedule("2025-2026-1", "20230001")).thenReturn(List.of(Map.of(
|
||||
"courseName", "Java",
|
||||
"teacher", "Zhang",
|
||||
"classroom", "A101",
|
||||
"dayOfWeek", 1,
|
||||
"startTime", 1,
|
||||
"endTime", 2
|
||||
)));
|
||||
|
||||
List<CourseResponse> response = cquDataService.getSchedule(null, "2025-2026-1", "20230001");
|
||||
|
||||
assertThat(response).hasSize(1);
|
||||
assertThat(response.get(0).courseName()).isEqualTo("Java");
|
||||
assertThat(response.get(0).teacher()).isEqualTo("Zhang");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPersistGradesForLoggedInUserWhenAvailable() {
|
||||
CquApiProperties properties = new CquApiProperties();
|
||||
properties.setRequireLogin(true);
|
||||
cquDataService = new CquDataService(cquApiClient, courseRepository, gradeRepository, properties);
|
||||
User user = new User();
|
||||
user.setId(1L);
|
||||
user.setUsername("alice");
|
||||
user.setEmail("alice@example.com");
|
||||
user.setPasswordHash("encoded");
|
||||
user.setCreatedAt(LocalDateTime.now());
|
||||
when(cquApiClient.fetchGrades("2025-2026-1", "20230001")).thenReturn(List.of(Map.of(
|
||||
"courseName", "Java",
|
||||
"grade", 95,
|
||||
"semester", "2025-2026-1"
|
||||
)));
|
||||
Grade persisted = new Grade();
|
||||
persisted.setUser(user);
|
||||
persisted.setCourseName("Java");
|
||||
persisted.setGrade(95D);
|
||||
persisted.setSemester("2025-2026-1");
|
||||
persisted.setStudentId("20230001");
|
||||
when(gradeRepository.saveAll(anyList())).thenReturn(List.of(persisted));
|
||||
when(gradeRepository.findByUserIdAndStudentIdOrderBySemesterAscGradeDesc(1L, "20230001"))
|
||||
.thenReturn(List.of(persisted));
|
||||
|
||||
List<GradeResponse> response = cquDataService.getGrades(user, "2025-2026-1", "20230001");
|
||||
|
||||
assertThat(response).hasSize(1);
|
||||
assertThat(response.get(0).grade()).isEqualTo(95D);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.yoyuzh.cqu;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class CquMockDataFactoryTest {
|
||||
|
||||
@Test
|
||||
void shouldCreateMockScheduleForStudentAndSemester() {
|
||||
List<Map<String, Object>> result = CquMockDataFactory.createSchedule("2025-2026-1", "20230001");
|
||||
|
||||
assertThat(result).isNotEmpty();
|
||||
assertThat(result.get(0)).containsEntry("courseName", "高级 Java 程序设计");
|
||||
assertThat(result.get(0)).containsEntry("semester", "2025-2026-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateMockGradesForStudentAndSemester() {
|
||||
List<Map<String, Object>> result = CquMockDataFactory.createGrades("2025-2026-1", "20230001");
|
||||
|
||||
assertThat(result).isNotEmpty();
|
||||
assertThat(result.get(0)).containsEntry("studentId", "20230001");
|
||||
assertThat(result.get(0)).containsKey("grade");
|
||||
}
|
||||
}
|
||||
114
backend/src/test/java/com/yoyuzh/files/FileServiceTest.java
Normal file
114
backend/src/test/java/com/yoyuzh/files/FileServiceTest.java
Normal file
@@ -0,0 +1,114 @@
|
||||
package com.yoyuzh.files;
|
||||
|
||||
import com.yoyuzh.auth.User;
|
||||
import com.yoyuzh.common.BusinessException;
|
||||
import com.yoyuzh.config.FileStorageProperties;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class FileServiceTest {
|
||||
|
||||
@Mock
|
||||
private StoredFileRepository storedFileRepository;
|
||||
|
||||
private FileService fileService;
|
||||
|
||||
@TempDir
|
||||
Path tempDir;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
FileStorageProperties properties = new FileStorageProperties();
|
||||
properties.setRootDir(tempDir.toString());
|
||||
properties.setMaxFileSize(50 * 1024 * 1024);
|
||||
fileService = new FileService(storedFileRepository, properties);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldStoreUploadedFileUnderUserDirectory() {
|
||||
User user = createUser(7L);
|
||||
MockMultipartFile multipartFile = new MockMultipartFile(
|
||||
"file", "notes.txt", "text/plain", "hello".getBytes());
|
||||
when(storedFileRepository.existsByUserIdAndPathAndFilename(7L, "/docs", "notes.txt")).thenReturn(false);
|
||||
when(storedFileRepository.save(any(StoredFile.class))).thenAnswer(invocation -> {
|
||||
StoredFile file = invocation.getArgument(0);
|
||||
file.setId(10L);
|
||||
return file;
|
||||
});
|
||||
|
||||
FileMetadataResponse response = fileService.upload(user, "/docs", multipartFile);
|
||||
|
||||
assertThat(response.id()).isEqualTo(10L);
|
||||
assertThat(response.path()).isEqualTo("/docs");
|
||||
assertThat(response.directory()).isFalse();
|
||||
assertThat(tempDir.resolve("7/docs/notes.txt")).exists();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectDeletingOtherUsersFile() {
|
||||
User owner = createUser(1L);
|
||||
User requester = createUser(2L);
|
||||
StoredFile storedFile = createFile(100L, owner, "/docs", "notes.txt");
|
||||
when(storedFileRepository.findById(100L)).thenReturn(Optional.of(storedFile));
|
||||
|
||||
assertThatThrownBy(() -> fileService.delete(requester, 100L))
|
||||
.isInstanceOf(BusinessException.class)
|
||||
.hasMessageContaining("没有权限");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldListFilesByPathWithPagination() {
|
||||
User user = createUser(7L);
|
||||
StoredFile file = createFile(100L, user, "/docs", "notes.txt");
|
||||
when(storedFileRepository.findByUserIdAndPathOrderByDirectoryDescCreatedAtDesc(
|
||||
7L, "/docs", PageRequest.of(0, 10)))
|
||||
.thenReturn(new PageImpl<>(List.of(file)));
|
||||
|
||||
var result = fileService.list(user, "/docs", 0, 10);
|
||||
|
||||
assertThat(result.items()).hasSize(1);
|
||||
assertThat(result.items().get(0).filename()).isEqualTo("notes.txt");
|
||||
}
|
||||
|
||||
private User createUser(Long id) {
|
||||
User user = new User();
|
||||
user.setId(id);
|
||||
user.setUsername("user-" + id);
|
||||
user.setEmail("user-" + id + "@example.com");
|
||||
user.setPasswordHash("encoded");
|
||||
user.setCreatedAt(LocalDateTime.now());
|
||||
return user;
|
||||
}
|
||||
|
||||
private StoredFile createFile(Long id, User user, String path, String filename) {
|
||||
StoredFile file = new StoredFile();
|
||||
file.setId(id);
|
||||
file.setUser(user);
|
||||
file.setFilename(filename);
|
||||
file.setPath(path);
|
||||
file.setSize(5L);
|
||||
file.setDirectory(false);
|
||||
file.setStorageName(filename);
|
||||
file.setCreatedAt(LocalDateTime.now());
|
||||
return file;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user