-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1784c24
commit 4da8f04
Showing
7 changed files
with
324 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# This workflow uses actions that are not certified by GitHub. | ||
# They are provided by a third-party and are governed by | ||
# separate terms of service, privacy policy, and support | ||
# documentation. | ||
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time | ||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle | ||
|
||
name: Java CI with Gradle | ||
|
||
on: | ||
push: | ||
branches: [ main ] | ||
pull_request: | ||
branches: [ main ] | ||
|
||
permissions: | ||
contents: read | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v3 | ||
- name: Set up JDK 21 | ||
uses: actions/setup-java@v3 | ||
with: | ||
java-version: '21' | ||
distribution: 'temurin' | ||
- name: Build with Gradle | ||
uses: gradle/gradle-build-action@v2 | ||
with: | ||
arguments: build |
13 changes: 0 additions & 13 deletions
13
server/src/test/java/org/hyperskill/community/flashcards/FlashcardsApplicationTests.java
This file was deleted.
Oops, something went wrong.
82 changes: 82 additions & 0 deletions
82
.../src/test/java/org/hyperskill/community/flashcards/security/RegisterServerSecurityIT.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package org.hyperskill.community.flashcards.security; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import org.hyperskill.community.flashcards.registration.UserDto; | ||
import org.junit.jupiter.api.AfterAll; | ||
import org.junit.jupiter.api.BeforeAll; | ||
import org.junit.jupiter.api.Disabled; | ||
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.boot.testcontainers.service.connection.ServiceConnection; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.test.context.aot.DisabledInAotMode; | ||
import org.springframework.test.web.servlet.MockMvc; | ||
import org.testcontainers.containers.MongoDBContainer; | ||
import org.testcontainers.junit.jupiter.Container; | ||
import org.testcontainers.junit.jupiter.Testcontainers; | ||
import org.testcontainers.utility.DockerImageName; | ||
|
||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; | ||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
|
||
@SpringBootTest | ||
@Testcontainers | ||
@AutoConfigureMockMvc | ||
@Disabled // must fix test container connections... | ||
@DisabledInAotMode // bug in Spring 3.2 @MockBean does not work in AOT mode (https://github.com/spring-projects/spring-boot/issues/36997) | ||
class RegisterServerSecurityIT { | ||
|
||
@Autowired | ||
MockMvc mockMvc; | ||
|
||
@Container | ||
@ServiceConnection | ||
static MongoDBContainer mongoDbContainer = new MongoDBContainer(DockerImageName.parse("mongo:latest")); | ||
|
||
@Autowired | ||
ObjectMapper objectMapper; | ||
|
||
@BeforeAll | ||
static void setup() { | ||
mongoDbContainer.start(); | ||
} | ||
|
||
@AfterAll | ||
static void tearDown() { | ||
mongoDbContainer.stop(); | ||
} | ||
|
||
@Test | ||
void registerUnauthenticatedValidJson_AddsUser() throws Exception { | ||
mockMvc.perform(post("/api/register") | ||
.contentType(MediaType.APPLICATION_JSON) | ||
.content(objectMapper.writeValueAsString( | ||
new UserDto("[email protected]", "12345678")))) | ||
.andExpect(status().isOk()); | ||
} | ||
|
||
@Test | ||
void registerUnauthenticatedExistingUser_Gives400() throws Exception { | ||
mockMvc.perform(post("/api/register") | ||
.contentType(MediaType.APPLICATION_JSON) | ||
.content(objectMapper.writeValueAsString( | ||
new UserDto("[email protected]", "12345678")))) | ||
.andExpect(status().isOk()); | ||
mockMvc.perform(post("/api/register") // and again | ||
.contentType(MediaType.APPLICATION_JSON) | ||
.content(objectMapper.writeValueAsString( | ||
new UserDto("[email protected]", "12345678")))) | ||
.andExpect(status().isBadRequest()); | ||
} | ||
|
||
@Test | ||
void registerUnauthenticatedInvalidDto_Gives400() throws Exception { | ||
mockMvc.perform(post("/api/register") // and again | ||
.contentType(MediaType.APPLICATION_JSON) | ||
.content(objectMapper.writeValueAsString( | ||
new UserDto("wrong", "1234")))) | ||
.andExpect(status().isBadRequest()); | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
server/src/test/java/org/hyperskill/community/flashcards/service/RegisterServiceTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package org.hyperskill.community.flashcards.service; | ||
|
||
|
||
import org.hyperskill.community.flashcards.registration.RegisterService; | ||
import org.hyperskill.community.flashcards.registration.User; | ||
import org.hyperskill.community.flashcards.registration.UserAlreadyExistsException; | ||
import org.junit.jupiter.api.Test; | ||
import org.mockito.InjectMocks; | ||
import org.mockito.Mock; | ||
import org.mockito.junit.jupiter.MockitoSettings; | ||
import org.springframework.data.mongodb.core.MongoTemplate; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
import static org.mockito.Mockito.never; | ||
import static org.mockito.Mockito.any; | ||
import static org.mockito.Mockito.verify; | ||
import static org.mockito.Mockito.when; | ||
|
||
@MockitoSettings | ||
class RegisterServiceTest { | ||
|
||
@Mock | ||
MongoTemplate mongoTemplate; | ||
|
||
@InjectMocks | ||
RegisterService service; | ||
|
||
@Test | ||
void whenUserExist_RegisterUserThrows() { | ||
when(mongoTemplate.findById("test", User.class)).thenReturn(new User()); | ||
var newUser = new User().setUsername("test"); | ||
assertThrows(UserAlreadyExistsException.class, () -> service.registerUser(newUser)); | ||
verify(mongoTemplate, never()).save(any(User.class)); | ||
} | ||
|
||
@Test | ||
void whenRegisterUser_UserSaved() { | ||
when(mongoTemplate.findById("test", User.class)).thenReturn(null); | ||
var newUser = new User().setUsername("test"); | ||
service.registerUser(newUser); | ||
verify(mongoTemplate).save(newUser); | ||
} | ||
} |
71 changes: 71 additions & 0 deletions
71
server/src/test/java/org/hyperskill/community/flashcards/web/JpaUnitTestValidator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package org.hyperskill.community.flashcards.web; | ||
|
||
import jakarta.validation.ConstraintViolation; | ||
import jakarta.validation.Validation; | ||
import jakarta.validation.Validator; | ||
|
||
import java.lang.reflect.Constructor; | ||
import java.lang.reflect.Field; | ||
import java.lang.reflect.RecordComponent; | ||
import java.util.Arrays; | ||
import java.util.Set; | ||
import java.util.function.Supplier; | ||
|
||
public class JpaUnitTestValidator<T> { | ||
|
||
private final Validator validator; | ||
private final Supplier<? extends T> getValidFunction; | ||
private Constructor<? extends T> recordConstructor; | ||
|
||
public JpaUnitTestValidator(Supplier<? extends T> getValidObjectFunction, Class<? extends T> dtoClass) { | ||
try (var validatorFactory = Validation.buildDefaultValidatorFactory()) { | ||
this.validator = validatorFactory.getValidator(); | ||
} | ||
this.getValidFunction = getValidObjectFunction; | ||
initRecordConstructorIfNeeded(dtoClass); | ||
} | ||
|
||
private void initRecordConstructorIfNeeded(Class<? extends T> dtoClass) { | ||
if (dtoClass.isRecord()) { | ||
Class<?>[] componentTypes = Arrays.stream(dtoClass.getRecordComponents()) | ||
.map(RecordComponent::getType) | ||
.toArray(Class<?>[]::new); | ||
try { | ||
recordConstructor = dtoClass.getDeclaredConstructor(componentTypes); | ||
} catch (NoSuchMethodException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} | ||
|
||
public Set<ConstraintViolation<T>> getConstraintViolationsOnUpdate(String fieldToUpdate, Object newValue) | ||
throws Exception { | ||
T object = modifyDto(getValidFunction.get(), fieldToUpdate, newValue); | ||
return validator.validate(object); | ||
} | ||
|
||
private T modifyDto(T dto, String fieldName, Object value) throws Exception { | ||
if (recordConstructor != null) { | ||
return createModifiedRecord(dto, fieldName, value); | ||
} else { | ||
return updateFieldByReflection(dto, fieldName, value); | ||
} | ||
} | ||
|
||
private T updateFieldByReflection(T dto, String fieldName, Object value) throws Exception { | ||
Field field = dto.getClass().getDeclaredField(fieldName); | ||
field.setAccessible(true); | ||
field.set(dto, value); | ||
return dto; | ||
} | ||
|
||
private T createModifiedRecord(T dto, String fieldName, Object value) throws Exception { | ||
var recordComponents = dto.getClass().getRecordComponents(); | ||
Object[] modifiedValues = new Object[recordComponents.length]; | ||
for (int i = 0; i < recordComponents.length; i++) { | ||
modifiedValues[i] = recordComponents[i].getName().equals(fieldName) | ||
? value : recordComponents[i].getAccessor().invoke(dto); | ||
} | ||
return recordConstructor.newInstance(modifiedValues); | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
...st/java/org/hyperskill/community/flashcards/web/RegisterControllerValidationUnitTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package org.hyperskill.community.flashcards.web; | ||
|
||
import org.hyperskill.community.flashcards.registration.UserDto; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.Arguments; | ||
import org.junit.jupiter.params.provider.MethodSource; | ||
|
||
import java.util.stream.Stream; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertFalse; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
class RegisterControllerValidationUnitTest { | ||
|
||
JpaUnitTestValidator<UserDto> validator = new JpaUnitTestValidator<>(this::getValidUser, UserDto.class); | ||
|
||
UserDto getValidUser() { | ||
return new UserDto("[email protected]", "long_password"); | ||
} | ||
|
||
@ParameterizedTest | ||
@MethodSource | ||
void whenValidUserDto_NoError(String fieldName, Object validValue) throws Exception { | ||
assertTrue(validator.getConstraintViolationsOnUpdate(fieldName, validValue).isEmpty()); | ||
} | ||
|
||
static Stream<Arguments> whenValidUserDto_NoError() { | ||
return Stream.of( | ||
Arguments.of("email", "[email protected]"), | ||
Arguments.of("email", "[email protected]"), | ||
Arguments.of("email", "hans_josef.schmitz.ext4@main_google.com"), | ||
Arguments.of("password", "12345678"), | ||
Arguments.of("password", "12345678_very_long_one!\"§$%&/()=?.,-,),") | ||
); | ||
} | ||
|
||
@ParameterizedTest | ||
@MethodSource | ||
void whenInvalidUserDto_ValidationError(String fieldName, Object validValue) throws Exception { | ||
assertFalse(validator.getConstraintViolationsOnUpdate(fieldName, validValue).isEmpty()); | ||
} | ||
|
||
static Stream<Arguments> whenInvalidUserDto_ValidationError() { | ||
return Stream.of( | ||
Arguments.of("email", " "), | ||
Arguments.of("email", ""), | ||
Arguments.of("email", null), | ||
Arguments.of("email", "a@b"), | ||
Arguments.of("email", "hans@lmu."), | ||
Arguments.of("email", "hans.josef.schmitz.ext4@main_google.com"), | ||
Arguments.of("email", "hans-josef@main_google.com"), | ||
Arguments.of("email", ".ext4@main_google.com"), | ||
Arguments.of("email", "[email protected]"), | ||
Arguments.of("email", "[email protected]"), | ||
Arguments.of("email", "hansjosefgoogle.com"), | ||
Arguments.of("email", "hansjosef@dd"), | ||
Arguments.of("password", "1234567"), | ||
Arguments.of("password", " "), | ||
Arguments.of("password", " "), | ||
Arguments.of("password", ""), | ||
Arguments.of("password", null) | ||
); | ||
} | ||
} |
31 changes: 31 additions & 0 deletions
31
server/src/test/java/org/hyperskill/community/flashcards/web/UserMapperTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package org.hyperskill.community.flashcards.web; | ||
|
||
import org.hyperskill.community.flashcards.registration.UserDto; | ||
import org.hyperskill.community.flashcards.registration.UserMapper; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | ||
import org.springframework.security.crypto.password.PasswordEncoder; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
class UserMapperTest { | ||
UserDto dto; | ||
UserMapper mapper; | ||
PasswordEncoder passwordEncoder; | ||
|
||
@BeforeEach | ||
void setup() { | ||
passwordEncoder = new BCryptPasswordEncoder(); | ||
mapper = new UserMapper(passwordEncoder); | ||
dto = new UserDto("[email protected]", "secret"); | ||
} | ||
|
||
@Test | ||
void toDocument() { | ||
var mapped = mapper.toDocument(dto); | ||
assertEquals(dto.email(), mapped.getUsername()); | ||
assertTrue(passwordEncoder.matches(dto.password(), mapped.getPassword())); | ||
} | ||
} |