diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 843303f3e9..c8065135d6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] # Development versions commons-dev = "f7c7d2c" -commons-dao-dev = "d970c6cc2e" +commons-dao-dev = "83498e7eb1" plugin-api-dev = "87b8788" # Platform versions diff --git a/src/main/java/com/epam/ta/reportportal/core/tms/controller/TmsManualLaunchController.java b/src/main/java/com/epam/ta/reportportal/core/tms/controller/TmsManualLaunchController.java index cfba5aa96c..9e1d1a2f69 100644 --- a/src/main/java/com/epam/ta/reportportal/core/tms/controller/TmsManualLaunchController.java +++ b/src/main/java/com/epam/ta/reportportal/core/tms/controller/TmsManualLaunchController.java @@ -8,7 +8,7 @@ import com.epam.ta.reportportal.core.tms.dto.TmsTestCaseExecutionRS; import com.epam.ta.reportportal.core.tms.dto.TmsTestFolderRS; import com.epam.ta.reportportal.core.tms.dto.batch.BatchAddTestCasesToLaunchRQ; -import com.epam.ta.reportportal.core.tms.dto.batch.BatchOperationResultRS; +import com.epam.ta.reportportal.core.tms.dto.batch.BatchTestCaseOperationResultRS; import com.epam.ta.reportportal.ws.reporting.OperationCompletionRS; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -20,7 +20,6 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -121,7 +120,7 @@ public OperationCompletionRS batchDeleteManualLaunches( // Test Case management in Launch @PostMapping("/launch/{launchId}/test-case") @Operation(summary = "Add single test case to Manual Launch") - public BatchOperationResultRS addTestCaseToLaunch( + public BatchTestCaseOperationResultRS addTestCaseToLaunch( @Parameter(description = "Project key", required = true) @PathVariable String projectKey, @Parameter(description = "Launch ID", required = true) @@ -135,7 +134,7 @@ public BatchOperationResultRS addTestCaseToLaunch( @PostMapping("/launch/{launchId}/test-case/batch") @Operation(summary = "Add multiple test cases to Manual Launch") - public BatchOperationResultRS addTestCasesToLaunch( + public BatchTestCaseOperationResultRS addTestCasesToLaunch( @Parameter(description = "Project key", required = true) @PathVariable String projectKey, @Parameter(description = "Launch ID", required = true) diff --git a/src/main/java/com/epam/ta/reportportal/core/tms/controller/TmsTestPlanController.java b/src/main/java/com/epam/ta/reportportal/core/tms/controller/TmsTestPlanController.java index 4db03a53ba..f90d8b4625 100644 --- a/src/main/java/com/epam/ta/reportportal/core/tms/controller/TmsTestPlanController.java +++ b/src/main/java/com/epam/ta/reportportal/core/tms/controller/TmsTestPlanController.java @@ -3,12 +3,13 @@ import com.epam.ta.reportportal.commons.EntityUtils; import com.epam.ta.reportportal.commons.ReportPortalUser; import com.epam.ta.reportportal.commons.querygen.Filter; +import com.epam.ta.reportportal.core.tms.dto.DuplicateTmsTestPlanRS; import com.epam.ta.reportportal.core.tms.dto.TmsTestCaseInTestPlanRS; import com.epam.ta.reportportal.core.tms.dto.TmsTestFolderRS; import com.epam.ta.reportportal.core.tms.dto.TmsTestPlanRQ; import com.epam.ta.reportportal.core.tms.dto.TmsTestPlanRS; import com.epam.ta.reportportal.core.tms.dto.batch.BatchAddTestCasesToPlanRQ; -import com.epam.ta.reportportal.core.tms.dto.batch.BatchOperationResultRS; +import com.epam.ta.reportportal.core.tms.dto.batch.BatchTestCaseOperationResultRS; import com.epam.ta.reportportal.core.tms.dto.batch.BatchRemoveTestCasesFromPlanRQ; import com.epam.ta.reportportal.core.tms.service.TmsTestPlanService; import com.epam.ta.reportportal.entity.tms.TmsTestCase; @@ -182,7 +183,7 @@ public TmsTestPlanRS patchTestPlan(@PathVariable String projectKey, tags = {"Batch Operations"} ) @ApiResponse(responseCode = "204", description = "Test cases added to test plan successfully") - public BatchOperationResultRS addTestCasesToPlan(@PathVariable String projectKey, + public BatchTestCaseOperationResultRS addTestCasesToPlan(@PathVariable String projectKey, @PathVariable("id") Long testPlanId, @Valid @RequestBody BatchAddTestCasesToPlanRQ addRequest, @AuthenticationPrincipal ReportPortalUser user) { @@ -208,7 +209,7 @@ public BatchOperationResultRS addTestCasesToPlan(@PathVariable String projectKey tags = {"Batch Operations"} ) @ApiResponse(responseCode = "204", description = "Test cases removed from test plan successfully") - public BatchOperationResultRS removeTestCasesFromPlan(@PathVariable String projectKey, + public BatchTestCaseOperationResultRS removeTestCasesFromPlan(@PathVariable String projectKey, @PathVariable("id") Long testPlanId, @Valid @RequestBody BatchRemoveTestCasesFromPlanRQ removeRequest, @AuthenticationPrincipal ReportPortalUser user) { @@ -252,9 +253,13 @@ public Page getTestFoldersFromPlan(@PathVariable String project description = "Duplicates test plan by test plan ID." ) @ApiResponse(responseCode = "200", description = "Duplicated test plan") - public TmsTestPlanRS duplicateTestPlan(@PathVariable String projectKey, + public DuplicateTmsTestPlanRS duplicateTestPlan(@PathVariable String projectKey, @PathVariable("id") Long testPlanId, @AuthenticationPrincipal ReportPortalUser user) { - throw new UnsupportedOperationException(); + return tmsTestPlanService.duplicate( + projectExtractor + .extractMembershipDetails(user, EntityUtils.normalizeId(projectKey)) + .getProjectId(), + testPlanId); } } diff --git a/src/main/java/com/epam/ta/reportportal/core/tms/dto/DuplicateTmsTestPlanRS.java b/src/main/java/com/epam/ta/reportportal/core/tms/dto/DuplicateTmsTestPlanRS.java new file mode 100644 index 0000000000..16d127c6dc --- /dev/null +++ b/src/main/java/com/epam/ta/reportportal/core/tms/dto/DuplicateTmsTestPlanRS.java @@ -0,0 +1,24 @@ +package com.epam.ta.reportportal.core.tms.dto; + +import com.epam.ta.reportportal.core.tms.dto.batch.BatchTestCaseOperationResultRS; +import com.fasterxml.jackson.annotation.JsonInclude; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class DuplicateTmsTestPlanRS { + + private Long id; + private String name; + private String description; + private TmsTestPlanExecutionStatisticRS executionStatistic; + private List attributes; + private BatchTestCaseOperationResultRS duplicationStatistic; +} diff --git a/src/main/java/com/epam/ta/reportportal/core/tms/dto/TmsTestPlanExecutionStatisticRS.java b/src/main/java/com/epam/ta/reportportal/core/tms/dto/TmsTestPlanExecutionStatisticRS.java new file mode 100644 index 0000000000..5c4611154c --- /dev/null +++ b/src/main/java/com/epam/ta/reportportal/core/tms/dto/TmsTestPlanExecutionStatisticRS.java @@ -0,0 +1,17 @@ +package com.epam.ta.reportportal.core.tms.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class TmsTestPlanExecutionStatisticRS { + private Long covered; + private Long total; +} diff --git a/src/main/java/com/epam/ta/reportportal/core/tms/dto/TmsTestPlanRS.java b/src/main/java/com/epam/ta/reportportal/core/tms/dto/TmsTestPlanRS.java index 280c9bb90b..b379ccf65a 100644 --- a/src/main/java/com/epam/ta/reportportal/core/tms/dto/TmsTestPlanRS.java +++ b/src/main/java/com/epam/ta/reportportal/core/tms/dto/TmsTestPlanRS.java @@ -1,6 +1,5 @@ package com.epam.ta.reportportal.core.tms.dto; -import com.epam.ta.reportportal.entity.tms.TmsTestPlanExecutionStatisticRS; import com.fasterxml.jackson.annotation.JsonInclude; import java.util.List; import lombok.Data; diff --git a/src/main/java/com/epam/ta/reportportal/core/tms/dto/batch/BatchOperationError.java b/src/main/java/com/epam/ta/reportportal/core/tms/dto/batch/BatchTestCaseOperationError.java similarity index 75% rename from src/main/java/com/epam/ta/reportportal/core/tms/dto/batch/BatchOperationError.java rename to src/main/java/com/epam/ta/reportportal/core/tms/dto/batch/BatchTestCaseOperationError.java index cd1680b80d..ed977e8bab 100644 --- a/src/main/java/com/epam/ta/reportportal/core/tms/dto/batch/BatchOperationError.java +++ b/src/main/java/com/epam/ta/reportportal/core/tms/dto/batch/BatchTestCaseOperationError.java @@ -7,7 +7,7 @@ @Data @NoArgsConstructor @AllArgsConstructor -public class BatchOperationError { - private Long testCaseId; +public class BatchTestCaseOperationError { + private Long testCaseIds; private String errorMessage; } diff --git a/src/main/java/com/epam/ta/reportportal/core/tms/dto/batch/BatchOperationResultRS.java b/src/main/java/com/epam/ta/reportportal/core/tms/dto/batch/BatchTestCaseOperationResultRS.java similarity index 51% rename from src/main/java/com/epam/ta/reportportal/core/tms/dto/batch/BatchOperationResultRS.java rename to src/main/java/com/epam/ta/reportportal/core/tms/dto/batch/BatchTestCaseOperationResultRS.java index 7fce93d41b..e5bbdceeb6 100644 --- a/src/main/java/com/epam/ta/reportportal/core/tms/dto/batch/BatchOperationResultRS.java +++ b/src/main/java/com/epam/ta/reportportal/core/tms/dto/batch/BatchTestCaseOperationResultRS.java @@ -11,9 +11,10 @@ @Builder @NoArgsConstructor @AllArgsConstructor -public class BatchOperationResultRS { - private int totalCount; - private int successCount; - private int failureCount; - private List errors; +public class BatchTestCaseOperationResultRS { + private Integer totalCount; + private Integer successCount; + private Integer failureCount; + private List successTestCaseIds; + private List errors; } diff --git a/src/main/java/com/epam/ta/reportportal/core/tms/mapper/TmsTestCaseMapper.java b/src/main/java/com/epam/ta/reportportal/core/tms/mapper/TmsTestCaseMapper.java index f4a2fad62c..b43f614be5 100644 --- a/src/main/java/com/epam/ta/reportportal/core/tms/mapper/TmsTestCaseMapper.java +++ b/src/main/java/com/epam/ta/reportportal/core/tms/mapper/TmsTestCaseMapper.java @@ -1,14 +1,17 @@ package com.epam.ta.reportportal.core.tms.mapper; -import com.epam.ta.reportportal.entity.tms.TmsTestCase; -import com.epam.ta.reportportal.entity.tms.TmsTestCaseExecution; -import com.epam.ta.reportportal.entity.tms.TmsTestCaseVersion; -import com.epam.ta.reportportal.entity.tms.TmsTestFolder; import com.epam.ta.reportportal.core.tms.dto.TmsTestCaseRQ; import com.epam.ta.reportportal.core.tms.dto.TmsTestCaseRS; +import com.epam.ta.reportportal.core.tms.dto.batch.BatchTestCaseOperationError; +import com.epam.ta.reportportal.core.tms.dto.batch.BatchTestCaseOperationResultRS; import com.epam.ta.reportportal.core.tms.mapper.config.CommonMapperConfig; import com.epam.ta.reportportal.entity.project.Project; +import com.epam.ta.reportportal.entity.tms.TmsTestCase; +import com.epam.ta.reportportal.entity.tms.TmsTestCaseExecution; +import com.epam.ta.reportportal.entity.tms.TmsTestCaseVersion; +import com.epam.ta.reportportal.entity.tms.TmsTestFolder; import java.util.Collection; +import java.util.List; import java.util.Map; import org.mapstruct.BeanMapping; import org.mapstruct.Mapper; @@ -123,10 +126,21 @@ protected TmsTestFolder convertToTmsTestFolder(Long tmsTestFolderId, @Mapping(target = "testItems", ignore = true) @Mapping(target = "createdAt", ignore = true) @Mapping(target = "updatedAt", ignore = true) - @Mapping(target = "name", source = "originalTestCase.name") + @Mapping(target = "name", ignore = true) //unique name will be set later in service @Mapping(target = "priority", source = "originalTestCase.priority") @Mapping(target = "description", source = "originalTestCase.description") @Mapping(target = "testFolder", source = "targetFolder") public abstract TmsTestCase duplicateTestCase(TmsTestCase originalTestCase, TmsTestFolder targetFolder); + + public BatchTestCaseOperationResultRS toBatchOperationResult(List successfulIds, + List errors) { + BatchTestCaseOperationResultRS result = new BatchTestCaseOperationResultRS(); + result.setSuccessCount(successfulIds.size()); + result.setFailureCount(errors.size()); + result.setTotalCount(successfulIds.size() + errors.size()); + result.setErrors(errors); + result.setSuccessTestCaseIds(successfulIds); + return result; + } } diff --git a/src/main/java/com/epam/ta/reportportal/core/tms/mapper/TmsTestPlanAttributeMapper.java b/src/main/java/com/epam/ta/reportportal/core/tms/mapper/TmsTestPlanAttributeMapper.java index 17fbf1ef65..938e0a7553 100644 --- a/src/main/java/com/epam/ta/reportportal/core/tms/mapper/TmsTestPlanAttributeMapper.java +++ b/src/main/java/com/epam/ta/reportportal/core/tms/mapper/TmsTestPlanAttributeMapper.java @@ -1,5 +1,6 @@ package com.epam.ta.reportportal.core.tms.mapper; +import com.epam.ta.reportportal.entity.tms.TmsTestPlan; import com.epam.ta.reportportal.entity.tms.TmsTestPlanAttribute; import com.epam.ta.reportportal.core.tms.dto.TmsTestPlanAttributeRQ; import com.epam.ta.reportportal.core.tms.dto.TmsTestPlanAttributeRS; @@ -25,4 +26,13 @@ public abstract TmsTestPlanAttribute convertToTmsTestPlanAttribute( @Mapping(target = "value", source = "tmsTestPlanAttribute.value") public abstract TmsTestPlanAttributeRS convertTmsPlanAttributeRS( TmsTestPlanAttribute tmsTestPlanAttribute); -} + + @Mapping(target = "id.testPlanId", ignore = true) + @Mapping(target = "id.attributeId", source = "originalAttribute.id.attributeId") + @Mapping(target = "attribute", source = "originalAttribute.attribute") + @Mapping(target = "value", source = "originalAttribute.value") + @Mapping(target = "testPlan", source = "newTestPlan") + public abstract TmsTestPlanAttribute duplicateTestPlanAttribute( + TmsTestPlanAttribute originalAttribute, + TmsTestPlan newTestPlan); +} \ No newline at end of file diff --git a/src/main/java/com/epam/ta/reportportal/core/tms/mapper/TmsTestPlanExecutionMapper.java b/src/main/java/com/epam/ta/reportportal/core/tms/mapper/TmsTestPlanExecutionMapper.java index fdcafd0f4a..3ac8efea85 100644 --- a/src/main/java/com/epam/ta/reportportal/core/tms/mapper/TmsTestPlanExecutionMapper.java +++ b/src/main/java/com/epam/ta/reportportal/core/tms/mapper/TmsTestPlanExecutionMapper.java @@ -1,17 +1,18 @@ package com.epam.ta.reportportal.core.tms.mapper; +import com.epam.ta.reportportal.core.tms.dto.TmsTestPlanExecutionStatisticRS; import com.epam.ta.reportportal.core.tms.mapper.config.CommonMapperConfig; -import com.epam.ta.reportportal.entity.tms.TmsTestPlanExecutionStatisticRS; +import com.epam.ta.reportportal.entity.tms.TmsTestPlanExecutionStatistic; import org.mapstruct.Mapper; @Mapper(config = CommonMapperConfig.class) public interface TmsTestPlanExecutionMapper { TmsTestPlanExecutionStatisticRS toDto( - com.epam.ta.reportportal.entity.tms.TmsTestPlanExecutionStatisticRS executionStatistic); + TmsTestPlanExecutionStatistic executionStatistic); - default TmsTestPlanExecutionStatisticRS createEmptyStatistics() { - return TmsTestPlanExecutionStatisticRS.builder() + default TmsTestPlanExecutionStatistic createEmptyStatistics() { + return TmsTestPlanExecutionStatistic.builder() .total(0L) .covered(0L) .build(); diff --git a/src/main/java/com/epam/ta/reportportal/core/tms/mapper/TmsTestPlanMapper.java b/src/main/java/com/epam/ta/reportportal/core/tms/mapper/TmsTestPlanMapper.java index 29e9737d12..5896765a9f 100644 --- a/src/main/java/com/epam/ta/reportportal/core/tms/mapper/TmsTestPlanMapper.java +++ b/src/main/java/com/epam/ta/reportportal/core/tms/mapper/TmsTestPlanMapper.java @@ -1,12 +1,15 @@ package com.epam.ta.reportportal.core.tms.mapper; -import com.epam.ta.reportportal.entity.tms.TmsTestPlan; +import com.epam.ta.reportportal.core.tms.dto.DuplicateTmsTestPlanRS; +import com.epam.ta.reportportal.core.tms.dto.TmsTestPlanExecutionStatisticRS; import com.epam.ta.reportportal.core.tms.dto.TmsTestPlanRQ; import com.epam.ta.reportportal.core.tms.dto.TmsTestPlanRS; -import com.epam.ta.reportportal.core.tms.dto.batch.BatchOperationError; -import com.epam.ta.reportportal.core.tms.dto.batch.BatchOperationResultRS; +import com.epam.ta.reportportal.core.tms.dto.batch.BatchTestCaseOperationError; +import com.epam.ta.reportportal.core.tms.dto.batch.BatchTestCaseOperationResultRS; import com.epam.ta.reportportal.core.tms.mapper.config.CommonMapperConfig; +import com.epam.ta.reportportal.entity.tms.TmsTestPlan; import com.epam.ta.reportportal.entity.tms.TmsTestPlanWithStatistic; +import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import org.mapstruct.BeanMapping; @@ -19,7 +22,9 @@ import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; -@Mapper(config = CommonMapperConfig.class, uses = TmsTestPlanAttributeMapper.class) +@Mapper(config = CommonMapperConfig.class, uses = { + TmsTestPlanAttributeMapper.class, TmsTestPlanExecutionMapper.class +}) public abstract class TmsTestPlanMapper { @Mapping(target = "attributes", source = "attributes") @@ -30,7 +35,8 @@ public abstract class TmsTestPlanMapper { @Mapping(target = "description", source = "tmsTestPlan.testPlan.description") @Mapping(target = "attributes", source = "tmsTestPlan.testPlan.attributes") @Mapping(target = "executionStatistic", source = "tmsTestPlan.executionStatistic") - public abstract TmsTestPlanRS convertTmsTestPlanWithStatisticToRS(TmsTestPlanWithStatistic tmsTestPlan); + public abstract TmsTestPlanRS convertTmsTestPlanWithStatisticToRS( + TmsTestPlanWithStatistic tmsTestPlan); @Mapping(target = "project.id", source = "projectId") @Mapping(target = "attributes", ignore = true) @@ -57,9 +63,9 @@ public Page convertToRS(Page testPlansByCriteria) { testPlansByCriteria.getTotalElements()); } - public BatchOperationResultRS convertToRS(int totalCount, int successCount, - List errors) { - return BatchOperationResultRS.builder() + public BatchTestCaseOperationResultRS convertToRS(int totalCount, int successCount, + List errors) { + return BatchTestCaseOperationResultRS.builder() .totalCount(totalCount) .successCount(successCount) .failureCount(totalCount - successCount) @@ -80,4 +86,62 @@ public Page convertTmsTestPlanWithStatisticToRS( totalCount ); } + + @Mapping(target = "id", ignore = true) + @Mapping(target = "searchVector", ignore = true) + @Mapping(target = "attributes", ignore = true) + @Mapping(target = "milestones", ignore = true) + @Mapping(target = "launches", ignore = true) + @Mapping(target = "testCases", ignore = true) + public abstract TmsTestPlan duplicateTestPlan(TmsTestPlan originalTestPlan); + + @Mapping(target = "duplicationStatistic", ignore = true) + public abstract DuplicateTmsTestPlanRS toDuplicateTmsTestPlanRS(TmsTestPlan testPlan); + + public DuplicateTmsTestPlanRS buildDuplicateTestPlanResponse(TmsTestPlan testPlan, + BatchTestCaseOperationResultRS duplicationStatistic) { + var response = toDuplicateTmsTestPlanRS(testPlan); + response.setDuplicationStatistic(duplicationStatistic); + response.setExecutionStatistic(TmsTestPlanExecutionStatisticRS + .builder() + .covered(0L) + .total(Long.valueOf(duplicationStatistic.getTotalCount())) + .build()); + return response; + } + + public BatchTestCaseOperationResultRS combineDuplicateTestPlanBatchResults( + BatchTestCaseOperationResultRS duplicateTestCasesResult, + BatchTestCaseOperationResultRS addTestCasesToPlanResult) { + var allErrors = new ArrayList(); + if (duplicateTestCasesResult.getErrors() != null) { + allErrors.addAll(duplicateTestCasesResult.getErrors()); + } + if (addTestCasesToPlanResult.getErrors() != null) { + allErrors.addAll(addTestCasesToPlanResult.getErrors()); + } + + var result = new BatchTestCaseOperationResultRS(); + result.setSuccessCount( + addTestCasesToPlanResult.getSuccessCount()); // Only truly successful are those added to plan + result.setFailureCount(duplicateTestCasesResult.getFailureCount() + addTestCasesToPlanResult.getFailureCount()); + result.setErrors(allErrors); + result.setTotalCount(duplicateTestCasesResult.getTotalCount()); + return result; + } + + public BatchTestCaseOperationResultRS createFailedBatchResult(List failedIds, + String errorMessage) { + var errors = failedIds + .stream() + .map(id -> new BatchTestCaseOperationError(id, errorMessage)) + .collect(Collectors.toList()); + + var result = new BatchTestCaseOperationResultRS(); + result.setSuccessCount(0); + result.setFailureCount(failedIds.size()); + result.setErrors(errors); + result.setTotalCount(failedIds.size()); + return result; + } } diff --git a/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestCaseService.java b/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestCaseService.java index 0b9d9a4d86..1cd7818db3 100644 --- a/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestCaseService.java +++ b/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestCaseService.java @@ -5,6 +5,7 @@ import com.epam.ta.reportportal.core.tms.dto.TmsTestCaseRS; import com.epam.ta.reportportal.core.tms.dto.batch.BatchDeleteTestCasesRQ; import com.epam.ta.reportportal.core.tms.dto.batch.BatchDuplicateTestCasesRQ; +import com.epam.ta.reportportal.core.tms.dto.batch.BatchTestCaseOperationResultRS; import com.epam.ta.reportportal.core.tms.dto.batch.BatchPatchTestCaseAttributesRQ; import com.epam.ta.reportportal.core.tms.dto.batch.BatchPatchTestCasesRQ; import com.epam.ta.reportportal.model.Page; @@ -73,4 +74,6 @@ void exportToFile(Long projectId, List ids, String format, boolean include * @return A list of data transfer objects containing details of the duplicated test cases. */ List duplicate(long projectId, BatchDuplicateTestCasesRQ duplicateRequest); + + BatchTestCaseOperationResultRS duplicateTestCases(long projectId, List originalTestCaseIds); } diff --git a/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestCaseServiceImpl.java b/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestCaseServiceImpl.java index ed33eed295..4660ca051e 100644 --- a/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestCaseServiceImpl.java +++ b/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestCaseServiceImpl.java @@ -5,24 +5,27 @@ import com.epam.reportportal.rules.exception.ErrorType; import com.epam.reportportal.rules.exception.ReportPortalException; import com.epam.ta.reportportal.commons.querygen.Filter; -import com.epam.ta.reportportal.dao.tms.TmsTestCaseRepository; -import com.epam.ta.reportportal.dao.tms.TmsTestPlanTestCaseRepository; import com.epam.ta.reportportal.core.tms.dto.NewTestFolderRQ; import com.epam.ta.reportportal.core.tms.dto.TmsTestCaseRQ; import com.epam.ta.reportportal.core.tms.dto.TmsTestCaseRS; import com.epam.ta.reportportal.core.tms.dto.batch.BatchDeleteTestCasesRQ; import com.epam.ta.reportportal.core.tms.dto.batch.BatchDuplicateTestCasesRQ; +import com.epam.ta.reportportal.core.tms.dto.batch.BatchTestCaseOperationError; +import com.epam.ta.reportportal.core.tms.dto.batch.BatchTestCaseOperationResultRS; import com.epam.ta.reportportal.core.tms.dto.batch.BatchPatchTestCaseAttributesRQ; import com.epam.ta.reportportal.core.tms.dto.batch.BatchPatchTestCasesRQ; import com.epam.ta.reportportal.core.tms.mapper.TmsTestCaseMapper; import com.epam.ta.reportportal.core.tms.mapper.factory.TmsTestCaseExporterFactory; import com.epam.ta.reportportal.core.tms.mapper.factory.TmsTestCaseImporterFactory; +import com.epam.ta.reportportal.dao.tms.TmsTestCaseRepository; +import com.epam.ta.reportportal.dao.tms.TmsTestPlanTestCaseRepository; import com.epam.ta.reportportal.dao.tms.filterable.TmsTestCaseFilterableRepository; import com.epam.ta.reportportal.entity.tms.TmsTestCase; import com.epam.ta.reportportal.model.Page; import com.epam.ta.reportportal.ws.converter.PagedResourcesAssembler; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; +import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -105,7 +108,8 @@ public TmsTestCaseRS create(long projectId, TmsTestCaseRQ tmsTestCaseRQ) { tmsTestCaseRepository.save(tmsTestCase); if (CollectionUtils.isNotEmpty(tmsTestCaseRQ.getAttributes())) { - tmsTestCaseAttributeService.createTestCaseAttributes(tmsTestCase, tmsTestCaseRQ.getAttributes()); + tmsTestCaseAttributeService.createTestCaseAttributes(tmsTestCase, + tmsTestCaseRQ.getAttributes()); } var defaultVersion = tmsTestCaseVersionService.createDefaultTestCaseVersion(tmsTestCase, @@ -260,7 +264,8 @@ public void exportToFile(Long projectId, List testCaseIds, String format, @Override @Transactional(readOnly = true) - public Page getTestCasesByCriteria(long projectId, Filter filter, Pageable pageable) { + public Page getTestCasesByCriteria(long projectId, Filter filter, + Pageable pageable) { var testCaseIds = tmsTestCaseFilterableRepository.findIdsByProjectIdAndFilter( projectId, filter, pageable ); @@ -385,6 +390,53 @@ public List duplicate(long projectId, BatchDuplicateTestCasesRQ d .toList(); } + @Override + @Transactional + public BatchTestCaseOperationResultRS duplicateTestCases(long projectId, List testCaseIds) { + var errors = new ArrayList(); + var successfulIds = new ArrayList(); + + for (var testCaseId : testCaseIds) { + try { + var originalTestCase = tmsTestCaseRepository + .findByProjectIdAndId(projectId, testCaseId) + .orElseThrow(() -> new ReportPortalException( + NOT_FOUND, TEST_CASE_NOT_FOUND_BY_ID.formatted(testCaseId, projectId)) + ); + + var originalDefaultVersion = tmsTestCaseVersionService.getDefaultVersion(testCaseId); + + var duplicatedTestCase = tmsTestCaseMapper.duplicateTestCase( + originalTestCase, originalTestCase.getTestFolder() + ); + + duplicatedTestCase.setName(generateUniqueTestCaseName( + projectId, + originalTestCase.getName(), + originalTestCase.getTestFolder().getId()) + ); + + duplicatedTestCase = tmsTestCaseRepository.save(duplicatedTestCase); + + tmsTestCaseVersionService.duplicateDefaultVersion(duplicatedTestCase, + originalDefaultVersion); + + if (CollectionUtils.isNotEmpty(originalTestCase.getAttributes())) { + tmsTestCaseAttributeService.duplicateTestCaseAttributes(originalTestCase, + duplicatedTestCase); + } + + successfulIds.add(duplicatedTestCase.getId()); + + } catch (Exception e) { + errors.add(new BatchTestCaseOperationError(testCaseId, + "Failed to duplicate test case: " + e.getMessage())); + } + } + + return tmsTestCaseMapper.toBatchOperationResult(successfulIds, errors); + } + private Long getTestFolderId(long projectId, Long testFolderId, NewTestFolderRQ testFolderRQ) { if (Objects.isNull(testFolderId) && Objects.isNull(testFolderRQ)) { @@ -416,10 +468,15 @@ public TmsTestCaseRS duplicateTestCase(long projectId, Long testCaseId, Long tar var targetFolder = tmsTestFolderService.getEntityById(projectId, targetFolderId); - var duplicatedTestCase = tmsTestCaseRepository.save( - tmsTestCaseMapper.duplicateTestCase(originalTestCase, targetFolder) + var duplicatedTestCase = tmsTestCaseMapper.duplicateTestCase(originalTestCase, targetFolder); + + duplicatedTestCase.setName(generateUniqueTestCaseName( + projectId, originalTestCase.getName(), + originalTestCase.getTestFolder().getId()) ); + duplicatedTestCase = tmsTestCaseRepository.save(duplicatedTestCase); + var duplicatedDefaultVersion = tmsTestCaseVersionService.duplicateDefaultVersion( duplicatedTestCase, originalDefaultVersion); @@ -445,4 +502,18 @@ public List getExistingTestCaseIds(Long projectId, List testCaseIds) } return tmsTestCaseRepository.findExistingTestCaseIds(projectId, testCaseIds); } + + private String generateUniqueTestCaseName(long projectId, String originalName, Long testFolderId) { + var baseName = originalName + "-copy"; + var uniqueName = baseName; + var counter = 1; + + while (tmsTestCaseRepository.existsByNameAndTestFolder(projectId, uniqueName, testFolderId)) { + uniqueName = baseName + "-" + counter; + counter++; + } + + return uniqueName; + } + } diff --git a/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanAttributeService.java b/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanAttributeService.java index fd55b01087..ecb14d9856 100644 --- a/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanAttributeService.java +++ b/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanAttributeService.java @@ -15,4 +15,13 @@ void patchTestPlanAttributes(TmsTestPlan existingTestPlan, List attributes); void deleteAllByTestPlanId(Long testPlanId); -} + + /** + * Duplicates attributes from original test plan to new test plan. + * Uses existing TmsAttribute entities but creates new TmsTestPlanAttribute associations. + * + * @param originalTestPlan The original test plan with attributes to duplicate. + * @param newTestPlan The new test plan to attach duplicated attributes to. + */ + void duplicateTestPlanAttributes(TmsTestPlan originalTestPlan, TmsTestPlan newTestPlan); +} \ No newline at end of file diff --git a/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanAttributeServiceImpl.java b/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanAttributeServiceImpl.java index be3a9f7695..049ce18283 100644 --- a/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanAttributeServiceImpl.java +++ b/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanAttributeServiceImpl.java @@ -7,7 +7,9 @@ import com.epam.ta.reportportal.core.tms.dto.TmsTestPlanAttributeRQ; import com.epam.ta.reportportal.core.tms.mapper.TmsTestPlanAttributeMapper; import java.util.List; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -37,7 +39,10 @@ public void createTestPlanAttributes(TmsTestPlan tmsTestPlan, @Transactional public void updateTestPlanAttributes(TmsTestPlan existingTestPlan, List attributes) { - tmsTestPlanAttributeRepository.deleteAllByTestPlanId(existingTestPlan.getId()); + if (CollectionUtils.isNotEmpty(existingTestPlan.getAttributes())) { + tmsTestPlanAttributeRepository.deleteAll(existingTestPlan.getAttributes()); + existingTestPlan.getAttributes().clear(); + } createTestPlanAttributes(existingTestPlan, attributes); } @@ -61,4 +66,23 @@ public void patchTestPlanAttributes(TmsTestPlan existingTestPlan, public void deleteAllByTestPlanId(Long testPlanId) { tmsTestPlanAttributeRepository.deleteAllByTestPlanId(testPlanId); } + + @Override + @Transactional + public void duplicateTestPlanAttributes(TmsTestPlan originalTestPlan, TmsTestPlan newTestPlan) { + if (isEmpty(originalTestPlan.getAttributes())) { + return; + } + + var newTestPlanAttributes = originalTestPlan + .getAttributes() + .stream() + .map(originalAttr -> tmsTestPlanAttributeMapper.duplicateTestPlanAttribute( + originalAttr, newTestPlan)) + .collect(Collectors.toSet()); + + newTestPlan.setAttributes(newTestPlanAttributes); + newTestPlanAttributes.forEach(attr -> attr.setTestPlan(newTestPlan)); + tmsTestPlanAttributeRepository.saveAll(newTestPlanAttributes); + } } diff --git a/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanExecutionService.java b/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanExecutionService.java index 1c56bf81db..ac67146b94 100644 --- a/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanExecutionService.java +++ b/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanExecutionService.java @@ -1,12 +1,12 @@ package com.epam.ta.reportportal.core.tms.service; import com.epam.ta.reportportal.entity.tms.TmsTestPlan; -import com.epam.ta.reportportal.entity.tms.TmsTestPlanExecutionStatisticRS; +import com.epam.ta.reportportal.entity.tms.TmsTestPlanExecutionStatistic; import com.epam.ta.reportportal.entity.tms.TmsTestPlanWithStatistic; public interface TmsTestPlanExecutionService { - TmsTestPlanExecutionStatisticRS getStatisticsForTestPlan(Long testPlanId); + TmsTestPlanExecutionStatistic getStatisticsForTestPlan(Long testPlanId); TmsTestPlanWithStatistic enrichWithStatistics(TmsTestPlan testPlan); } diff --git a/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanExecutionServiceImpl.java b/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanExecutionServiceImpl.java index d793354017..88bc264e86 100644 --- a/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanExecutionServiceImpl.java +++ b/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanExecutionServiceImpl.java @@ -3,7 +3,7 @@ import com.epam.ta.reportportal.core.tms.mapper.TmsTestPlanExecutionMapper; import com.epam.ta.reportportal.dao.tms.TmsTestPlanStatisticsRepository; import com.epam.ta.reportportal.entity.tms.TmsTestPlan; -import com.epam.ta.reportportal.entity.tms.TmsTestPlanExecutionStatisticRS; +import com.epam.ta.reportportal.entity.tms.TmsTestPlanExecutionStatistic; import com.epam.ta.reportportal.entity.tms.TmsTestPlanWithStatistic; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -20,7 +20,7 @@ public class TmsTestPlanExecutionServiceImpl implements TmsTestPlanExecutionServ @Override @Transactional(readOnly = true) - public TmsTestPlanExecutionStatisticRS getStatisticsForTestPlan(Long testPlanId) { + public TmsTestPlanExecutionStatistic getStatisticsForTestPlan(Long testPlanId) { log.debug("Loading execution statistics for test plan: {}", testPlanId); try { @@ -32,12 +32,10 @@ public TmsTestPlanExecutionStatisticRS getStatisticsForTestPlan(Long testPlanId) return tmsTestPlanExecutionMapper.createEmptyStatistics(); } - var result = tmsTestPlanExecutionMapper.toDto(statistics); - log.debug("Loaded statistics for test plan {}: total={}, covered={}", - testPlanId, result.getTotal(), result.getCovered()); + testPlanId, statistics.getTotal(), statistics.getCovered()); - return result; + return statistics; } catch (Exception e) { log.error("Failed to load statistics for test plan {}", testPlanId, e); return tmsTestPlanExecutionMapper.createEmptyStatistics(); diff --git a/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanService.java b/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanService.java index c953e831d4..4a257ac985 100644 --- a/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanService.java +++ b/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanService.java @@ -1,9 +1,10 @@ package com.epam.ta.reportportal.core.tms.service; import com.epam.ta.reportportal.commons.querygen.Filter; +import com.epam.ta.reportportal.core.tms.dto.DuplicateTmsTestPlanRS; import com.epam.ta.reportportal.core.tms.dto.TmsTestPlanRQ; import com.epam.ta.reportportal.core.tms.dto.TmsTestPlanRS; -import com.epam.ta.reportportal.core.tms.dto.batch.BatchOperationResultRS; +import com.epam.ta.reportportal.core.tms.dto.batch.BatchTestCaseOperationResultRS; import com.epam.ta.reportportal.model.Page; import jakarta.validation.constraints.NotEmpty; import java.util.List; @@ -13,11 +14,13 @@ public interface TmsTestPlanService extends CrudService getByCriteria(Long projectId, Filter filter, Pageable pageable); - BatchOperationResultRS addTestCasesToPlan(Long projectId, Long testPlanId, @NotEmpty List testCaseIds); + BatchTestCaseOperationResultRS addTestCasesToPlan(Long projectId, Long testPlanId, @NotEmpty List testCaseIds); - BatchOperationResultRS removeTestCasesFromPlan(Long projectId, Long testPlanId, @NotEmpty List testCaseIds); + BatchTestCaseOperationResultRS removeTestCasesFromPlan(Long projectId, Long testPlanId, @NotEmpty List testCaseIds); boolean addTestCaseToTestPlan(Long testPlanId, Long testCaseId); boolean removeSingleTestCaseFromPlan(Long testPlanId, Long testCaseId); + + DuplicateTmsTestPlanRS duplicate(Long projectId, Long testPlanId); } diff --git a/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanServiceImpl.java b/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanServiceImpl.java index c5e9b97225..12e013c786 100644 --- a/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanServiceImpl.java +++ b/src/main/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanServiceImpl.java @@ -4,16 +4,18 @@ import com.epam.reportportal.rules.exception.ReportPortalException; import com.epam.ta.reportportal.commons.querygen.Filter; +import com.epam.ta.reportportal.core.tms.dto.DuplicateTmsTestPlanRS; import com.epam.ta.reportportal.core.tms.dto.TmsTestPlanRQ; import com.epam.ta.reportportal.core.tms.dto.TmsTestPlanRS; -import com.epam.ta.reportportal.core.tms.dto.batch.BatchOperationError; -import com.epam.ta.reportportal.core.tms.dto.batch.BatchOperationResultRS; +import com.epam.ta.reportportal.core.tms.dto.batch.BatchTestCaseOperationError; +import com.epam.ta.reportportal.core.tms.dto.batch.BatchTestCaseOperationResultRS; import com.epam.ta.reportportal.core.tms.mapper.TmsTestPlanMapper; +import com.epam.ta.reportportal.dao.tms.TmsTestCaseRepository; import com.epam.ta.reportportal.dao.tms.TmsTestPlanRepository; import com.epam.ta.reportportal.dao.tms.TmsTestPlanTestCaseRepository; import com.epam.ta.reportportal.dao.tms.filterable.TmsTestPlanFilterableRepository; import com.epam.ta.reportportal.entity.tms.TmsTestPlan; -import com.epam.ta.reportportal.entity.tms.TmsTestPlanExecutionStatisticRS; +import com.epam.ta.reportportal.entity.tms.TmsTestPlanExecutionStatistic; import com.epam.ta.reportportal.entity.tms.TmsTestPlanWithStatistic; import com.epam.ta.reportportal.model.Page; import com.epam.ta.reportportal.ws.converter.PagedResourcesAssembler; @@ -46,6 +48,7 @@ public class TmsTestPlanServiceImpl implements TmsTestPlanService { private final TmsTestPlanAttributeService tmsTestPlanAttributeService; private final TmsTestPlanTestCaseRepository tmsTestPlanTestCaseRepository; private final TmsTestCaseService tmsTestCaseService; + private final TmsTestCaseRepository tmsTestCaseRepository; private final TmsTestPlanExecutionService tmsTestPlanExecutionService; @Override @@ -72,7 +75,7 @@ public TmsTestPlanRS create(long projectId, TmsTestPlanRQ testPlanRQ) { return tmsTestPlanMapper.convertTmsTestPlanWithStatisticToRS( TmsTestPlanWithStatistic.of( tmsTestPlan, - new TmsTestPlanExecutionStatisticRS(0, 0) //TODO fix that + new TmsTestPlanExecutionStatistic(0, 0) //TODO fix that ) ); } @@ -149,12 +152,13 @@ public Page getByCriteria(Long projectId, Filter filter, Pageable return PagedResourcesAssembler .pageConverter() .apply(tmsTestPlanMapper - .convertTmsTestPlanWithStatisticToRS(orderedTestPlans, pageable, testPlanIds.getTotalElements())); + .convertTmsTestPlanWithStatisticToRS(orderedTestPlans, pageable, + testPlanIds.getTotalElements())); } @Override @Transactional - public BatchOperationResultRS addTestCasesToPlan(Long projectId, Long testPlanId, + public BatchTestCaseOperationResultRS addTestCasesToPlan(Long projectId, Long testPlanId, @NotEmpty List testCaseIds) { if (!testPlanRepository.existsByIdAndProject_Id(testPlanId, projectId)) { throw new ReportPortalException( @@ -162,7 +166,7 @@ public BatchOperationResultRS addTestCasesToPlan(Long projectId, Long testPlanId ); } - var errors = new ArrayList(); + var errors = new ArrayList(); var successCount = 0; var totalCount = testCaseIds.size(); @@ -180,14 +184,14 @@ public BatchOperationResultRS addTestCasesToPlan(Long projectId, Long testPlanId try { // Check if a test case exists if (!existingTestCaseIds.contains(testCaseId)) { - errors.add(new BatchOperationError(testCaseId, + errors.add(new BatchTestCaseOperationError(testCaseId, String.format("Test case with id %s not found", testCaseId))); continue; } // Check if already added to the plan if (testCaseIdsAreInTestPlan.contains(testCaseId)) { - errors.add(new BatchOperationError( + errors.add(new BatchTestCaseOperationError( testCaseId, String.format("Test case with id %s already exists in test plan", testCaseId) )); @@ -199,10 +203,10 @@ public BatchOperationResultRS addTestCasesToPlan(Long projectId, Long testPlanId successCount++; testCaseIdsAreInTestPlan.add(testCaseId); } else { - errors.add(new BatchOperationError(testCaseId, "Failed to add test case")); + errors.add(new BatchTestCaseOperationError(testCaseId, "Failed to add test case")); } } catch (Exception e) { - errors.add(new BatchOperationError(testCaseId, e.getMessage())); + errors.add(new BatchTestCaseOperationError(testCaseId, e.getMessage())); } } @@ -211,9 +215,9 @@ public BatchOperationResultRS addTestCasesToPlan(Long projectId, Long testPlanId @Override @Transactional - public BatchOperationResultRS removeTestCasesFromPlan(Long projectId, Long testPlanId, + public BatchTestCaseOperationResultRS removeTestCasesFromPlan(Long projectId, Long testPlanId, List testCaseIds) { - var errors = new ArrayList(); + var errors = new ArrayList(); var successCount = 0; var totalCount = testCaseIds.size(); @@ -226,7 +230,7 @@ public BatchOperationResultRS removeTestCasesFromPlan(Long projectId, Long testP try { // Check if a test case is in the plan if (!testCaseIdsInTestPlan.contains(testCaseId)) { - errors.add(new BatchOperationError(testCaseId, + errors.add(new BatchTestCaseOperationError(testCaseId, String.format("Test case with id %s not found in test plan", testCaseId))); continue; } @@ -236,12 +240,12 @@ public BatchOperationResultRS removeTestCasesFromPlan(Long projectId, Long testP successCount++; testCaseIdsInTestPlan.remove(testCaseId); } else { - errors.add(new BatchOperationError( + errors.add(new BatchTestCaseOperationError( testCaseId, "Failed to remove test case") ); } } catch (Exception e) { - errors.add(new BatchOperationError(testCaseId, e.getMessage())); + errors.add(new BatchTestCaseOperationError(testCaseId, e.getMessage())); } } @@ -260,6 +264,64 @@ public boolean addTestCaseToTestPlan(Long testPlanId, Long testCaseId) { } } + @Override + @Transactional + public DuplicateTmsTestPlanRS duplicate(Long projectId, Long testPlanId) { + // Get original test plan + var originalTestPlan = testPlanRepository + .findByIdAndProjectId(testPlanId, projectId) + .orElseThrow(() -> new ReportPortalException( + NOT_FOUND, TMS_TEST_PLAN_NOT_FOUND_BY_ID.formatted(testPlanId, projectId)) + ); + + // Duplicate test plan entity + var duplicatedTestPlan = tmsTestPlanMapper.duplicateTestPlan(originalTestPlan); + + duplicatedTestPlan = testPlanRepository.save(duplicatedTestPlan); + + // Duplicate test plan attributes + tmsTestPlanAttributeService.duplicateTestPlanAttributes(originalTestPlan, duplicatedTestPlan); + + // Get test case IDs from the original plan + var originalTestCaseIds = tmsTestPlanTestCaseRepository.findTestCaseIdsByTestPlanId( + originalTestPlan.getId()); + + // Process test case duplication and addition to plan + var duplicateTestCasesStatistic = processBatchTestCaseDuplication(projectId, duplicatedTestPlan.getId(), + originalTestCaseIds); + + return tmsTestPlanMapper.buildDuplicateTestPlanResponse(duplicatedTestPlan, duplicateTestCasesStatistic); + } + + private BatchTestCaseOperationResultRS processBatchTestCaseDuplication(long projectId, Long newTestPlanId, + List originalTestCaseIds) { + if (originalTestCaseIds.isEmpty()) { + return tmsTestPlanMapper.createFailedBatchResult(Collections.emptyList(), + "No test cases to duplicate"); + } + + // Step 1: Duplicate test cases in batch + var duplicationResult = tmsTestCaseService.duplicateTestCases(projectId, originalTestCaseIds); + + // Step 2: Add successfully duplicated test cases to the new plan + if (!duplicationResult.getSuccessTestCaseIds().isEmpty()) { + try { + var addToPlanResult = addTestCasesToPlan(projectId, newTestPlanId, + duplicationResult.getSuccessTestCaseIds()); + return tmsTestPlanMapper.combineDuplicateTestPlanBatchResults(duplicationResult, addToPlanResult); + } catch (Exception e) { + // If adding to plan fails completely, mark all duplicated test cases as failed + var failedAddResult = tmsTestPlanMapper.createFailedBatchResult( + duplicationResult.getSuccessTestCaseIds(), + "Failed to add duplicated test case to plan: " + e.getMessage() + ); + return tmsTestPlanMapper.combineDuplicateTestPlanBatchResults(duplicationResult, failedAddResult); + } + } + + return duplicationResult; + } + @Override @Transactional public boolean removeSingleTestCaseFromPlan(Long testPlanId, Long testCaseId) { diff --git a/src/test/java/com/epam/ta/reportportal/core/tms/controller/integration/TmsTestPlanIntegrationTest.java b/src/test/java/com/epam/ta/reportportal/core/tms/controller/integration/TmsTestPlanIntegrationTest.java index e083c2250a..d7a7e0c558 100644 --- a/src/test/java/com/epam/ta/reportportal/core/tms/controller/integration/TmsTestPlanIntegrationTest.java +++ b/src/test/java/com/epam/ta/reportportal/core/tms/controller/integration/TmsTestPlanIntegrationTest.java @@ -2,6 +2,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -11,24 +13,36 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import com.epam.ta.reportportal.entity.tms.TmsTestPlan; -import com.epam.ta.reportportal.dao.tms.TmsTestPlanRepository; +import com.epam.ta.reportportal.core.tms.dto.DuplicateTmsTestPlanRS; import com.epam.ta.reportportal.core.tms.dto.TmsTestPlanAttributeRQ; import com.epam.ta.reportportal.core.tms.dto.TmsTestPlanRQ; import com.epam.ta.reportportal.core.tms.dto.batch.BatchAddTestCasesToPlanRQ; import com.epam.ta.reportportal.core.tms.dto.batch.BatchRemoveTestCasesFromPlanRQ; +import com.epam.ta.reportportal.dao.tms.TmsTestCaseAttributeRepository; +import com.epam.ta.reportportal.dao.tms.TmsTestCaseRepository; +import com.epam.ta.reportportal.dao.tms.TmsTestPlanRepository; +import com.epam.ta.reportportal.dao.tms.TmsTestPlanTestCaseRepository; +import com.epam.ta.reportportal.entity.tms.TmsTestCase; +import com.epam.ta.reportportal.entity.tms.TmsTestCaseAttribute; +import com.epam.ta.reportportal.entity.tms.TmsTestPlan; import com.epam.ta.reportportal.ws.BaseMvcTest; import com.fasterxml.jackson.databind.ObjectMapper; +import com.jayway.jsonpath.JsonPath; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Optional; +import java.util.Set; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.web.servlet.MvcResult; /** * @author Konstantin Shaplyko @@ -38,7 +52,18 @@ public class TmsTestPlanIntegrationTest extends BaseMvcTest { private static final String SUPERADMIN_PROJECT_KEY = "superadmin_personal"; + private final ObjectMapper objectMapper = new ObjectMapper(); + + @PersistenceContext + private EntityManager entityManager; + + @Autowired + private TmsTestCaseAttributeRepository tmsTestCaseAttributeRepository; + @Autowired + private TmsTestCaseRepository testCaseRepository; + @Autowired + private TmsTestPlanTestCaseRepository tmsTestPlanTestCaseRepository; @Autowired private TmsTestPlanRepository testPlanRepository; @@ -80,7 +105,7 @@ void getTestPlansByCriteriaIntegrationTest() throws Exception { .with(token(oAuthHelper.getSuperadminToken()))) .andExpect(status().isOk()) .andExpect(jsonPath("$.content").isArray()) - .andExpect(jsonPath("$.content.length()").value(6)); + .andExpect(jsonPath("$.content.length()").value(9)); } @Test @@ -140,7 +165,7 @@ void getTestPlansByCriteriaWithoutSearchParameterIntegrationTest() throws Except .with(token(oAuthHelper.getSuperadminToken()))) .andExpect(status().isOk()) .andExpect(jsonPath("$.content").isArray()) - .andExpect(jsonPath("$.content.length()").value(6)); + .andExpect(jsonPath("$.content.length()").value(9)); } @Test @@ -195,8 +220,8 @@ void deleteTestPlanIntegrationTest() throws Exception { @Test void patchTestPlanTest() throws Exception { TmsTestPlanAttributeRQ attributQ = new TmsTestPlanAttributeRQ(); - attributQ.setValue("value5"); - attributQ.setId(5L); + attributQ.setValue("value6"); + attributQ.setId(6L); TmsTestPlanRQ tmsTestPlan = new TmsTestPlanRQ(); tmsTestPlan.setName("updated_name5"); @@ -520,7 +545,8 @@ void patchTestPlanWithExecutionStatisticIntegrationTest() throws Exception { .with(token(oAuthHelper.getSuperadminToken()))) .andExpect(status().isOk()) .andExpect(jsonPath("$.id").value(102L)) - .andExpect(jsonPath("$.description").value("Patched description for test plan with executions")) + .andExpect( + jsonPath("$.description").value("Patched description for test plan with executions")) .andExpect(jsonPath("$.executionStatistic").exists()) .andExpect(jsonPath("$.executionStatistic.total").value(3)) .andExpect(jsonPath("$.executionStatistic.covered").value(2)); @@ -537,6 +563,230 @@ void getTestPlanWithMixedExecutionStatusesIntegrationTest() throws Exception { .andExpect(status().isOk()) .andExpect(jsonPath("$.id").value(102L)) .andExpect(jsonPath("$.executionStatistic.total").value(3)) - .andExpect(jsonPath("$.executionStatistic.covered").value(2)); // Only PASSED and FAILED count + .andExpect( + jsonPath("$.executionStatistic.covered").value(2)); // Only PASSED and FAILED count + } + + @Test + void duplicateTestPlanIntegrationTest() throws Exception { + // Given - test plan 1 exists with 3 test cases (4, 5, 6) and attributes + long originalTestPlanId = 1L; + + // When - duplicate test plan + mockMvc.perform( + post("/v1/project/" + SUPERADMIN_PROJECT_KEY + "/tms/test-plan/" + originalTestPlanId) + .contentType(MediaType.APPLICATION_JSON) + .with(token(oAuthHelper.getSuperadminToken()))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").exists()) + .andExpect(jsonPath("$.id").isNumber()) + .andExpect(jsonPath("$.name").value("Test Plan 1")) + .andExpect(jsonPath("$.description").value("Description for test plan 1")) + .andExpect(jsonPath("$.executionStatistic").exists()) + .andExpect(jsonPath("$.executionStatistic.total").value(3)) + .andExpect(jsonPath("$.executionStatistic.covered").value(0)) + .andExpect(jsonPath("$.duplicationStatistic").exists()) + .andExpect(jsonPath("$.duplicationStatistic.totalCount").value(3)) + .andExpect(jsonPath("$.duplicationStatistic.successCount").value(3)) + .andExpect(jsonPath("$.duplicationStatistic.failureCount").value(0)) + .andExpect(jsonPath("$.duplicationStatistic.errors").isArray()) + .andExpect(jsonPath("$.duplicationStatistic.errors.length()").value(0)); + } + + @Test + void duplicateTestPlanWithAttributesIntegrationTest() throws Exception { + // Given - test plan with attributes exists + long originalTestPlanId = 4L; // has environment_id and product_version_id + + // When - duplicate test plan + MvcResult result = mockMvc.perform( + post("/v1/project/" + SUPERADMIN_PROJECT_KEY + "/tms/test-plan/" + originalTestPlanId) + .contentType(MediaType.APPLICATION_JSON) + .with(token(oAuthHelper.getSuperadminToken()))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").exists()) + .andExpect(jsonPath("$.name").value("name4")) + .andExpect(jsonPath("$.description").value("description4")) + .andExpect(jsonPath("$.attributes").exists()) + .andReturn(); + + entityManager.clear(); + + // Then - verify duplicated test plan has correct attributes + String content = result.getResponse().getContentAsString(); + var duplicatedTestPlanResponse = objectMapper.readValue(content, DuplicateTmsTestPlanRS.class); + var duplicatedTestPlanId = duplicatedTestPlanResponse.getId(); + + Optional duplicatedTestPlan = testPlanRepository.findById(duplicatedTestPlanId); + assertTrue(duplicatedTestPlan.isPresent()); + assertEquals("name4", duplicatedTestPlan.get().getName()); + assertEquals("description4", duplicatedTestPlan.get().getDescription()); + } + + @Test + void duplicateTestPlanWithTestCasesIntegrationTest() throws Exception { + // Given - test plan 2 with 3 test cases (7, 8, 9) + Long originalTestPlanId = 2L; + long initialTestCaseCount = testCaseRepository.count(); + + // When - duplicate test plan + MvcResult result = mockMvc.perform( + post("/v1/project/" + SUPERADMIN_PROJECT_KEY + "/tms/test-plan/" + originalTestPlanId) + .contentType(MediaType.APPLICATION_JSON) + .with(token(oAuthHelper.getSuperadminToken()))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").exists()) + .andExpect(jsonPath("$.duplicationStatistic.successCount").value(3)) + .andReturn(); + + // Then - verify test cases were duplicated and added to new plan + String content = result.getResponse().getContentAsString(); + var duplicatedTestPlan = objectMapper.readValue(content, DuplicateTmsTestPlanRS.class); + var duplicatedTestPlanId = duplicatedTestPlan.getId(); + + // Verify new test cases were created + long finalTestCaseCount = testCaseRepository.count(); + assertEquals(initialTestCaseCount + 3, finalTestCaseCount); + + // Verify duplicated test cases are in the new plan + List testCaseIdsInDuplicatedPlan = tmsTestPlanTestCaseRepository.findTestCaseIdsByTestPlanId( + duplicatedTestPlanId); + assertEquals(3, testCaseIdsInDuplicatedPlan.size()); + + // Verify original test cases still exist and are in original plan + List testCaseIdsInOriginalPlan = tmsTestPlanTestCaseRepository.findTestCaseIdsByTestPlanId( + originalTestPlanId); + assertEquals(3, testCaseIdsInOriginalPlan.size()); + assertEquals(Set.of(7L, 8L, 9L), new HashSet<>(testCaseIdsInOriginalPlan)); + } + + @Test + void duplicateTestPlanWithTestCaseAttributesIntegrationTest() throws Exception { + // Given - test plan 1 has test cases with attributes + long originalTestPlanId = 1L; + + // When - duplicate test plan + MvcResult result = mockMvc.perform( + post("/v1/project/" + SUPERADMIN_PROJECT_KEY + "/tms/test-plan/" + originalTestPlanId) + .contentType(MediaType.APPLICATION_JSON) + .with(token(oAuthHelper.getSuperadminToken()))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.duplicationStatistic.successCount").value(3)) + .andReturn(); + + // Then - verify duplicated test cases have attributes + String content = result.getResponse().getContentAsString(); + var duplicatedTestPlan = objectMapper.readValue(content, DuplicateTmsTestPlanRS.class); + var duplicatedTestPlanId = duplicatedTestPlan.getId(); + + List duplicatedTestCaseIds = tmsTestPlanTestCaseRepository.findTestCaseIdsByTestPlanId( + duplicatedTestPlanId); + assertEquals(3, duplicatedTestCaseIds.size()); + + // Verify each duplicated test case has the expected attributes + for (Long testCaseId : duplicatedTestCaseIds) { + TmsTestCase duplicatedTestCase = testCaseRepository.findById(testCaseId).orElseThrow(); + assertNotNull(duplicatedTestCase); + + // Check that test case has attributes (the exact attributes depend on SQL data) + if (duplicatedTestCase.getName().contains("Test Case 4")) { + // Original test case 4 had attribute with id=4, value="test value 4" + List attributes = tmsTestCaseAttributeRepository.findAllById_TestCaseId( + testCaseId); + assertFalse(attributes.isEmpty()); + } + } + } + + @Test + void duplicateEmptyTestPlanIntegrationTest() throws Exception { + // Given - create an empty test plan + TmsTestPlanRQ emptyTestPlan = new TmsTestPlanRQ(); + emptyTestPlan.setName("Empty Test Plan"); + emptyTestPlan.setDescription("Test plan with no test cases"); + + String jsonContent = objectMapper.writeValueAsString(emptyTestPlan); + + // Create empty test plan + MvcResult createResult = mockMvc.perform( + post("/v1/project/" + SUPERADMIN_PROJECT_KEY + "/tms/test-plan") + .contentType("application/json") + .content(jsonContent) + .with(token(oAuthHelper.getSuperadminToken()))) + .andExpect(status().isOk()) + .andReturn(); + + String createContent = createResult.getResponse().getContentAsString(); + var createdTestPlan = objectMapper.readValue(createContent, DuplicateTmsTestPlanRS.class); + var emptyTestPlanId = createdTestPlan.getId(); + + // When - duplicate empty test plan + mockMvc.perform( + post("/v1/project/" + SUPERADMIN_PROJECT_KEY + "/tms/test-plan/" + emptyTestPlanId) + .contentType(MediaType.APPLICATION_JSON) + .with(token(oAuthHelper.getSuperadminToken()))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").exists()) + .andExpect(jsonPath("$.name").value("Empty Test Plan")) + .andExpect(jsonPath("$.description").value("Test plan with no test cases")) + .andExpect(jsonPath("$.duplicationStatistic.totalCount").value(0)) + .andExpect(jsonPath("$.duplicationStatistic.successCount").value(0)) + .andExpect(jsonPath("$.duplicationStatistic.failureCount").value(0)); + } + + @Test + void duplicateNonExistentTestPlanIntegrationTest() throws Exception { + // Given - non-existent test plan ID + long nonExistentTestPlanId = 99999L; + + // When/Then - should return 404 Not Found + mockMvc.perform( + post("/v1/project/" + SUPERADMIN_PROJECT_KEY + "/tms/test-plan/" + nonExistentTestPlanId) + .contentType(MediaType.APPLICATION_JSON) + .with(token(oAuthHelper.getSuperadminToken()))) + .andExpect(status().isNotFound()); + } + + @Test + void duplicateTestPlanVerifyIndependenceIntegrationTest() throws Exception { + // Given - test plan 3 with test cases + Long originalTestPlanId = 3L; + + // When - duplicate test plan + MvcResult result = mockMvc.perform( + post("/v1/project/" + SUPERADMIN_PROJECT_KEY + "/tms/test-plan/" + originalTestPlanId) + .contentType(MediaType.APPLICATION_JSON) + .with(token(oAuthHelper.getSuperadminToken()))) + .andExpect(status().isOk()) + .andReturn(); + + String content = result.getResponse().getContentAsString(); + var duplicatedTestPlan = objectMapper.readValue(content, DuplicateTmsTestPlanRS.class); + var duplicatedTestPlanId = duplicatedTestPlan.getId(); + + // Then - verify that modifying original doesn't affect duplicate + // Get original and duplicated test cases + List originalTestCaseIds = tmsTestPlanTestCaseRepository.findTestCaseIdsByTestPlanId( + originalTestPlanId); + List duplicatedTestCaseIds = tmsTestPlanTestCaseRepository.findTestCaseIdsByTestPlanId( + duplicatedTestPlanId); + + assertEquals(originalTestCaseIds.size(), duplicatedTestCaseIds.size()); + + // Verify they are different test cases (different IDs) + assertFalse(originalTestCaseIds.containsAll(duplicatedTestCaseIds)); + assertFalse(duplicatedTestCaseIds.containsAll(originalTestCaseIds)); + + // Verify original and duplicate test cases have same names but different IDs + for (int i = 0; i < originalTestCaseIds.size(); i++) { + TmsTestCase original = testCaseRepository.findById(originalTestCaseIds.get(i)).orElseThrow(); + TmsTestCase duplicate = testCaseRepository.findById(duplicatedTestCaseIds.get(i)) + .orElseThrow(); + + assertNotEquals(original.getId(), duplicate.getId()); + assertEquals(original.getName() + "-copy", duplicate.getName()); + assertEquals(original.getDescription(), duplicate.getDescription()); + assertEquals(original.getPriority(), duplicate.getPriority()); + } } } diff --git a/src/test/java/com/epam/ta/reportportal/core/tms/controller/unit/TmsTestPlanControllerTest.java b/src/test/java/com/epam/ta/reportportal/core/tms/controller/unit/TmsTestPlanControllerTest.java index f4b2573129..51dae55e29 100644 --- a/src/test/java/com/epam/ta/reportportal/core/tms/controller/unit/TmsTestPlanControllerTest.java +++ b/src/test/java/com/epam/ta/reportportal/core/tms/controller/unit/TmsTestPlanControllerTest.java @@ -17,10 +17,11 @@ import com.epam.ta.reportportal.commons.ReportPortalUser; import com.epam.ta.reportportal.commons.querygen.Filter; import com.epam.ta.reportportal.core.tms.controller.TmsTestPlanController; +import com.epam.ta.reportportal.core.tms.dto.DuplicateTmsTestPlanRS; import com.epam.ta.reportportal.core.tms.dto.TmsTestPlanRQ; import com.epam.ta.reportportal.core.tms.dto.TmsTestPlanRS; import com.epam.ta.reportportal.core.tms.dto.batch.BatchAddTestCasesToPlanRQ; -import com.epam.ta.reportportal.core.tms.dto.batch.BatchOperationResultRS; +import com.epam.ta.reportportal.core.tms.dto.batch.BatchTestCaseOperationResultRS; import com.epam.ta.reportportal.core.tms.dto.batch.BatchRemoveTestCasesFromPlanRQ; import com.epam.ta.reportportal.core.tms.service.TmsTestPlanService; import com.epam.ta.reportportal.entity.organization.MembershipDetails; @@ -371,7 +372,7 @@ void addTestCasesToPlanTest() throws Exception { .testCaseIds(testCaseIds) .build(); - BatchOperationResultRS expectedResult = BatchOperationResultRS.builder() + BatchTestCaseOperationResultRS expectedResult = BatchTestCaseOperationResultRS.builder() .totalCount(3) .successCount(3) .failureCount(0) @@ -407,7 +408,7 @@ void addTestCasesToPlanWithSingleTestCaseTest() throws Exception { .testCaseIds(testCaseIds) .build(); - BatchOperationResultRS expectedResult = BatchOperationResultRS.builder() + BatchTestCaseOperationResultRS expectedResult = BatchTestCaseOperationResultRS.builder() .totalCount(1) .successCount(1) .failureCount(0) @@ -460,7 +461,7 @@ void addTestCasesToPlanWithPartialSuccessTest() throws Exception { .testCaseIds(testCaseIds) .build(); - BatchOperationResultRS expectedResult = BatchOperationResultRS.builder() + BatchTestCaseOperationResultRS expectedResult = BatchTestCaseOperationResultRS.builder() .totalCount(3) .successCount(2) .failureCount(1) @@ -495,7 +496,7 @@ void removeTestCasesFromPlanTest() throws Exception { .testCaseIds(testCaseIds) .build(); - BatchOperationResultRS expectedResult = BatchOperationResultRS.builder() + BatchTestCaseOperationResultRS expectedResult = BatchTestCaseOperationResultRS.builder() .totalCount(3) .successCount(3) .failureCount(0) @@ -531,7 +532,7 @@ void removeTestCasesFromPlanWithSingleTestCaseTest() throws Exception { .testCaseIds(testCaseIds) .build(); - BatchOperationResultRS expectedResult = BatchOperationResultRS.builder() + BatchTestCaseOperationResultRS expectedResult = BatchTestCaseOperationResultRS.builder() .totalCount(1) .successCount(1) .failureCount(0) @@ -584,7 +585,7 @@ void removeTestCasesFromPlanWithMultipleTestCasesTest() throws Exception { .testCaseIds(testCaseIds) .build(); - BatchOperationResultRS expectedResult = BatchOperationResultRS.builder() + BatchTestCaseOperationResultRS expectedResult = BatchTestCaseOperationResultRS.builder() .totalCount(5) .successCount(5) .failureCount(0) @@ -619,7 +620,7 @@ void removeTestCasesFromPlanWithPartialSuccessTest() throws Exception { .testCaseIds(testCaseIds) .build(); - BatchOperationResultRS expectedResult = BatchOperationResultRS.builder() + BatchTestCaseOperationResultRS expectedResult = BatchTestCaseOperationResultRS.builder() .totalCount(3) .successCount(2) .failureCount(1) @@ -654,7 +655,7 @@ void addTestCasesToPlanWithLargeListTest() throws Exception { .testCaseIds(testCaseIds) .build(); - BatchOperationResultRS expectedResult = BatchOperationResultRS.builder() + BatchTestCaseOperationResultRS expectedResult = BatchTestCaseOperationResultRS.builder() .totalCount(10) .successCount(10) .failureCount(0) @@ -679,4 +680,52 @@ void addTestCasesToPlanWithLargeListTest() throws Exception { verify(projectExtractor).extractMembershipDetails(eq(testUser), anyString()); verify(tmsTestPlanService).addTestCasesToPlan(projectId, testPlanId, testCaseIds); } + + @Test + void duplicateTestPlanTest() throws Exception { + // Given + Long testPlanId = 2L; + Long duplicatedTestPlanId = 3L; + + DuplicateTmsTestPlanRS expectedResult = DuplicateTmsTestPlanRS.builder() + .id(duplicatedTestPlanId) + .build(); + + given(tmsTestPlanService.duplicate(projectId, testPlanId)) + .willReturn(expectedResult); + + // When & Then + mockMvc.perform(post("/v1/project/{projectKey}/tms/test-plan/{testPlanId}", + projectKey, testPlanId) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(duplicatedTestPlanId)); + + verify(projectExtractor).extractMembershipDetails(eq(testUser), anyString()); + verify(tmsTestPlanService).duplicate(projectId, testPlanId); + } + + @Test + void duplicateTestPlanWithDifferentProjectTest() throws Exception { + // Given + Long testPlanId = 5L; + Long duplicatedTestPlanId = 6L; + + DuplicateTmsTestPlanRS expectedResult = DuplicateTmsTestPlanRS.builder() + .id(duplicatedTestPlanId) + .build(); + + given(tmsTestPlanService.duplicate(projectId, testPlanId)) + .willReturn(expectedResult); + + // When & Then + mockMvc.perform(post("/v1/project/{projectKey}/tms/test-plan/{testPlanId}", + projectKey, testPlanId) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(duplicatedTestPlanId)); + + verify(projectExtractor).extractMembershipDetails(eq(testUser), anyString()); + verify(tmsTestPlanService).duplicate(projectId, testPlanId); + } } diff --git a/src/test/java/com/epam/ta/reportportal/core/tms/service/TmsTestCaseServiceImplTest.java b/src/test/java/com/epam/ta/reportportal/core/tms/service/TmsTestCaseServiceImplTest.java index fdc4f404e5..fcf3f4be63 100644 --- a/src/test/java/com/epam/ta/reportportal/core/tms/service/TmsTestCaseServiceImplTest.java +++ b/src/test/java/com/epam/ta/reportportal/core/tms/service/TmsTestCaseServiceImplTest.java @@ -8,9 +8,11 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -34,6 +36,7 @@ import com.epam.ta.reportportal.core.tms.dto.TmsTextManualScenarioRQ; import com.epam.ta.reportportal.core.tms.dto.batch.BatchDeleteTestCasesRQ; import com.epam.ta.reportportal.core.tms.dto.batch.BatchDuplicateTestCasesRQ; +import com.epam.ta.reportportal.core.tms.dto.batch.BatchTestCaseOperationResultRS; import com.epam.ta.reportportal.core.tms.dto.batch.BatchPatchTestCaseAttributesRQ; import com.epam.ta.reportportal.core.tms.dto.batch.BatchPatchTestCasesRQ; import com.epam.ta.reportportal.core.tms.mapper.TmsTestCaseMapper; @@ -167,6 +170,7 @@ void setUp() { testCase.setId(testCaseId); testCase.setName("Test Case"); testCase.setDescription("Description"); + testCase.setTestFolder(testFolder); testCaseVersion = new TmsTestCaseVersion(); testCaseVersion.setId(1L); @@ -1114,7 +1118,6 @@ void getTestCasesByCriteria_WithMultipleTestCases_ShouldReturnOrderedResults() { verify(tmsTestCaseVersionService).getDefaultVersions(testCaseIds); verify(tmsTestCaseRepository).findByProjectIdAndIds(projectId, testCaseIds); verify(tmsTestCaseExecutionService).getLastTestCasesExecutionsByTestCaseIds(testCaseIds); - // Проверяем что маппер получил список в правильном порядке (3L, 1L, 2L) verify(tmsTestCaseMapper).convert(any(List.class), eq(defaultVersions), eq(lastExecutions), eq(pageable), eq(3L)); } @@ -1494,6 +1497,7 @@ void duplicate_WithValidTestCaseIds_ShouldDuplicateTestCases() { var originalTestCase1 = new TmsTestCase(); originalTestCase1.setId(1L); originalTestCase1.setName("Test Case 1"); + originalTestCase1.setTestFolder(testFolder); var duplicatedTestCase1 = new TmsTestCase(); duplicatedTestCase1.setId(11L); duplicatedTestCase1.setName("Test Case 1 (Copy)"); @@ -1506,15 +1510,19 @@ void duplicate_WithValidTestCaseIds_ShouldDuplicateTestCases() { when(tmsTestFolderService.getEntityById(projectId, targetFolderId)).thenReturn(testFolder); when(tmsTestCaseMapper.duplicateTestCase(originalTestCase1, testFolder)) .thenReturn(duplicatedTestCase1); + when(tmsTestCaseRepository.existsByNameAndTestFolder(eq(projectId), anyString(), eq(testFolderId))) + .thenReturn(false); when(tmsTestCaseRepository.save(duplicatedTestCase1)).thenReturn(duplicatedTestCase1); when(tmsTestCaseVersionService.duplicateDefaultVersion(duplicatedTestCase1, originalVersion1)) .thenReturn(duplicatedVersion1); when(tmsTestCaseMapper.convert(duplicatedTestCase1, duplicatedVersion1)) .thenReturn(duplicatedTestCaseRS1); + // Mock для второго тест-кейса var originalTestCase2 = new TmsTestCase(); originalTestCase2.setId(2L); originalTestCase2.setName("Test Case 2"); + originalTestCase2.setTestFolder(testFolder); var duplicatedTestCase2 = new TmsTestCase(); duplicatedTestCase2.setId(12L); duplicatedTestCase2.setName("Test Case 2 (Copy)"); @@ -1576,6 +1584,7 @@ void duplicate_WithTestFolder_ShouldDuplicateTestCases() { var originalTestCase = new TmsTestCase(); originalTestCase.setId(1L); originalTestCase.setName("Original Test Case"); + originalTestCase.setTestFolder(this.testFolder); var duplicatedTestCase = new TmsTestCase(); duplicatedTestCase.setId(20L); duplicatedTestCase.setName("Original Test Case (Copy)"); @@ -1588,6 +1597,8 @@ void duplicate_WithTestFolder_ShouldDuplicateTestCases() { when(tmsTestFolderService.getEntityById(projectId, targetFolderId)).thenReturn(this.testFolder); when(tmsTestCaseMapper.duplicateTestCase(originalTestCase, this.testFolder)) .thenReturn(duplicatedTestCase); + when(tmsTestCaseRepository.existsByNameAndTestFolder(eq(projectId), anyString(), eq(testFolderId))) + .thenReturn(false); when(tmsTestCaseRepository.save(duplicatedTestCase)).thenReturn(duplicatedTestCase); when(tmsTestCaseVersionService.duplicateDefaultVersion(duplicatedTestCase, originalVersion)) .thenReturn(duplicatedVersion); @@ -1609,7 +1620,7 @@ void duplicate_WithTestFolder_ShouldDuplicateTestCases() { } @Test - void duplicate_WithEmptyTagsTaggedTestCase_ShouldDuplicateTestCaseWithWithoutEMptyTags() { + void duplicate_WithEmptyTagsTaggedTestCase_ShouldDuplicateTestCaseWithoutEmptyTags() { // Given var testCaseIds = List.of(1L); var targetFolderId = 10L; @@ -1629,7 +1640,8 @@ void duplicate_WithEmptyTagsTaggedTestCase_ShouldDuplicateTestCaseWithWithoutEMp var originalTestCase = new TmsTestCase(); originalTestCase.setId(1L); - originalTestCase.setAttributes(Set.of()); // Non-empty tags + originalTestCase.setAttributes(Set.of()); // Empty tags + originalTestCase.setTestFolder(testFolder); var duplicatedTestCase = new TmsTestCase(); duplicatedTestCase.setId(11L); var originalVersion = new TmsTestCaseVersion(); @@ -1641,6 +1653,8 @@ void duplicate_WithEmptyTagsTaggedTestCase_ShouldDuplicateTestCaseWithWithoutEMp when(tmsTestFolderService.getEntityById(projectId, targetFolderId)).thenReturn(testFolder); when(tmsTestCaseMapper.duplicateTestCase(originalTestCase, testFolder)) .thenReturn(duplicatedTestCase); + when(tmsTestCaseRepository.existsByNameAndTestFolder(eq(projectId), anyString(), eq(testFolderId))) + .thenReturn(false); when(tmsTestCaseRepository.save(duplicatedTestCase)).thenReturn(duplicatedTestCase); when(tmsTestCaseVersionService.duplicateDefaultVersion(duplicatedTestCase, originalVersion)) .thenReturn(duplicatedVersion); @@ -1738,6 +1752,235 @@ void duplicate_WithEmptyTestCaseIds_ShouldReturnEmptyList() { verify(tmsTestCaseRepository, never()).save(any()); } + @Test + void duplicateTestCase_WithUniqueNameGeneration_ShouldGenerateUniqueName() { + // Given + var originalTestCase = new TmsTestCase(); + originalTestCase.setId(1L); + originalTestCase.setName("Test Case"); + originalTestCase.setTestFolder(testFolder); + + var duplicatedTestCase = new TmsTestCase(); + duplicatedTestCase.setId(11L); + duplicatedTestCase.setName("Test Case-copy"); + + var originalVersion = new TmsTestCaseVersion(); + var duplicatedVersion = new TmsTestCaseVersion(); + + when(tmsTestCaseRepository.findByProjectIdAndId(projectId, 1L)) + .thenReturn(Optional.of(originalTestCase)); + when(tmsTestCaseVersionService.getDefaultVersion(1L)).thenReturn(originalVersion); + when(tmsTestFolderService.getEntityById(projectId, testFolderId)).thenReturn(testFolder); + when(tmsTestCaseMapper.duplicateTestCase(originalTestCase, testFolder)) + .thenReturn(duplicatedTestCase); + when(tmsTestCaseRepository.existsByNameAndTestFolder(projectId, "Test Case-copy", testFolderId)) + .thenReturn(false); + when(tmsTestCaseRepository.save(duplicatedTestCase)).thenReturn(duplicatedTestCase); + when(tmsTestCaseVersionService.duplicateDefaultVersion(duplicatedTestCase, originalVersion)) + .thenReturn(duplicatedVersion); + when(tmsTestCaseMapper.convert(duplicatedTestCase, duplicatedVersion)) + .thenReturn(testCaseRS); + + // When + var result = sut.duplicateTestCase(projectId, 1L, testFolderId); + + // Then + assertNotNull(result); + verify(tmsTestCaseRepository).existsByNameAndTestFolder(projectId, "Test Case-copy", testFolderId); + verify(tmsTestCaseRepository).save(duplicatedTestCase); + } + + @Test + void duplicateTestCase_WithExistingName_ShouldGenerateIncrementalName() { + // Given + var originalTestCase = new TmsTestCase(); + originalTestCase.setId(1L); + originalTestCase.setName("Test Case"); + originalTestCase.setTestFolder(testFolder); + + var duplicatedTestCase = new TmsTestCase(); + duplicatedTestCase.setId(11L); + + var originalVersion = new TmsTestCaseVersion(); + var duplicatedVersion = new TmsTestCaseVersion(); + + when(tmsTestCaseRepository.findByProjectIdAndId(projectId, 1L)) + .thenReturn(Optional.of(originalTestCase)); + when(tmsTestCaseVersionService.getDefaultVersion(1L)).thenReturn(originalVersion); + when(tmsTestFolderService.getEntityById(projectId, testFolderId)).thenReturn(testFolder); + when(tmsTestCaseMapper.duplicateTestCase(originalTestCase, testFolder)) + .thenReturn(duplicatedTestCase); + when(tmsTestCaseRepository.existsByNameAndTestFolder(projectId, "Test Case-copy", testFolderId)) + .thenReturn(true); + when(tmsTestCaseRepository.existsByNameAndTestFolder(projectId, "Test Case-copy-1", testFolderId)) + .thenReturn(false); + when(tmsTestCaseRepository.save(duplicatedTestCase)).thenReturn(duplicatedTestCase); + when(tmsTestCaseVersionService.duplicateDefaultVersion(duplicatedTestCase, originalVersion)) + .thenReturn(duplicatedVersion); + when(tmsTestCaseMapper.convert(duplicatedTestCase, duplicatedVersion)) + .thenReturn(testCaseRS); + + // When + var result = sut.duplicateTestCase(projectId, 1L, testFolderId); + + // Then + assertNotNull(result); + verify(tmsTestCaseRepository).existsByNameAndTestFolder(projectId, "Test Case-copy", testFolderId); + verify(tmsTestCaseRepository).existsByNameAndTestFolder(projectId, "Test Case-copy-1", testFolderId); + verify(tmsTestCaseRepository).save(duplicatedTestCase); + } + + @Test + void duplicateTestCases_WithAllSuccessful_ShouldReturnSuccessResult() { + // Given + var testCaseIds = Arrays.asList(1L, 2L); + + var originalTestCase1 = new TmsTestCase(); + originalTestCase1.setId(1L); + originalTestCase1.setName("Test Case 1"); + originalTestCase1.setTestFolder(testFolder); + + var duplicatedTestCase1 = new TmsTestCase(); + duplicatedTestCase1.setId(11L); + duplicatedTestCase1.setName("Test Case 1-copy"); + + var originalTestCase2 = new TmsTestCase(); + originalTestCase2.setId(2L); + originalTestCase2.setName("Test Case 2"); + originalTestCase2.setTestFolder(testFolder); + + var duplicatedTestCase2 = new TmsTestCase(); + duplicatedTestCase2.setId(12L); + duplicatedTestCase2.setName("Test Case 2-copy"); + + var originalVersion = new TmsTestCaseVersion(); + var duplicatedVersion = new TmsTestCaseVersion(); + + var expectedResult = new BatchTestCaseOperationResultRS(); + expectedResult.setSuccessTestCaseIds(Arrays.asList(11L, 12L)); + expectedResult.setErrors(Collections.emptyList()); + + when(tmsTestCaseRepository.findByProjectIdAndId(projectId, 1L)) + .thenReturn(Optional.of(originalTestCase1)); + when(tmsTestCaseRepository.findByProjectIdAndId(projectId, 2L)) + .thenReturn(Optional.of(originalTestCase2)); + when(tmsTestCaseVersionService.getDefaultVersion(1L)).thenReturn(originalVersion); + when(tmsTestCaseVersionService.getDefaultVersion(2L)).thenReturn(originalVersion); + when(tmsTestCaseMapper.duplicateTestCase(originalTestCase1, testFolder)) + .thenReturn(duplicatedTestCase1); + when(tmsTestCaseMapper.duplicateTestCase(originalTestCase2, testFolder)) + .thenReturn(duplicatedTestCase2); + when(tmsTestCaseRepository.existsByNameAndTestFolder(eq(projectId), anyString(), eq(testFolderId))) + .thenReturn(false); + when(tmsTestCaseRepository.save(any(TmsTestCase.class))) + .thenAnswer(invocation -> invocation.getArgument(0)); + when(tmsTestCaseVersionService.duplicateDefaultVersion(any(TmsTestCase.class), eq(originalVersion))) + .thenReturn(duplicatedVersion); + when(tmsTestCaseMapper.toBatchOperationResult(Arrays.asList(11L, 12L), Collections.emptyList())) + .thenReturn(expectedResult); + + // When + var result = sut.duplicateTestCases(projectId, testCaseIds); + + // Then + assertNotNull(result); + assertEquals(2, result.getSuccessTestCaseIds().size()); + assertTrue(result.getErrors().isEmpty()); + verify(tmsTestCaseRepository, times(2)).save(any(TmsTestCase.class)); + verify(tmsTestCaseMapper).toBatchOperationResult(Arrays.asList(11L, 12L), Collections.emptyList()); + } + + @Test + void duplicateTestCases_WithSomeFailures_ShouldReturnPartialResult() { + // Given + var testCaseIds = Arrays.asList(1L, 2L); + + var originalTestCase1 = new TmsTestCase(); + originalTestCase1.setId(1L); + originalTestCase1.setName("Test Case 1"); + originalTestCase1.setTestFolder(testFolder); + + var duplicatedTestCase1 = new TmsTestCase(); + duplicatedTestCase1.setId(11L); + duplicatedTestCase1.setName("Test Case 1-copy"); + + var originalVersion = new TmsTestCaseVersion(); + var duplicatedVersion = new TmsTestCaseVersion(); + + var expectedResult = new BatchTestCaseOperationResultRS(); + expectedResult.setSuccessTestCaseIds(List.of(11L)); + expectedResult.setErrors(new ArrayList<>()); + + when(tmsTestCaseRepository.findByProjectIdAndId(projectId, 1L)) + .thenReturn(Optional.of(originalTestCase1)); + when(tmsTestCaseRepository.findByProjectIdAndId(projectId, 2L)) + .thenReturn(Optional.empty()); + when(tmsTestCaseVersionService.getDefaultVersion(1L)).thenReturn(originalVersion); + when(tmsTestCaseMapper.duplicateTestCase(originalTestCase1, testFolder)) + .thenReturn(duplicatedTestCase1); + when(tmsTestCaseRepository.existsByNameAndTestFolder(eq(projectId), anyString(), eq(testFolderId))) + .thenReturn(false); + when(tmsTestCaseRepository.save(duplicatedTestCase1)).thenReturn(duplicatedTestCase1); + when(tmsTestCaseVersionService.duplicateDefaultVersion(duplicatedTestCase1, originalVersion)) + .thenReturn(duplicatedVersion); + when(tmsTestCaseMapper.toBatchOperationResult(eq(List.of(11L)), anyList())) + .thenReturn(expectedResult); + + // When + var result = sut.duplicateTestCases(projectId, testCaseIds); + + // Then + assertNotNull(result); + assertEquals(1, result.getSuccessTestCaseIds().size()); + assertEquals(11L, result.getSuccessTestCaseIds().getFirst()); + verify(tmsTestCaseRepository).save(duplicatedTestCase1); + verify(tmsTestCaseMapper).toBatchOperationResult(eq(List.of(11L)), anyList()); + } + + @Test + void duplicateTestCases_WithAttributes_ShouldDuplicateAttributes() { + // Given + var testCaseIds = List.of(1L); + + var originalTestCase = new TmsTestCase(); + originalTestCase.setId(1L); + originalTestCase.setName("Test Case"); + originalTestCase.setTestFolder(testFolder); + originalTestCase.setAttributes(Set.of(new com.epam.ta.reportportal.entity.tms.TmsTestCaseAttribute())); + + var duplicatedTestCase = new TmsTestCase(); + duplicatedTestCase.setId(11L); + duplicatedTestCase.setName("Test Case-copy"); + + var originalVersion = new TmsTestCaseVersion(); + var duplicatedVersion = new TmsTestCaseVersion(); + + var expectedResult = new BatchTestCaseOperationResultRS(); + expectedResult.setSuccessTestCaseIds(List.of(11L)); + expectedResult.setErrors(Collections.emptyList()); + + when(tmsTestCaseRepository.findByProjectIdAndId(projectId, 1L)) + .thenReturn(Optional.of(originalTestCase)); + when(tmsTestCaseVersionService.getDefaultVersion(1L)).thenReturn(originalVersion); + when(tmsTestCaseMapper.duplicateTestCase(originalTestCase, testFolder)) + .thenReturn(duplicatedTestCase); + when(tmsTestCaseRepository.existsByNameAndTestFolder(eq(projectId), anyString(), eq(testFolderId))) + .thenReturn(false); + when(tmsTestCaseRepository.save(duplicatedTestCase)).thenReturn(duplicatedTestCase); + when(tmsTestCaseVersionService.duplicateDefaultVersion(duplicatedTestCase, originalVersion)) + .thenReturn(duplicatedVersion); + when(tmsTestCaseMapper.toBatchOperationResult(List.of(11L), Collections.emptyList())) + .thenReturn(expectedResult); + + // When + var result = sut.duplicateTestCases(projectId, testCaseIds); + + // Then + assertNotNull(result); + verify(tmsTestCaseAttributeService).duplicateTestCaseAttributes(originalTestCase, duplicatedTestCase); + verify(tmsTestCaseMapper).toBatchOperationResult(List.of(11L), Collections.emptyList()); + } + @Test void existsById_WhenTestCaseExists_ShouldReturnTrue() { // Given diff --git a/src/test/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanAttributeServiceImplTest.java b/src/test/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanAttributeServiceImplTest.java index 6a139be292..6b0b4d5f2f 100644 --- a/src/test/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanAttributeServiceImplTest.java +++ b/src/test/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanAttributeServiceImplTest.java @@ -2,13 +2,19 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; +import com.epam.ta.reportportal.entity.tms.TmsAttribute; import com.epam.ta.reportportal.entity.tms.TmsTestPlan; import com.epam.ta.reportportal.entity.tms.TmsTestPlanAttribute; +import com.epam.ta.reportportal.entity.tms.TmsTestPlanAttributeId; import com.epam.ta.reportportal.dao.tms.TmsTestPlanAttributeRepository; import com.epam.ta.reportportal.core.tms.dto.TmsTestPlanAttributeRQ; import com.epam.ta.reportportal.core.tms.mapper.TmsTestPlanAttributeMapper; @@ -41,7 +47,6 @@ void shouldNotCreateTestPlanAttributesWhenEmptyAttributes() { assertDoesNotThrow(() -> sut.createTestPlanAttributes(tmsTestPlan, attributes)); - // Assert verifyNoInteractions(tmsTestPlanAttributeMapper, tmsTestPlanAttributeRepository); } @@ -56,10 +61,8 @@ void shouldCreateTestPlanAttributes() { when(tmsTestPlanAttributeMapper.convertToTmsTestPlanAttributes(attributesRQ)).thenReturn( attributes); - // Act assertDoesNotThrow(() -> sut.createTestPlanAttributes(tmsTestPlan, attributesRQ)); - // Assert assertEquals(attributes, tmsTestPlan.getAttributes()); attributes.forEach(attribute -> assertEquals(tmsTestPlan, attribute.getTestPlan())); @@ -74,7 +77,7 @@ void shouldRemoveAllTestPlanAttributesWhenEmptyAttributes() { assertDoesNotThrow(() -> sut.updateTestPlanAttributes(existingTestPlan, attributes)); - verify(tmsTestPlanAttributeRepository).deleteAllByTestPlanId(existingTestPlan.getId()); + verify(tmsTestPlanAttributeRepository, never()).deleteAll(any()); verifyNoInteractions(tmsTestPlanAttributeMapper); } @@ -85,7 +88,7 @@ void shouldRemoveAllTestPlanAttributesWhenNullAttributes() { assertDoesNotThrow(() -> sut.updateTestPlanAttributes(existingTestPlan, null)); - verify(tmsTestPlanAttributeRepository).deleteAllByTestPlanId(existingTestPlan.getId()); + verify(tmsTestPlanAttributeRepository, never()).deleteAll(any()); verifyNoInteractions(tmsTestPlanAttributeMapper); } @@ -96,13 +99,14 @@ void shouldTestUpdateTestPlanAttributes() { var attributes = new HashSet(); attributes.add(new TmsTestPlanAttribute()); attributes.add(new TmsTestPlanAttribute()); + existingTestPlan.setAttributes(attributes); when(tmsTestPlanAttributeMapper.convertToTmsTestPlanAttributes(attributesRQ)).thenReturn( attributes); assertDoesNotThrow(() -> sut.updateTestPlanAttributes(existingTestPlan, attributesRQ)); - verify(tmsTestPlanAttributeRepository).deleteAllByTestPlanId(existingTestPlan.getId()); + verify(tmsTestPlanAttributeRepository).deleteAll(existingTestPlan.getAttributes()); assertEquals(attributes, existingTestPlan.getAttributes()); attributes.forEach(attribute -> assertEquals(existingTestPlan, attribute.getTestPlan())); @@ -170,4 +174,178 @@ void shouldDeleteAllByTestPlanId() { verify(tmsTestPlanAttributeRepository).deleteAllByTestPlanId(testPlanId); } + + @Test + void shouldNotDuplicateTestPlanAttributesWhenEmptyAttributes() { + // Arrange + var originalTestPlan = new TmsTestPlan(); + originalTestPlan.setAttributes(Collections.emptySet()); + var newTestPlan = new TmsTestPlan(); + + // Act + assertDoesNotThrow(() -> sut.duplicateTestPlanAttributes(originalTestPlan, newTestPlan)); + + // Assert - no interactions with mapper or repository + verifyNoInteractions(tmsTestPlanAttributeMapper, tmsTestPlanAttributeRepository); + } + + @Test + void shouldNotDuplicateTestPlanAttributesWhenNullAttributes() { + // Arrange + var originalTestPlan = new TmsTestPlan(); + originalTestPlan.setAttributes(null); + var newTestPlan = new TmsTestPlan(); + + // Act + assertDoesNotThrow(() -> sut.duplicateTestPlanAttributes(originalTestPlan, newTestPlan)); + + // Assert - no interactions with mapper or repository + verifyNoInteractions(tmsTestPlanAttributeMapper, tmsTestPlanAttributeRepository); + } + + @Test + void shouldDuplicateTestPlanAttributesSuccessfully() { + // Arrange + var originalTestPlan = new TmsTestPlan(); + originalTestPlan.setId(1L); + + var attribute1 = new TmsAttribute(); + attribute1.setId(10L); + var attribute2 = new TmsAttribute(); + attribute2.setId(20L); + + var originalAttribute1 = new TmsTestPlanAttribute(); + originalAttribute1.setId(new TmsTestPlanAttributeId(1L, 10L)); + originalAttribute1.setTestPlan(originalTestPlan); + originalAttribute1.setAttribute(attribute1); + originalAttribute1.setValue("value1"); + + var originalAttribute2 = new TmsTestPlanAttribute(); + originalAttribute2.setId(new TmsTestPlanAttributeId(1L, 20L)); + originalAttribute2.setTestPlan(originalTestPlan); + originalAttribute2.setAttribute(attribute2); + originalAttribute2.setValue("value2"); + + Set originalAttributes = new HashSet<>(); + originalAttributes.add(originalAttribute1); + originalAttributes.add(originalAttribute2); + originalTestPlan.setAttributes(originalAttributes); + + var newTestPlan = new TmsTestPlan(); + newTestPlan.setId(2L); + + var duplicatedAttribute1 = new TmsTestPlanAttribute(); + duplicatedAttribute1.setId(new TmsTestPlanAttributeId(2L, 10L)); + duplicatedAttribute1.setAttribute(attribute1); + duplicatedAttribute1.setValue("value1"); + + var duplicatedAttribute2 = new TmsTestPlanAttribute(); + duplicatedAttribute2.setId(new TmsTestPlanAttributeId(2L, 20L)); + duplicatedAttribute2.setAttribute(attribute2); + duplicatedAttribute2.setValue("value2"); + + when(tmsTestPlanAttributeMapper.duplicateTestPlanAttribute(originalAttribute1, newTestPlan)) + .thenReturn(duplicatedAttribute1); + when(tmsTestPlanAttributeMapper.duplicateTestPlanAttribute(originalAttribute2, newTestPlan)) + .thenReturn(duplicatedAttribute2); + + // Act + assertDoesNotThrow(() -> sut.duplicateTestPlanAttributes(originalTestPlan, newTestPlan)); + + // Assert + assertNotNull(newTestPlan.getAttributes()); + assertEquals(2, newTestPlan.getAttributes().size()); + assertTrue(newTestPlan.getAttributes().contains(duplicatedAttribute1)); + assertTrue(newTestPlan.getAttributes().contains(duplicatedAttribute2)); + + // Verify all attributes have correct test plan reference + newTestPlan.getAttributes().forEach(attr -> assertEquals(newTestPlan, attr.getTestPlan())); + + verify(tmsTestPlanAttributeMapper).duplicateTestPlanAttribute(originalAttribute1, newTestPlan); + verify(tmsTestPlanAttributeMapper).duplicateTestPlanAttribute(originalAttribute2, newTestPlan); + verify(tmsTestPlanAttributeRepository).saveAll(any(Set.class)); + } + + @Test + void shouldDuplicateTestPlanAttributesWithSingleAttribute() { + // Arrange + var originalTestPlan = new TmsTestPlan(); + originalTestPlan.setId(1L); + + var attribute = new TmsAttribute(); + attribute.setId(10L); + + var originalAttribute = new TmsTestPlanAttribute(); + originalAttribute.setId(new TmsTestPlanAttributeId(1L, 10L)); + originalAttribute.setTestPlan(originalTestPlan); + originalAttribute.setAttribute(attribute); + originalAttribute.setValue("test-value"); + + Set originalAttributes = new HashSet<>(); + originalAttributes.add(originalAttribute); + originalTestPlan.setAttributes(originalAttributes); + + var newTestPlan = new TmsTestPlan(); + newTestPlan.setId(2L); + + var duplicatedAttribute = new TmsTestPlanAttribute(); + duplicatedAttribute.setId(new TmsTestPlanAttributeId(2L, 10L)); + duplicatedAttribute.setAttribute(attribute); + duplicatedAttribute.setValue("test-value"); + + when(tmsTestPlanAttributeMapper.duplicateTestPlanAttribute(originalAttribute, newTestPlan)) + .thenReturn(duplicatedAttribute); + + // Act + assertDoesNotThrow(() -> sut.duplicateTestPlanAttributes(originalTestPlan, newTestPlan)); + + // Assert + assertNotNull(newTestPlan.getAttributes()); + assertEquals(1, newTestPlan.getAttributes().size()); + assertTrue(newTestPlan.getAttributes().contains(duplicatedAttribute)); + assertEquals(newTestPlan, duplicatedAttribute.getTestPlan()); + + verify(tmsTestPlanAttributeMapper).duplicateTestPlanAttribute(originalAttribute, newTestPlan); + verify(tmsTestPlanAttributeRepository).saveAll(any(Set.class)); + } + + @Test + void shouldDuplicateTestPlanAttributesAndSetCorrectReferences() { + // Arrange + var originalTestPlan = new TmsTestPlan(); + originalTestPlan.setId(1L); + + var attribute = new TmsAttribute(); + attribute.setId(10L); + + var originalAttribute = new TmsTestPlanAttribute(); + originalAttribute.setId(new TmsTestPlanAttributeId(1L, 10L)); + originalAttribute.setTestPlan(originalTestPlan); + originalAttribute.setAttribute(attribute); + + Set originalAttributes = new HashSet<>(); + originalAttributes.add(originalAttribute); + originalTestPlan.setAttributes(originalAttributes); + + var newTestPlan = new TmsTestPlan(); + newTestPlan.setId(2L); + + var duplicatedAttribute = new TmsTestPlanAttribute(); + duplicatedAttribute.setId(new TmsTestPlanAttributeId(2L, 10L)); + duplicatedAttribute.setAttribute(attribute); + // Initially, the duplicated attribute might not have the test plan set + + when(tmsTestPlanAttributeMapper.duplicateTestPlanAttribute(originalAttribute, newTestPlan)) + .thenReturn(duplicatedAttribute); + + // Act + assertDoesNotThrow(() -> sut.duplicateTestPlanAttributes(originalTestPlan, newTestPlan)); + + // Assert - verify that test plan reference is set correctly + assertEquals(newTestPlan, duplicatedAttribute.getTestPlan()); + assertEquals(1, newTestPlan.getAttributes().size()); + + verify(tmsTestPlanAttributeMapper).duplicateTestPlanAttribute(eq(originalAttribute), eq(newTestPlan)); + verify(tmsTestPlanAttributeRepository).saveAll(any(Set.class)); + } } diff --git a/src/test/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanExecutionServiceImplTest.java b/src/test/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanExecutionServiceImplTest.java index 401a2d1777..3eb466eb89 100644 --- a/src/test/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanExecutionServiceImplTest.java +++ b/src/test/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanExecutionServiceImplTest.java @@ -11,7 +11,7 @@ import com.epam.ta.reportportal.core.tms.mapper.TmsTestPlanExecutionMapper; import com.epam.ta.reportportal.dao.tms.TmsTestPlanStatisticsRepository; import com.epam.ta.reportportal.entity.tms.TmsTestPlan; -import com.epam.ta.reportportal.entity.tms.TmsTestPlanExecutionStatisticRS; +import com.epam.ta.reportportal.entity.tms.TmsTestPlanExecutionStatistic; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -31,8 +31,8 @@ class TmsTestPlanExecutionServiceImplTest { private Long testPlanId; private TmsTestPlan testPlan; - private TmsTestPlanExecutionStatisticRS statistics; - private TmsTestPlanExecutionStatisticRS emptyStatistics; + private TmsTestPlanExecutionStatistic statistics; + private TmsTestPlanExecutionStatistic emptyStatistics; @BeforeEach void setUp() { @@ -48,9 +48,9 @@ void setUp() { testPlan.setName("Test Plan"); testPlan.setDescription("Test Plan Description"); - statistics = new TmsTestPlanExecutionStatisticRS(10, 5); + statistics = new TmsTestPlanExecutionStatistic(10, 5); - emptyStatistics = new TmsTestPlanExecutionStatisticRS(0, 0); + emptyStatistics = new TmsTestPlanExecutionStatistic(0, 0); } @Test @@ -58,7 +58,6 @@ void getStatisticsForTestPlan_WhenStatisticsExist_ShouldReturnStatistics() { // Given when(tmsTestPlanStatisticsRepository.getExecutionStatisticsByTestPlanId(testPlanId)) .thenReturn(statistics); - when(tmsTestPlanExecutionMapper.toDto(statistics)).thenReturn(statistics); // When var result = sut.getStatisticsForTestPlan(testPlanId); @@ -68,7 +67,6 @@ void getStatisticsForTestPlan_WhenStatisticsExist_ShouldReturnStatistics() { assertEquals(10L, result.getTotal()); assertEquals(5L, result.getCovered()); verify(tmsTestPlanStatisticsRepository).getExecutionStatisticsByTestPlanId(testPlanId); - verify(tmsTestPlanExecutionMapper).toDto(statistics); verify(tmsTestPlanExecutionMapper, never()).createEmptyStatistics(); } @@ -88,7 +86,6 @@ void getStatisticsForTestPlan_WhenStatisticsIsNull_ShouldReturnEmptyStatistics() assertEquals(0L, result.getCovered()); verify(tmsTestPlanStatisticsRepository).getExecutionStatisticsByTestPlanId(testPlanId); verify(tmsTestPlanExecutionMapper).createEmptyStatistics(); - verify(tmsTestPlanExecutionMapper, never()).toDto(any()); } @Test @@ -107,20 +104,18 @@ void getStatisticsForTestPlan_WhenExceptionOccurs_ShouldReturnEmptyStatistics() assertEquals(0L, result.getCovered()); verify(tmsTestPlanStatisticsRepository).getExecutionStatisticsByTestPlanId(testPlanId); verify(tmsTestPlanExecutionMapper).createEmptyStatistics(); - verify(tmsTestPlanExecutionMapper, never()).toDto(any()); } @Test void getStatisticsForTestPlan_WithZeroValues_ShouldReturnZeroStatistics() { // Given - var zeroStatistics = TmsTestPlanExecutionStatisticRS.builder() + var zeroStatistics = TmsTestPlanExecutionStatistic.builder() .total(0L) .covered(0L) .build(); when(tmsTestPlanStatisticsRepository.getExecutionStatisticsByTestPlanId(testPlanId)) .thenReturn(zeroStatistics); - when(tmsTestPlanExecutionMapper.toDto(zeroStatistics)).thenReturn(zeroStatistics); // When var result = sut.getStatisticsForTestPlan(testPlanId); @@ -130,21 +125,18 @@ void getStatisticsForTestPlan_WithZeroValues_ShouldReturnZeroStatistics() { assertEquals(0L, result.getTotal()); assertEquals(0L, result.getCovered()); verify(tmsTestPlanStatisticsRepository).getExecutionStatisticsByTestPlanId(testPlanId); - verify(tmsTestPlanExecutionMapper).toDto(zeroStatistics); } @Test void getStatisticsForTestPlan_WithFullyCoveredPlan_ShouldReturnCorrectStatistics() { // Given - var fullyCoveredStatistics = TmsTestPlanExecutionStatisticRS.builder() + var fullyCoveredStatistics = TmsTestPlanExecutionStatistic.builder() .total(10L) .covered(10L) .build(); when(tmsTestPlanStatisticsRepository.getExecutionStatisticsByTestPlanId(testPlanId)) .thenReturn(fullyCoveredStatistics); - when(tmsTestPlanExecutionMapper.toDto(fullyCoveredStatistics)) - .thenReturn(fullyCoveredStatistics); // When var result = sut.getStatisticsForTestPlan(testPlanId); @@ -154,17 +146,15 @@ void getStatisticsForTestPlan_WithFullyCoveredPlan_ShouldReturnCorrectStatistics assertEquals(10L, result.getTotal()); assertEquals(10L, result.getCovered()); verify(tmsTestPlanStatisticsRepository).getExecutionStatisticsByTestPlanId(testPlanId); - verify(tmsTestPlanExecutionMapper).toDto(fullyCoveredStatistics); } @Test void getStatisticsForTestPlan_WithNotCoveredPlan_ShouldReturnCorrectStatistics() { // Given - var notCoveredStatistics = new TmsTestPlanExecutionStatisticRS(10, 0); + var notCoveredStatistics = new TmsTestPlanExecutionStatistic(10, 0); when(tmsTestPlanStatisticsRepository.getExecutionStatisticsByTestPlanId(testPlanId)) .thenReturn(notCoveredStatistics); - when(tmsTestPlanExecutionMapper.toDto(notCoveredStatistics)).thenReturn(notCoveredStatistics); // When var result = sut.getStatisticsForTestPlan(testPlanId); @@ -174,17 +164,15 @@ void getStatisticsForTestPlan_WithNotCoveredPlan_ShouldReturnCorrectStatistics() assertEquals(10L, result.getTotal()); assertEquals(0L, result.getCovered()); verify(tmsTestPlanStatisticsRepository).getExecutionStatisticsByTestPlanId(testPlanId); - verify(tmsTestPlanExecutionMapper).toDto(notCoveredStatistics); } @Test void getStatisticsForTestPlan_WithLargeNumbers_ShouldReturnCorrectStatistics() { // Given - var largeStatistics = new TmsTestPlanExecutionStatisticRS(1000, 750); + var largeStatistics = new TmsTestPlanExecutionStatistic(1000, 750); when(tmsTestPlanStatisticsRepository.getExecutionStatisticsByTestPlanId(testPlanId)) .thenReturn(largeStatistics); - when(tmsTestPlanExecutionMapper.toDto(largeStatistics)).thenReturn(largeStatistics); // When var result = sut.getStatisticsForTestPlan(testPlanId); @@ -194,7 +182,6 @@ void getStatisticsForTestPlan_WithLargeNumbers_ShouldReturnCorrectStatistics() { assertEquals(1000L, result.getTotal()); assertEquals(750L, result.getCovered()); verify(tmsTestPlanStatisticsRepository).getExecutionStatisticsByTestPlanId(testPlanId); - verify(tmsTestPlanExecutionMapper).toDto(largeStatistics); } @Test @@ -214,7 +201,6 @@ void enrichWithStatistics_WhenTestPlanExists_ShouldReturnEnrichedTestPlan() { // Given when(tmsTestPlanStatisticsRepository.getExecutionStatisticsByTestPlanId(testPlanId)) .thenReturn(statistics); - when(tmsTestPlanExecutionMapper.toDto(statistics)).thenReturn(statistics); // When var result = sut.enrichWithStatistics(testPlan); @@ -230,7 +216,6 @@ void enrichWithStatistics_WhenTestPlanExists_ShouldReturnEnrichedTestPlan() { assertEquals(10L, result.getExecutionStatistic().getTotal()); assertEquals(5L, result.getExecutionStatistic().getCovered()); verify(tmsTestPlanStatisticsRepository).getExecutionStatisticsByTestPlanId(testPlanId); - verify(tmsTestPlanExecutionMapper).toDto(statistics); } @Test @@ -286,14 +271,13 @@ void enrichWithStatistics_WhenExceptionOccurs_ShouldReturnTestPlanWithEmptyStati @Test void enrichWithStatistics_WithZeroStatistics_ShouldReturnEnrichedTestPlan() { // Given - var zeroStatistics = TmsTestPlanExecutionStatisticRS.builder() + var zeroStatistics = TmsTestPlanExecutionStatistic.builder() .total(0L) .covered(0L) .build(); when(tmsTestPlanStatisticsRepository.getExecutionStatisticsByTestPlanId(testPlanId)) .thenReturn(zeroStatistics); - when(tmsTestPlanExecutionMapper.toDto(zeroStatistics)).thenReturn(zeroStatistics); // When var result = sut.enrichWithStatistics(testPlan); @@ -305,7 +289,6 @@ void enrichWithStatistics_WithZeroStatistics_ShouldReturnEnrichedTestPlan() { assertEquals(0L, result.getExecutionStatistic().getTotal()); assertEquals(0L, result.getExecutionStatistic().getCovered()); verify(tmsTestPlanStatisticsRepository).getExecutionStatisticsByTestPlanId(testPlanId); - verify(tmsTestPlanExecutionMapper).toDto(zeroStatistics); } @Test @@ -316,11 +299,10 @@ void enrichWithStatistics_WithDifferentTestPlanIds_ShouldLoadCorrectStatistics() anotherTestPlan.setId(anotherTestPlanId); anotherTestPlan.setName("Another Test Plan"); - var anotherStatistics = new TmsTestPlanExecutionStatisticRS(20, 15); + var anotherStatistics = new TmsTestPlanExecutionStatistic(20, 15); when(tmsTestPlanStatisticsRepository.getExecutionStatisticsByTestPlanId(anotherTestPlanId)) .thenReturn(anotherStatistics); - when(tmsTestPlanExecutionMapper.toDto(anotherStatistics)).thenReturn(anotherStatistics); // When var result = sut.enrichWithStatistics(anotherTestPlan); @@ -332,6 +314,5 @@ void enrichWithStatistics_WithDifferentTestPlanIds_ShouldLoadCorrectStatistics() assertEquals(20L, result.getExecutionStatistic().getTotal()); assertEquals(15L, result.getExecutionStatistic().getCovered()); verify(tmsTestPlanStatisticsRepository).getExecutionStatisticsByTestPlanId(anotherTestPlanId); - verify(tmsTestPlanExecutionMapper).toDto(anotherStatistics); } } diff --git a/src/test/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanServiceImplTest.java b/src/test/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanServiceImplTest.java index a5be6c41bc..71cd6ccb26 100644 --- a/src/test/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanServiceImplTest.java +++ b/src/test/java/com/epam/ta/reportportal/core/tms/service/TmsTestPlanServiceImplTest.java @@ -10,6 +10,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -19,11 +20,13 @@ import com.epam.reportportal.rules.exception.ErrorType; import com.epam.reportportal.rules.exception.ReportPortalException; import com.epam.ta.reportportal.commons.querygen.Filter; +import com.epam.ta.reportportal.core.tms.dto.DuplicateTmsTestPlanRS; import com.epam.ta.reportportal.core.tms.dto.TmsTestPlanRQ; import com.epam.ta.reportportal.core.tms.dto.TmsTestPlanRS; -import com.epam.ta.reportportal.core.tms.dto.batch.BatchOperationError; -import com.epam.ta.reportportal.core.tms.dto.batch.BatchOperationResultRS; +import com.epam.ta.reportportal.core.tms.dto.batch.BatchTestCaseOperationError; +import com.epam.ta.reportportal.core.tms.dto.batch.BatchTestCaseOperationResultRS; import com.epam.ta.reportportal.core.tms.mapper.TmsTestPlanMapper; +import com.epam.ta.reportportal.dao.tms.TmsTestCaseRepository; import com.epam.ta.reportportal.dao.tms.TmsTestPlanRepository; import com.epam.ta.reportportal.dao.tms.TmsTestPlanTestCaseRepository; import com.epam.ta.reportportal.dao.tms.filterable.TmsTestPlanFilterableRepository; @@ -61,6 +64,9 @@ class TmsTestPlanServiceImplTest { @Mock private TmsTestCaseService tmsTestCaseService; + @Mock + private TmsTestCaseRepository tmsTestCaseRepository; + @Mock private TmsTestPlanExecutionService tmsTestPlanExecutionService; @@ -244,7 +250,7 @@ void shouldGetByCriteriaWhenEmpty() { assertNotNull(result.getContent()); assertEquals(0, result.getContent().size()); assertEquals(10, result.getPage().getSize()); - assertEquals(1, result.getPage().getNumber()); // PagedResourcesAssembler добавляет +1 + assertEquals(1, result.getPage().getNumber()); // PagedResourcesAssembler adds +1 assertEquals(0, result.getPage().getTotalElements()); assertEquals(0, result.getPage().getTotalPages()); @@ -358,7 +364,7 @@ void shouldAddTestCasesToPlanSuccessfully() { var projectId = 1L; var testPlanId = 2L; var testCaseIds = List.of(10L, 20L, 30L); - var expectedResult = BatchOperationResultRS.builder() + var expectedResult = BatchTestCaseOperationResultRS.builder() .totalCount(3) .successCount(2) .failureCount(1) @@ -391,11 +397,11 @@ void shouldAddTestCasesToPlanWithErrors() { var projectId = 1L; var testPlanId = 2L; var testCaseIds = List.of(10L, 999L); // 999L doesn't exist - var expectedResult = BatchOperationResultRS.builder() + var expectedResult = BatchTestCaseOperationResultRS.builder() .totalCount(2) .successCount(1) .failureCount(1) - .errors(List.of(new BatchOperationError(999L, "Test case with id 999 not found"))) + .errors(List.of(new BatchTestCaseOperationError(999L, "Test case with id 999 not found"))) .build(); when(testPlanRepository.existsByIdAndProject_Id(testPlanId, projectId)).thenReturn(true); @@ -436,7 +442,7 @@ void shouldRemoveTestCasesFromPlanSuccessfully() { var projectId = 1L; var testPlanId = 2L; var testCaseIds = List.of(10L, 20L); - var expectedResult = BatchOperationResultRS.builder() + var expectedResult = BatchTestCaseOperationResultRS.builder() .totalCount(2) .successCount(2) .failureCount(0) @@ -464,12 +470,12 @@ void shouldRemoveTestCasesFromPlanWithErrors() { var projectId = 1L; var testPlanId = 2L; var testCaseIds = List.of(10L, 999L); // 999L not in plan - var expectedResult = BatchOperationResultRS.builder() + var expectedResult = BatchTestCaseOperationResultRS.builder() .totalCount(2) .successCount(1) .failureCount(1) .errors( - List.of(new BatchOperationError(999L, "Test case with id 999 not found in test plan"))) + List.of(new BatchTestCaseOperationError(999L, "Test case with id 999 not found in test plan"))) .build(); when(tmsTestPlanTestCaseRepository.findTestCaseIdsByTestPlanId(testPlanId)) @@ -490,7 +496,7 @@ void shouldHandleEmptyTestCaseListForAdd() { var projectId = 1L; var testPlanId = 2L; var testCaseIds = List.of(); - var expectedResult = BatchOperationResultRS.builder() + var expectedResult = BatchTestCaseOperationResultRS.builder() .totalCount(0) .successCount(0) .failureCount(0) @@ -516,7 +522,7 @@ void shouldHandleEmptyTestCaseListForRemove() { var projectId = 1L; var testPlanId = 2L; var testCaseIds = List.of(); - var expectedResult = BatchOperationResultRS.builder() + var expectedResult = BatchTestCaseOperationResultRS.builder() .totalCount(0) .successCount(0) .failureCount(0) @@ -534,7 +540,7 @@ void shouldHandleEmptyTestCaseListForRemove() { verify(tmsTestPlanMapper).convertToRS(eq(0), eq(0), anyList()); } - // Tests for new helper methods + // Tests for helper methods @Test void shouldAddTestCaseToTestPlanSuccessfully() { @@ -625,4 +631,338 @@ void shouldHandleExceptionWhenRemovingSingleTestCaseFromPlan() { assertFalse(result); verify(tmsTestPlanTestCaseRepository).deleteByTestPlanIdAndTestCaseId(testPlanId, testCaseId); } + + // Tests for duplicate method + + @Test + void shouldDuplicateTestPlanSuccessfully() { + var projectId = 1L; + var testPlanId = 2L; + var originalTestPlan = new TmsTestPlan(); + originalTestPlan.setId(testPlanId); + + var duplicatedTestPlan = new TmsTestPlan(); + duplicatedTestPlan.setId(3L); + + var originalTestCaseIds = List.of(10L, 20L); + var duplicatedTestCaseIds = List.of(30L, 40L); + + var duplicationResult = BatchTestCaseOperationResultRS.builder() + .totalCount(2) + .successCount(2) + .failureCount(0) + .successTestCaseIds(duplicatedTestCaseIds) + .errors(List.of()) + .build(); + + var addToPlanResult = BatchTestCaseOperationResultRS.builder() + .totalCount(2) + .successCount(2) + .failureCount(0) + .errors(List.of()) + .build(); + + var combinedResult = BatchTestCaseOperationResultRS.builder() + .totalCount(2) + .successCount(2) + .failureCount(0) + .errors(List.of()) + .build(); + + var expectedResponse = DuplicateTmsTestPlanRS.builder() + .id(3L) + .duplicationStatistic(combinedResult) + .build(); + + when(testPlanRepository.findByIdAndProjectId(testPlanId, projectId)) + .thenReturn(Optional.of(originalTestPlan)); + when(tmsTestPlanMapper.duplicateTestPlan(originalTestPlan)).thenReturn(duplicatedTestPlan); + when(testPlanRepository.save(duplicatedTestPlan)).thenReturn(duplicatedTestPlan); + when(tmsTestPlanTestCaseRepository.findTestCaseIdsByTestPlanId(testPlanId)) + .thenReturn(originalTestCaseIds); + when(tmsTestCaseService.duplicateTestCases(projectId, originalTestCaseIds)) + .thenReturn(duplicationResult); + when(tmsTestCaseService.getExistingTestCaseIds(projectId, duplicatedTestCaseIds)) + .thenReturn(duplicatedTestCaseIds); + when(tmsTestPlanTestCaseRepository.findTestCaseIdsByTestPlanId(3L)) + .thenReturn(List.of()); + when(tmsTestPlanTestCaseRepository.insertTestPlanTestCaseIgnoreConflict(eq(3L), anyLong())) + .thenReturn(1); + when(testPlanRepository.existsByIdAndProject_Id(3L, projectId)).thenReturn(true); + when(tmsTestPlanMapper.convertToRS(anyInt(), anyInt(), anyList())) + .thenReturn(addToPlanResult); + when(tmsTestPlanMapper.combineDuplicateTestPlanBatchResults(duplicationResult, addToPlanResult)) + .thenReturn(combinedResult); + when(tmsTestPlanMapper.buildDuplicateTestPlanResponse(duplicatedTestPlan, combinedResult)) + .thenReturn(expectedResponse); + + var result = sut.duplicate(projectId, testPlanId); + + assertNotNull(result); + assertEquals(3L, result.getId()); + verify(testPlanRepository).findByIdAndProjectId(testPlanId, projectId); + verify(tmsTestPlanMapper).duplicateTestPlan(originalTestPlan); + verify(testPlanRepository).save(duplicatedTestPlan); + verify(tmsTestPlanAttributeService).duplicateTestPlanAttributes(originalTestPlan, duplicatedTestPlan); + verify(tmsTestPlanTestCaseRepository).findTestCaseIdsByTestPlanId(testPlanId); + verify(tmsTestCaseService).duplicateTestCases(projectId, originalTestCaseIds); + verify(tmsTestPlanMapper).buildDuplicateTestPlanResponse(duplicatedTestPlan, combinedResult); + } + + @Test + void shouldDuplicateTestPlanWithoutTestCases() { + var projectId = 1L; + var testPlanId = 2L; + var originalTestPlan = new TmsTestPlan(); + originalTestPlan.setId(testPlanId); + + var duplicatedTestPlan = new TmsTestPlan(); + duplicatedTestPlan.setId(3L); + + var emptyResult = BatchTestCaseOperationResultRS.builder() + .totalCount(0) + .successCount(0) + .failureCount(0) + .errors(List.of()) + .build(); + + var expectedResponse = DuplicateTmsTestPlanRS.builder() + .id(3L) + .duplicationStatistic(emptyResult) + .build(); + + when(testPlanRepository.findByIdAndProjectId(testPlanId, projectId)) + .thenReturn(Optional.of(originalTestPlan)); + when(tmsTestPlanMapper.duplicateTestPlan(originalTestPlan)).thenReturn(duplicatedTestPlan); + when(testPlanRepository.save(duplicatedTestPlan)).thenReturn(duplicatedTestPlan); + when(tmsTestPlanTestCaseRepository.findTestCaseIdsByTestPlanId(testPlanId)) + .thenReturn(List.of()); + when(tmsTestPlanMapper.createFailedBatchResult(eq(Collections.emptyList()), anyString())) + .thenReturn(emptyResult); + when(tmsTestPlanMapper.buildDuplicateTestPlanResponse(duplicatedTestPlan, emptyResult)) + .thenReturn(expectedResponse); + + var result = sut.duplicate(projectId, testPlanId); + + assertNotNull(result); + assertEquals(3L, result.getId()); + verify(testPlanRepository).findByIdAndProjectId(testPlanId, projectId); + verify(tmsTestPlanMapper).duplicateTestPlan(originalTestPlan); + verify(testPlanRepository).save(duplicatedTestPlan); + verify(tmsTestPlanAttributeService).duplicateTestPlanAttributes(originalTestPlan, duplicatedTestPlan); + verify(tmsTestCaseService, never()).duplicateTestCases(anyLong(), anyList()); + verify(tmsTestPlanMapper).buildDuplicateTestPlanResponse(duplicatedTestPlan, emptyResult); + } + + @Test + void shouldThrowNotFoundWhenDuplicatingNonExistentTestPlan() { + var projectId = 1L; + var testPlanId = 2L; + + when(testPlanRepository.findByIdAndProjectId(testPlanId, projectId)) + .thenReturn(Optional.empty()); + + var exception = assertThrows(ReportPortalException.class, () -> + sut.duplicate(projectId, testPlanId) + ); + + assertEquals(ErrorType.NOT_FOUND, exception.getErrorType()); + verify(testPlanRepository).findByIdAndProjectId(testPlanId, projectId); + verify(tmsTestPlanMapper, never()).duplicateTestPlan(any()); + verify(testPlanRepository, never()).save(any()); + } + + @Test + void shouldHandlePartialTestCaseDuplicationFailure() { + var projectId = 1L; + var testPlanId = 2L; + var originalTestPlan = new TmsTestPlan(); + originalTestPlan.setId(testPlanId); + + var duplicatedTestPlan = new TmsTestPlan(); + duplicatedTestPlan.setId(3L); + + var originalTestCaseIds = List.of(10L, 20L, 30L); + var duplicatedTestCaseIds = List.of(40L, 50L); // Only 2 out of 3 duplicated + + var duplicationResult = BatchTestCaseOperationResultRS.builder() + .totalCount(3) + .successCount(2) + .failureCount(1) + .successTestCaseIds(duplicatedTestCaseIds) + .errors(List.of(new BatchTestCaseOperationError(30L, "Failed to duplicate"))) + .build(); + + var addToPlanResult = BatchTestCaseOperationResultRS.builder() + .totalCount(2) + .successCount(2) + .failureCount(0) + .errors(List.of()) + .build(); + + var combinedResult = BatchTestCaseOperationResultRS.builder() + .totalCount(3) + .successCount(2) + .failureCount(1) + .errors(List.of(new BatchTestCaseOperationError(30L, "Failed to duplicate"))) + .build(); + + var expectedResponse = DuplicateTmsTestPlanRS.builder() + .id(3L) + .duplicationStatistic(combinedResult) + .build(); + + when(testPlanRepository.findByIdAndProjectId(testPlanId, projectId)) + .thenReturn(Optional.of(originalTestPlan)); + when(tmsTestPlanMapper.duplicateTestPlan(originalTestPlan)).thenReturn(duplicatedTestPlan); + when(testPlanRepository.save(duplicatedTestPlan)).thenReturn(duplicatedTestPlan); + when(tmsTestPlanTestCaseRepository.findTestCaseIdsByTestPlanId(testPlanId)) + .thenReturn(originalTestCaseIds); + when(tmsTestCaseService.duplicateTestCases(projectId, originalTestCaseIds)) + .thenReturn(duplicationResult); + when(tmsTestCaseService.getExistingTestCaseIds(projectId, duplicatedTestCaseIds)) + .thenReturn(duplicatedTestCaseIds); + when(tmsTestPlanTestCaseRepository.findTestCaseIdsByTestPlanId(3L)) + .thenReturn(List.of()); + when(tmsTestPlanTestCaseRepository.insertTestPlanTestCaseIgnoreConflict(eq(3L), anyLong())) + .thenReturn(1); + when(testPlanRepository.existsByIdAndProject_Id(3L, projectId)).thenReturn(true); + when(tmsTestPlanMapper.convertToRS(anyInt(), anyInt(), anyList())) + .thenReturn(addToPlanResult); + when(tmsTestPlanMapper.combineDuplicateTestPlanBatchResults(duplicationResult, addToPlanResult)) + .thenReturn(combinedResult); + when(tmsTestPlanMapper.buildDuplicateTestPlanResponse(duplicatedTestPlan, combinedResult)) + .thenReturn(expectedResponse); + + var result = sut.duplicate(projectId, testPlanId); + + assertNotNull(result); + assertEquals(3L, result.getId()); + assertNotNull(result.getDuplicationStatistic()); + assertEquals(1, result.getDuplicationStatistic().getFailureCount()); + verify(tmsTestCaseService).duplicateTestCases(projectId, originalTestCaseIds); + verify(tmsTestPlanMapper).combineDuplicateTestPlanBatchResults(duplicationResult, addToPlanResult); + } + + @Test + void shouldHandleCompleteTestCaseDuplicationFailure() { + var projectId = 1L; + var testPlanId = 2L; + var originalTestPlan = new TmsTestPlan(); + originalTestPlan.setId(testPlanId); + + var duplicatedTestPlan = new TmsTestPlan(); + duplicatedTestPlan.setId(3L); + + var originalTestCaseIds = List.of(10L, 20L); + + var duplicationResult = BatchTestCaseOperationResultRS.builder() + .totalCount(2) + .successCount(0) + .failureCount(2) + .successTestCaseIds(List.of()) + .errors(List.of( + new BatchTestCaseOperationError(10L, "Failed to duplicate"), + new BatchTestCaseOperationError(20L, "Failed to duplicate") + )) + .build(); + + var expectedResponse = DuplicateTmsTestPlanRS.builder() + .id(3L) + .duplicationStatistic(duplicationResult) + .build(); + + when(testPlanRepository.findByIdAndProjectId(testPlanId, projectId)) + .thenReturn(Optional.of(originalTestPlan)); + when(tmsTestPlanMapper.duplicateTestPlan(originalTestPlan)).thenReturn(duplicatedTestPlan); + when(testPlanRepository.save(duplicatedTestPlan)).thenReturn(duplicatedTestPlan); + when(tmsTestPlanTestCaseRepository.findTestCaseIdsByTestPlanId(testPlanId)) + .thenReturn(originalTestCaseIds); + when(tmsTestCaseService.duplicateTestCases(projectId, originalTestCaseIds)) + .thenReturn(duplicationResult); + when(tmsTestPlanMapper.buildDuplicateTestPlanResponse(duplicatedTestPlan, duplicationResult)) + .thenReturn(expectedResponse); + + var result = sut.duplicate(projectId, testPlanId); + + assertNotNull(result); + assertEquals(3L, result.getId()); + assertNotNull(result.getDuplicationStatistic()); + assertEquals(2, result.getDuplicationStatistic().getFailureCount()); + assertEquals(0, result.getDuplicationStatistic().getSuccessCount()); + verify(tmsTestCaseService).duplicateTestCases(projectId, originalTestCaseIds); + verify(tmsTestCaseService, never()).getExistingTestCaseIds(anyLong(), anyList()); + verify(tmsTestPlanMapper, never()).combineDuplicateTestPlanBatchResults(any(), any()); + } + + @Test + void shouldHandleExceptionWhenAddingDuplicatedTestCasesToPlan() { + var projectId = 1L; + var testPlanId = 2L; + var originalTestPlan = new TmsTestPlan(); + originalTestPlan.setId(testPlanId); + + var duplicatedTestPlan = new TmsTestPlan(); + duplicatedTestPlan.setId(3L); + + var originalTestCaseIds = List.of(10L, 20L); + var duplicatedTestCaseIds = List.of(30L, 40L); + + var duplicationResult = BatchTestCaseOperationResultRS.builder() + .totalCount(2) + .successCount(2) + .failureCount(0) + .successTestCaseIds(duplicatedTestCaseIds) + .errors(List.of()) + .build(); + + var failedAddResult = BatchTestCaseOperationResultRS.builder() + .totalCount(2) + .successCount(0) + .failureCount(2) + .errors(List.of( + new BatchTestCaseOperationError(30L, "Failed to add duplicated test case to plan: Database error"), + new BatchTestCaseOperationError(40L, "Failed to add duplicated test case to plan: Database error") + )) + .build(); + + var combinedResult = BatchTestCaseOperationResultRS.builder() + .totalCount(2) + .successCount(0) + .failureCount(2) + .errors(failedAddResult.getErrors()) + .build(); + + var expectedResponse = DuplicateTmsTestPlanRS.builder() + .id(3L) + .duplicationStatistic(combinedResult) + .build(); + + when(testPlanRepository.findByIdAndProjectId(testPlanId, projectId)) + .thenReturn(Optional.of(originalTestPlan)); + when(tmsTestPlanMapper.duplicateTestPlan(originalTestPlan)).thenReturn(duplicatedTestPlan); + when(testPlanRepository.save(duplicatedTestPlan)).thenReturn(duplicatedTestPlan); + when(tmsTestPlanTestCaseRepository.findTestCaseIdsByTestPlanId(testPlanId)) + .thenReturn(originalTestCaseIds); + when(tmsTestCaseService.duplicateTestCases(projectId, originalTestCaseIds)) + .thenReturn(duplicationResult); + when(testPlanRepository.existsByIdAndProject_Id(3L, projectId)) + .thenThrow(new RuntimeException("Database error")); + when(tmsTestPlanMapper.createFailedBatchResult(duplicatedTestCaseIds, + "Failed to add duplicated test case to plan: Database error")) + .thenReturn(failedAddResult); + when(tmsTestPlanMapper.combineDuplicateTestPlanBatchResults(duplicationResult, failedAddResult)) + .thenReturn(combinedResult); + when(tmsTestPlanMapper.buildDuplicateTestPlanResponse(duplicatedTestPlan, combinedResult)) + .thenReturn(expectedResponse); + + var result = sut.duplicate(projectId, testPlanId); + + assertNotNull(result); + assertEquals(3L, result.getId()); + assertNotNull(result.getDuplicationStatistic()); + assertEquals(2, result.getDuplicationStatistic().getFailureCount()); + verify(tmsTestPlanMapper).createFailedBatchResult(eq(duplicatedTestCaseIds), anyString()); + verify(tmsTestPlanMapper).combineDuplicateTestPlanBatchResults(duplicationResult, failedAddResult); + } } diff --git a/src/test/resources/db/tms/tms-product-version/tms-test-plan-fill.sql b/src/test/resources/db/tms/tms-product-version/tms-test-plan-fill.sql index 93ca8a0a61..26114b61bc 100644 --- a/src/test/resources/db/tms/tms-product-version/tms-test-plan-fill.sql +++ b/src/test/resources/db/tms/tms-product-version/tms-test-plan-fill.sql @@ -10,7 +10,6 @@ values (3, 'name3', 1); insert into tms_attribute (id, "key") values (3, 'value3'); - insert into tms_product_version (id, documentation, "version", project_id) values (4, 'documentation4', 'version4', 1); @@ -26,7 +25,6 @@ values (4, 'value4'); insert into tms_test_plan (id, "name", description, project_id, environment_id, product_version_id) values (4, 'name4', 'description4', 1, 4, 4); - insert into tms_product_version (id, documentation, "version", project_id) values (5, 'documentation5', 'version5', 1); @@ -42,7 +40,6 @@ values (5, 'value5'); insert into tms_test_plan (id, "name", description, project_id, environment_id, product_version_id) values (5, 'name5', 'description5', 1, 5, 5); - insert into tms_product_version (id, documentation, "version", project_id) values (6, 'documentation6', 'version6', 1); @@ -67,12 +64,35 @@ values (101, 'Test Plan without Executions', 'Test plan with no test case execut insert into tms_test_plan (id, "name", description, project_id) values (102, 'Test Plan with Mixed Statuses', 'Test plan with mixed execution statuses', 1); +-- Additional test plans for duplication tests +insert into tms_test_plan (id, "name", description, project_id) +values (1, 'Test Plan 1', 'Description for test plan 1', 1); + +insert into tms_test_plan (id, "name", description, project_id) +values (2, 'Test Plan 2', 'Description for test plan 2', 1); + +insert into tms_test_plan (id, "name", description, project_id) +values (3, 'Test Plan 3', 'Description for test plan 3', 1); + -- Test folders for test cases insert into tms_test_folder (id, "name", project_id) values (7, 'Test Folder 1', 1), (8, 'Test Folder 2', 1), (9, 'Test Folder 3', 1); +-- Additional test folders for duplication tests +insert into tms_test_folder (id, "name", description, project_id) +values (4, 'Test Folder 4', 'Description for test folder 4', 1); + +insert into tms_test_folder (id, "name", description, project_id) +values (5, 'Test Folder 5', 'Description for test folder 5', 1); + +insert into tms_test_folder (id, "name", description, project_id) +values (6, 'Test Folder 6', 'Description for test folder 6', 1); + +insert into tms_test_folder (id, "name", description, project_id) +values (10, 'Test Folder 10', 'Description for test folder 10', 1); + -- Test cases for batch operations testing insert into tms_test_case (id, "name", description, test_folder_id) values (7, 'Test Case 7', 'Description for test case 7', 7), @@ -91,6 +111,12 @@ values (7, 'Test Case 7', 'Description for test case 7', 7), (20, 'Test Case 20', 'Description for test case 20', 8), (21, 'Test Case 21', 'Description for test case 21', 9); +-- Test cases for duplication functionality +insert into tms_test_case (id, "name", description, test_folder_id, priority) +values (4, 'Test Case 4', 'Description for test case 4', 4, 'HIGH'), + (5, 'Test Case 5', 'Description for test case 5', 5, 'MEDIUM'), + (6, 'Test Case 6', 'Description for test case 6', 6, 'LOW'); + -- Test cases for execution statistics tests insert into tms_test_case (id, "name", description, test_folder_id) values (100, 'Test Case for Plan 100 - Covered', 'Test case with execution', 7), @@ -113,17 +139,211 @@ values (101, 102), (101, 103); insert into tms_test_plan_test_case (test_plan_id, test_case_id) values (102, 104), (102, 105), (102, 106); +-- Test plan associations for duplication tests +-- Test Plan 1 contains test cases 4, 5, 6 +insert into tms_test_plan_test_case (test_plan_id, test_case_id) +values (1, 4), (1, 5), (1, 6); + +-- Test Plan 2 contains test cases 7, 8, 9 +insert into tms_test_plan_test_case (test_plan_id, test_case_id) +values (2, 7), (2, 8), (2, 9); + +-- Test Plan 3 contains test cases 10, 11, 12 +insert into tms_test_plan_test_case (test_plan_id, test_case_id) +values (3, 10), (3, 11), (3, 12); + +-- Additional attributes for duplication tests +insert into tms_attribute (id, "key") +values (1, 'test1'); + +insert into tms_attribute (id, "key") +values (2, 'test2'); + +insert into tms_attribute (id, "key") +values (7, 'priority'); + +insert into tms_attribute (id, "key") +values (8, 'environment'); + +-- Test plan attributes for duplication testing +insert into tms_test_plan_attribute (test_plan_id, attribute_id, value) +values (4, 4, 'test plan attribute value 4'); + +insert into tms_test_plan_attribute (test_plan_id, attribute_id, value) +values (5, 5, 'test plan attribute value 5'); + +insert into tms_test_plan_attribute (test_plan_id, attribute_id, value) +values (1, 1, 'original plan attribute 1'); + +insert into tms_test_plan_attribute (test_plan_id, attribute_id, value) +values (2, 2, 'original plan attribute 2'); + +-- Test case attributes for duplication testing +insert into tms_test_case_attribute (test_case_id, attribute_id, value) +values (4, 4, 'test case 4 attribute'); + +insert into tms_test_case_attribute (test_case_id, attribute_id, value) +values (5, 5, 'test case 5 attribute'); + +insert into tms_test_case_attribute (test_case_id, attribute_id, value) +values (6, 6, 'test case 6 attribute'); + +insert into tms_test_case_attribute (test_case_id, attribute_id, value) +values (7, 4, 'test case 7 attribute'); + +insert into tms_test_case_attribute (test_case_id, attribute_id, value) +values (8, 5, 'test case 8 attribute'); + +insert into tms_test_case_attribute (test_case_id, attribute_id, value) +values (9, 6, 'test case 9 attribute'); + +insert into tms_test_case_attribute (test_case_id, attribute_id, value) +values (10, 4, 'test case 10 attribute'); + +insert into tms_test_case_attribute (test_case_id, attribute_id, value) +values (11, 5, 'test case 11 attribute'); + +insert into tms_test_case_attribute (test_case_id, attribute_id, value) +values (12, 6, 'test case 12 attribute'); + +-- Test case versions for complete duplication testing +insert into tms_test_case_version (id, test_case_id, "name", is_default, is_draft) +values (104, 4, 'Default Version 4', true, false); + +insert into tms_test_case_version (id, test_case_id, "name", is_default, is_draft) +values (105, 5, 'Default Version 5', true, false); + +insert into tms_test_case_version (id, test_case_id, "name", is_default, is_draft) +values (106, 6, 'Default Version 6', true, false); + +insert into tms_test_case_version (id, test_case_id, "name", is_default, is_draft) +values (107, 7, 'Default Version 7', true, false); + +insert into tms_test_case_version (id, test_case_id, "name", is_default, is_draft) +values (108, 8, 'Default Version 8', true, false); + +insert into tms_test_case_version (id, test_case_id, "name", is_default, is_draft) +values (109, 9, 'Default Version 9', true, false); + +insert into tms_test_case_version (id, test_case_id, "name", is_default, is_draft) +values (110, 10, 'Default Version 10', true, false); + +insert into tms_test_case_version (id, test_case_id, "name", is_default, is_draft) +values (111, 11, 'Default Version 11', true, false); + +insert into tms_test_case_version (id, test_case_id, "name", is_default, is_draft) +values (112, 12, 'Default Version 12', true, false); + +-- Manual scenarios for test cases to ensure complete duplication +insert into tms_manual_scenario (id, test_case_version_id, execution_estimation_time, link_to_requirements, type) +values (104, 104, 30, 'REQ-004', 'TEXT'); + +insert into tms_manual_scenario (id, test_case_version_id, execution_estimation_time, link_to_requirements, type) +values (105, 105, 25, 'REQ-005', 'TEXT'); + +insert into tms_manual_scenario (id, test_case_version_id, execution_estimation_time, link_to_requirements, type) +values (106, 106, 20, 'REQ-006', 'STEPS'); + +insert into tms_manual_scenario (id, test_case_version_id, execution_estimation_time, link_to_requirements, type) +values (107, 107, 35, 'REQ-007', 'TEXT'); + +insert into tms_manual_scenario (id, test_case_version_id, execution_estimation_time, link_to_requirements, type) +values (108, 108, 40, 'REQ-008', 'TEXT'); + +insert into tms_manual_scenario (id, test_case_version_id, execution_estimation_time, link_to_requirements, type) +values (109, 109, 45, 'REQ-009', 'STEPS'); + +insert into tms_manual_scenario (id, test_case_version_id, execution_estimation_time, link_to_requirements, type) +values (110, 110, 50, 'REQ-010', 'TEXT'); + +insert into tms_manual_scenario (id, test_case_version_id, execution_estimation_time, link_to_requirements, type) +values (111, 111, 55, 'REQ-011', 'TEXT'); + +insert into tms_manual_scenario (id, test_case_version_id, execution_estimation_time, link_to_requirements, type) +values (112, 112, 60, 'REQ-012', 'STEPS'); + +-- Text manual scenarios for TEXT type scenarios +insert into tms_text_manual_scenario (manual_scenario_id, instructions, expected_result) +values (104, 'Execute test case 4 functionality', 'System should respond correctly for TC4'); + +insert into tms_text_manual_scenario (manual_scenario_id, instructions, expected_result) +values (105, 'Execute test case 5 functionality', 'System should respond correctly for TC5'); + +insert into tms_text_manual_scenario (manual_scenario_id, instructions, expected_result) +values (107, 'Execute test case 7 functionality', 'System should respond correctly for TC7'); + +insert into tms_text_manual_scenario (manual_scenario_id, instructions, expected_result) +values (108, 'Execute test case 8 functionality', 'System should respond correctly for TC8'); + +insert into tms_text_manual_scenario (manual_scenario_id, instructions, expected_result) +values (110, 'Execute test case 10 functionality', 'System should respond correctly for TC10'); + +insert into tms_text_manual_scenario (manual_scenario_id, instructions, expected_result) +values (111, 'Execute test case 11 functionality', 'System should respond correctly for TC11'); + +-- Steps manual scenarios for STEPS type scenarios +insert into tms_steps_manual_scenario (manual_scenario_id) +values (106), (109), (112); + +-- Steps for STEPS type scenarios (using correct column names: instructions, expected_result, steps_manual_scenario_id) +insert into tms_step (id, instructions, expected_result, steps_manual_scenario_id) +values (104, 'Step 1 for TC6: Initialize system', 'System should be ready', 106); + +insert into tms_step (id, instructions, expected_result, steps_manual_scenario_id) +values (105, 'Step 2 for TC6: Execute action', 'Action should complete', 106); + +insert into tms_step (id, instructions, expected_result, steps_manual_scenario_id) +values (106, 'Step 1 for TC9: Setup test environment', 'Environment should be ready', 109); + +insert into tms_step (id, instructions, expected_result, steps_manual_scenario_id) +values (107, 'Step 2 for TC9: Run test scenario', 'Scenario should pass', 109); + +insert into tms_step (id, instructions, expected_result, steps_manual_scenario_id) +values (108, 'Step 1 for TC12: Prepare test data', 'Data should be ready', 112); + +insert into tms_step (id, instructions, expected_result, steps_manual_scenario_id) +values (109, 'Step 2 for TC12: Execute test', 'Test should complete', 112); + +-- Manual scenario preconditions for complete testing +insert into tms_manual_scenario_preconditions (id, manual_scenario_id, value) +values (104, 104, 'System must be initialized for TC4'); + +insert into tms_manual_scenario_preconditions (id, manual_scenario_id, value) +values (105, 105, 'User must be authenticated for TC5'); + +insert into tms_manual_scenario_preconditions (id, manual_scenario_id, value) +values (106, 106, 'Test environment ready for TC6'); + +insert into tms_manual_scenario_preconditions (id, manual_scenario_id, value) +values (107, 107, 'Database initialized for TC7'); + +insert into tms_manual_scenario_preconditions (id, manual_scenario_id, value) +values (108, 108, 'Network connectivity for TC8'); + +insert into tms_manual_scenario_preconditions (id, manual_scenario_id, value) +values (109, 109, 'Test data prepared for TC9'); + +insert into tms_manual_scenario_preconditions (id, manual_scenario_id, value) +values (110, 110, 'System configuration for TC10'); + +insert into tms_manual_scenario_preconditions (id, manual_scenario_id, value) +values (111, 111, 'Application deployed for TC11'); + +insert into tms_manual_scenario_preconditions (id, manual_scenario_id, value) +values (112, 112, 'Test environment ready for TC12'); + +-- Launch data for execution statistics testing insert into launch (id, uuid, project_id, user_id, name, description, start_time, end_time, number, last_modified, mode, status, has_retries, rerun, approximate_duration, retention_policy, test_plan_id) values (200, '550e8400-e29b-41d4-a716-446655440200', 1, 1, 'Execution Stats Launch 1', 'Launch for testing execution statistics', '2023-10-10 10:00:00.000000', '2023-10-10 11:00:00.000000', 200, '2023-10-10 11:00:00.000000', 'DEFAULT', 'PASSED', false, false, 0, 'REGULAR', 100); insert into launch (id, uuid, project_id, user_id, name, description, start_time, end_time, number, last_modified, mode, status, has_retries, rerun, approximate_duration, retention_policy, test_plan_id) values (201, '550e8400-e29b-41d4-a716-446655440201', 1, 1, 'Execution Stats Launch 2', 'Launch for testing mixed execution statuses', '2023-10-11 10:00:00.000000', '2023-10-11 11:00:00.000000', 201, '2023-10-11 11:00:00.000000', 'DEFAULT', 'FAILED', false, false, 0, 'REGULAR', 102); +-- Test items for executions insert into test_item (item_id, uuid, name, code_ref, type, start_time, description, last_modified, path, unique_id, test_case_id, has_children, has_retries, has_stats, parent_id, retry_of, launch_id, test_case_hash) values (2000, '550e8400-e29b-41d4-a716-446655442000', 'Execution Stats Test Item 100', 'com.test.ExecutionStats100', 'TEST', '2023-10-10 10:30:00.000000', 'Test execution for statistics test case 100', '2023-10-10 10:35:00.000000', '2000', 'exec-stats-tc-100', null, false, false, true, null, null, 200, 2839500); -- Test items for test plan 102 with mixed statuses --- Test item for test case 104 (будет PASSED через test_item_results) insert into test_item (item_id, uuid, name, code_ref, type, start_time, description, last_modified, path, unique_id, test_case_id, has_children, has_retries, has_stats, parent_id, retry_of, launch_id, test_case_hash) values (2040, '550e8400-e29b-41d4-a716-446655442040', 'Mixed Status Test Item 104', 'com.test.MixedStatus104', 'TEST', '2023-10-11 10:30:00.000000', 'Test execution with PASSED status', '2023-10-11 10:35:00.000000', '2040', 'mixed-status-tc-104', null, false, false, true, null, null, 201, 2839501); @@ -133,28 +353,23 @@ values (2050, '550e8400-e29b-41d4-a716-446655442050', 'Mixed Status Test Item 10 insert into test_item (item_id, uuid, name, code_ref, type, start_time, description, last_modified, path, unique_id, test_case_id, has_children, has_retries, has_stats, parent_id, retry_of, launch_id, test_case_hash) values (2060, '550e8400-e29b-41d4-a716-446655442060', 'Mixed Status Test Item 106', 'com.test.MixedStatus106', 'TEST', '2023-10-11 10:50:00.000000', 'Test execution with SKIPPED status', '2023-10-11 10:55:00.000000', '2060', 'mixed-status-tc-106', null, false, false, true, null, null, 201, 2839503); +-- Test item results insert into test_item_results (result_id, status, end_time, duration) values (2000, 'PASSED', '2023-10-10 10:35:00.000000', 300.0); --- Results for mixed status test items --- Result for test item 2040 (PASSED - should be covered) insert into test_item_results (result_id, status, end_time, duration) values (2040, 'PASSED', '2023-10-11 10:35:00.000000', 300.0); --- Result for test item 2050 (FAILED - should be covered) insert into test_item_results (result_id, status, end_time, duration) values (2050, 'FAILED', '2023-10-11 10:45:00.000000', 300.0); --- Result for test item 2060 (SKIPPED - should NOT be covered) insert into test_item_results (result_id, status, end_time, duration) values (2060, 'SKIPPED', '2023-10-11 10:55:00.000000', 300.0); --- NEW: TMS Test Case Executions for execution statistics --- Execution for test case 100 (covered) +-- TMS Test Case Executions for execution statistics insert into tms_test_case_execution (id, test_case_id, test_item_id, test_case_snapshot) values (10, 100, 2000, '{"id": 100, "name": "Test Case for Plan 100 - Covered"}'); --- Executions for test plan 102 (mixed statuses) insert into tms_test_case_execution (id, test_case_id, test_item_id, test_case_snapshot) values (11, 104, 2040, '{"id": 104, "name": "Test Case for Plan 102 - Passed"}'); @@ -164,8 +379,13 @@ values (12, 105, 2050, '{"id": 105, "name": "Test Case for Plan 102 - Failed"}') insert into tms_test_case_execution (id, test_case_id, test_item_id, test_case_snapshot) values (13, 106, 2060, '{"id": 106, "name": "Test Case for Plan 102 - Skipped"}'); --- Update sequences +-- Update sequences for new IDs SELECT setval('tms_test_plan_id_seq', (SELECT COALESCE(MAX(id), 1) FROM tms_test_plan)); SELECT setval('tms_test_folder_id_seq', (SELECT COALESCE(MAX(id), 1) FROM tms_test_folder)); SELECT setval('tms_test_case_id_seq', (SELECT COALESCE(MAX(id), 1) FROM tms_test_case)); SELECT setval('tms_test_case_execution_id_seq', (SELECT COALESCE(MAX(id), 1) FROM tms_test_case_execution)); +SELECT setval('tms_test_case_version_id_seq', (SELECT COALESCE(MAX(id), 1) FROM tms_test_case_version)); +SELECT setval('tms_manual_scenario_id_seq', (SELECT COALESCE(MAX(id), 1) FROM tms_manual_scenario)); +SELECT setval('tms_manual_scenario_preconditions_id_seq', (SELECT COALESCE(MAX(id), 1) FROM tms_manual_scenario_preconditions)); +SELECT setval('tms_step_id_seq', (SELECT COALESCE(MAX(id), 1) FROM tms_step)); +SELECT setval('tms_attribute_id_seq', (SELECT COALESCE(MAX(id), 1) FROM tms_attribute));