diff --git a/pegase-env-local/sql_utils/init_project.sql b/pegase-env-local/sql_utils/init_project.sql index b33f3dd..912bc31 100644 --- a/pegase-env-local/sql_utils/init_project.sql +++ b/pegase-env-local/sql_utils/init_project.sql @@ -66,4 +66,16 @@ values ('me00247', 1), ('me00247', 2), ('me00247', 3), ('no0099', 1), - ('no0099', 2); \ No newline at end of file + ('no0099', 2); + +insert into pegase_local_db_schema.trajectory (id, file_name, file_size, checksum, type, version, created_by, creation_date, last_modification_content_date) +values (3, 'areas_BP23_A_ref_v2', 6822, 'd73a71ca53c7952eb99ab46eec3aeb24fda4d109652f0f539581227124c25010', 'AREA', 1, 'zayd', '2024-07-22 15:13:56.860045', '2024-07-09 10:55:27.467000'), + (1, 'areas_BP23_A_ref', 7132, 'bf64818cc16aa62110184b7e889188ce7cf0ee6a8b0f04a7721209bbd64c4b46', 'AREA', 1, 'MOUAD', '2024-07-22 14:52:31.234005', '2024-07-22 12:38:14.614000'), + (2, 'links_BP23_A_ref', 12679, '55be2a4685154fa0a5c45e5bac70a637fbb15a354d58afd5ff56b2c61b5be082', 'LINK', 1, 'mouad', '2024-07-22 14:52:56.325083', '2024-05-28 16:19:51.429000'); + +insert into pegase_local_db_schema.scenario_trajectory (scenario_id, trajectory_id) +values (1, 1), + (1, 2), + (1, 3), + (2, 1), + (3, 2); \ No newline at end of file diff --git a/src/main/java/com/rte_france/antares/datamanager_back/controller/ProjectController.java b/src/main/java/com/rte_france/antares/datamanager_back/controller/ProjectController.java index ab78cb8..6c6f484 100644 --- a/src/main/java/com/rte_france/antares/datamanager_back/controller/ProjectController.java +++ b/src/main/java/com/rte_france/antares/datamanager_back/controller/ProjectController.java @@ -68,4 +68,10 @@ public ResponseEntity findProjectById(@PathVariable Integer id) { public void deleteProject(@PathVariable Integer id) { projectService.deleteProjectById(id); } + + @Operation(summary = "Search projects by partial name for auto-completion") + @GetMapping("/autocomplete") + public ResponseEntity> searchProjectsByName(@RequestParam String partialName) { + return new ResponseEntity<>(projectService.searchProjectsByName(partialName), HttpStatus.OK); + } } diff --git a/src/main/java/com/rte_france/antares/datamanager_back/controller/StudyController.java b/src/main/java/com/rte_france/antares/datamanager_back/controller/StudyController.java index 9bc196e..cbd256d 100644 --- a/src/main/java/com/rte_france/antares/datamanager_back/controller/StudyController.java +++ b/src/main/java/com/rte_france/antares/datamanager_back/controller/StudyController.java @@ -2,6 +2,7 @@ import com.rte_france.antares.datamanager_back.dto.StudyDTO; import com.rte_france.antares.datamanager_back.service.StudyService; +import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; @@ -10,10 +11,9 @@ import org.springframework.data.domain.Sort; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.List; import static com.rte_france.antares.datamanager_back.mapper.StudyMapper.toStudyPage; @@ -35,9 +35,9 @@ public ResponseEntity> searchStudies( @RequestParam(value = "sortColumn", required = false) String sortColumn, @RequestParam(value = "sortDirection", required = false) String sortDirection) { - Sort sorting = Sort.by(SORTING_CRITERION); + Sort sorting = Sort.by(Sort.Direction.DESC,SORTING_CRITERION); - if (!sortColumn.isEmpty() && !sortDirection.isEmpty()) { + if (sortColumn != null && !sortColumn.isEmpty() && !sortDirection.isEmpty()) { Sort.Direction direction = Sort.Direction.fromString(sortDirection); sorting = Sort.by(direction, sortColumn); } @@ -45,4 +45,24 @@ public ResponseEntity> searchStudies( return new ResponseEntity<>(toStudyPage(studyService.findStudiesByCriteria(search, projectId, paging)), HttpStatus.OK); } + + + @Operation(summary = "Search keywords by partial name for auto-completion") + @GetMapping("/keywords/search") + public ResponseEntity> searchKeywordsByPartialName(@RequestParam String partialName) { + return new ResponseEntity<>(studyService.searchKeywordsByPartialName(partialName), HttpStatus.OK); + } + + + @PostMapping + public ResponseEntity createStudy(@RequestBody StudyDTO studyDTO) { + StudyDTO createdStudy = studyService.createStudy(studyDTO); + return new ResponseEntity<>(createdStudy, HttpStatus.CREATED); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteStudyById(@PathVariable Integer id) { + studyService.deleteStudyById(id); + return ResponseEntity.noContent().build(); + } } diff --git a/src/main/java/com/rte_france/antares/datamanager_back/dto/StudyDTO.java b/src/main/java/com/rte_france/antares/datamanager_back/dto/StudyDTO.java index 3e774ef..5b3cdc1 100644 --- a/src/main/java/com/rte_france/antares/datamanager_back/dto/StudyDTO.java +++ b/src/main/java/com/rte_france/antares/datamanager_back/dto/StudyDTO.java @@ -35,4 +35,7 @@ public class StudyDTO { @JsonProperty("horizon") String horizon; + + @JsonProperty("trajectoryIds") + List trajectoryIds; } diff --git a/src/main/java/com/rte_france/antares/datamanager_back/mapper/StudyMapper.java b/src/main/java/com/rte_france/antares/datamanager_back/mapper/StudyMapper.java index 0901372..ac2f147 100644 --- a/src/main/java/com/rte_france/antares/datamanager_back/mapper/StudyMapper.java +++ b/src/main/java/com/rte_france/antares/datamanager_back/mapper/StudyMapper.java @@ -3,12 +3,15 @@ import com.rte_france.antares.datamanager_back.dto.StudyDTO; import com.rte_france.antares.datamanager_back.repository.model.StudyEntity; +import com.rte_france.antares.datamanager_back.repository.model.TrajectoryEntity; import lombok.AccessLevel; import lombok.Builder; import lombok.NoArgsConstructor; import lombok.Value; import org.springframework.data.domain.Page; +import java.util.Collections; + @Value @Builder(toBuilder = true) @NoArgsConstructor(access = AccessLevel.PRIVATE) @@ -24,6 +27,7 @@ public static StudyDTO toStudyDTO(StudyEntity entity) { .tags(entity.getTags()) .horizon(entity.getHorizon()) .status(entity.getStatus().name()) + .trajectoryIds(entity.getTrajectories() != null ? entity.getTrajectories().stream().map(TrajectoryEntity::getId).toList() : Collections.emptyList()) .build(); } diff --git a/src/main/java/com/rte_france/antares/datamanager_back/repository/ProjectRepository.java b/src/main/java/com/rte_france/antares/datamanager_back/repository/ProjectRepository.java index e02d674..0267019 100644 --- a/src/main/java/com/rte_france/antares/datamanager_back/repository/ProjectRepository.java +++ b/src/main/java/com/rte_france/antares/datamanager_back/repository/ProjectRepository.java @@ -6,8 +6,17 @@ import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + +import java.util.List; + public interface ProjectRepository extends JpaRepository { Page findAll(Specification spec, Pageable pageable); + Optional findByName(String name); + + List findByNameContainingIgnoreCase(String partialName); + + } diff --git a/src/main/java/com/rte_france/antares/datamanager_back/repository/StudyRepository.java b/src/main/java/com/rte_france/antares/datamanager_back/repository/StudyRepository.java index b10af10..d915b5c 100644 --- a/src/main/java/com/rte_france/antares/datamanager_back/repository/StudyRepository.java +++ b/src/main/java/com/rte_france/antares/datamanager_back/repository/StudyRepository.java @@ -5,10 +5,22 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; -public interface StudyRepository extends JpaRepository { +import java.util.List; + +public interface StudyRepository extends JpaRepository { Page findAll(Specification spec, Pageable pageable); + @Query("SELECT DISTINCT t FROM StudyEntity s JOIN s.tags t WHERE t IN :tags") + List findExistingTags(@Param("tags") List tags); + + @Query("SELECT DISTINCT tag FROM StudyEntity s JOIN s.tags tag WHERE tag LIKE %:partialName%") + List findKeywordsByPartialName(String partialName); + + @Query("SELECT CASE WHEN COUNT(s) > 0 THEN true ELSE false END FROM StudyEntity s WHERE s.name = :name AND s.project.name = :projectName") + boolean existsByNameAndProjectName(@Param("name") String name, @Param("projectName") String projectName); } diff --git a/src/main/java/com/rte_france/antares/datamanager_back/repository/TrajectoryRepository.java b/src/main/java/com/rte_france/antares/datamanager_back/repository/TrajectoryRepository.java index d07ef1b..f083756 100644 --- a/src/main/java/com/rte_france/antares/datamanager_back/repository/TrajectoryRepository.java +++ b/src/main/java/com/rte_france/antares/datamanager_back/repository/TrajectoryRepository.java @@ -11,7 +11,9 @@ import java.util.Optional; @Repository -public interface TrajectoryRepository extends JpaRepository { +public interface TrajectoryRepository extends JpaRepository { + + Optional findTrajectoryEntityById(Integer id); @ExecutionTime Optional findFirstByFileNameOrderByVersionDesc(String fileName); diff --git a/src/main/java/com/rte_france/antares/datamanager_back/repository/model/ProjectEntity.java b/src/main/java/com/rte_france/antares/datamanager_back/repository/model/ProjectEntity.java index 480f48b..6a691dd 100644 --- a/src/main/java/com/rte_france/antares/datamanager_back/repository/model/ProjectEntity.java +++ b/src/main/java/com/rte_france/antares/datamanager_back/repository/model/ProjectEntity.java @@ -18,7 +18,8 @@ @Table(name = "project") public class ProjectEntity { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "project_seq_gen") + @SequenceGenerator(name = "project_seq_gen", sequenceName = "project_sequence", allocationSize = 1) @Column(name = "id", nullable = false) private Integer id; diff --git a/src/main/java/com/rte_france/antares/datamanager_back/repository/model/StudyEntity.java b/src/main/java/com/rte_france/antares/datamanager_back/repository/model/StudyEntity.java index d43ee3b..0120174 100644 --- a/src/main/java/com/rte_france/antares/datamanager_back/repository/model/StudyEntity.java +++ b/src/main/java/com/rte_france/antares/datamanager_back/repository/model/StudyEntity.java @@ -3,6 +3,7 @@ import jakarta.persistence.*; import jakarta.validation.constraints.Size; import lombok.*; +import org.hibernate.annotations.Cascade; import java.time.LocalDateTime; import java.util.LinkedHashSet; @@ -18,7 +19,8 @@ @Table(name = "scenario") public class StudyEntity { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "study_seq_gen") + @SequenceGenerator(name = "study_seq_gen", sequenceName = "study_sequence", allocationSize = 1) @Column(name = "id", nullable = false) private Integer id; @@ -42,6 +44,9 @@ public class StudyEntity { private String horizon; + @OneToMany(mappedBy = "studyEntity", orphanRemoval = true) + private Set studyTrajectoryEntities = new LinkedHashSet<>(); + @ManyToMany(mappedBy = "scenarioEntities") private Set trajectories = new LinkedHashSet<>(); diff --git a/src/main/java/com/rte_france/antares/datamanager_back/service/ProjectService.java b/src/main/java/com/rte_france/antares/datamanager_back/service/ProjectService.java index 7623eba..e90c8eb 100644 --- a/src/main/java/com/rte_france/antares/datamanager_back/service/ProjectService.java +++ b/src/main/java/com/rte_france/antares/datamanager_back/service/ProjectService.java @@ -1,5 +1,6 @@ package com.rte_france.antares.datamanager_back.service; +import com.rte_france.antares.datamanager_back.dto.ProjectDto; import com.rte_france.antares.datamanager_back.repository.model.ProjectEntity; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -20,6 +21,8 @@ public interface ProjectService { void deleteProjectById(Integer projectId); + List searchProjectsByName(String partialName); + } diff --git a/src/main/java/com/rte_france/antares/datamanager_back/service/StudyService.java b/src/main/java/com/rte_france/antares/datamanager_back/service/StudyService.java index 41aeaef..a54e065 100644 --- a/src/main/java/com/rte_france/antares/datamanager_back/service/StudyService.java +++ b/src/main/java/com/rte_france/antares/datamanager_back/service/StudyService.java @@ -1,12 +1,20 @@ package com.rte_france.antares.datamanager_back.service; +import com.rte_france.antares.datamanager_back.dto.StudyDTO; import com.rte_france.antares.datamanager_back.repository.model.StudyEntity; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import java.util.List; + public interface StudyService { Page findStudiesByCriteria(String search,Integer idProject, Pageable pageable); + StudyDTO createStudy(StudyDTO studyDTO); + + List searchKeywordsByPartialName(String partialName); + + void deleteStudyById(Integer id); } diff --git a/src/main/java/com/rte_france/antares/datamanager_back/service/impl/ProjectServiceImpl.java b/src/main/java/com/rte_france/antares/datamanager_back/service/impl/ProjectServiceImpl.java index 8cf3879..84c0de8 100644 --- a/src/main/java/com/rte_france/antares/datamanager_back/service/impl/ProjectServiceImpl.java +++ b/src/main/java/com/rte_france/antares/datamanager_back/service/impl/ProjectServiceImpl.java @@ -1,7 +1,9 @@ package com.rte_france.antares.datamanager_back.service.impl; +import com.rte_france.antares.datamanager_back.dto.ProjectDto; import com.rte_france.antares.datamanager_back.exception.BadRequestException; import com.rte_france.antares.datamanager_back.exception.ResourceNotFoundException; +import com.rte_france.antares.datamanager_back.mapper.ProjectMapper; import com.rte_france.antares.datamanager_back.repository.PinnedProjectRepository; import com.rte_france.antares.datamanager_back.repository.ProjectRepository; import com.rte_france.antares.datamanager_back.repository.model.PinnedProjectEntity; @@ -26,6 +28,7 @@ import java.time.LocalDateTime; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; @Slf4j @@ -67,6 +70,14 @@ public Page findProjectsByCriteria(String search, Pageable paging return projectRepository.findAll(paging); } + @Override + public List searchProjectsByName(String partialName) { + List projectEntities = projectRepository.findByNameContainingIgnoreCase(partialName); + return projectEntities.stream() + .map(ProjectMapper::toProjectDto) + .collect(Collectors.toList()); + } + public static Specification hasStudyName(String studyName) { return (Root root, CriteriaQuery query, CriteriaBuilder criteriaBuilder) -> { Join studies = root.join("studies"); diff --git a/src/main/java/com/rte_france/antares/datamanager_back/service/impl/StudyServiceImpl.java b/src/main/java/com/rte_france/antares/datamanager_back/service/impl/StudyServiceImpl.java index ac61ef3..717c69e 100644 --- a/src/main/java/com/rte_france/antares/datamanager_back/service/impl/StudyServiceImpl.java +++ b/src/main/java/com/rte_france/antares/datamanager_back/service/impl/StudyServiceImpl.java @@ -1,8 +1,14 @@ package com.rte_france.antares.datamanager_back.service.impl; +import com.rte_france.antares.datamanager_back.dto.StudyDTO; +import com.rte_france.antares.datamanager_back.exception.BadRequestException; +import com.rte_france.antares.datamanager_back.repository.ProjectRepository; import com.rte_france.antares.datamanager_back.repository.StudyRepository; +import com.rte_france.antares.datamanager_back.repository.TrajectoryRepository; import com.rte_france.antares.datamanager_back.repository.model.ProjectEntity; import com.rte_france.antares.datamanager_back.repository.model.StudyEntity; +import com.rte_france.antares.datamanager_back.repository.model.StudyStatus; +import com.rte_france.antares.datamanager_back.repository.model.TrajectoryEntity; import com.rte_france.antares.datamanager_back.service.StudyService; import com.rte_france.antares.datamanager_back.util.Utils; import jakarta.persistence.criteria.CriteriaBuilder; @@ -16,8 +22,16 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.rte_france.antares.datamanager_back.mapper.StudyMapper.toStudyDTO; @Slf4j @Service @@ -26,6 +40,10 @@ public class StudyServiceImpl implements StudyService { private final StudyRepository studyRepository; + private final ProjectRepository projectRepository; + + private final TrajectoryRepository trajectoryRepository; + @Override public Page findStudiesByCriteria(String search, Integer idProject, Pageable pageable) { Specification spec = Specification.where(null); @@ -57,6 +75,93 @@ public Page findStudiesByCriteria(String search, Integer idProject, return studyRepository.findAll(spec, pageable); } + @Override + public List searchKeywordsByPartialName(String partialName) { + return studyRepository.findKeywordsByPartialName(partialName); + } + + @Override + public void deleteStudyById(Integer id) { + //delete study if exists + studyRepository.findById(id).ifPresentOrElse(studyRepository::delete, () -> { + throw new BadRequestException("Study with id " + id + " not found."); + }); + } + + @Override + public StudyDTO createStudy(StudyDTO studyDTO) { + String studyName = studyDTO.getName() + "-" + studyDTO.getHorizon() + "_REF"; + studyDTO.setName(studyName); + if (studyDTO.getProject() == null || studyDTO.getProject().isEmpty()) { + throw new BadRequestException("Project name must be provided."); + } + validateHorizon(studyDTO); + validateTags(studyDTO); + + if (studyExists(studyDTO.getName(), studyDTO.getProject())) { + throw new BadRequestException("A study with the same name already exists for the given project."); + } + + ProjectEntity projectEntity = projectRepository.findByName(studyDTO.getProject()) + .orElseGet(() -> projectRepository.save(ProjectEntity.builder() + .name(studyDTO.getProject()) + .createdBy(studyDTO.getCreatedBy()) + .creationDate(LocalDateTime.now()) + .build())); + + return toStudyDTO(buildAndSaveNewStudy(studyDTO, projectEntity)); + } + + private StudyEntity buildAndSaveNewStudy(StudyDTO studyDTO, ProjectEntity projectEntity) { + String horizon = studyDTO.getHorizon() + "-" + (Integer.parseInt(studyDTO.getHorizon()) + 1); + Set trajectories = CollectionUtils.isEmpty(studyDTO.getTrajectoryIds()) + ? Collections.emptySet() + : convertToTrajectoryEntities(studyDTO.getTrajectoryIds()); + + StudyEntity studyEntity = StudyEntity.builder() + .name(studyDTO.getName()) + .createdBy(studyDTO.getCreatedBy()) + .creationDate(LocalDateTime.now()) + .project(projectEntity) + .horizon(horizon) + .status(StudyStatus.IN_PROGRESS) + .tags(studyDTO.getTags()) + .trajectories(trajectories) + .build(); + + trajectories.forEach(trajectory -> trajectory.getScenarioEntities().add(studyEntity)); + return studyRepository.save(studyEntity); + } + + private Set convertToTrajectoryEntities(List trajectoryIds) { + return trajectoryIds.stream() + .map(trajectoryRepository::findTrajectoryEntityById) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toSet()); + } + + private static void validateTags(StudyDTO studyDTO) { + if (studyDTO.getTags() != null && studyDTO.getTags().size() > 10) { + throw new BadRequestException("Tags list must not exceed 10 items."); + } + } + + private boolean studyExists(String studyName, String projectName) { + return studyRepository.existsByNameAndProjectName(studyName, projectName); + } + + private static void validateHorizon(StudyDTO studyDTO) { + int currentYear = LocalDateTime.now().getYear(); + try { + int horizonYear = Integer.parseInt(studyDTO.getHorizon()); + if (horizonYear < currentYear) { + throw new BadRequestException("Horizon year must be greater than the current year."); + } + } catch (NumberFormatException e) { + throw new BadRequestException("Horizon must be a valid year."); + } + } public static Specification hasProjectName(String projectName) { return (Root root, CriteriaQuery query, CriteriaBuilder criteriaBuilder) -> { @@ -68,7 +173,7 @@ public static Specification hasProjectName(String projectName) { public static Specification hasProjectId(Integer projectId) { return (Root root, CriteriaQuery query, CriteriaBuilder criteriaBuilder) -> { Join project = root.join("project"); - return criteriaBuilder.equal(project.get("id"), projectId ); + return criteriaBuilder.equal(project.get("id"), projectId); }; } diff --git a/src/main/java/com/rte_france/antares/datamanager_back/util/Utils.java b/src/main/java/com/rte_france/antares/datamanager_back/util/Utils.java index 2b9f8af..f004a5b 100644 --- a/src/main/java/com/rte_france/antares/datamanager_back/util/Utils.java +++ b/src/main/java/com/rte_france/antares/datamanager_back/util/Utils.java @@ -1,5 +1,9 @@ package com.rte_france.antares.datamanager_back.util; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; import com.rte_france.antares.datamanager_back.exception.AlreadyProcessedException; import com.rte_france.antares.datamanager_back.exception.ResourceNotFoundException; import com.rte_france.antares.datamanager_back.exception.TechnicalAntaresDataMangerException; @@ -189,5 +193,10 @@ public static boolean hasValidDateFormat(String dateStr) { } } - + public static String asJsonString(final Object obj) throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + return mapper.writeValueAsString(obj); + } } \ No newline at end of file diff --git a/src/main/resources/application-localhost.properties b/src/main/resources/application-localhost.properties index 8c42fc0..03151cb 100644 --- a/src/main/resources/application-localhost.properties +++ b/src/main/resources/application-localhost.properties @@ -4,7 +4,7 @@ #spring.datasource.url=jdbc:postgresql://pegase_db:5434/pegase_local_db?currentSchema=pegase_local_db_schema spring.datasource.url=jdbc:postgresql://localhost:5434/pegase_local_db?currentSchema=pegase_local_db_schema spring.datasource.username=postgres -spring.datasource.password= +spring.datasource.password=postgres spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.maximum-pool-size=4 diff --git a/src/main/resources/db/changelog/1.0.0/V07-add_sequence_study_project.sql b/src/main/resources/db/changelog/1.0.0/V07-add_sequence_study_project.sql new file mode 100644 index 0000000..81d7d0b --- /dev/null +++ b/src/main/resources/db/changelog/1.0.0/V07-add_sequence_study_project.sql @@ -0,0 +1,15 @@ +-- liquibase formatted sql +-- changeset elazaarmou:100V7-1 +CREATE SEQUENCE study_sequence + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + CREATE SEQUENCE project_sequence + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; \ No newline at end of file diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index 2f76766..1709818 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -11,4 +11,6 @@ databaseChangeLog: - include: file: classpath*:db/changelog/1.0.0/V05-create_user_and_pinned_project.sql - include: - file: classpath*:db/changelog/1.0.0/V06-create_project_tags_table.sql \ No newline at end of file + file: classpath*:db/changelog/1.0.0/V06-create_project_tags_table.sql + - include: + file: classpath*:db/changelog/1.0.0/V07-add_sequence_study_project.sql \ No newline at end of file diff --git a/src/test/java/com/rte_france/antares/datamanager_back/controller/ProjectControllerTest.java b/src/test/java/com/rte_france/antares/datamanager_back/controller/ProjectControllerTest.java index 59fd65e..ffb7196 100644 --- a/src/test/java/com/rte_france/antares/datamanager_back/controller/ProjectControllerTest.java +++ b/src/test/java/com/rte_france/antares/datamanager_back/controller/ProjectControllerTest.java @@ -188,4 +188,28 @@ void deleteProject_returnsBadRequestWhenProjectContainsStudies() throws Exceptio .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isInternalServerError()); } + + @Test + void searchProjectsByNameReturnsMatchingProjects() throws Exception { + ProjectDto projectDto = ProjectDto.builder().id(1).name("Project 1").build(); + when(projectService.searchProjectsByName("Proj")).thenReturn(List.of(projectDto)); + + mockMvc.perform(get("/v1/project/autocomplete") + .param("partialName", "Proj") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].id").value(1)) + .andExpect(jsonPath("$[0].name").value("Project 1")); + } + + @Test + void searchProjectsByNameReturnsEmptyListWhenNoMatches() throws Exception { + when(projectService.searchProjectsByName("NonExistent")).thenReturn(List.of()); + + mockMvc.perform(get("/v1/project/autocomplete") + .param("partialName", "NonExistent") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isEmpty()); + } } \ No newline at end of file diff --git a/src/test/java/com/rte_france/antares/datamanager_back/controller/StudyControllerTest.java b/src/test/java/com/rte_france/antares/datamanager_back/controller/StudyControllerTest.java index c6decda..35b4e1f 100644 --- a/src/test/java/com/rte_france/antares/datamanager_back/controller/StudyControllerTest.java +++ b/src/test/java/com/rte_france/antares/datamanager_back/controller/StudyControllerTest.java @@ -1,9 +1,12 @@ package com.rte_france.antares.datamanager_back.controller; +import com.rte_france.antares.datamanager_back.dto.StudyDTO; +import com.rte_france.antares.datamanager_back.exception.BadRequestException; import com.rte_france.antares.datamanager_back.repository.model.ProjectEntity; import com.rte_france.antares.datamanager_back.repository.model.StudyEntity; import com.rte_france.antares.datamanager_back.repository.model.StudyStatus; import com.rte_france.antares.datamanager_back.service.impl.StudyServiceImpl; +import com.rte_france.antares.datamanager_back.util.Utils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -20,10 +23,12 @@ import org.springframework.web.context.WebApplicationContext; import java.util.Collections; +import java.util.List; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -31,59 +36,147 @@ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class StudyControllerTest { - @Autowired - protected WebApplicationContext wac; + @Autowired + protected WebApplicationContext wac; - protected MockMvc mockMvc; + protected MockMvc mockMvc; - @MockBean - StudyServiceImpl studyService; + @MockBean + StudyServiceImpl studyService; - @BeforeEach - public void setup() { - this.mockMvc = MockMvcBuilders - .webAppContextSetup(wac) - .build(); - } + @BeforeEach + public void setup() { + this.mockMvc = MockMvcBuilders + .webAppContextSetup(wac) + .build(); + } @Test - void getStudiesReturnsPageOfStudies() throws Exception { - StudyEntity studyEntity = StudyEntity.builder().id(1).name("name").status(StudyStatus.IN_PROGRESS) - .project(ProjectEntity.builder().name("project").build()) - .build(); - when(studyService.findStudiesByCriteria(any(),any(), any())).thenReturn(new PageImpl<>(Collections.singletonList(studyEntity))); - this.mockMvc.perform(get("/v1/study/search") - .contentType(MediaType.APPLICATION_JSON_VALUE) - .param("search", "toto") - .param("page", "1") - .param("size", "2") - .param("sortColumn", "createdBy") - .param("sortDirection", "desc") - .accept(MediaType.APPLICATION_JSON_VALUE)) - - //Then - .andExpect(status().isOk()) - .andDo(MockMvcResultHandlers.print()) - .andReturn(); - verify(studyService, times(1)).findStudiesByCriteria(any(), any(), any()); + void getStudiesReturnsPageOfStudies() throws Exception { + StudyEntity studyEntity = StudyEntity.builder().id(1).name("name").status(StudyStatus.IN_PROGRESS) + .project(ProjectEntity.builder().name("project").build()) + .build(); + when(studyService.findStudiesByCriteria(any(), any(), any())).thenReturn(new PageImpl<>(Collections.singletonList(studyEntity))); + this.mockMvc.perform(get("/v1/study/search") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .param("search", "toto") + .param("page", "1") + .param("size", "2") + .param("sortColumn", "createdBy") + .param("sortDirection", "desc") + .accept(MediaType.APPLICATION_JSON_VALUE)) + + //Then + .andExpect(status().isOk()) + .andDo(MockMvcResultHandlers.print()) + .andReturn(); + verify(studyService, times(1)).findStudiesByCriteria(any(), any(), any()); } @Test - void getStudiesReturnsEmptyPageWhenNoStudiesFound() throws Exception { - when(studyService.findStudiesByCriteria(any(),any(), any())).thenReturn(Page.empty()); + void getStudiesReturnsEmptyPageWhenNoStudiesFound() throws Exception { + when(studyService.findStudiesByCriteria(any(), any(), any())).thenReturn(Page.empty()); this.mockMvc.perform(get("/v1/study/search") - .contentType(MediaType.APPLICATION_JSON_VALUE) - .param("search", "toto") - .param("page", "1") - .param("size", "2") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .param("search", "toto") + .param("page", "1") + .param("size", "2") .param("sortColumn", "createdBy") .param("sortDirection", "desc") - .accept(MediaType.APPLICATION_JSON_VALUE)) + .accept(MediaType.APPLICATION_JSON_VALUE)) + + //Then + .andExpect(status().isOk()) + .andDo(MockMvcResultHandlers.print()) + .andReturn(); + verify(studyService, times(1)).findStudiesByCriteria(any(), any(), any()); + } + + @Test + void searchKeywordsByPartialNameReturnsMatchingKeywords() throws Exception { + when(studyService.searchKeywordsByPartialName("key")).thenReturn(List.of("keyword1", "keyword2")); + + mockMvc.perform(get("/v1/study/keywords/search") + .param("partialName", "key") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0]").value("keyword1")) + .andExpect(jsonPath("$[1]").value("keyword2")); + } + + @Test + void searchKeywordsByPartialNameReturnsEmptyListWhenNoMatches() throws Exception { + when(studyService.searchKeywordsByPartialName("nonExistent")).thenReturn(List.of()); + + mockMvc.perform(get("/v1/study/keywords/search") + .param("partialName", "nonExistent") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isEmpty()); + } + + + @Test + void createStudyReturnsCreatedStudy() throws Exception { + StudyDTO studyDTO = StudyDTO.builder().name("Study 1").createdBy("User 1").build(); + StudyDTO createdStudyDTO = StudyDTO.builder().id(1).name("Study 1").createdBy("User 1").build(); + + when(studyService.createStudy(any(StudyDTO.class))).thenReturn(createdStudyDTO); + + this.mockMvc.perform(post("/v1/study") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(Utils.asJsonString(studyDTO)) + .accept(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.id").value(1)) + .andExpect(jsonPath("$.name").value("Study 1")) + .andExpect(jsonPath("$.createdBy").value("User 1")) + .andDo(MockMvcResultHandlers.print()) + .andReturn(); + + verify(studyService, times(1)).createStudy(any(StudyDTO.class)); + } + + @Test + void createStudyThrowsBadRequestWhenNoProjectInfoProvided() throws Exception { + StudyDTO studyDTO = StudyDTO.builder().name("Study 1").createdBy("User 1").build(); + + when(studyService.createStudy(any(StudyDTO.class))).thenThrow(new BadRequestException("Either project name or project ID must be provided.")); + + this.mockMvc.perform(post("/v1/study") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(Utils.asJsonString(studyDTO)) + .accept(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isInternalServerError()) + .andDo(MockMvcResultHandlers.print()) + .andReturn(); + + verify(studyService, times(1)).createStudy(any(StudyDTO.class)); + } + + @Test + void deleteStudyByIdReturnsNoContentWhenStudyExists() throws Exception { + doNothing().when(studyService).deleteStudyById(1); + + this.mockMvc.perform(delete("/v1/study/1") + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isNoContent()) + .andDo(MockMvcResultHandlers.print()) + .andReturn(); + + verify(studyService, times(1)).deleteStudyById(1); + } + + @Test + void deleteStudyByIdThrowsBadRequestWhenStudyNotFound() throws Exception { + doThrow(new BadRequestException("Study with id 1 not found.")).when(studyService).deleteStudyById(1); + + this.mockMvc.perform(delete("/v1/study/1") + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isInternalServerError()) + .andDo(MockMvcResultHandlers.print()) + .andReturn(); - //Then - .andExpect(status().isOk()) - .andDo(MockMvcResultHandlers.print()) - .andReturn(); - verify(studyService, times(1)).findStudiesByCriteria(any(), any(), any()); + verify(studyService, times(1)).deleteStudyById(1); } } diff --git a/src/test/java/com/rte_france/antares/datamanager_back/repository/ProjectRepositoryTest.java b/src/test/java/com/rte_france/antares/datamanager_back/repository/ProjectRepositoryTest.java index b20c1f9..6c42f95 100644 --- a/src/test/java/com/rte_france/antares/datamanager_back/repository/ProjectRepositoryTest.java +++ b/src/test/java/com/rte_france/antares/datamanager_back/repository/ProjectRepositoryTest.java @@ -13,6 +13,7 @@ import org.springframework.test.context.jdbc.SqlGroup; import org.springframework.test.context.junit.jupiter.SpringExtension; +import java.util.List; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; @@ -58,4 +59,29 @@ void getProjectDetails_byProjectId_returnsProjectDetails() { assertThat(projectEntity).isNotNull(); } + + @Test +void findByNameContainingIgnoreCaseReturnsMatchingProjects() { + List projects = projectRepository.findByNameContainingIgnoreCase("Proj"); + + assertThat(projects).isNotNull(); + assertThat(projects).isNotEmpty(); + assertThat(projects.get(0).getName()).containsIgnoringCase("Proj"); +} + +@Test +void findByNameContainingIgnoreCaseReturnsEmptyListWhenNoMatches() { + List projects = projectRepository.findByNameContainingIgnoreCase("NonExistent"); + + assertThat(projects).isNotNull(); + assertThat(projects).isEmpty(); +} + +@Test +void findByNameContainingIgnoreCaseHandlesNullInput() { + List projects = projectRepository.findByNameContainingIgnoreCase(null); + + assertThat(projects).isNotNull(); + assertThat(projects).isEmpty(); +} } diff --git a/src/test/java/com/rte_france/antares/datamanager_back/repository/StudyRepositoryTest.java b/src/test/java/com/rte_france/antares/datamanager_back/repository/StudyRepositoryTest.java index 5766556..5d82bcf 100644 --- a/src/test/java/com/rte_france/antares/datamanager_back/repository/StudyRepositoryTest.java +++ b/src/test/java/com/rte_france/antares/datamanager_back/repository/StudyRepositoryTest.java @@ -13,6 +13,8 @@ import org.springframework.test.context.jdbc.SqlGroup; import org.springframework.test.context.junit.jupiter.SpringExtension; +import java.util.List; + import static org.assertj.core.api.Assertions.assertThat; @@ -48,4 +50,29 @@ void findAll_returnsEmptyPageForNonExistentStudyEntities() { assertThat(page).isNotNull(); assertThat(page.getContent()).isEmpty(); } + + @Test + void findKeywordsByPartialNameReturnsMatchingKeywords() { + List keywords = studyRepository.findKeywordsByPartialName("config"); + + assertThat(keywords).isNotNull(); + assertThat(keywords).isNotEmpty(); + assertThat(keywords).contains("config"); + } + + @Test + void findKeywordsByPartialNameReturnsEmptyListWhenNoMatches() { + List keywords = studyRepository.findKeywordsByPartialName("nonExistent"); + + assertThat(keywords).isNotNull(); + assertThat(keywords).isEmpty(); + } + + @Test + void findKeywordsByPartialNameHandlesNullInput() { + List keywords = studyRepository.findKeywordsByPartialName(null); + + assertThat(keywords).isNotNull(); + assertThat(keywords).isEmpty(); + } } diff --git a/src/test/java/com/rte_france/antares/datamanager_back/service/ProjectServiceImplTest.java b/src/test/java/com/rte_france/antares/datamanager_back/service/ProjectServiceImplTest.java index d593466..a2b059a 100644 --- a/src/test/java/com/rte_france/antares/datamanager_back/service/ProjectServiceImplTest.java +++ b/src/test/java/com/rte_france/antares/datamanager_back/service/ProjectServiceImplTest.java @@ -1,5 +1,6 @@ package com.rte_france.antares.datamanager_back.service; +import com.rte_france.antares.datamanager_back.dto.ProjectDto; import com.rte_france.antares.datamanager_back.exception.BadRequestException; import com.rte_france.antares.datamanager_back.exception.ResourceNotFoundException; import com.rte_france.antares.datamanager_back.repository.PinnedProjectRepository; @@ -259,4 +260,36 @@ void deleteProjectById_throwsExceptionWhenProjectContainsStudies() { assertEquals("Project contains studies and cannot be deleted", exception.getMessage()); verify(projectRepository, never()).deleteById(projectId); } + +@Test +void searchProjectsByNameReturnsMatchingProjects() { + ProjectEntity projectEntity = new ProjectEntity(); + projectEntity.setId(1); + projectEntity.setName("Project 1"); + when(projectRepository.findByNameContainingIgnoreCase("Proj")).thenReturn(List.of(projectEntity)); + + List result = projectService.searchProjectsByName("Proj"); + + assertEquals(1, result.size()); + assertEquals("Project 1", result.get(0).getName()); + verify(projectRepository, times(1)).findByNameContainingIgnoreCase("Proj"); +} + +@Test +void searchProjectsByNameReturnsEmptyListWhenNoMatches() { + when(projectRepository.findByNameContainingIgnoreCase("NonExistent")).thenReturn(List.of()); + + List result = projectService.searchProjectsByName("NonExistent"); + + assertEquals(0, result.size()); + verify(projectRepository, times(1)).findByNameContainingIgnoreCase("NonExistent"); +} + +@Test +void searchProjectsByNameHandlesNullInput() { + List result = projectService.searchProjectsByName(null); + + assertEquals(0, result.size()); + verify(projectRepository, times(1)).findByNameContainingIgnoreCase(null); +} } diff --git a/src/test/java/com/rte_france/antares/datamanager_back/service/StudyServiceImplTest.java b/src/test/java/com/rte_france/antares/datamanager_back/service/StudyServiceImplTest.java index a2349b5..2029a92 100644 --- a/src/test/java/com/rte_france/antares/datamanager_back/service/StudyServiceImplTest.java +++ b/src/test/java/com/rte_france/antares/datamanager_back/service/StudyServiceImplTest.java @@ -1,7 +1,12 @@ package com.rte_france.antares.datamanager_back.service; +import com.rte_france.antares.datamanager_back.dto.StudyDTO; +import com.rte_france.antares.datamanager_back.exception.BadRequestException; +import com.rte_france.antares.datamanager_back.repository.ProjectRepository; import com.rte_france.antares.datamanager_back.repository.StudyRepository; +import com.rte_france.antares.datamanager_back.repository.model.ProjectEntity; import com.rte_france.antares.datamanager_back.repository.model.StudyEntity; +import com.rte_france.antares.datamanager_back.repository.model.StudyStatus; import com.rte_france.antares.datamanager_back.service.impl.StudyServiceImpl; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -14,9 +19,13 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; +import java.time.Year; import java.util.List; +import java.util.Optional; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) @@ -25,6 +34,9 @@ class StudyServiceImplTest { @Mock private StudyRepository studyRepository; + @Mock + private ProjectRepository projectRepository; + @InjectMocks private StudyServiceImpl studyServiceImpl; @@ -74,4 +86,144 @@ void findStudiesByCriteria_returnsFilteredStudiesWhenSearchAndIdProjectAreNotNul assertEquals(studyPage, result); verify(studyRepository).findAll(any(Specification.class), eq(pageable)); } + + @Test + void searchKeywordsByPartialNameReturnsMatchingKeywords() { + when(studyRepository.findKeywordsByPartialName("key")).thenReturn(List.of("keyword1", "keyword2")); + + List keywords = studyServiceImpl.searchKeywordsByPartialName("key"); + + assertThat(keywords).isNotNull(); + assertThat(keywords).isNotEmpty(); + assertThat(keywords).contains("keyword1", "keyword2"); + verify(studyRepository, times(1)).findKeywordsByPartialName("key"); + } + + @Test + void searchKeywordsByPartialNameReturnsEmptyListWhenNoMatches() { + when(studyRepository.findKeywordsByPartialName("nonExistent")).thenReturn(List.of()); + + List keywords = studyServiceImpl.searchKeywordsByPartialName("nonExistent"); + + assertThat(keywords).isNotNull(); + assertThat(keywords).isEmpty(); + verify(studyRepository, times(1)).findKeywordsByPartialName("nonExistent"); + } + + @Test + void searchKeywordsByPartialNameHandlesNullInput() { + when(studyRepository.findKeywordsByPartialName(null)).thenReturn(List.of()); + + List keywords = studyServiceImpl.searchKeywordsByPartialName(null); + + assertThat(keywords).isNotNull(); + assertThat(keywords).isEmpty(); + verify(studyRepository, times(1)).findKeywordsByPartialName(null); + } + + @Test + void createStudyCreatesNewProjectWhenProjectNotExists() { + ProjectEntity newProject = new ProjectEntity(); + newProject.setId(1); + newProject.setName("New Project"); + String currentYear = String.valueOf(Year.now().getValue()); + String nextYear = String.valueOf(Year.now().getValue() + 1); + String horizon = currentYear + "-" + nextYear; + StudyDTO studyDTO = StudyDTO.builder().name("Study 1").createdBy("User 1").project("New Project").horizon(currentYear).build(); + + String studyName = "Study 1-" + currentYear + "-" + nextYear + "ref"; + ProjectEntity existingProject = new ProjectEntity(); + existingProject.setId(1); + existingProject.setName("Existing Project"); + StudyEntity studyEntity = new StudyEntity(); + studyEntity.setId(1); + studyEntity.setName(studyName); + studyEntity.setCreatedBy("User 1"); + studyEntity.setProject(existingProject); + studyEntity.setHorizon(horizon); + studyEntity.setStatus(StudyStatus.IN_PROGRESS); + + when(projectRepository.findByName("New Project")).thenReturn(Optional.empty()); + when(projectRepository.save(any(ProjectEntity.class))).thenReturn(newProject); + when(studyRepository.save(any(StudyEntity.class))).thenReturn(studyEntity); + + StudyDTO result = studyServiceImpl.createStudy(studyDTO); + + assertEquals(1, result.getId()); + assertEquals(studyName, result.getName()); + assertEquals("User 1", result.getCreatedBy()); + assertEquals(horizon, result.getHorizon()); + verify(projectRepository, times(1)).findByName("New Project"); + verify(projectRepository, times(1)).save(any(ProjectEntity.class)); + verify(studyRepository, times(1)).save(any(StudyEntity.class)); + } + + @Test + void createStudyUsesExistingProjectWhenProjectExists() { + String currentYear = String.valueOf(Year.now().getValue()); + String nextYear = String.valueOf(Year.now().getValue() + 1); + String horizon = currentYear + "-" + nextYear; + + String studyName = "Study 1-" + currentYear + "-" + nextYear + "ref"; + StudyDTO studyDTO = StudyDTO.builder().name("Study 1").createdBy("User 1").project("Existing Project").horizon(currentYear).build(); + ProjectEntity existingProject = new ProjectEntity(); + existingProject.setId(1); + existingProject.setName("Existing Project"); + StudyEntity studyEntity = new StudyEntity(); + studyEntity.setId(1); + studyEntity.setName(studyName); + studyEntity.setCreatedBy("User 1"); + studyEntity.setProject(existingProject); + studyEntity.setHorizon(horizon); + studyEntity.setStatus(StudyStatus.IN_PROGRESS); + when(projectRepository.findByName("Existing Project")).thenReturn(Optional.of(existingProject)); + when(studyRepository.save(any(StudyEntity.class))).thenReturn(studyEntity); + + StudyDTO result = studyServiceImpl.createStudy(studyDTO); + + assertEquals(1, result.getId()); + assertEquals(studyName, result.getName()); + assertEquals("User 1", result.getCreatedBy()); + assertEquals(horizon, result.getHorizon()); + verify(projectRepository, times(1)).findByName("Existing Project"); + verify(studyRepository, times(1)).save(any(StudyEntity.class)); + } + + @Test + void createStudyThrowsBadRequestWhenNoProjectNameProvided() { + StudyDTO studyDTO = StudyDTO.builder().name("Study 1").createdBy("User 1").build(); + + BadRequestException exception = assertThrows(BadRequestException.class, () -> { + studyServiceImpl.createStudy(studyDTO); + }); + + assertEquals("Project name must be provided.", exception.getMessage()); + verify(projectRepository, never()).findByName(anyString()); + verify(projectRepository, never()).save(any(ProjectEntity.class)); + verify(studyRepository, never()).save(any(StudyEntity.class)); + } + + @Test + void deleteStudyByIdDeletesStudyWhenExists() { + StudyEntity studyEntity = new StudyEntity(); + studyEntity.setId(1); + + when(studyRepository.findById(1)).thenReturn(Optional.of(studyEntity)); + + studyServiceImpl.deleteStudyById(1); + + verify(studyRepository, times(1)).delete(studyEntity); + } + + @Test + void deleteStudyByIdThrowsBadRequestExceptionWhenStudyNotFound() { + when(studyRepository.findById(1)).thenReturn(Optional.empty()); + + BadRequestException exception = assertThrows(BadRequestException.class, () -> { + studyServiceImpl.deleteStudyById(1); + }); + + assertEquals("Study with id 1 not found.", exception.getMessage()); + verify(studyRepository, never()).delete(any(StudyEntity.class)); + } } diff --git a/src/test/resources/db/clean_db.sql b/src/test/resources/db/clean_db.sql index 60bdba9..aeb260f 100644 --- a/src/test/resources/db/clean_db.sql +++ b/src/test/resources/db/clean_db.sql @@ -1,3 +1,4 @@ +delete from public.scenario_tags; delete from public.pinned_project; delete from public.thermal_cost_type; delete from public.area; diff --git a/src/test/resources/db/init_db.sql b/src/test/resources/db/init_db.sql index a080086..c1a69af 100644 --- a/src/test/resources/db/init_db.sql +++ b/src/test/resources/db/init_db.sql @@ -40,3 +40,10 @@ values ('me00247', 1), ('no0099', 1), ('no0099', 2); +insert into public.scenario_tags (scenario_id, tag) +values (3, 'config'), + (1, 'elec'), + (3, 'antares'), + (3, 'bilan 22'), + (1, 'gaz'), + (2, 'figma'); \ No newline at end of file