diff --git a/README.md b/README.md index c72b2bb..c211769 100644 --- a/README.md +++ b/README.md @@ -183,3 +183,5 @@ For further questions or contributions, feel free to open an issue or submit a p --- +(link jira)[https://bouananisfn.atlassian.net/jira/software/projects/PIG/boards/39] + diff --git a/pom.xml b/pom.xml index cb74529..69caf20 100644 --- a/pom.xml +++ b/pom.xml @@ -6,35 +6,22 @@ org.springframework.boot spring-boot-starter-parent 3.3.5 - + ma.yc PigeonSkyRace 0.0.1-SNAPSHOT PigeonSkyRace saas project foodeals - - - - - - - - - - - - - 21 1.2.4 21 21 - 1.6.2 + org.springframework.boot spring-boot-starter-data-mongodb @@ -47,15 +34,6 @@ org.springframework.boot spring-boot-starter-web - - org.springframework.modulith - spring-modulith-starter-core - - - org.springframework.modulith - spring-modulith-starter-mongodb - - org.springframework.boot spring-boot-devtools @@ -68,34 +46,36 @@ true - org.projectlombok - lombok - true + org.springframework.boot + spring-boot-starter-test + test org.springframework.boot - spring-boot-starter-test + spring-boot-testcontainers test + + org.springframework.modulith - spring-modulith-starter-test - test + spring-modulith-starter-core - org.springframework.boot - spring-boot-testcontainers - test + org.springframework.modulith + spring-modulith-starter-mongodb - org.testcontainers - junit-jupiter + org.springframework.modulith + spring-modulith-starter-test test + + - org.testcontainers - mongodb - test + org.projectlombok + lombok + true org.mapstruct @@ -107,6 +87,8 @@ spring-security-crypto 6.3.4 + + org.springdoc springdoc-openapi-starter-webmvc-ui @@ -128,6 +110,17 @@ 1.76 + + + org.testcontainers + junit-jupiter + test + + + org.testcontainers + mongodb + test + @@ -140,7 +133,6 @@ - @@ -155,7 +147,6 @@ - org.apache.maven.plugins maven-compiler-plugin @@ -182,8 +173,6 @@ go-offline-maven-plugin 1.2.8 - - - + \ No newline at end of file diff --git a/src/main/java/ma/yc/PigeonSkyRace/common/infrastructure/web/GlobalExceptionHandler.java b/src/main/java/ma/yc/PigeonSkyRace/common/infrastructure/web/GlobalExceptionHandler.java index daa7eb9..9557ef6 100644 --- a/src/main/java/ma/yc/PigeonSkyRace/common/infrastructure/web/GlobalExceptionHandler.java +++ b/src/main/java/ma/yc/PigeonSkyRace/common/infrastructure/web/GlobalExceptionHandler.java @@ -1,5 +1,7 @@ package ma.yc.PigeonSkyRace.common.infrastructure.web; +import ma.yc.PigeonSkyRace.common.domain.exception.NotFoundException; +import ma.yc.PigeonSkyRace.competition.domain.Exception.FailedToRegister; import ma.yc.PigeonSkyRace.piegon.domain.exception.InvalidLoftException; import ma.yc.PigeonSkyRace.piegon.domain.exception.InvalidLoftIdFormatException; import ma.yc.PigeonSkyRace.user.domain.exception.InvalidCredentialsException; @@ -17,6 +19,13 @@ @RestControllerAdvice public class GlobalExceptionHandler { + + @ExceptionHandler(NotFoundException.class) + public ResponseEntityhandleNotFoundException(NotFoundException e) { + ErrorResponse errorResponse = new ErrorResponse(LocalDateTime.now(),HttpStatus.BAD_REQUEST.value(), "Not Found",e.getMessage() ); + return new ResponseEntity(errorResponse, HttpStatus.BAD_REQUEST); + } + @ExceptionHandler(InvalidLoftIdFormatException.class) public ResponseEntity handleInvalidLoftIdFormatException ( InvalidLoftIdFormatException ex ) { return ResponseEntity @@ -37,6 +46,7 @@ public ResponseEntity handleUserRegistrationException ( UserRegis return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST); } + @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public ErrorResponse handleValidationExceptions ( MethodArgumentNotValidException ex ) { @@ -56,6 +66,12 @@ public ResponseEntity handleInvalidCredentialsException ( Invalid ErrorResponse error = new ErrorResponse(LocalDateTime.now(), HttpStatus.UNAUTHORIZED.value(), "Authentication Error", e.getMessage()); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(error); } + @ExceptionHandler(FailedToRegister.class) + public ResponseEntity handleRegisterFailed ( FailedToRegister e ) { + ErrorResponse error = new ErrorResponse(LocalDateTime.now(), HttpStatus.UNAUTHORIZED.value(), "register Error", e.getMessage()); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(error); + } + } record ErrorResponse(LocalDateTime timestamp, int status, String error, String message) { diff --git a/src/main/java/ma/yc/PigeonSkyRace/competition/application/dto/request/CompetitionRequestDto.java b/src/main/java/ma/yc/PigeonSkyRace/competition/application/dto/request/CompetitionRequestDto.java index a47de21..0b55232 100644 --- a/src/main/java/ma/yc/PigeonSkyRace/competition/application/dto/request/CompetitionRequestDto.java +++ b/src/main/java/ma/yc/PigeonSkyRace/competition/application/dto/request/CompetitionRequestDto.java @@ -1,11 +1,12 @@ package ma.yc.PigeonSkyRace.competition.application.dto.request; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; import ma.yc.PigeonSkyRace.competition.domain.ValueObject.AdmissionPercentage; -import ma.yc.PigeonSkyRace.competition.domain.ValueObject.Coordinate; import ma.yc.PigeonSkyRace.competition.domain.ValueObject.SeasonId; +import ma.yc.PigeonSkyRace.piegon.application.dto.request.CoordinateRequestDTO; import java.time.LocalDateTime; @@ -13,7 +14,10 @@ public record CompetitionRequestDto( @NotBlank String name, @NotBlank String description, @Positive Integer maxPigeons, - @NotNull Coordinate coordinate, + @Valid + @NotNull(message = "coordinate cannot be null" ) + CoordinateRequestDTO coordinate, + @Valid @NotNull AdmissionPercentage admissionPercentage, @NotNull LocalDateTime dateStart, @NotNull LocalDateTime dateEnd, diff --git a/src/main/java/ma/yc/PigeonSkyRace/competition/application/service/CompetitionPigeonApplicationService.java b/src/main/java/ma/yc/PigeonSkyRace/competition/application/service/CompetitionPigeonApplicationService.java index a093c43..f908426 100644 --- a/src/main/java/ma/yc/PigeonSkyRace/competition/application/service/CompetitionPigeonApplicationService.java +++ b/src/main/java/ma/yc/PigeonSkyRace/competition/application/service/CompetitionPigeonApplicationService.java @@ -6,10 +6,12 @@ import ma.yc.PigeonSkyRace.competition.domain.entity.SeasonPigeon; import javax.swing.text.html.Option; +import java.util.List; import java.util.Optional; public interface CompetitionPigeonApplicationService { CompetitionPigeon findBySeasonPigeonAndCompetition(SeasonPigeon seasonPigeon, Competition competition); CompetitionPigeon findById(CompetitionPigeonId id); + List findByCompetition(Competition competition); } diff --git a/src/main/java/ma/yc/PigeonSkyRace/competition/domain/ValueObject/Coordinate.java b/src/main/java/ma/yc/PigeonSkyRace/competition/domain/ValueObject/Coordinate.java index ee6e44e..e4f9945 100644 --- a/src/main/java/ma/yc/PigeonSkyRace/competition/domain/ValueObject/Coordinate.java +++ b/src/main/java/ma/yc/PigeonSkyRace/competition/domain/ValueObject/Coordinate.java @@ -3,7 +3,9 @@ import jakarta.validation.constraints.NotNull; -public record Coordinate( Double latitude, Double longitude) { +import java.io.Serializable; + +public record Coordinate(@NotNull Double latitude, @NotNull Double longitude) implements Serializable { public Coordinate { if (latitude < -90 || latitude > 90) { throw new IllegalArgumentException("Latitude must be between -90 and 90 degrees."); diff --git a/src/main/java/ma/yc/PigeonSkyRace/competition/domain/service/Impl/CompetitionPigeonDomainService.java b/src/main/java/ma/yc/PigeonSkyRace/competition/domain/service/Impl/CompetitionPigeonDomainService.java index 1bfc5c6..6e8449f 100644 --- a/src/main/java/ma/yc/PigeonSkyRace/competition/domain/service/Impl/CompetitionPigeonDomainService.java +++ b/src/main/java/ma/yc/PigeonSkyRace/competition/domain/service/Impl/CompetitionPigeonDomainService.java @@ -77,4 +77,8 @@ public CompetitionPigeon findById(CompetitionPigeonId id){ return repository.findById(id).orElseThrow(() -> new NotFoundException("competitionPigeon", id)); } + @Override + public List findByCompetition(Competition competition) { + return repository.findByCompetition(competition); + } } diff --git a/src/main/java/ma/yc/PigeonSkyRace/competition/domain/service/Impl/CompetitionServiceImpl.java b/src/main/java/ma/yc/PigeonSkyRace/competition/domain/service/Impl/CompetitionServiceImpl.java index d7466a8..17a4dc3 100644 --- a/src/main/java/ma/yc/PigeonSkyRace/competition/domain/service/Impl/CompetitionServiceImpl.java +++ b/src/main/java/ma/yc/PigeonSkyRace/competition/domain/service/Impl/CompetitionServiceImpl.java @@ -15,12 +15,14 @@ import static ma.yc.PigeonSkyRace.common.application.service.Helper.calculateDistance; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; import java.time.LocalDateTime; import java.util.List; import java.util.stream.Collectors; @Service +@Validated @RequiredArgsConstructor public class CompetitionServiceImpl implements CompetitionService, CompetitionApplicationService { diff --git a/src/main/java/ma/yc/PigeonSkyRace/competition/domain/service/Impl/SeasonPigeonDomainService.java b/src/main/java/ma/yc/PigeonSkyRace/competition/domain/service/Impl/SeasonPigeonDomainService.java index 3d3249f..6e8466c 100644 --- a/src/main/java/ma/yc/PigeonSkyRace/competition/domain/service/Impl/SeasonPigeonDomainService.java +++ b/src/main/java/ma/yc/PigeonSkyRace/competition/domain/service/Impl/SeasonPigeonDomainService.java @@ -29,6 +29,9 @@ public SeasonPigeonResponseDto RegisterToSeason(SeasonPigeonRequestDto seasonPig ).ifPresent(existing -> { throw new FailedToRegister("This pigeon is already registered in the season."); }); + if(!seasonPigeonRequestDto.season().getIsActive()){ + throw new FailedToRegister("This pigeon is not active."); + } SeasonPigeon savedSeasonPigeon = repository.save(mapper.toEntity(seasonPigeonRequestDto)); diff --git a/src/main/java/ma/yc/PigeonSkyRace/competition/infrastructure/repository/CompetitionPigeonRepository.java b/src/main/java/ma/yc/PigeonSkyRace/competition/infrastructure/repository/CompetitionPigeonRepository.java index ac6020f..5d9a2e2 100644 --- a/src/main/java/ma/yc/PigeonSkyRace/competition/infrastructure/repository/CompetitionPigeonRepository.java +++ b/src/main/java/ma/yc/PigeonSkyRace/competition/infrastructure/repository/CompetitionPigeonRepository.java @@ -4,6 +4,7 @@ import ma.yc.PigeonSkyRace.competition.domain.entity.Competition; import ma.yc.PigeonSkyRace.competition.domain.entity.CompetitionPigeon; +import ma.yc.PigeonSkyRace.competition.domain.entity.Season; import ma.yc.PigeonSkyRace.competition.domain.entity.SeasonPigeon; import ma.yc.PigeonSkyRace.piegon.domain.model.aggregate.Pigeon; import org.springframework.data.mongodb.repository.MongoRepository; diff --git a/src/main/java/ma/yc/PigeonSkyRace/piegon/application/dto/request/LoftRequestDTO.java b/src/main/java/ma/yc/PigeonSkyRace/piegon/application/dto/request/LoftRequestDTO.java index a7e42ae..150486f 100644 --- a/src/main/java/ma/yc/PigeonSkyRace/piegon/application/dto/request/LoftRequestDTO.java +++ b/src/main/java/ma/yc/PigeonSkyRace/piegon/application/dto/request/LoftRequestDTO.java @@ -11,5 +11,6 @@ public record LoftRequestDTO( CoordinateRequestDTO coordinate, @EntityExists(entity = User.class, message = "The specified breeder does not exist") - @NotNull String userId) { + @NotNull + String userId) { } \ No newline at end of file diff --git a/src/main/java/ma/yc/PigeonSkyRace/result/api/ResultController.java b/src/main/java/ma/yc/PigeonSkyRace/result/api/ResultController.java index 7179882..547abd0 100644 --- a/src/main/java/ma/yc/PigeonSkyRace/result/api/ResultController.java +++ b/src/main/java/ma/yc/PigeonSkyRace/result/api/ResultController.java @@ -30,28 +30,37 @@ public class ResultController { @PostMapping("/{competitionId}") - public ResponseEntity> createResult ( @PathVariable String competitionId, @RequestBody ResultRequestDto resultRequestDto ) { + public ResponseEntity> createResult( + @PathVariable String competitionId, + @RequestBody ResultRequestDto resultRequestDto) { CompetitionResponseDto competitionResponseDto = competitionApplicationService.getCompetition(CompetitionId.fromString(competitionId)); - ResultResponseDto responseDto = resultService.createResult(resultRequestDto, competitionResponseDto); + ResultResponseDto responseDto = resultService.createResult(resultRequestDto, competitionResponseDto); - ResponseApi response = new ResponseApi<>(responseDto, "The information stored with success for competition ", HttpStatus.CREATED); + ResponseApi response = new ResponseApi<>( + responseDto, + "The information stored with success for competition ", + HttpStatus.CREATED + ); return new ResponseEntity<>(response, HttpStatus.CREATED); } - @GetMapping("/{id}") - public ResponseEntity> getResult ( @PathVariable String id ) { - List resultsResponseDto = resultService.calculatePoint(CompetitionPigeonId.fromString(id)); + @GetMapping("/{competitionId}") + public ResponseEntity> getResult(@PathVariable String competitionId){ + CompetitionResponseDto competitionResponseDto = competitionApplicationService.getCompetition(CompetitionId.fromString(competitionId)); + List resultsResponseDto = resultService.calculatePoint(competitionResponseDto); return new ResponseEntity<>(resultsResponseDto, HttpStatus.CREATED); } - @GetMapping("/{id}/pdf") - public ResponseEntity downloadResultPdf ( @PathVariable String id ) { + @GetMapping("/{competitionId}/pdf") + public ResponseEntity downloadResultPdf ( @PathVariable String competitionId ) { try { - List results = resultService.calculatePoint(CompetitionPigeonId.fromString(id)); + CompetitionResponseDto competitionResponseDto = competitionApplicationService.getCompetition(CompetitionId.fromString(competitionId)); + + List results = resultService.calculatePoint(competitionResponseDto); byte[] pdfBytes = pdfGenerationService.generateResultsPdf(results); diff --git a/src/main/java/ma/yc/PigeonSkyRace/result/application/mapping/ResultMapper.java b/src/main/java/ma/yc/PigeonSkyRace/result/application/mapping/ResultMapper.java index c9bf36d..5e3ea24 100644 --- a/src/main/java/ma/yc/PigeonSkyRace/result/application/mapping/ResultMapper.java +++ b/src/main/java/ma/yc/PigeonSkyRace/result/application/mapping/ResultMapper.java @@ -19,6 +19,7 @@ public abstract class ResultMapper { @Mapping(target = "bandNumber", expression = "java(result.getCompetitionPigeon().getSeasonPigeon().getPigeon().getBandNumber())") @Mapping(target = "loft", expression = "java(getLoftName(result.getCompetitionPigeon().getSeasonPigeon().getPigeon().getLoft()))") + @Mapping(target = "createdDate", expression = "java(java.time.LocalDateTime.now())") public abstract ResultResponseDto toDto(Result result); protected String getLoftName(LoftId loftId) { diff --git a/src/main/java/ma/yc/PigeonSkyRace/result/domain/service/ResultService.java b/src/main/java/ma/yc/PigeonSkyRace/result/domain/service/ResultService.java index 9078763..6e78e46 100644 --- a/src/main/java/ma/yc/PigeonSkyRace/result/domain/service/ResultService.java +++ b/src/main/java/ma/yc/PigeonSkyRace/result/domain/service/ResultService.java @@ -12,5 +12,5 @@ public interface ResultService { ResultResponseDto createResult(ResultRequestDto requestDto, CompetitionResponseDto competition); - List calculatePoint(CompetitionPigeonId id); + List calculatePoint(CompetitionResponseDto competition); } diff --git a/src/main/java/ma/yc/PigeonSkyRace/result/domain/service/impl/ResultDomainService.java b/src/main/java/ma/yc/PigeonSkyRace/result/domain/service/impl/ResultDomainService.java index 7939dda..7d61a44 100644 --- a/src/main/java/ma/yc/PigeonSkyRace/result/domain/service/impl/ResultDomainService.java +++ b/src/main/java/ma/yc/PigeonSkyRace/result/domain/service/impl/ResultDomainService.java @@ -10,6 +10,7 @@ import ma.yc.PigeonSkyRace.competition.domain.ValueObject.CompetitionId; import ma.yc.PigeonSkyRace.competition.domain.ValueObject.CompetitionPigeonId; import ma.yc.PigeonSkyRace.competition.domain.ValueObject.Coordinate; +import ma.yc.PigeonSkyRace.competition.domain.entity.Competition; import ma.yc.PigeonSkyRace.competition.domain.entity.CompetitionPigeon; import ma.yc.PigeonSkyRace.competition.domain.entity.SeasonPigeon; import ma.yc.PigeonSkyRace.piegon.application.service.LoftApplicationService; @@ -24,6 +25,7 @@ import org.springframework.stereotype.Service; import java.time.Duration; +import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -66,20 +68,21 @@ private double calculateSpeed(double distance, Duration flightTime) { @Override - public List calculatePoint(CompetitionPigeonId id) { - List results = repository.findAllByCompetitionPigeonOrderBySpeed(competitionPigeonApplicationService.findById(id)); - results.getFirst().setPoints(100.0); - - for (int i = 1; i < results.size(); i++) { - Double point = 100.0 - (i * 100.0 / results.get(i).getCompetitionPigeon().getCompetition().getMaxPigeons()); - results.get(i).setPoints(point); - repository.save(results.get(i)); + public List calculatePoint(CompetitionResponseDto competition) { + List competitionPigeon = competitionPigeonApplicationService.findByCompetition(competitionMapper.toEntity(competition)); + List results = new ArrayList<>(); + for (CompetitionPigeon cp : competitionPigeon) { + results.add(repository.findByCompetitionPigeon(cp)); } - return results.stream() - .map(mapper::toDto) - .sorted((curr, next) -> Double.compare(next.points(), curr.points())) - .collect(Collectors.toList()); + List sortedResults = results.stream().sorted((curr, next) -> Double.compare(next.getSpeed(), curr.getSpeed())).toList(); + sortedResults.getFirst().setPoints(100.0); + for (int i = 1; i < sortedResults.size(); i++) { + Double point = 100.0 - (i * 100.0 / sortedResults.get(i).getCompetitionPigeon().getCompetition().getMaxPigeons()); + sortedResults.get(i).setPoints(point); + repository.save(sortedResults.get(i)); + } + return sortedResults.stream().map(mapper::toDto).sorted((curr, next) -> Double.compare(next.points(), curr.points())).toList(); } diff --git a/src/main/java/ma/yc/PigeonSkyRace/result/infrastructure/repository/ResultRepository.java b/src/main/java/ma/yc/PigeonSkyRace/result/infrastructure/repository/ResultRepository.java index 7d4e74b..9b6d033 100644 --- a/src/main/java/ma/yc/PigeonSkyRace/result/infrastructure/repository/ResultRepository.java +++ b/src/main/java/ma/yc/PigeonSkyRace/result/infrastructure/repository/ResultRepository.java @@ -8,6 +8,6 @@ import java.util.List; public interface ResultRepository extends MongoRepository { - List findAllByCompetitionPigeonOrderBySpeed ( CompetitionPigeon competitionPigeon ); + Result findByCompetitionPigeon(CompetitionPigeon competitionPigeon); } diff --git a/src/test/java/ma/yc/PigeonSkyRace/serviceTest/CompetitionServiceImplTest.java b/src/test/java/ma/yc/PigeonSkyRace/serviceTest/CompetitionServiceImplTest.java index b9b7c80..2ea5197 100644 --- a/src/test/java/ma/yc/PigeonSkyRace/serviceTest/CompetitionServiceImplTest.java +++ b/src/test/java/ma/yc/PigeonSkyRace/serviceTest/CompetitionServiceImplTest.java @@ -12,6 +12,7 @@ import ma.yc.PigeonSkyRace.competition.domain.entity.Competition; import ma.yc.PigeonSkyRace.competition.infrastructure.repository.CompetitionRepository; import ma.yc.PigeonSkyRace.competition.domain.service.Impl.CompetitionServiceImpl; +import ma.yc.PigeonSkyRace.piegon.application.dto.request.CoordinateRequestDTO; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; @@ -56,7 +57,7 @@ void setUp() { competition.setCoordinate(new Coordinate(34.05, -118.25)); SeasonId seasonId = new SeasonId(); AdmissionPercentage num = new AdmissionPercentage(12.3); - competitionRequestDto = new CompetitionRequestDto("Spring Competition", "A fun competition", 100,new Coordinate(32.2949,-8.1755), num, LocalDateTime.now(), LocalDateTime.now(), seasonId); + competitionRequestDto = new CompetitionRequestDto("Spring Competition", "A fun competition", 100,new CoordinateRequestDTO(32.2949,-8.1755), num, LocalDateTime.now(), LocalDateTime.now(), seasonId); coordinate = new Coordinate(35.68, 139.69); competitionResponseDto = new CompetitionResponseDto(competitionId, "Spring Competition", "A fun competition", coordinate, 100,100.0, num,seasonId, LocalDateTime.now(), LocalDateTime.now(), null); } diff --git a/src/test/java/ma/yc/PigeonSkyRace/serviceTest/ResultDomainServiceTest.java b/src/test/java/ma/yc/PigeonSkyRace/serviceTest/ResultDomainServiceTest.java new file mode 100644 index 0000000..0f6f015 --- /dev/null +++ b/src/test/java/ma/yc/PigeonSkyRace/serviceTest/ResultDomainServiceTest.java @@ -0,0 +1,138 @@ +package ma.yc.PigeonSkyRace.serviceTest; + +import ma.yc.PigeonSkyRace.competition.application.dto.response.CompetitionResponseDto; +import ma.yc.PigeonSkyRace.competition.application.mapping.CompetitionMapper; +import ma.yc.PigeonSkyRace.competition.application.service.CompetitionPigeonApplicationService; +import ma.yc.PigeonSkyRace.competition.application.service.SeasonApplicationService; +import ma.yc.PigeonSkyRace.competition.application.service.SeasonPigeonApplicationService; +import ma.yc.PigeonSkyRace.competition.domain.ValueObject.AdmissionPercentage; +import ma.yc.PigeonSkyRace.competition.domain.ValueObject.Coordinate; +import ma.yc.PigeonSkyRace.competition.domain.ValueObject.SeasonId; +import ma.yc.PigeonSkyRace.competition.domain.entity.CompetitionPigeon; +import ma.yc.PigeonSkyRace.competition.domain.entity.SeasonPigeon; +import ma.yc.PigeonSkyRace.piegon.application.service.LoftApplicationService; +import ma.yc.PigeonSkyRace.piegon.application.service.PigeonApplicationService; +import ma.yc.PigeonSkyRace.piegon.domain.model.aggregate.Pigeon; +import ma.yc.PigeonSkyRace.piegon.domain.model.valueObject.BandNumber; +import ma.yc.PigeonSkyRace.result.application.dto.request.ResultRequestDto; +import ma.yc.PigeonSkyRace.result.application.dto.response.ResultResponseDto; +import ma.yc.PigeonSkyRace.result.application.mapping.ResultMapper; +import ma.yc.PigeonSkyRace.result.domain.entity.Result; +import ma.yc.PigeonSkyRace.result.domain.service.impl.ResultDomainService; +import ma.yc.PigeonSkyRace.result.domain.valueObject.ResultId; +import ma.yc.PigeonSkyRace.result.infrastructure.repository.ResultRepository; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +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 static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +@DisplayName("Result Domain Service Test") +public class ResultDomainServiceTest { + + @Mock + private ResultRepository resultRepository; + @Mock + private PigeonApplicationService pigeonApplicationService; + @Mock + private ResultMapper resultMapper; + @Mock + private SeasonPigeonApplicationService seasonPigeonApplicationService; + @Mock + private SeasonApplicationService seasonApplicationService; + @Mock + private CompetitionPigeonApplicationService competitionPigeonApplicationService; + @Mock + private LoftApplicationService loftApplicationService; + @Mock + private CompetitionMapper competitionMapper; + + @InjectMocks + private ResultDomainService resultService; + + @Nested + @DisplayName("createResult() method tests") + class CreateResult { + + @Test + @DisplayName("should create and return result successfully") + void shouldCreateResult() { + ResultRequestDto request = new ResultRequestDto( + new BandNumber("AU 2023 XYZ 3234534"), + LocalDateTime.now() + ); + + CompetitionResponseDto competition = new CompetitionResponseDto( + null, "Race1", "Description", new Coordinate(1.21332, 2.11232), + 100, 500.0, new AdmissionPercentage(12.2), new SeasonId(), + LocalDateTime.now(), LocalDateTime.now().plusHours(1), LocalDateTime.now() + ); + + Result result = new Result(); + ResultResponseDto response = new ResultResponseDto( + new ResultId(), 500.0, 1200.0, 100.0, + new BandNumber("AU 2021 XYZ 3234534"), + "Loft A", LocalDateTime.now(), LocalDateTime.now().plusHours(2) + ); + + when(pigeonApplicationService.findPigeonByBandNumber(request.bandNumber())) + .thenReturn(mock(Pigeon.class)); + when(seasonPigeonApplicationService.findSeasonPigeonPigeonAndSeason(any(), any())) + .thenReturn(mock(SeasonPigeon.class)); + when(competitionPigeonApplicationService.findBySeasonPigeonAndCompetition(any(), any())) + .thenReturn(mock(CompetitionPigeon.class)); + when(resultMapper.toEntity(request)).thenReturn(result); + when(loftApplicationService.geLoftCoordinate(any())).thenReturn(mock(Coordinate.class)); + when(resultMapper.toDto(any(Result.class))).thenReturn(response); + when(resultRepository.save(any(Result.class))).thenReturn(result); + + ResultResponseDto resultResponse = resultService.createResult(request, competition); + + assertNotNull(resultResponse); + assertEquals(response.distance(), resultResponse.distance()); + verify(resultRepository).save(any(Result.class)); + } + } + + @Nested + @DisplayName("calculatePoint() method tests") + class CalculatePoint { + + @Test + @DisplayName("should calculate and assign points successfully") + void shouldCalculatePoints() { + CompetitionResponseDto competition = new CompetitionResponseDto( + null, "Race1", "Description", null, + 100, 500.0, null, null, + LocalDateTime.now(), LocalDateTime.now().plusHours(1), LocalDateTime.now() + ); + + CompetitionPigeon cp1 = mock(CompetitionPigeon.class); + CompetitionPigeon cp2 = mock(CompetitionPigeon.class); + Result result1 = new Result(); + Result result2 = new Result(); + + result1.setSpeed(1200.0); + result2.setSpeed(1100.0); + + when(competitionPigeonApplicationService.findByCompetition(any())) + .thenReturn(List.of(cp1, cp2)); + when(resultRepository.findByCompetitionPigeon(cp1)).thenReturn(result1); + when(resultRepository.findByCompetitionPigeon(cp2)).thenReturn(result2); + + List results = resultService.calculatePoint(competition); + + assertEquals(2, results.size()); + verify(resultRepository, times(2)).save(any(Result.class)); + } + } +}