added some tests and fixed server secret to be persistent
CI / backend-tests (push) Has been cancelled

This commit is contained in:
2026-06-10 14:36:38 +02:00
parent 61f9404a8a
commit 09b6193077
13 changed files with 483 additions and 51 deletions
@@ -0,0 +1,156 @@
package dev.ksan.etfoglasiserver.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.ksan.etfoglasiserver.config.SecurityConfig;
import dev.ksan.etfoglasiserver.model.Entry;
import dev.ksan.etfoglasiserver.service.EntryService;
import dev.ksan.etfoglasiserver.service.JWTService;
import dev.ksan.etfoglasiserver.service.MyUserDetailsService;
import dev.ksan.etfoglasiserver.util.SubjectLoader;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.Import;
import org.springframework.data.domain.PageImpl;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;
import java.util.List;
import java.util.UUID;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
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.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(EntryController.class)
@Import(SecurityConfig.class)
class EntryControllerTest {
@Autowired
MockMvc mvc;
@Autowired
ObjectMapper objectMapper;
@MockitoBean
EntryService entryService;
@MockitoBean
JWTService jwtService;
@MockitoBean
MyUserDetailsService userDetailsService;
@MockitoBean
SubjectLoader subjectLoader;
@Test
void getEntries_noParams_returns200() throws Exception {
when(entryService.getEntries(null, null, 0))
.thenReturn(new PageImpl<>(List.of()));
mvc.perform(get("/api/entries"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.content").isArray());
}
@Test
void getEntries_withSubjectId_parsesUUIDCorrectly() throws Exception {
UUID subjectId = UUID.randomUUID();
when(entryService.getEntries(eq(subjectId), isNull(), eq(0)))
.thenReturn(new PageImpl<>(List.of()));
mvc.perform(get("/api/entries")
.param("subjectId", subjectId.toString())
.param("page", "0"))
.andExpect(status().isOk());
verify(entryService).getEntries(eq(subjectId), isNull(), eq(0));
}
@Test
void getEntries_withGroupName_passesGroupNameToService() throws Exception {
when(entryService.getEntries(isNull(), eq("Physics"), eq(0)))
.thenReturn(new PageImpl<>(List.of()));
mvc.perform(get("/api/entries").param("groupName", "Physics"))
.andExpect(status().isOk());
verify(entryService).getEntries(isNull(), eq("Physics"), eq(0));
}
@Test
void getEntries_invalidUUID_returns400() throws Exception {
mvc.perform(get("/api/entries").param("subjectId", "not-a-uuid"))
.andExpect(status().isBadRequest());
}
@Test
void getGroups_returnsList() throws Exception {
when(entryService.getAllGroups()).thenReturn(List.of("Math", "Physics", "CS"));
mvc.perform(get("/api/groups"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()").value(3))
.andExpect(jsonPath("$[0]").value("Math"));
}
@Test
void getGroups_empty_returns200WithEmptyArray() throws Exception {
when(entryService.getAllGroups()).thenReturn(List.of());
mvc.perform(get("/api/groups"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()").value(0));
}
@Test
@WithMockUser
void getEntry_existingId_returns200() throws Exception {
Entry entry = new Entry();
entry.setTitle("Test Entry");
when(entryService.getEntryById("entry-1")).thenReturn(entry);
mvc.perform(get("/api/entries/entry-1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.title").value("Test Entry"));
}
@Test
void getEntry_unauthenticated_returns401or403() throws Exception {
mvc.perform(get("/api/entries/entry-1"))
.andExpect(status().is(anyOf(equalTo(401), equalTo(403))));
}
@Test
@WithMockUser
void deleteEntry_authenticated_returns200() throws Exception {
doNothing().when(entryService).deleteEntry("entry-1");
mvc.perform(delete("/api/entries/entry-1"))
.andExpect(status().isOk());
}
@Test
void deleteEntry_unauthenticated_returns401or403() throws Exception {
mvc.perform(delete("/api/entries/entry-1"))
.andExpect(status().is(anyOf(equalTo(401), equalTo(403))));
}
}
@@ -0,0 +1,216 @@
package dev.ksan.etfoglasiserver.controller;
import dev.ksan.etfoglasiserver.config.SecurityConfig;
import dev.ksan.etfoglasiserver.dto.JwtResponseDTO;
import dev.ksan.etfoglasiserver.dto.UserCreationDTO;
import dev.ksan.etfoglasiserver.dto.UserDTO;
import dev.ksan.etfoglasiserver.dto.UserLoginDTO;
import dev.ksan.etfoglasiserver.model.User;
import dev.ksan.etfoglasiserver.service.JWTService;
import dev.ksan.etfoglasiserver.service.MyUserDetailsService;
import dev.ksan.etfoglasiserver.service.UserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.ksan.etfoglasiserver.util.SubjectLoader;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.Import;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Optional;
import java.util.UUID;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.when;
import static org.springframework.http.MediaType.APPLICATION_JSON;
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.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(UserController.class)
@Import(SecurityConfig.class)
class UserControllerTest {
@Autowired
MockMvc mvc;
@Autowired
ObjectMapper objectMapper;
@MockitoBean
SubjectLoader subjectLoader;
@MockitoBean
UserService userService;
@MockitoBean
JWTService jwtService;
@MockitoBean
MyUserDetailsService userDetailsService;
private static final String EMAIL = "test@example.com";
@Test
void login_validCredentials_returns200WithToken() throws Exception {
JwtResponseDTO response = new JwtResponseDTO("jwt-token", EMAIL, "user-id", "OK");
when(userService.verify(any(UserLoginDTO.class))).thenReturn(response);
mvc.perform(post("/api/login")
.contentType(APPLICATION_JSON)
.content("""
{"email":"test@example.com","password":"correct"}
"""))
.andExpect(status().isOk())
.andExpect(jsonPath("$.token").value("jwt-token"))
.andExpect(jsonPath("$.email").value(EMAIL));
}
@Test
void login_badCredentials_returns400() throws Exception {
when(userService.verify(any()))
.thenThrow(new RuntimeException("bad credentials"));
mvc.perform(post("/api/login")
.contentType(APPLICATION_JSON)
.content("""
{"email":"test@example.com","password":"wrong"}
"""))
.andExpect(status().isBadRequest());
}
@Test
void login_nullResponse_returns400() throws Exception {
when(userService.verify(any())).thenReturn(null);
mvc.perform(post("/api/login")
.contentType(APPLICATION_JSON)
.content(objectMapper.writeValueAsString(
new UserLoginDTO("x@x.com", "p"))))
.andExpect(status().isBadRequest());
}
@Test
void register_newUser_returns201() throws Exception {
User user = new User();
user.setEmail(EMAIL);
when(userService.register(any()))
.thenReturn(new UserDTO(user));
mvc.perform(post("/api/register")
.contentType(APPLICATION_JSON)
.content("""
{"email":"test@example.com","password":"pass123"}
"""))
.andExpect(status().isCreated());
}
@Test
void register_duplicateEmail_returns400() throws Exception {
when(userService.register(any()))
.thenThrow(new RuntimeException("Email already exists"));
mvc.perform(post("/api/register")
.contentType(APPLICATION_JSON)
.content(objectMapper.writeValueAsString(
new UserCreationDTO("dupe@example.com", "pass"))))
.andExpect(status().isBadRequest());
}
@Test
@WithMockUser(username = EMAIL)
void getUser_authenticated_returns200() throws Exception {
User user = new User();
user.setEmail(EMAIL);
when(userService.getAccountByEmail(EMAIL))
.thenReturn(Optional.of(user));
mvc.perform(get("/api/users/me"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.email").value(EMAIL));
}
@Test
void getUser_unauthenticated_returns401or403() throws Exception {
mvc.perform(get("/api/users/me"))
.andExpect(status().is(anyOf(equalTo(401), equalTo(403))));
}
@Test
@WithMockUser(username = EMAIL)
void getUser_notFound_returns404() throws Exception {
when(userService.getAccountByEmail(EMAIL))
.thenReturn(Optional.empty());
mvc.perform(get("/api/users/me"))
.andExpect(status().isNotFound());
}
@Test
@WithMockUser(username = EMAIL)
void updateUser_validBody_returns200() throws Exception {
User user = new User();
user.setEmail("new@example.com");
when(userService.updateUser(eq(EMAIL), any()))
.thenReturn(new UserDTO(user));
mvc.perform(put("/api/users/me")
.contentType(APPLICATION_JSON)
.content(objectMapper.writeValueAsString(
new UserCreationDTO("new@example.com", "newpass"))))
.andExpect(status().isOk())
.andExpect(jsonPath("$.email").value("new@example.com"));
}
@Test
@WithMockUser(username = EMAIL)
void addSubject_validId_returns200() throws Exception {
UUID subjectId = UUID.randomUUID();
User user = new User();
user.setEmail(EMAIL);
when(userService.addSubject(EMAIL, subjectId))
.thenReturn(new UserDTO(user));
mvc.perform(put("/api/users/me/subjects/{id}", subjectId))
.andExpect(status().isOk());
}
@Test
@WithMockUser(username = EMAIL)
void removeSubject_validId_returns200() throws Exception {
UUID subjectId = UUID.randomUUID();
User user = new User();
user.setEmail(EMAIL);
when(userService.removeSubject(EMAIL, subjectId))
.thenReturn(new UserDTO(user));
mvc.perform(delete("/api/users/me/subjects/{id}", subjectId))
.andExpect(status().isOk());
}
@Test
@WithMockUser(username = EMAIL)
void deleteUser_authenticated_returns204() throws Exception {
doNothing().when(userService).deleteUser(EMAIL);
mvc.perform(delete("/api/users/me"))
.andExpect(status().isNoContent());
}
}
@@ -0,0 +1,56 @@
package dev.ksan.etfoglasiserver.service;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.test.context.TestPropertySource;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
@SpringBootTest
@TestPropertySource(locations = "classpath:application.properties")
class JWTServiceTest {
@Autowired
JWTService jwtService;
private org.springframework.security.core.userdetails.UserDetails userDetails(String email) {
return org.springframework.security.core.userdetails.User
.withUsername(email)
.password("irrelevant")
.roles("USER")
.build();
}
@Test
void generateToken_extractEmail_returnsCorrectEmail() {
String token = jwtService.generateToken("alice@example.com");
assertThat(jwtService.extractEmail(token)).isEqualTo("alice@example.com");
}
@Test
void validateToken_correctUser_returnsTrue() {
String token = jwtService.generateToken("alice@example.com");
assertThat(jwtService.validateToken(token, userDetails("alice@example.com"))).isTrue();
}
@Test
void validateToken_differentUser_returnsFalse() {
String token = jwtService.generateToken("alice@example.com");
assertThat(jwtService.validateToken(token, userDetails("bob@example.com"))).isFalse();
}
@Test
void validateToken_tamperedToken_throwsException() {
String token = jwtService.generateToken("alice@example.com");
String tampered = token.substring(0, token.length() - 4) + "XXXX";
assertThatThrownBy(() -> jwtService.validateToken(tampered, userDetails("alice@example.com")))
.isInstanceOf(Exception.class);
}
}
@@ -4,4 +4,5 @@ spring.datasource.username=test
spring.datasource.password=test
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
firebase.enabled=false
firebase.enabled=false
jwt.secret=testkeynekikaojer123451231231231231231231231=