Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug fixes #288

Merged
merged 8 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,4 @@ jobs:
echo "SSL_PASSWORD=${{ secrets.SSL_PASSWORD }}" >> .env
docker compose --profile prod down
docker compose --profile prod up -d --pull always
docker image prune
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ public static List<QuestionCategory> getQuestionCategoriesForGamemode(GameMode g
gamemode = KIWI_QUEST;
}
return switch (gamemode) {
case KIWI_QUEST -> List.of(QuestionCategory.ART, QuestionCategory.MUSIC, QuestionCategory.GEOGRAPHY);
case KIWI_QUEST -> List.of(QuestionCategory.ART,/* QuestionCategory.MUSIC, */ QuestionCategory.GEOGRAPHY);
case FOOTBALL_SHOWDOWN -> List.of(QuestionCategory.SPORTS);
case GEO_GENIUS -> List.of(QuestionCategory.GEOGRAPHY);
case VIDEOGAME_ADVENTURE -> List.of(QuestionCategory.VIDEOGAMES);
case ANCIENT_ODYSSEY -> List.of(QuestionCategory.MUSIC,QuestionCategory.ART);
case ANCIENT_ODYSSEY -> List.of(/*QuestionCategory.MUSIC,*/QuestionCategory.ART);
case RANDOM -> List.of(QuestionCategory.values());
case CUSTOM -> questionCategoriesForCustom;
};
Expand Down Expand Up @@ -70,11 +70,12 @@ private static List<QuestionCategoryDto> getQuestionCategoriesEn(){
.description("Are you an art expert? Prove it!")
.internalRepresentation(QuestionCategory.ART)
.build(),
/*
QuestionCategoryDto.builder()
.name("Music")
.description("Are you a music lover? Prove it!")
.internalRepresentation(QuestionCategory.MUSIC)
.build(),
.build(),*/
QuestionCategoryDto.builder()
.name("Geography")
.description("Are you a geography expert? Prove it!")
Expand All @@ -100,11 +101,12 @@ private static List<QuestionCategoryDto> getQuestionCategoriesEs(){
.description("¿Eres un experto en arte? ¡Demuéstralo!")
.internalRepresentation(QuestionCategory.ART)
.build(),
/*
QuestionCategoryDto.builder()
.name("Música")
.description("¿Eres un melómano? ¡Demuéstralo!")
.internalRepresentation(QuestionCategory.MUSIC)
.build(),
.build(),*/
QuestionCategoryDto.builder()
.name("Geografía")
.description("¿Eres un experto en geografía? ¡Demuéstralo!")
Expand Down
16 changes: 14 additions & 2 deletions api/src/main/java/lab/en2b/quizapi/game/GameService.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package lab.en2b.quizapi.game;

import lab.en2b.quizapi.commons.exceptions.InternalApiErrorException;
import lab.en2b.quizapi.commons.user.UserService;
import lab.en2b.quizapi.commons.utils.GameModeUtils;
import lab.en2b.quizapi.game.dtos.*;
import lab.en2b.quizapi.game.mappers.GameResponseDtoMapper;
import lab.en2b.quizapi.questions.question.Question;
import lab.en2b.quizapi.questions.question.QuestionService;
import lab.en2b.quizapi.questions.question.dtos.QuestionCategoryDto;
import lab.en2b.quizapi.questions.question.dtos.QuestionResponseDto;
Expand Down Expand Up @@ -55,18 +57,28 @@ public GameResponseDto newGame(String lang, GameMode gamemode, CustomGameDto new
* @param authentication the authentication of the user
* @return the game with the new round started
*/
@Transactional
public GameResponseDto startRound(Long id, Authentication authentication) {
// Get the game by id and user
Game game = gameRepository.findByIdForUser(id, userService.getUserByAuthentication(authentication).getId()).orElseThrow();
// Check if the game should be over
wasGameMeantToBeOver(game);
// Start a new round
game.newRound(questionService.findRandomQuestion(game.getLanguage(),game.getQuestionCategoriesForGamemode()));

game.newRound(generateQuestionForGame(game));
return gameResponseDtoMapper.apply(gameRepository.save(game));
}

private Question generateQuestionForGame(Game game){
Optional<Question> question = questionService.findRandomQuestion(game.getLanguage(),game.getQuestionCategoriesForGamemode());
if(question.isPresent()){
return question.get();
} else {
game.isGameOver();
gameRepository.save(game);
throw new InternalApiErrorException("Could not find a question for the game");
}
}

/**
* Gets the current question for the game
* @param id the id of the game to get the question for
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@

public enum QuestionCategory {
//HISTORY, GEOGRAPHY, SCIENCE, MATH, LITERATURE, ART, SPORTS, MUSIC, MOVIES, TV, POLITICS, OTHER
GEOGRAPHY, SPORTS, MUSIC, ART, VIDEOGAMES
GEOGRAPHY, SPORTS, //MUSIC,
ART, VIDEOGAMES
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
import org.springframework.data.jpa.repository.Query;

import java.util.List;
import java.util.Optional;

public interface QuestionRepository extends JpaRepository<Question,Long> {
@Query(value = "SELECT q.* FROM questions q INNER JOIN answers a ON q.correct_answer_id=a.id WHERE a.language = ?1 " +
"AND q.question_category IN ?2 " +
" ORDER BY RANDOM() LIMIT 1 ", nativeQuery = true)
Question findRandomQuestion(String lang, List<String> questionCategories);
Optional<Question> findRandomQuestion(String lang, List<String> questionCategories);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

@Service
@RequiredArgsConstructor
Expand Down Expand Up @@ -42,7 +43,10 @@ else if(question.getAnswers().stream().noneMatch(i -> i.getId().equals(answerDto
}

public QuestionResponseDto getRandomQuestion(String lang) {
return questionResponseDtoMapper.apply(findRandomQuestion(lang, List.of(QuestionCategory.values())));
Optional<Question> q = findRandomQuestion(lang, List.of(QuestionCategory.values()));
if(q.isEmpty())
throw new InternalApiErrorException("No questions found");
return questionResponseDtoMapper.apply(q.get());
}

/**
Expand All @@ -51,15 +55,13 @@ public QuestionResponseDto getRandomQuestion(String lang) {
* @return The random question
*/

public Question findRandomQuestion(String language, List<QuestionCategory> questionCategoriesForCustom) {
public Optional<Question> findRandomQuestion(String language, List<QuestionCategory> questionCategoriesForCustom) {
if (language==null || language.isBlank()) {
language = "en";
}
Question q = questionRepository.findRandomQuestion(language,questionCategoriesForCustom.stream().map(Enum::toString).toList());
if(q==null) {
throw new InternalApiErrorException("No questions found for the specified language!");
}
loadAnswers(q);
Optional<Question> q = questionRepository.findRandomQuestion(language,questionCategoriesForCustom.stream().map(Enum::toString).toList());
q.ifPresent(this::loadAnswers);

return q;
}

Expand Down
39 changes: 25 additions & 14 deletions api/src/test/java/lab/en2b/quizapi/game/GameServiceTest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package lab.en2b.quizapi.game;

import ch.qos.logback.core.util.TimeUtil;
import lab.en2b.quizapi.commons.exceptions.InternalApiErrorException;
import lab.en2b.quizapi.commons.user.User;
import lab.en2b.quizapi.commons.user.dtos.UserResponseDto;
import lab.en2b.quizapi.commons.user.UserService;
Expand Down Expand Up @@ -253,7 +254,7 @@ public void isGameActiveNoActiveGame(){
public void startRound(){
when(gameRepository.findByIdForUser(any(), any())).thenReturn(Optional.of(defaultGame));
when(gameRepository.save(any())).thenAnswer(invocation -> invocation.getArgument(0));
when(questionService.findRandomQuestion(any(),any())).thenReturn(defaultQuestion);
when(questionService.findRandomQuestion(any(),any())).thenReturn(Optional.of(defaultQuestion));
when(userService.getUserByAuthentication(authentication)).thenReturn(defaultUser);
GameResponseDto gameDto = gameService.startRound(1L, authentication);
GameResponseDto result = defaultGameResponseDto;
Expand All @@ -262,11 +263,18 @@ public void startRound(){
result.setRoundStartTime(Instant.ofEpochMilli(defaultGame.getRoundStartTime()).toString());
assertEquals(result, gameDto);
}

@Test
public void startRoundNoQuestionCouldBeGenerated(){
when(gameRepository.findByIdForUser(any(), any())).thenReturn(Optional.of(defaultGame));
when(gameRepository.save(any())).thenAnswer(invocation -> invocation.getArgument(0));
when(userService.getUserByAuthentication(authentication)).thenReturn(defaultUser);
when(questionService.findRandomQuestion(any(),any())).thenReturn(Optional.empty());
assertThrows(InternalApiErrorException.class, () -> gameService.startRound(1L, authentication));
}
@Test
public void startRoundGameOver(){
when(gameRepository.findByIdForUser(any(), any())).thenReturn(Optional.of(defaultGame));
when(questionService.findRandomQuestion(any(),any())).thenReturn(defaultQuestion);
when(questionService.findRandomQuestion(any(),any())).thenReturn(Optional.of(defaultQuestion));
when(userService.getUserByAuthentication(authentication)).thenReturn(defaultUser);
defaultGame.setActualRound(10L);
assertThrows(IllegalStateException.class, () -> gameService.startRound(1L,authentication));
Expand All @@ -276,7 +284,7 @@ public void startRoundGameOver(){
public void startRoundWhenRoundNotFinished(){
when(gameRepository.findByIdForUser(any(), any())).thenReturn(Optional.of(defaultGame));
when(gameRepository.save(any())).thenAnswer(invocation -> invocation.getArgument(0));
when(questionService.findRandomQuestion(any(),any())).thenReturn(defaultQuestion);
when(questionService.findRandomQuestion(any(),any())).thenReturn(Optional.of(defaultQuestion));
when(userService.getUserByAuthentication(authentication)).thenReturn(defaultUser);
gameService.startRound(1L,authentication);
assertThrows(IllegalStateException.class, () -> gameService.startRound(1L,authentication));
Expand All @@ -286,7 +294,7 @@ public void startRoundWhenRoundNotFinished(){
public void getCurrentQuestion() {
when(gameRepository.findByIdForUser(any(), any())).thenReturn(Optional.of(defaultGame));
when(gameRepository.save(any())).thenAnswer(invocation -> invocation.getArgument(0));
when(questionService.findRandomQuestion(any(),any())).thenReturn(defaultQuestion);
when(questionService.findRandomQuestion(any(),any())).thenReturn(Optional.of(defaultQuestion));
when(userService.getUserByAuthentication(authentication)).thenReturn(defaultUser);
gameService.startRound(1L,authentication);
QuestionResponseDto questionDto = gameService.getCurrentQuestion(1L,authentication);
Expand All @@ -312,7 +320,7 @@ public void getCurrentQuestionRoundNotStarted() {
public void getCurrentQuestionRoundFinished() {
when(gameRepository.findByIdForUser(any(), any())).thenReturn(Optional.of(defaultGame));
when(gameRepository.save(any())).thenAnswer(invocation -> invocation.getArgument(0));
when(questionService.findRandomQuestion(any(),any())).thenReturn(defaultQuestion);
when(questionService.findRandomQuestion(any(),any())).thenReturn(Optional.of(defaultQuestion));
when(userService.getUserByAuthentication(authentication)).thenReturn(defaultUser);
gameService.startRound(1L,authentication);
defaultGame.setRoundStartTime(Instant.now().minusSeconds(100).toEpochMilli());
Expand All @@ -324,7 +332,7 @@ public void getCurrentQuestionGameFinished() {
when(gameRepository.findByIdForUser(any(), any())).thenReturn(Optional.of(defaultGame));
when(userService.getUserByAuthentication(authentication)).thenReturn(defaultUser);
when(gameRepository.save(any())).thenAnswer(invocation -> invocation.getArgument(0));
when(questionService.findRandomQuestion(any(),any())).thenReturn(defaultQuestion);
when(questionService.findRandomQuestion(any(),any())).thenReturn(Optional.of(defaultQuestion));
gameService.startRound(1L,authentication);
defaultGame.setGameOver(true);
defaultGame.setActualRound(10L);
Expand All @@ -336,7 +344,7 @@ public void answerQuestionCorrectly(){
when(gameRepository.findByIdForUser(any(), any())).thenReturn(Optional.of(defaultGame));
when(gameRepository.save(any())).thenAnswer(invocation -> invocation.getArgument(0));
when(userService.getUserByAuthentication(authentication)).thenReturn(defaultUser);
when(questionService.findRandomQuestion(any(),any())).thenReturn(defaultQuestion);
when(questionService.findRandomQuestion(any(),any())).thenReturn(Optional.of(defaultQuestion));
gameService.newGame(null,null,null,authentication);
gameService.startRound(1L, authentication);
gameService.answerQuestion(1L, new GameAnswerDto(1L), authentication);
Expand All @@ -350,7 +358,7 @@ public void answerQuestionIncorrectly(){
when(gameRepository.findByIdForUser(any(), any())).thenReturn(Optional.of(defaultGame));
when(gameRepository.save(any())).thenAnswer(invocation -> invocation.getArgument(0));
when(userService.getUserByAuthentication(authentication)).thenReturn(defaultUser);
when(questionService.findRandomQuestion(any(),any())).thenReturn(defaultQuestion);
when(questionService.findRandomQuestion(any(),any())).thenReturn(Optional.of(defaultQuestion));
gameService.newGame(null,null,null,authentication);
gameService.startRound(1L, authentication);
gameService.answerQuestion(1L, new GameAnswerDto(2L), authentication);
Expand All @@ -364,7 +372,7 @@ public void answerQuestionWhenGameHasFinished(){
when(gameRepository.findByIdForUser(any(), any())).thenReturn(Optional.of(defaultGame));
when(gameRepository.save(any())).thenAnswer(invocation -> invocation.getArgument(0));
when(userService.getUserByAuthentication(authentication)).thenReturn(defaultUser);
when(questionService.findRandomQuestion(any(),any())).thenReturn(defaultQuestion);
when(questionService.findRandomQuestion(any(),any())).thenReturn(Optional.of(defaultQuestion));
gameService.newGame(null,null,null,authentication);
gameService.startRound(1L, authentication);
defaultGame.setGameOver(true);
Expand All @@ -377,7 +385,7 @@ public void answerQuestionLastRound(){
when(gameRepository.findByIdForUser(any(), any())).thenReturn(Optional.of(defaultGame));
when(gameRepository.save(any())).thenAnswer(invocation -> invocation.getArgument(0));
when(userService.getUserByAuthentication(authentication)).thenReturn(defaultUser);
when(questionService.findRandomQuestion(any(),any())).thenReturn(defaultQuestion);
when(questionService.findRandomQuestion(any(),any())).thenReturn(Optional.of(defaultQuestion));
gameService.newGame(null,null,null,authentication);
defaultGame.setActualRound(8L);
gameService.startRound(1L, authentication);
Expand All @@ -390,7 +398,7 @@ public void answerQuestionWhenRoundHasFinished(){
when(gameRepository.findByIdForUser(any(), any())).thenReturn(Optional.of(defaultGame));
when(gameRepository.save(any())).thenAnswer(invocation -> invocation.getArgument(0));
when(userService.getUserByAuthentication(authentication)).thenReturn(defaultUser);
when(questionService.findRandomQuestion(any(),any())).thenReturn(defaultQuestion);
when(questionService.findRandomQuestion(any(),any())).thenReturn(Optional.of(defaultQuestion));
gameService.newGame(null,null,null,authentication);
gameService.startRound(1L, authentication);
defaultGame.setRoundStartTime(Instant.now().minusSeconds(100).toEpochMilli());
Expand All @@ -402,7 +410,7 @@ public void answerQuestionInvalidId(){
when(gameRepository.findByIdForUser(any(), any())).thenReturn(Optional.of(defaultGame));
when(gameRepository.save(any())).thenAnswer(invocation -> invocation.getArgument(0));
when(userService.getUserByAuthentication(authentication)).thenReturn(defaultUser);
when(questionService.findRandomQuestion(any(),any())).thenReturn(defaultQuestion);
when(questionService.findRandomQuestion(any(),any())).thenReturn(Optional.of(defaultQuestion));
gameService.newGame(null,null,null,authentication);
gameService.startRound(1L, authentication);
assertThrows(IllegalArgumentException.class, () -> gameService.answerQuestion(1L, new GameAnswerDto(3L), authentication));
Expand All @@ -413,6 +421,7 @@ public void changeLanguage(){
when(gameRepository.findByIdForUser(any(), any())).thenReturn(Optional.of(defaultGame));
when(gameRepository.save(any())).thenAnswer(invocation -> invocation.getArgument(0));
when(userService.getUserByAuthentication(authentication)).thenReturn(defaultUser);
when(questionService.findRandomQuestion(any(),any())).thenReturn(Optional.of(defaultQuestion));
gameService.newGame(null,null,null,authentication);
gameService.startRound(1L, authentication);
gameService.changeLanguage(1L, "es", authentication);
Expand All @@ -424,6 +433,7 @@ public void changeLanguage(){
public void changeLanguageGameOver(){
when(gameRepository.findByIdForUser(any(), any())).thenReturn(Optional.of(defaultGame));
when(gameRepository.save(any())).thenAnswer(invocation -> invocation.getArgument(0));
when(questionService.findRandomQuestion(any(),any())).thenReturn(Optional.of(defaultQuestion));
when(userService.getUserByAuthentication(authentication)).thenReturn(defaultUser);

gameService.newGame(null,null,null,authentication);
Expand All @@ -448,7 +458,7 @@ public void getGameDetails(){
when(gameRepository.findByIdForUser(any(), any())).thenReturn(Optional.of(defaultGame));
when(userService.getUserByAuthentication(authentication)).thenReturn(defaultUser);
when(gameRepository.save(any())).thenAnswer(invocation -> invocation.getArgument(0));

when(questionService.findRandomQuestion(any(),any())).thenReturn(Optional.of(defaultQuestion));
GameResponseDto gameDto = gameService.newGame(null,null,null,authentication);
gameService.startRound(1L, authentication);
gameService.getGameDetails(1L, authentication);
Expand All @@ -461,6 +471,7 @@ public void getGameDetailsInvalidId(){
when(gameRepository.findByIdForUser(1L, 1L)).thenReturn(Optional.of(defaultGame));
when(userService.getUserByAuthentication(authentication)).thenReturn(defaultUser);
when(gameRepository.save(any())).thenAnswer(invocation -> invocation.getArgument(0));
when(questionService.findRandomQuestion(any(),any())).thenReturn(Optional.of(defaultQuestion));
gameService.newGame(null,null,null,authentication);
gameService.startRound(1L, authentication);
assertThrows(NoSuchElementException.class, () -> gameService.getGameDetails(2L, authentication));
Expand Down
Loading