diff --git a/src/main/java/pt/ua/deti/codespell/codespellbackend/controller/LevelController.java b/src/main/java/pt/ua/deti/codespell/codespellbackend/controller/LevelController.java index 12b643f..ddad2a2 100644 --- a/src/main/java/pt/ua/deti/codespell/codespellbackend/controller/LevelController.java +++ b/src/main/java/pt/ua/deti/codespell/codespellbackend/controller/LevelController.java @@ -32,12 +32,14 @@ public LevelController(LevelService levelService, ScoreService scoreService, Cod } @GetMapping("/{level_id}/leaderboards") - public List getLevelLeaderboard(@PathVariable(value = "level_id") ObjectId levelId, Settings settings) { + public List getLevelLeaderboard(@PathVariable(value = "level_id") String levelIdStr, @RequestBody Settings settings) { + ObjectId levelId = new ObjectId(levelIdStr); return scoreService.getScoresByLevelAndSettings(levelId, settings); } @GetMapping("/{level_id}/documentation") - public List getLevelDocumentation(@PathVariable(value = "level_id") ObjectId levelId, ProgrammingLanguage language) { + public List getLevelDocumentation(@PathVariable(value = "level_id") String levelIdStr, ProgrammingLanguage language) { + ObjectId levelId = new ObjectId(levelIdStr); Level level = levelService.findByLevelId(levelId); return level.getDocumentation(); } @@ -48,7 +50,8 @@ public List getLevelsList(ProgrammingLanguage programmingLanguage, SkillL } @GetMapping("/{level_id}/solutions") - public List getLevelSolutions(@PathVariable(value = "level_id") ObjectId levelId, ProgrammingLanguage language) { + public List getLevelSolutions(@PathVariable(value = "level_id") String levelIdStr, ProgrammingLanguage language) { + ObjectId levelId = new ObjectId(levelIdStr); Level level = levelService.findByLevelId(levelId); return level.getSolutions(); } @@ -74,7 +77,8 @@ public MessageResponse submitLevelSolution(@PathVariable(value = "level_id") Str } @GetMapping("/{level_id}") - public Level getCurrentLevel(@PathVariable(value = "level_id") ObjectId levelId) { + public Level getCurrentLevel(@PathVariable(value = "level_id") String levelIdStr) { + ObjectId levelId = new ObjectId(levelIdStr); return levelService.findByLevelId(levelId); } } diff --git a/src/main/java/pt/ua/deti/codespell/codespellbackend/controller/UserController.java b/src/main/java/pt/ua/deti/codespell/codespellbackend/controller/UserController.java index 9375ad4..88cdb81 100644 --- a/src/main/java/pt/ua/deti/codespell/codespellbackend/controller/UserController.java +++ b/src/main/java/pt/ua/deti/codespell/codespellbackend/controller/UserController.java @@ -10,12 +10,15 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import pt.ua.deti.codespell.codespellbackend.model.Achievement; import pt.ua.deti.codespell.codespellbackend.model.Game; import pt.ua.deti.codespell.codespellbackend.model.User; +import pt.ua.deti.codespell.codespellbackend.request.ChangeNameRequest; +import pt.ua.deti.codespell.codespellbackend.request.ChangePasswordRequest; import pt.ua.deti.codespell.codespellbackend.request.MessageResponse; import pt.ua.deti.codespell.codespellbackend.service.UserService; @@ -50,18 +53,20 @@ public List getUserAchievements(@PathVariable(value = "username") S } @PutMapping("/{username}/password") - public MessageResponse changePassword(@PathVariable(value = "username") String username, String newPassword) { + public MessageResponse changePassword(@PathVariable(value = "username") String username, @RequestBody ChangePasswordRequest changePasswordRequest) { User user = getUserDetails(username); + String newPassword = changePasswordRequest.getPassword(); user.setPassword(passwordEncoder.encode(newPassword)); - + userService.updateUser(user); return new MessageResponse(Date.from(Instant.now()), "Your password was successfully changed."); } @PutMapping("/{username}/name") - public MessageResponse changeName(@PathVariable(value = "username") String username, String newName) { + public MessageResponse changeName(@PathVariable(value = "username") String username, @RequestBody ChangeNameRequest changeNameRequest) { User user = getUserDetails(username); + String newName = changeNameRequest.getName(); user.setName(newName); - + userService.updateUser(user); return new MessageResponse(Date.from(Instant.now()), "Your name was successfully changed."); } } diff --git a/src/main/java/pt/ua/deti/codespell/codespellbackend/request/ChangeNameRequest.java b/src/main/java/pt/ua/deti/codespell/codespellbackend/request/ChangeNameRequest.java new file mode 100644 index 0000000..9830985 --- /dev/null +++ b/src/main/java/pt/ua/deti/codespell/codespellbackend/request/ChangeNameRequest.java @@ -0,0 +1,11 @@ +package pt.ua.deti.codespell.codespellbackend.request; + +import lombok.Data; +import lombok.Generated; + +@Data +@Generated +public class ChangeNameRequest { + private final String email; + private final String name; +} diff --git a/src/main/java/pt/ua/deti/codespell/codespellbackend/request/ChangePasswordRequest.java b/src/main/java/pt/ua/deti/codespell/codespellbackend/request/ChangePasswordRequest.java new file mode 100644 index 0000000..ad88b8d --- /dev/null +++ b/src/main/java/pt/ua/deti/codespell/codespellbackend/request/ChangePasswordRequest.java @@ -0,0 +1,11 @@ +package pt.ua.deti.codespell.codespellbackend.request; + +import lombok.Data; +import lombok.Generated; + +@Data +@Generated +public class ChangePasswordRequest { + private final String email; + private final String password; +} diff --git a/src/main/java/pt/ua/deti/codespell/codespellbackend/service/UserService.java b/src/main/java/pt/ua/deti/codespell/codespellbackend/service/UserService.java index 13dd336..fd36a7d 100644 --- a/src/main/java/pt/ua/deti/codespell/codespellbackend/service/UserService.java +++ b/src/main/java/pt/ua/deti/codespell/codespellbackend/service/UserService.java @@ -44,4 +44,11 @@ public void registerUser(User user) { } + public void updateUser(User user) { + + if (!userRepository.existsByUsername(user.getUsername())) + throw new UserNotFoundException(String.format("The user %s could not be found.", user.getUsername())); + userRepository.save(user); + + } } diff --git a/src/test/java/pt/ua/deti/codespell/codespellbackend/controller/ChapterControllerTest.java b/src/test/java/pt/ua/deti/codespell/codespellbackend/controller/ChapterControllerTest.java new file mode 100644 index 0000000..4e3a7f1 --- /dev/null +++ b/src/test/java/pt/ua/deti/codespell/codespellbackend/controller/ChapterControllerTest.java @@ -0,0 +1,88 @@ +package pt.ua.deti.codespell.codespellbackend.controller; + +import java.util.Collections; +import java.util.List; + +import org.bson.types.ObjectId; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.mongo.AutoConfigureDataMongo; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import pt.ua.deti.codespell.codespellbackend.model.*; +import pt.ua.deti.codespell.codespellbackend.repository.ChapterRepository; +import pt.ua.deti.codespell.codespellbackend.request.LoginRequest; +import pt.ua.deti.codespell.codespellbackend.request.MessageResponse; +import pt.ua.deti.codespell.codespellbackend.security.auth.AuthTokenResponse; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@AutoConfigureDataMongo +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class ChapterControllerTest { + + @Autowired + private TestRestTemplate restTemplate; + + @Autowired + private ChapterRepository chapterRepository; + + private HttpEntity jwtEntity; + + @BeforeAll + void beforeAll() { + LoginRequest loginRequest = new LoginRequest("artur.romao@ua.pt", "artur123"); + restTemplate.postForEntity("/api/auth/register", new User("artur", "artur.romao@ua.pt", "artur123", Collections.emptyList()), MessageResponse.class); + ResponseEntity loginResponse = restTemplate.postForEntity("/api/auth/login", loginRequest, AuthTokenResponse.class); + AuthTokenResponse authTokenResponse = loginResponse.getBody(); + + String token = "Bearer " + authTokenResponse.getToken(); + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", token); + jwtEntity = new HttpEntity(headers); + } + + @BeforeEach + void setUp() { + Chapter chapter1 = new Chapter(new ObjectId(), "Variables", "Learn the basics about Java variables.", + 1, Collections.emptyList(), Collections.emptyList(), new Settings(ProgrammingLanguage.JAVA, SkillLevel.NOVICE)); + Chapter chapter2 = new Chapter(new ObjectId(), "Object Oriented Programming", "Learn the basics about Java OOP.", + 2, Collections.emptyList(), Collections.emptyList(), new Settings(ProgrammingLanguage.JAVA, SkillLevel.NOVICE)); + + chapterRepository.save(chapter1); + chapterRepository.save(chapter2); + } + + @AfterEach + void tearDown() { + chapterRepository.deleteAll(); + } + + @Test + @DisplayName("Return the list of all chapters") + void getAllChapters() { + String url = "/api/chapter/"; + + ResponseEntity> response = restTemplate + .exchange(url, HttpMethod.GET, jwtEntity, new ParameterizedTypeReference>() { + }); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).extracting(Chapter::getTitle).hasSize(2).doesNotContainNull(); + assertThat(response.getBody()).extracting(Chapter::getTitle).containsOnly("Variables", "Object Oriented Programming"); + assertThat(response.getBody()).extracting(Chapter::getDescription).containsOnly("Learn the basics about Java variables.", "Learn the basics about Java OOP."); + } +} diff --git a/src/test/java/pt/ua/deti/codespell/codespellbackend/controller/LevelControllerTest.java b/src/test/java/pt/ua/deti/codespell/codespellbackend/controller/LevelControllerTest.java new file mode 100644 index 0000000..561dee6 --- /dev/null +++ b/src/test/java/pt/ua/deti/codespell/codespellbackend/controller/LevelControllerTest.java @@ -0,0 +1,205 @@ +package pt.ua.deti.codespell.codespellbackend.controller; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.bson.types.ObjectId; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.mongo.AutoConfigureDataMongo; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import pt.ua.deti.codespell.codespellbackend.model.*; +import pt.ua.deti.codespell.codespellbackend.repository.LevelRepository; +import pt.ua.deti.codespell.codespellbackend.repository.ScoreRepository; +import pt.ua.deti.codespell.codespellbackend.request.LoginRequest; +import pt.ua.deti.codespell.codespellbackend.request.MessageResponse; +import pt.ua.deti.codespell.codespellbackend.security.auth.AuthTokenResponse; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@AutoConfigureDataMongo +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class LevelControllerTest { + + @Autowired + private TestRestTemplate restTemplate; + + @Autowired + private LevelRepository levelRepository; + + @Autowired + private ScoreRepository scoreRepository; + + private HttpEntity jwtEntity; + + private String url; + + private Level level; + + private HttpHeaders headers; + + @AfterEach + public void tearDown() { + scoreRepository.deleteAll(); + levelRepository.deleteAll(); + } + + @BeforeAll + void beforeAll() { + LoginRequest loginRequest = new LoginRequest("artur.romao@ua.pt", "artur123"); + restTemplate.postForEntity("/api/auth/register", new User("artur", "artur.romao@ua.pt", "artur123", Collections.emptyList()), MessageResponse.class); + ResponseEntity loginResponse = restTemplate.postForEntity("/api/auth/login", loginRequest, AuthTokenResponse.class); + AuthTokenResponse authTokenResponse = loginResponse.getBody(); + + String token = "Bearer " + authTokenResponse.getToken(); + headers = new HttpHeaders(); + headers.set("Authorization", token); + jwtEntity = new HttpEntity(headers); + } + + @BeforeEach + void setUp() { + url = "/api/level/"; + + Solution solution = new Solution(new ObjectId(), new ObjectId(), 100, + "class HelloWorldApp {\npublic static void main(String[] args) {\nString a = 'b';\nSystem.out.println(a);\n}\n}"); + List solutions = new ArrayList(); + solutions.add(solution); + + Documentation documentation = new Documentation(new ObjectId(), "Print in Java", "System.out.println()", + "https://www.javatpoint.com/how-to-print-in-java"); + List documentations = new ArrayList(); + documentations.add(documentation); + + level = new Level(new ObjectId(), "Print a Variable", "Learn how to print a variable.", + new ObjectId(), 1, Collections.emptyList(), Collections.emptyList(), documentations, + solutions, new Settings(ProgrammingLanguage.JAVA, SkillLevel.NOVICE)); + + levelRepository.save(level); + + Score score = new Score(new ObjectId(), level.getId(), new ObjectId(), + 100, new Settings(ProgrammingLanguage.JAVA, SkillLevel.NOVICE)); + + scoreRepository.save(score); + } + + + /* @Test + @DisplayName("Get level leaderboard") + void getLevelLeaderboard() { + url += String.valueOf(level.getId()) + "/leaderboards"; + + //LevelLeaderboardRequest levelLeaderboardRequest = new LevelLeaderboardRequest(new Settings(ProgrammingLanguage.JAVA, SkillLevel.NOVICE)); + //HttpEntity request = new HttpEntity<>(levelLeaderboardRequest, headers); + HttpEntity request = new HttpEntity<>(new Settings(ProgrammingLanguage.JAVA, SkillLevel.NOVICE), headers); + + ResponseEntity> response = restTemplate + .exchange(url, HttpMethod.GET, request, new ParameterizedTypeReference>() { + }); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).extracting(Score::getPoints).hasSize(1).doesNotContainNull(); + assertThat(response.getBody()).extracting(Score::getPoints).containsOnly(100); + } */ + + @Test + @DisplayName("Get level documentation") + void getLevelDocumentation() { + url += String.valueOf(level.getId()) + "/documentation"; + ResponseEntity> response = restTemplate + .exchange(url, HttpMethod.GET, jwtEntity, new ParameterizedTypeReference>() { + }); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).extracting(Documentation::getTitle).hasSize(1).doesNotContainNull(); + assertThat(response.getBody()).extracting(Documentation::getTitle).containsOnly("Print in Java"); + assertThat(response.getBody()).extracting(Documentation::getDescription).containsOnly("System.out.println()"); + assertThat(response.getBody()).extracting(Documentation::getLink).containsOnly("https://www.javatpoint.com/how-to-print-in-java"); + } + + @Test + @DisplayName("Get levels list") + void getLevelsList() { + url += "/"; + ResponseEntity> response = restTemplate + .exchange(url, HttpMethod.GET, jwtEntity, new ParameterizedTypeReference>() { + }); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).extracting(Level::getTitle).hasSize(1).doesNotContainNull(); + assertThat(response.getBody()).extracting(Level::getTitle).containsOnly("Print a Variable"); + assertThat(response.getBody()).extracting(Level::getDescription).containsOnly("Learn how to print a variable."); + } + + @Test + @DisplayName("Get level solutions") + void getLevelSolutions() { + url += String.valueOf(level.getId()) + "/solutions"; + ResponseEntity> response = restTemplate + .exchange(url, HttpMethod.GET, jwtEntity, new ParameterizedTypeReference>() { + }); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).extracting(Solution::getScorePoints).hasSize(1).doesNotContainNull(); + assertThat(response.getBody()).extracting(Solution::getScorePoints).containsOnly(100); + assertThat(response.getBody()).extracting(Solution::getCode).containsOnly("class HelloWorldApp {\npublic static void main(String[] args) {\nString a = 'b';\nSystem.out.println(a);\n}\n}"); + } + + /* @Test + @DisplayName("Submit a level solution") + void submitLevelSolution() { + Solution solution = new Solution(new ObjectId(), new ObjectId(), 200, "Some code..."); + List solutions = level.getSolutions(); + solutions.add(solution); + level.setSolutions(solutions); + levelRepository.save(level); + url += String.valueOf(level.getId()) + "/submit/" + String.valueOf(solution.getId()); + HttpEntity request = new HttpEntity<>("Some code...", headers); + + ResponseEntity response = restTemplate + .exchange(url, HttpMethod.POST, request, new ParameterizedTypeReference() { + }); + + List levels = levelRepository.findAll(); + + assertThat(levelRepository.findById(level.getId())).isNotNull(); + assertThat(levels).hasSize(1).doesNotContainNull(); + assertThat(levels).extracting(Level::getTitle).containsOnly("Print a Variable"); + assertThat(levels).extracting(Level::getDescription).containsOnly("Learn how to print a variable."); + assertThat(levels).extracting(Level::getSolutions).containsOnly(level.getSolutions()); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).extracting(MessageResponse::getMessage).isEqualTo("Solution successfully submitted."); + } */ + + @Test + @DisplayName("Get current level") + void getCurrentLevel() { + url += "/" + String.valueOf(level.getId()); + ResponseEntity response = restTemplate + .exchange(url, HttpMethod.GET, jwtEntity, new ParameterizedTypeReference() { + }); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).isNotNull(); + assertThat(response.getBody().getTitle()).isEqualTo("Print a Variable"); + assertThat(response.getBody().getDescription()).isEqualTo("Learn how to print a variable."); + assertThat(response.getBody().getNumber()).isEqualTo(1); + + } +} diff --git a/src/test/java/pt/ua/deti/codespell/codespellbackend/controller/UserControllerTest.java b/src/test/java/pt/ua/deti/codespell/codespellbackend/controller/UserControllerTest.java new file mode 100644 index 0000000..2e7b984 --- /dev/null +++ b/src/test/java/pt/ua/deti/codespell/codespellbackend/controller/UserControllerTest.java @@ -0,0 +1,166 @@ +package pt.ua.deti.codespell.codespellbackend.controller; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import org.bson.types.ObjectId; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.mongo.AutoConfigureDataMongo; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.factory.PasswordEncoderFactories; +import org.springframework.security.crypto.password.PasswordEncoder; + +import pt.ua.deti.codespell.codespellbackend.model.*; +import pt.ua.deti.codespell.codespellbackend.repository.UserRepository; +import pt.ua.deti.codespell.codespellbackend.request.ChangeNameRequest; +import pt.ua.deti.codespell.codespellbackend.request.ChangePasswordRequest; +import pt.ua.deti.codespell.codespellbackend.request.LoginRequest; +import pt.ua.deti.codespell.codespellbackend.request.MessageResponse; +import pt.ua.deti.codespell.codespellbackend.security.auth.AuthTokenResponse; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@AutoConfigureDataMongo +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class UserControllerTest { + + @Autowired + private TestRestTemplate restTemplate; + + @Autowired + private UserRepository userRepository; + + private User user; + + private String url; + + private HttpEntity jwtEntity; + + private HttpHeaders headers; + + @BeforeAll + void beforeAll() { + user = new User("artur01", "artur.romao01@ua.pt", "12345", Collections.emptyList()); + LoginRequest loginRequest = new LoginRequest(user.getEmail(), user.getPassword()); + + restTemplate.postForEntity("/api/auth/register", user, MessageResponse.class); + + ResponseEntity response = restTemplate.postForEntity("/api/auth/login", loginRequest, AuthTokenResponse.class); + AuthTokenResponse authTokenResponse = response.getBody(); + + String token = "Bearer " + authTokenResponse.getToken(); + headers = new HttpHeaders(); + headers.set("Authorization", token); + jwtEntity = new HttpEntity(headers); + } + + @BeforeEach + void setUp() { + url = "/api/user/"; + user = new User("artur01", "artur.romao01@ua.pt", "12345", Collections.emptyList()); + + Achievement achievement = new Achievement(new ObjectId(), "First level completed!", "Congrats on your first level passed :)", new Date(), true); + List achievements = new ArrayList(); + achievements.add(achievement); + + Game game = new Game(new ObjectId(), Collections.emptyList(), Collections.emptyList(), new Score(new ObjectId(), new ObjectId(), new ObjectId(), 100, new Settings(ProgrammingLanguage.JAVA, SkillLevel.NOVICE)), new Settings(ProgrammingLanguage.JAVA, SkillLevel.NOVICE), achievements); + List games = new ArrayList(); + game.setAchievements(achievements); + games.add(game); + user.setGames(games); + + userRepository.save(user); + } + + @AfterEach + public void tearDown() { + userRepository.deleteAll(); + } + + @Test + @DisplayName("Get user details") + void getUserDetails() { + url += user.getUsername() + "/details"; + ResponseEntity response = restTemplate + .exchange(url, HttpMethod.GET, jwtEntity, new ParameterizedTypeReference() { + }); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).extracting(User::getUsername).isEqualTo("artur01"); + assertThat(response.getBody()).extracting(User::getEmail).isEqualTo("artur.romao01@ua.pt"); + } + + @Test + @DisplayName("Get user achievements") + void getUserAchievements() { + url += user.getUsername() + "/achievements"; + ResponseEntity> response = restTemplate + .exchange(url, HttpMethod.GET, jwtEntity, new ParameterizedTypeReference>() { + }); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).extracting(Achievement::getTitle).hasSize(1).doesNotContainNull(); + assertThat(response.getBody()).extracting(Achievement::getTitle).containsOnly("First level completed!"); + assertThat(response.getBody()).extracting(Achievement::getDescription).containsOnly("Congrats on your first level passed :)"); + } + + @Test + @DisplayName("Change user password") + void changePassword() { + url += user.getUsername() + "/password"; + ChangePasswordRequest changePasswordRequest = new ChangePasswordRequest(user.getEmail(), "54321"); + HttpEntity httpEntityRequest = new HttpEntity<>(changePasswordRequest, headers); + ResponseEntity response = restTemplate.exchange(url, HttpMethod.PUT, httpEntityRequest, new ParameterizedTypeReference(){ + }); + + List users = userRepository.findAll(); + + assertThat(userRepository.findByUsername("artur01")).isNotNull(); + assertThat(users).hasSize(1).doesNotContainNull(); + assertThat(users).extracting(User::getUsername).containsOnly("artur01"); + assertThat(users).extracting(User::getEmail).containsOnly("artur.romao01@ua.pt"); + + //PasswordEncoder passwordEncoder = new PasswordEncoder(); + //assertThat(users).extracting(User::getPassword).isEqualTo(passwordEncoder.encode("54321")); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).extracting(MessageResponse::getMessage).isEqualTo("Your password was successfully changed."); + } + + @Test + @DisplayName("Change user name field") + void changeName() { + url += user.getUsername() + "/name"; + ChangeNameRequest changeNameRequest = new ChangeNameRequest("artur.romao01@ua.pt", "Artur"); + HttpEntity httpEntityRequest = new HttpEntity<>(changeNameRequest, headers); + ResponseEntity response = restTemplate.exchange(url, HttpMethod.PUT, httpEntityRequest, new ParameterizedTypeReference(){ + }); + + List users = userRepository.findAll(); + + assertThat(userRepository.findByUsername("artur01")).isNotNull(); + assertThat(users).hasSize(1).doesNotContainNull(); + assertThat(users).extracting(User::getUsername).containsOnly("artur01"); + assertThat(users).extracting(User::getEmail).containsOnly("artur.romao01@ua.pt"); + assertThat(users).extracting(User::getName).containsOnly("Artur"); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).extracting(MessageResponse::getMessage).isEqualTo("Your name was successfully changed."); + } +} \ No newline at end of file diff --git a/src/test/java/pt/ua/deti/codespell/codespellbackend/repository/ChapterRepositoryTest.java b/src/test/java/pt/ua/deti/codespell/codespellbackend/repository/ChapterRepositoryTest.java new file mode 100644 index 0000000..f3224b4 --- /dev/null +++ b/src/test/java/pt/ua/deti/codespell/codespellbackend/repository/ChapterRepositoryTest.java @@ -0,0 +1,74 @@ +package pt.ua.deti.codespell.codespellbackend.repository; + +import java.util.Collections; + +import org.bson.types.ObjectId; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest; +import org.springframework.data.mongodb.core.MongoTemplate; + +import pt.ua.deti.codespell.codespellbackend.model.Chapter; +import pt.ua.deti.codespell.codespellbackend.model.ProgrammingLanguage; +import pt.ua.deti.codespell.codespellbackend.model.Settings; +import pt.ua.deti.codespell.codespellbackend.model.SkillLevel; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataMongoTest +public class ChapterRepositoryTest { + + @Autowired + private MongoTemplate mongoTemplate; + + @Autowired + private ChapterRepository chapterRepository; + + private Chapter chapter; + + @BeforeEach + void setUp() { + chapter = new Chapter(new ObjectId(), "Object Oriented Programming", "Learn OOP principles.", + 5, Collections.emptyList(), Collections.emptyList(), new Settings(ProgrammingLanguage.JAVA, SkillLevel.NOVICE)); + } + + @AfterEach + void tearDown() { + mongoTemplate.dropCollection(Chapter.class); + } + + @Test + @DisplayName("Find chapter by id") + void findById() { + chapterRepository.save(chapter); + + Chapter loadedChapter = chapterRepository.findById(chapter.getId()); + + assertThat(loadedChapter) + .isNotNull() + .isEqualTo(chapter); + } + + @Test + @DisplayName("Find non-existent chapter by id") + void findNonExistentChapterById() { + Chapter loadedChapter = chapterRepository.findById(new ObjectId()); + assertThat(loadedChapter).isNull(); + } + + @Test + @DisplayName("Check if chapter exists by id") + void existsById() { + chapterRepository.save(chapter); + assertThat(chapterRepository.existsById(chapter.getId())).isTrue(); + } + + @Test + @DisplayName("Check if chapter exists by id") + void noExistsById() { + assertThat(chapterRepository.existsById(new ObjectId())).isFalse(); + } +} diff --git a/src/test/java/pt/ua/deti/codespell/codespellbackend/repository/LevelRepositoryTest.java b/src/test/java/pt/ua/deti/codespell/codespellbackend/repository/LevelRepositoryTest.java new file mode 100644 index 0000000..72812cd --- /dev/null +++ b/src/test/java/pt/ua/deti/codespell/codespellbackend/repository/LevelRepositoryTest.java @@ -0,0 +1,75 @@ +package pt.ua.deti.codespell.codespellbackend.repository; + +import java.util.Collections; + +import org.bson.types.ObjectId; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest; +import org.springframework.data.mongodb.core.MongoTemplate; + +import pt.ua.deti.codespell.codespellbackend.model.Level; +import pt.ua.deti.codespell.codespellbackend.model.ProgrammingLanguage; +import pt.ua.deti.codespell.codespellbackend.model.Settings; +import pt.ua.deti.codespell.codespellbackend.model.SkillLevel; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataMongoTest +public class LevelRepositoryTest { + + @Autowired + private MongoTemplate mongoTemplate; + + @Autowired + private LevelRepository levelRepository; + + private Level level; + + @BeforeEach + void setUp() { + level = new Level(new ObjectId(), "Printing variables", "Learn how to initialize and print variables", + new ObjectId(), 1, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), + Collections.emptyList(), new Settings(ProgrammingLanguage.JAVA, SkillLevel.NOVICE)); + } + + @AfterEach + void tearDown() { + mongoTemplate.dropCollection(Level.class); + } + + @Test + @DisplayName("Find level by id") + void findById() { + levelRepository.save(level); + + Level loadedLevel = levelRepository.findById(level.getId()); + + assertThat(loadedLevel) + .isNotNull() + .isEqualTo(level); + } + + @Test + @DisplayName("Find non-existent level by id") + void findNonExistentLevelById() { + Level loadedLevel = levelRepository.findById(new ObjectId()); + assertThat(loadedLevel).isNull(); + } + + @Test + @DisplayName("Check if level exists by id") + void existsById() { + levelRepository.save(level); + assertThat(levelRepository.existsById(level.getId())).isTrue(); + } + + @Test + @DisplayName("Check if non-existent level exists by id") + void noExistsById() { + assertThat(levelRepository.existsById(new ObjectId())).isFalse(); + } +} diff --git a/src/test/java/pt/ua/deti/codespell/codespellbackend/repository/ScoreRepositoryTest.java b/src/test/java/pt/ua/deti/codespell/codespellbackend/repository/ScoreRepositoryTest.java new file mode 100644 index 0000000..8be6e73 --- /dev/null +++ b/src/test/java/pt/ua/deti/codespell/codespellbackend/repository/ScoreRepositoryTest.java @@ -0,0 +1,66 @@ +package pt.ua.deti.codespell.codespellbackend.repository; + +import java.util.List; + +import org.bson.types.ObjectId; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest; +import org.springframework.data.mongodb.core.MongoTemplate; + +import pt.ua.deti.codespell.codespellbackend.model.*; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataMongoTest +public class ScoreRepositoryTest { + + @Autowired + private MongoTemplate mongoTemplate; + + @Autowired + private ScoreRepository scoreRepository; + + private Score score; + + @BeforeEach + void setUp() { + score = new Score(new ObjectId(), new ObjectId(), new ObjectId(), 3000, new Settings(ProgrammingLanguage.JAVA, SkillLevel.NOVICE)); + } + + @AfterEach + void tearDown() { + mongoTemplate.dropCollection(Score.class); + } + + @Test + @DisplayName("Find all scores given certain Settings and LevelId") + void findAllScoresByLevelIdAndSettings() { + scoreRepository.save(score); + + List loadedScores = scoreRepository.findByLevelIdAndSettings(score.getLevelId(), score.getSettings()); + + assertThat(loadedScores.get(0)) + .isNotNull() + .isEqualTo(score); + } + + @Test + @DisplayName("Find non-existent scores given invalid Settings or LevelId") + void findNonExistentScoresByInvalidLevelIdOrSettings() { + // Test with invalid LevelId and valid Settings + List loadedScores = scoreRepository.findByLevelIdAndSettings(new ObjectId(), score.getSettings()); + assertThat(loadedScores).isEmpty(); + + // Test with valid LevelId and invalid Settings + loadedScores = scoreRepository.findByLevelIdAndSettings(score.getLevelId(), new Settings(ProgrammingLanguage.JAVA, SkillLevel.ADVANCED)); + assertThat(loadedScores).isEmpty(); + + // Test with invalid LevelId and invalid Settings + loadedScores = scoreRepository.findByLevelIdAndSettings(new ObjectId(), new Settings(ProgrammingLanguage.JAVA, SkillLevel.ADVANCED)); + assertThat(loadedScores).isEmpty(); + } +} diff --git a/src/test/java/pt/ua/deti/codespell/codespellbackend/service/ChapterServiceTest.java b/src/test/java/pt/ua/deti/codespell/codespellbackend/service/ChapterServiceTest.java new file mode 100644 index 0000000..0649284 --- /dev/null +++ b/src/test/java/pt/ua/deti/codespell/codespellbackend/service/ChapterServiceTest.java @@ -0,0 +1,88 @@ +package pt.ua.deti.codespell.codespellbackend.service; + +import java.util.Collections; +import java.util.List; +import java.util.Arrays; + +import org.bson.types.ObjectId; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import pt.ua.deti.codespell.codespellbackend.exception.implementations.ChapterNotFoundException; +import pt.ua.deti.codespell.codespellbackend.model.*; +import pt.ua.deti.codespell.codespellbackend.repository.ChapterRepository; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class ChapterServiceTest { + + @Mock + private ChapterRepository chapterRepository; + + @InjectMocks + private ChapterService chapterService; + + private Chapter chapter; + + private List listOfChapters; + + @BeforeEach + void setUp() { + chapter = new Chapter(new ObjectId(), "Object Oriented Programming", "Learn OOP principles", + 3, Collections.emptyList(), Collections.emptyList(), new Settings(ProgrammingLanguage.JAVA, SkillLevel.NOVICE)); + listOfChapters = Arrays.asList(chapter); + } + + @Test + @DisplayName("Find all chapters.") + void findAllChapters() { + when(chapterRepository.findAll()).thenReturn(listOfChapters); + + assertThat(chapterService.getAllChapters()) + .isNotNull() + .isEqualTo(listOfChapters); + + verify(chapterRepository, Mockito.times(1)).findAll(); + } + + @Test + @DisplayName("Find chapter by id.") + void findById() { + + when(chapterRepository.findById(chapter.getId())).thenReturn(chapter); + when(chapterRepository.existsById(chapter.getId())).thenReturn(true); + + assertThat(chapterService.getChapterById(chapter.getId())) + .isNotNull() + .isEqualTo(chapter); + + verify(chapterRepository, Mockito.times(1)).findById(any(ObjectId.class)); + verify(chapterRepository, Mockito.times(1)).existsById(any(ObjectId.class)); + } + + @Test + @DisplayName("Find chapter by non-existent id.") + void findByNonExistentId() { + + when(chapterRepository.existsById(chapter.getId())).thenReturn(false); + + assertThatThrownBy(() -> chapterService.getChapterById(chapter.getId())) + .isInstanceOf(ChapterNotFoundException.class) + .hasMessage(String.format("The chapter %s could not be found.", chapter.getId())); + + verify(chapterRepository, Mockito.times(0)).findById(any(ObjectId.class)); + verify(chapterRepository, Mockito.times(1)).existsById(any(ObjectId.class)); + + } +} \ No newline at end of file diff --git a/src/test/java/pt/ua/deti/codespell/codespellbackend/service/LevelServiceTest.java b/src/test/java/pt/ua/deti/codespell/codespellbackend/service/LevelServiceTest.java new file mode 100644 index 0000000..225ec3c --- /dev/null +++ b/src/test/java/pt/ua/deti/codespell/codespellbackend/service/LevelServiceTest.java @@ -0,0 +1,94 @@ +package pt.ua.deti.codespell.codespellbackend.service; + + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.bson.types.ObjectId; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import pt.ua.deti.codespell.codespellbackend.exception.implementations.LevelNotFoundException; +import pt.ua.deti.codespell.codespellbackend.model.*; +import pt.ua.deti.codespell.codespellbackend.repository.LevelRepository; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + + +@ExtendWith(MockitoExtension.class) +public class LevelServiceTest { + + @Mock + private LevelRepository levelRepository; + + @InjectMocks + private LevelService levelService; + + private Level level; + + private List listOfLevels; + + @BeforeEach + void setUp() { + level = new Level(new ObjectId(), "Variables", "Basic Java variables knowledge.", + new ObjectId(), 1, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), + Collections.emptyList(), new Settings(ProgrammingLanguage.JAVA, SkillLevel.NOVICE)); + listOfLevels = Arrays.asList(level); + } + + @Test + @DisplayName("Find all levels.") + void findAllLevels() { + when(levelRepository.findAll()).thenReturn(listOfLevels); + + assertThat(levelService.getAllLevels()) + .isNotNull() + .isEqualTo(listOfLevels); + + verify(levelRepository, Mockito.times(1)).findAll(); + } + + @Test + @DisplayName("Find level by id.") + void findById() { + + when(levelRepository.findById(level.getId())).thenReturn(level); + when(levelRepository.existsById(level.getId())).thenReturn(true); + + assertThat(levelService.findByLevelId(level.getId())) + .isNotNull() + .isEqualTo(level); + + verify(levelRepository, Mockito.times(1)).findById(any(ObjectId.class)); + verify(levelRepository, Mockito.times(1)).existsById(any(ObjectId.class)); + + } + + @Test + @DisplayName("Find level by non-existent id.") + void findByNonExistentId() { + + when(levelRepository.existsById(level.getId())).thenReturn(false); + + assertThatThrownBy(() -> levelService.findByLevelId(level.getId())) + .isInstanceOf(LevelNotFoundException.class) + .hasMessage(String.format("The level %s could not be found.", level.getId())); + + verify(levelRepository, Mockito.times(0)).findById(any(ObjectId.class)); + verify(levelRepository, Mockito.times(1)).existsById(any(ObjectId.class)); + + } + + +} diff --git a/src/test/java/pt/ua/deti/codespell/codespellbackend/service/ScoreServiceTest.java b/src/test/java/pt/ua/deti/codespell/codespellbackend/service/ScoreServiceTest.java new file mode 100644 index 0000000..847fc91 --- /dev/null +++ b/src/test/java/pt/ua/deti/codespell/codespellbackend/service/ScoreServiceTest.java @@ -0,0 +1,86 @@ +package pt.ua.deti.codespell.codespellbackend.service; + +import java.util.Arrays; +import java.util.List; + +import org.bson.types.ObjectId; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import pt.ua.deti.codespell.codespellbackend.model.*; +import pt.ua.deti.codespell.codespellbackend.repository.ScoreRepository; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class ScoreServiceTest { + + @Mock + private ScoreRepository scoreRepository; + + @InjectMocks + private ScoreService scoreService; + + private Score score; + + private List listOfScores; + + @BeforeEach + void setUp() { + score = new Score(new ObjectId(), new ObjectId(), new ObjectId(), 30, + new Settings(ProgrammingLanguage.JAVA, SkillLevel.NOVICE)); + listOfScores = Arrays.asList(score); + } + + @Test + @DisplayName("Find all scores given certain Settings and LevelId.") + void findAllScoresByLevelIdAndSettings() { + + when(scoreRepository.findByLevelIdAndSettings(score.getLevelId(), score.getSettings())).thenReturn(listOfScores); + + assertThat(scoreService.getScoresByLevelAndSettings(score.getLevelId(), score.getSettings())) + .isNotNull() + .isEqualTo(listOfScores); + + verify(scoreRepository, Mockito.times(1)).findByLevelIdAndSettings(any(ObjectId.class), any(Settings.class)); + } + + @Test + @DisplayName("Find non-existent scores by invalid Settings or LevelId.") + void findNonExistentScoresByInvalidSettingsOrLevelId() { + // Testing with valid LevelId and invalid Settings + when(scoreRepository.findByLevelIdAndSettings(score.getLevelId(), new Settings(ProgrammingLanguage.JAVA, SkillLevel.ADVANCED))).thenReturn(null); + + assertThat(scoreService.getScoresByLevelAndSettings(score.getLevelId(), new Settings(ProgrammingLanguage.JAVA, SkillLevel.ADVANCED))) + .isNull(); + + // Testing with invalid LevelId and valid Settings + ObjectId levelId = new ObjectId(); + + when(scoreRepository.findByLevelIdAndSettings(levelId, score.getSettings())).thenReturn(null); + + assertThat(scoreService.getScoresByLevelAndSettings(levelId, score.getSettings())) + .isNull(); + + // Testing with invalid LevelId and invalid Settings + levelId = new ObjectId(); + + when(scoreRepository.findByLevelIdAndSettings(levelId, new Settings(ProgrammingLanguage.JAVA, SkillLevel.ADVANCED))).thenReturn(null); + + assertThat(scoreService.getScoresByLevelAndSettings(levelId, new Settings(ProgrammingLanguage.JAVA, SkillLevel.ADVANCED))) + .isNull(); + + verify(scoreRepository, Mockito.times(3)).findByLevelIdAndSettings(any(ObjectId.class), any(Settings.class)); + + } + +}