diff --git a/openbas-api/src/main/java/io/openbas/rest/exercise/ExerciseApi.java b/openbas-api/src/main/java/io/openbas/rest/exercise/ExerciseApi.java index cb3d8fc788..34eb83097c 100644 --- a/openbas-api/src/main/java/io/openbas/rest/exercise/ExerciseApi.java +++ b/openbas-api/src/main/java/io/openbas/rest/exercise/ExerciseApi.java @@ -401,12 +401,13 @@ public Exercise duplicateExercise(@PathVariable @NotBlank final String exerciseI @PreAuthorize("isExercisePlanner(#exerciseId)") @Transactional(rollbackOn = Exception.class) public Exercise updateExerciseInformation( - @PathVariable String exerciseId, @Valid @RequestBody ExerciseInput input) { + @PathVariable String exerciseId, @Valid @RequestBody UpdateExerciseInput input) { Exercise exercise = exerciseRepository.findById(exerciseId).orElseThrow(ElementNotFoundException::new); + Set currentTagList = exercise.getTags(); exercise.setTags(iterableToSet(this.tagRepository.findAllById(input.getTagIds()))); exercise.setUpdateAttributes(input); - return exerciseRepository.save(exercise); + return exerciseService.updateExercice(exercise, currentTagList, input.isApplyTagRule()); } @PutMapping(EXERCISE_URI + "/{exerciseId}/start_date") @@ -432,8 +433,9 @@ public Exercise updateExerciseTags( @PathVariable String exerciseId, @Valid @RequestBody ExerciseUpdateTagsInput input) { Exercise exercise = exerciseRepository.findById(exerciseId).orElseThrow(ElementNotFoundException::new); + Set currentTagList = exercise.getTags(); exercise.setTags(iterableToSet(tagRepository.findAllById(input.getTagIds()))); - return exerciseRepository.save(exercise); + return exerciseService.updateExercice(exercise, currentTagList, input.isApplyTagRule()); } @PutMapping(EXERCISE_URI + "/{exerciseId}/logos") diff --git a/openbas-api/src/main/java/io/openbas/rest/exercise/form/ExerciseUpdateTagsInput.java b/openbas-api/src/main/java/io/openbas/rest/exercise/form/ExerciseUpdateTagsInput.java index bfc1f7c834..3869fcd545 100644 --- a/openbas-api/src/main/java/io/openbas/rest/exercise/form/ExerciseUpdateTagsInput.java +++ b/openbas-api/src/main/java/io/openbas/rest/exercise/form/ExerciseUpdateTagsInput.java @@ -12,4 +12,7 @@ public class ExerciseUpdateTagsInput { @JsonProperty("exercise_tags") private List tagIds = new ArrayList<>(); + + @JsonProperty("apply_tag_rule") + private boolean applyTagRule = false; } diff --git a/openbas-api/src/main/java/io/openbas/rest/exercise/form/UpdateExerciseInput.java b/openbas-api/src/main/java/io/openbas/rest/exercise/form/UpdateExerciseInput.java new file mode 100644 index 0000000000..5299537052 --- /dev/null +++ b/openbas-api/src/main/java/io/openbas/rest/exercise/form/UpdateExerciseInput.java @@ -0,0 +1,16 @@ +package io.openbas.rest.exercise.form; + +import static io.openbas.config.AppConfig.*; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Data +public class UpdateExerciseInput extends ExerciseInput { + @JsonProperty("apply_tag_rule") + private boolean applyTagRule = false; +} diff --git a/openbas-api/src/main/java/io/openbas/rest/exercise/service/ExerciseService.java b/openbas-api/src/main/java/io/openbas/rest/exercise/service/ExerciseService.java index 8788f92a95..464f670262 100644 --- a/openbas-api/src/main/java/io/openbas/rest/exercise/service/ExerciseService.java +++ b/openbas-api/src/main/java/io/openbas/rest/exercise/service/ExerciseService.java @@ -24,7 +24,9 @@ import io.openbas.rest.exercise.form.ExercisesGlobalScoresInput; import io.openbas.rest.exercise.response.ExercisesGlobalScoresOutput; import io.openbas.rest.inject.service.InjectDuplicateService; +import io.openbas.rest.inject.service.InjectService; import io.openbas.service.GrantService; +import io.openbas.service.TagRuleService; import io.openbas.service.TeamService; import io.openbas.service.VariableService; import io.openbas.utils.AtomicTestingUtils; @@ -68,6 +70,8 @@ public class ExerciseService { private final InjectDuplicateService injectDuplicateService; private final TeamService teamService; private final VariableService variableService; + private final TagRuleService tagRuleService; + private final InjectService injectService; private final ExerciseMapper exerciseMapper; private final InjectMapper injectMapper; @@ -614,4 +618,43 @@ public Iterable removeTeams( this.lessonsCategoryRepository.removeTeamsForExercise(exerciseId, teamIds); return teamRepository.findAllById(teamIds); } + + /** + * Update the simulation and each of the injects to add/remove default assets + * + * @param exercise + * @param currentTags list of the tags before the update + * @return + */ + @Transactional + public Exercise updateExercice( + @NotNull final Exercise exercise, @NotNull final Set currentTags, boolean applyRule) { + if (applyRule) { + // Get assets from the TagRule of the added tags + List defaultAssetsToAdd = + tagRuleService.getAssetsFromTagIds( + exercise.getTags().stream() + .filter(tag -> !currentTags.contains(tag)) + .map(Tag::getId) + .toList()); + + // Get assets from the TagRule of the removed tags + List defaultAssetsToRemove = + tagRuleService.getAssetsFromTagIds( + currentTags.stream() + .filter(tag -> !exercise.getTags().contains(tag)) + .map(Tag::getId) + .toList()); + + // Add/remove the default assets to/from the injects + exercise + .getInjects() + .forEach( + inject -> + injectService.applyDefaultAssetsToInject( + inject.getId(), defaultAssetsToAdd, defaultAssetsToRemove)); + } + exercise.setUpdatedAt(now()); + return exerciseRepository.save(exercise); + } } diff --git a/openbas-api/src/main/java/io/openbas/rest/inject/InjectApi.java b/openbas-api/src/main/java/io/openbas/rest/inject/InjectApi.java index 71dacc0d9f..4d68166be3 100644 --- a/openbas-api/src/main/java/io/openbas/rest/inject/InjectApi.java +++ b/openbas-api/src/main/java/io/openbas/rest/inject/InjectApi.java @@ -33,6 +33,7 @@ import io.openbas.rest.inject.service.InjectService; import io.openbas.service.InjectSearchService; import io.openbas.service.ScenarioService; +import io.openbas.service.TagRuleService; import io.openbas.telemetry.Tracing; import io.openbas.utils.pagination.SearchPaginationInput; import jakarta.validation.Valid; @@ -40,9 +41,7 @@ import jakarta.validation.constraints.NotNull; import java.time.Duration; import java.time.Instant; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.StreamSupport; import lombok.RequiredArgsConstructor; @@ -84,6 +83,7 @@ public class InjectApi extends RestBehavior { private final ExecutableInjectService executableInjectService; private final InjectSearchService injectSearchService; private final InjectDuplicateService injectDuplicateService; + private final TagRuleService tagRuleService; // -- INJECTS -- @@ -348,7 +348,13 @@ public Inject createInjectForExercise( .toList()); } inject.setTeams(fromIterable(teamRepository.findAllById(input.getTeams()))); - inject.setAssets(fromIterable(assetService.assets(input.getAssets()))); + + // add default assets + inject.setAssets( + this.tagRuleService.applyTagRuleToInjectCreation( + exercise.getTags().stream().map(Tag::getId).toList(), + assetService.assets(input.getAssets()))); + inject.setAssetGroups(fromIterable(assetGroupService.assetGroups(input.getAssetGroups()))); inject.setTags(iterableToSet(tagRepository.findAllById(input.getTagIds()))); List injectDocuments = @@ -582,7 +588,13 @@ public Inject createInjectForScenario( .toList()); } inject.setTeams(fromIterable(teamRepository.findAllById(input.getTeams()))); - inject.setAssets(fromIterable(assetService.assets(input.getAssets()))); + + // add default assets + inject.setAssets( + this.tagRuleService.applyTagRuleToInjectCreation( + scenario.getTags().stream().map(Tag::getId).toList(), + assetService.assets(input.getAssets()))); + inject.setAssetGroups(fromIterable(assetGroupService.assetGroups(input.getAssetGroups()))); inject.setTags(iterableToSet(tagRepository.findAllById(input.getTagIds()))); List injectDocuments = diff --git a/openbas-api/src/main/java/io/openbas/rest/inject/service/InjectService.java b/openbas-api/src/main/java/io/openbas/rest/inject/service/InjectService.java index c8b3921a94..1a85fa1ac3 100644 --- a/openbas-api/src/main/java/io/openbas/rest/inject/service/InjectService.java +++ b/openbas-api/src/main/java/io/openbas/rest/inject/service/InjectService.java @@ -5,10 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; -import io.openbas.database.model.ExecutionStatus; -import io.openbas.database.model.Inject; -import io.openbas.database.model.InjectDocument; -import io.openbas.database.model.InjectStatus; +import io.openbas.database.model.*; import io.openbas.database.repository.InjectDocumentRepository; import io.openbas.database.repository.InjectRepository; import io.openbas.database.repository.InjectStatusRepository; @@ -22,7 +19,10 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import java.time.Instant; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.Stream; import lombok.RequiredArgsConstructor; import lombok.extern.java.Log; @@ -141,6 +141,47 @@ public void delete(String id) { injectRepository.deleteById(id); } + /** + * Update an inject with default assets + * + * @param injectId + * @param defaultAssetsToAdd + * @param defaultAssetsToRemove + * @return + */ + @Transactional + public Inject applyDefaultAssetsToInject( + final String injectId, + final List defaultAssetsToAdd, + final List defaultAssetsToRemove) { + + // fetch the inject + Inject inject = + this.injectRepository.findById(injectId).orElseThrow(ElementNotFoundException::new); + + // remove/add default assets and remove duplicates + List currentAssets = inject.getAssets(); + // Get the Id of the assets to remove and filter the assets that are in both lists + List assetIdsToRemove = + defaultAssetsToRemove.stream() + .filter(asset -> !defaultAssetsToAdd.contains(asset)) + .map(Asset::getId) + .toList(); + Set uniqueAssetsIds = new HashSet<>(); + List newListOfAssets = + Stream.concat(currentAssets.stream(), defaultAssetsToAdd.stream()) + .filter(asset -> !assetIdsToRemove.contains(asset.getId())) + .filter(asset -> uniqueAssetsIds.add(asset.getId())) + .collect(Collectors.toList()); + + if (new HashSet<>(currentAssets).equals(new HashSet<>(newListOfAssets))) { + return inject; + } else { + inject.setAssets(newListOfAssets); + return this.injectRepository.save(inject); + } + } + private Inject findAndDuplicateInject(String id) { Inject injectOrigin = injectRepository.findById(id).orElseThrow(ElementNotFoundException::new); return InjectUtils.duplicateInject(injectOrigin); diff --git a/openbas-api/src/main/java/io/openbas/rest/scenario/ScenarioApi.java b/openbas-api/src/main/java/io/openbas/rest/scenario/ScenarioApi.java index c9f068d543..364a4810c3 100644 --- a/openbas-api/src/main/java/io/openbas/rest/scenario/ScenarioApi.java +++ b/openbas-api/src/main/java/io/openbas/rest/scenario/ScenarioApi.java @@ -31,6 +31,7 @@ import jakarta.validation.constraints.NotNull; import java.io.IOException; import java.util.List; +import java.util.Set; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -100,11 +101,12 @@ public Scenario scenario(@PathVariable @NotBlank final String scenarioId) { @PreAuthorize("isScenarioPlanner(#scenarioId)") public Scenario updateScenario( @PathVariable @NotBlank final String scenarioId, - @Valid @RequestBody final ScenarioInput input) { + @Valid @RequestBody final UpdateScenarioInput input) { Scenario scenario = this.scenarioService.scenario(scenarioId); + Set currentTagList = scenario.getTags(); scenario.setUpdateAttributes(input); scenario.setTags(iterableToSet(this.tagRepository.findAllById(input.getTagIds()))); - return this.scenarioService.updateScenario(scenario); + return this.scenarioService.updateScenario(scenario, currentTagList, input.isApplyTagRule()); } @PutMapping(SCENARIO_URI + "/{scenarioId}/information") @@ -131,8 +133,9 @@ public Scenario updateScenarioTags( @PathVariable @NotBlank final String scenarioId, @Valid @RequestBody final ScenarioUpdateTagsInput input) { Scenario scenario = this.scenarioService.scenario(scenarioId); + Set currentTagList = scenario.getTags(); scenario.setTags(iterableToSet(this.tagRepository.findAllById(input.getTagIds()))); - return scenarioService.updateScenario(scenario); + return this.scenarioService.updateScenario(scenario, currentTagList, input.isApplyTagRule()); } // -- EXPORT -- diff --git a/openbas-api/src/main/java/io/openbas/rest/scenario/form/ScenarioUpdateTagsInput.java b/openbas-api/src/main/java/io/openbas/rest/scenario/form/ScenarioUpdateTagsInput.java index c1cec13872..ee4bc37c6d 100644 --- a/openbas-api/src/main/java/io/openbas/rest/scenario/form/ScenarioUpdateTagsInput.java +++ b/openbas-api/src/main/java/io/openbas/rest/scenario/form/ScenarioUpdateTagsInput.java @@ -12,4 +12,7 @@ public class ScenarioUpdateTagsInput { @JsonProperty("scenario_tags") private List tagIds = new ArrayList<>(); + + @JsonProperty("apply_tag_rule") + private boolean applyTagRule = false; } diff --git a/openbas-api/src/main/java/io/openbas/rest/scenario/form/UpdateScenarioInput.java b/openbas-api/src/main/java/io/openbas/rest/scenario/form/UpdateScenarioInput.java new file mode 100644 index 0000000000..2ec3728f0f --- /dev/null +++ b/openbas-api/src/main/java/io/openbas/rest/scenario/form/UpdateScenarioInput.java @@ -0,0 +1,12 @@ +package io.openbas.rest.scenario.form; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.Getter; + +@Data +@Getter +public class UpdateScenarioInput extends ScenarioInput { + @JsonProperty("apply_tag_rule") + private boolean applyTagRule = false; +} diff --git a/openbas-api/src/main/java/io/openbas/service/ScenarioService.java b/openbas-api/src/main/java/io/openbas/service/ScenarioService.java index 70b4297c45..abfcaf50e6 100644 --- a/openbas-api/src/main/java/io/openbas/service/ScenarioService.java +++ b/openbas-api/src/main/java/io/openbas/service/ScenarioService.java @@ -33,6 +33,7 @@ import io.openbas.rest.exercise.exports.VariableWithValueMixin; import io.openbas.rest.exercise.form.ExerciseSimple; import io.openbas.rest.inject.service.InjectDuplicateService; +import io.openbas.rest.inject.service.InjectService; import io.openbas.rest.scenario.export.ScenarioExportMixins; import io.openbas.rest.scenario.export.ScenarioFileExport; import io.openbas.rest.scenario.form.ScenarioSimple; @@ -102,6 +103,9 @@ public class ScenarioService { private final TeamService teamService; private final FileService fileService; private final InjectDuplicateService injectDuplicateService; + private final TagRuleService tagRuleService; + private final InjectService injectService; + private final InjectRepository injectRepository; private final LessonsCategoryRepository lessonsCategoryRepository; @@ -290,6 +294,44 @@ public ExerciseSimple latestExerciseByExternalReference( } public Scenario updateScenario(@NotNull final Scenario scenario) { + return this.updateScenario(scenario, null, false); + } + + /** + * Update the scenario and each of the injects to add/remove default assets + * + * @param scenario + * @param currentTags list of the tags before the update + * @return + */ + @Transactional + public Scenario updateScenario( + @NotNull final Scenario scenario, Set currentTags, boolean applyRule) { + if (applyRule) { + // Get assets from the TagRule of the added tags + List defaultAssetsToAdd = + tagRuleService.getAssetsFromTagIds( + scenario.getTags().stream() + .filter(tag -> !currentTags.contains(tag)) + .map(Tag::getId) + .toList()); + + // Get assets from the TagRule of the removed tags + List defaultAssetsToRemove = + tagRuleService.getAssetsFromTagIds( + currentTags.stream() + .filter(tag -> !scenario.getTags().contains(tag)) + .map(Tag::getId) + .toList()); + + // Add/remove the default assets to/from the injects + scenario + .getInjects() + .forEach( + inject -> + injectService.applyDefaultAssetsToInject( + inject.getId(), defaultAssetsToAdd, defaultAssetsToRemove)); + } scenario.setUpdatedAt(now()); return this.scenarioRepository.save(scenario); } diff --git a/openbas-api/src/main/java/io/openbas/service/TagRuleService.java b/openbas-api/src/main/java/io/openbas/service/TagRuleService.java index f02d04fa18..eec7b04917 100644 --- a/openbas-api/src/main/java/io/openbas/service/TagRuleService.java +++ b/openbas-api/src/main/java/io/openbas/service/TagRuleService.java @@ -12,17 +12,17 @@ import io.openbas.rest.exception.ElementNotFoundException; import io.openbas.utils.pagination.SearchPaginationInput; import jakarta.validation.constraints.NotBlank; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; +import jakarta.validation.constraints.NotNull; +import java.util.*; import java.util.stream.Collectors; +import java.util.stream.Stream; import java.util.stream.StreamSupport; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; -@Service @RequiredArgsConstructor +@Service public class TagRuleService { private final TagRuleRepository tagRuleRepository; private final TagRepository tagRepository; @@ -83,6 +83,36 @@ protected Tag getTag(@NotBlank final String tagName) { .orElseThrow(() -> new ElementNotFoundException("Tag not found with name: " + tagName)); } + /** + * Return the set of assets to add from a tag id list + * + * @param tagIds + * @return set of assets to add by default + */ + public List getAssetsFromTagIds(@NotNull final List tagIds) { + return this.tagRuleRepository.findByTags(tagIds).stream() + .flatMap(tagRule -> tagRule.getAssets().stream()) + .toList(); + } + + /** + * Apply the rule to add the default assets to the input assets during Injects creation + * + * @param tagIds list of Assets of the Inject before applying the rules + * @param inputAssets list of Assets of the Inject before applying the rules + * @return return the new list of assets + */ + public List applyTagRuleToInjectCreation(List tagIds, List inputAssets) { + + List defaultAssets = this.getAssetsFromTagIds(tagIds); + + // remove duplicate + Set uniqueAssetsIds = new HashSet<>(); + return Stream.concat(inputAssets.stream(), defaultAssets.stream()) + .filter(asset -> uniqueAssetsIds.add(asset.getId())) + .toList(); + } + @VisibleForTesting protected List getAssets(final List assetIds) { return assetIds == null diff --git a/openbas-api/src/test/java/io/openbas/service/ExerciseServiceTest.java b/openbas-api/src/test/java/io/openbas/rest/exercise/service/ExerciseServiceTest.java similarity index 56% rename from openbas-api/src/test/java/io/openbas/service/ExerciseServiceTest.java rename to openbas-api/src/test/java/io/openbas/rest/exercise/service/ExerciseServiceTest.java index 3830cd1869..284e2e8770 100644 --- a/openbas-api/src/test/java/io/openbas/service/ExerciseServiceTest.java +++ b/openbas-api/src/test/java/io/openbas/rest/exercise/service/ExerciseServiceTest.java @@ -1,17 +1,24 @@ -package io.openbas.service; +package io.openbas.rest.exercise.service; -import static io.openbas.expectation.ExpectationType.*; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; +import io.openbas.database.model.Asset; +import io.openbas.database.model.Exercise; +import io.openbas.database.model.Inject; +import io.openbas.database.model.Tag; import io.openbas.database.repository.*; import io.openbas.rest.exercise.form.ExercisesGlobalScoresInput; -import io.openbas.rest.exercise.service.ExerciseService; import io.openbas.rest.inject.service.InjectDuplicateService; +import io.openbas.rest.inject.service.InjectService; +import io.openbas.service.GrantService; +import io.openbas.service.TagRuleService; +import io.openbas.service.TeamService; +import io.openbas.service.VariableService; import io.openbas.utils.ExerciseMapper; import io.openbas.utils.InjectMapper; import io.openbas.utils.ResultUtils; -import io.openbas.utils.fixtures.ExpectationResultsByTypeFixture; +import io.openbas.utils.fixtures.*; import java.util.*; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; @@ -41,6 +48,8 @@ class ExerciseServiceTest { @Mock private ExerciseTeamUserRepository exerciseTeamUserRepository; @Mock private InjectRepository injectRepository; @Mock private LessonsCategoryRepository lessonsCategoryRepository; + @Mock private TagRuleService tagRuleService; + @Mock private InjectService injectService; @InjectMocks private ExerciseService exerciseService; @@ -52,6 +61,8 @@ void setUp() { injectDuplicateService, teamService, variableService, + tagRuleService, + injectService, exerciseMapper, injectMapper, resultUtils, @@ -98,4 +109,59 @@ void getExercisesGlobalScores() { exerciseId1, ExpectationResultsByTypeFixture.exercise1GlobalScores, exerciseId2, ExpectationResultsByTypeFixture.exercise2GlobalScores)); } + + @Test + public void testUpdateExercise_WITH_apply_rule_true() { + Asset asset1 = AssetFixture.createDefaultAsset("asset1"); + Asset asset2 = AssetFixture.createDefaultAsset("asset2"); + Asset asset3 = AssetFixture.createDefaultAsset("asset3"); + Tag tag1 = TagFixture.getTag("Tag1"); + Tag tag2 = TagFixture.getTag("Tag2"); + Tag tag3 = TagFixture.getTag("Tag3"); + Inject inject1 = new Inject(); + inject1.setId("1"); + Inject inject2 = new Inject(); + inject1.setId("2"); + Exercise exercise = ExerciseFixture.getExercise(null); + exercise.setInjects(List.of(inject1, inject2)); + exercise.setTags(Set.of(tag1, tag2)); + Set currentTags = Set.of(tag2, tag3); + List assetsToAdd = List.of(asset1, asset2); + List assetsToRemove = List.of(asset3); + + when(tagRuleService.getAssetsFromTagIds(List.of(tag1.getId()))).thenReturn(assetsToAdd); + when(tagRuleService.getAssetsFromTagIds(List.of(tag3.getId()))).thenReturn(assetsToRemove); + when(exerciseRepository.save(exercise)).thenReturn(exercise); + + exerciseService.updateExercice(exercise, currentTags, true); + + exercise + .getInjects() + .forEach( + inject -> + verify(injectService) + .applyDefaultAssetsToInject(inject.getId(), assetsToAdd, assetsToRemove)); + verify(exerciseRepository).save(exercise); + } + + @Test + public void testUpdateExercise_WITH_apply_rule_false() { + io.openbas.database.model.Tag tag1 = TagFixture.getTag("Tag1"); + io.openbas.database.model.Tag tag2 = TagFixture.getTag("Tag2"); + io.openbas.database.model.Tag tag3 = TagFixture.getTag("Tag3"); + Inject inject1 = new Inject(); + inject1.setId("1"); + Inject inject2 = new Inject(); + inject1.setId("2"); + Exercise exercise = ExerciseFixture.getExercise(null); + exercise.setInjects(List.of(inject1, inject2)); + exercise.setTags(Set.of(tag1, tag2)); + Set currentTags = Set.of(tag2, tag3); + + when(exerciseRepository.save(exercise)).thenReturn(exercise); + + exerciseService.updateExercice(exercise, currentTags, false); + + verify(injectService, never()).applyDefaultAssetsToInject(any(), any(), any()); + } } diff --git a/openbas-api/src/test/java/io/openbas/rest/inject/service/InjectServiceTest.java b/openbas-api/src/test/java/io/openbas/rest/inject/service/InjectServiceTest.java new file mode 100644 index 0000000000..6183eb53bd --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/rest/inject/service/InjectServiceTest.java @@ -0,0 +1,114 @@ +package io.openbas.rest.inject.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.*; + +import io.openbas.database.model.Asset; +import io.openbas.database.model.Inject; +import io.openbas.database.repository.InjectRepository; +import io.openbas.rest.exception.ElementNotFoundException; +import io.openbas.utils.fixtures.AssetFixture; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +public class InjectServiceTest { + + private static final String INJECT_ID = "injectid"; + + @Mock private InjectRepository injectRepository; + + @InjectMocks private InjectService injectService; + + @Test + public void testApplyDefaultAssetsToInject_WITH_unexisting_inject() { + doReturn(Optional.empty()).when(injectRepository).findById(INJECT_ID); + assertThrows( + ElementNotFoundException.class, + () -> { + injectService.applyDefaultAssetsToInject(INJECT_ID, List.of(), List.of()); + }); + } + + @Test + public void testApplyDefaultAssetsToInject_WITH_add_and_remove() { + Asset asset1 = AssetFixture.createDefaultAsset("asset1"); + Asset asset2 = AssetFixture.createDefaultAsset("asset2"); + Asset asset3 = AssetFixture.createDefaultAsset("asset3"); + Asset asset4 = AssetFixture.createDefaultAsset("asset4"); + Inject inject = new Inject(); + inject.setId(INJECT_ID); + inject.setAssets(List.of(asset1, asset2, asset3)); + doReturn(Optional.of(inject)).when(injectRepository).findById(INJECT_ID); + + injectService.applyDefaultAssetsToInject(INJECT_ID, List.of(asset4), List.of(asset3)); + + ArgumentCaptor injectCaptor = ArgumentCaptor.forClass(Inject.class); + verify(injectRepository).save(injectCaptor.capture()); + Inject capturedInject = injectCaptor.getValue(); + assertEquals(INJECT_ID, capturedInject.getId()); + assertEquals( + new HashSet<>(List.of(asset1, asset2, asset4)), new HashSet<>(capturedInject.getAssets())); + } + + @Test + public void testApplyDefaultAssetsToInject_WITH_remove_all() { + Asset asset1 = AssetFixture.createDefaultAsset("asset1"); + Asset asset2 = AssetFixture.createDefaultAsset("asset2"); + Asset asset3 = AssetFixture.createDefaultAsset("asset3"); + Inject inject = new Inject(); + inject.setId(INJECT_ID); + inject.setAssets(List.of(asset1, asset2, asset3)); + doReturn(Optional.of(inject)).when(injectRepository).findById(INJECT_ID); + + injectService.applyDefaultAssetsToInject(INJECT_ID, List.of(), List.of(asset1, asset2, asset3)); + + ArgumentCaptor injectCaptor = ArgumentCaptor.forClass(Inject.class); + verify(injectRepository).save(injectCaptor.capture()); + Inject capturedInject = injectCaptor.getValue(); + assertEquals(INJECT_ID, capturedInject.getId()); + assertEquals(List.of(), capturedInject.getAssets()); + } + + @Test + public void testApplyDefaultAssetsToInject_WITH_add_all() { + Asset asset1 = AssetFixture.createDefaultAsset("asset1"); + Asset asset2 = AssetFixture.createDefaultAsset("asset2"); + Asset asset3 = AssetFixture.createDefaultAsset("asset3"); + Inject inject = new Inject(); + inject.setId(INJECT_ID); + inject.setAssets(List.of()); + doReturn(Optional.of(inject)).when(injectRepository).findById(INJECT_ID); + + injectService.applyDefaultAssetsToInject(INJECT_ID, List.of(asset1, asset2, asset3), List.of()); + + ArgumentCaptor injectCaptor = ArgumentCaptor.forClass(Inject.class); + verify(injectRepository).save(injectCaptor.capture()); + Inject capturedInject = injectCaptor.getValue(); + assertEquals(INJECT_ID, capturedInject.getId()); + assertEquals( + new HashSet<>(List.of(asset1, asset2, asset3)), new HashSet<>(capturedInject.getAssets())); + } + + @Test + public void testApplyDefaultAssetsToInject_WITH_no_change() { + Asset asset1 = AssetFixture.createDefaultAsset("asset1"); + Asset asset2 = AssetFixture.createDefaultAsset("asset2"); + Asset asset3 = AssetFixture.createDefaultAsset("asset3"); + Inject inject = new Inject(); + inject.setId(INJECT_ID); + inject.setAssets(List.of(asset1, asset2, asset3)); + doReturn(Optional.of(inject)).when(injectRepository).findById(INJECT_ID); + + injectService.applyDefaultAssetsToInject(INJECT_ID, List.of(asset1), List.of(asset1)); + + verify(injectRepository, never()).save(any()); + } +} diff --git a/openbas-api/src/test/java/io/openbas/service/ExerciseServiceIntegrationTest.java b/openbas-api/src/test/java/io/openbas/service/ExerciseServiceIntegrationTest.java index afa6470179..b2c6c3786c 100644 --- a/openbas-api/src/test/java/io/openbas/service/ExerciseServiceIntegrationTest.java +++ b/openbas-api/src/test/java/io/openbas/service/ExerciseServiceIntegrationTest.java @@ -13,6 +13,7 @@ import io.openbas.database.repository.*; import io.openbas.rest.exercise.service.ExerciseService; import io.openbas.rest.inject.service.InjectDuplicateService; +import io.openbas.rest.inject.service.InjectService; import io.openbas.utils.ExerciseMapper; import io.openbas.utils.InjectMapper; import io.openbas.utils.ResultUtils; @@ -35,6 +36,9 @@ class ExerciseServiceIntegrationTest { @Mock VariableService variableService; @Autowired private TeamService teamService; + @Autowired private TagRuleService tagRuleService; + @Autowired private InjectService injectService; + @Autowired private ExerciseMapper exerciseMapper; @Autowired private InjectMapper injectMapper; @Autowired private ResultUtils resultUtils; @@ -65,6 +69,8 @@ void setUp() { injectDuplicateService, teamService, variableService, + tagRuleService, + injectService, exerciseMapper, injectMapper, resultUtils, diff --git a/openbas-api/src/test/java/io/openbas/service/InjectServiceTest.java b/openbas-api/src/test/java/io/openbas/service/InjectServiceTest.java deleted file mode 100644 index 673a2e2336..0000000000 --- a/openbas-api/src/test/java/io/openbas/service/InjectServiceTest.java +++ /dev/null @@ -1,830 +0,0 @@ -package io.openbas.service; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import io.openbas.config.OpenBASOAuth2User; -import io.openbas.config.SessionHelper; -import io.openbas.database.model.*; -import io.openbas.database.repository.*; -import io.openbas.rest.exception.BadRequestException; -import io.openbas.rest.scenario.form.InjectsImportInput; -import io.openbas.rest.scenario.response.ImportMessage; -import io.openbas.rest.scenario.response.ImportPostSummary; -import io.openbas.rest.scenario.response.ImportTestSummary; -import io.openbas.utils.CustomMockMultipartFile; -import jakarta.annotation.Resource; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.time.LocalDateTime; -import java.time.Month; -import java.time.ZoneOffset; -import java.util.*; -import org.apache.commons.io.FilenameUtils; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.mock.web.MockMultipartFile; -import org.springframework.util.ResourceUtils; - -@ExtendWith(MockitoExtension.class) -class InjectServiceTest { - - @Mock InjectRepository injectRepository; - @Mock TeamRepository teamRepository; - @Mock ScenarioTeamUserRepository scenarioTeamUserRepository; - @Mock ExerciseTeamUserRepository exerciseTeamUserRepository; - @Mock UserRepository userRepository; - @Mock private InjectImportService injectImportService; - - private Scenario mockedScenario; - - private Exercise mockedExercise; - - private ImportMapper mockedImportMapper; - - private InjectsImportInput mockedInjectsImportInput; - @Resource protected ObjectMapper mapper; - - @BeforeEach - void setUp() { - injectImportService = - new InjectImportService( - injectRepository, - scenarioTeamUserRepository, - exerciseTeamUserRepository, - teamRepository, - userRepository); - - mockedScenario = new Scenario(); - mockedExercise = new Exercise(); - mapper = new ObjectMapper(); - } - - @DisplayName("Post and store an XLS file") - @Test - void postAnXLSFile() throws Exception { - // -- PREPARE -- - // Getting a test file - File testFile = ResourceUtils.getFile("classpath:xls-test-files/test_file_1.xlsx"); - - InputStream in = new FileInputStream(testFile); - MockMultipartFile xlsFile = - new MockMultipartFile("file", "my-awesome-file.xls", "application/xlsx", in.readAllBytes()); - - ImportPostSummary response = injectImportService.storeXlsFileForImport(xlsFile); - - // -- ASSERT -- - assertNotNull(response); - try { - UUID.fromString(response.getImportId()); - } catch (Exception ex) { - fail(); - } - assertEquals(1, response.getAvailableSheets().size()); - assertEquals("CHECKLIST", response.getAvailableSheets().get(0)); - } - - @DisplayName("Post and store a corrupted XLS file") - @Test - void postACorruptedXLSFile() throws Exception { - // Getting a test file - File testFile = ResourceUtils.getFile("classpath:xls-test-files/test_file_1.xlsx"); - // -- PREPARE -- - InputStream in = new FileInputStream(testFile); - MockMultipartFile xlsFile = - new CustomMockMultipartFile( - "file", "my-awesome-file.xls", "application/xlsx", in.readAllBytes()); - - // -- EXECUTE -- - try { - injectImportService.storeXlsFileForImport(xlsFile); - fail(); - } catch (Exception ex) { - assertTrue(ex instanceof BadRequestException); - } - } - - @DisplayName("Import an XLS file with relative date") - @Test - void testImportXlsRelativeDate() throws IOException { - try (MockedStatic sessionHelper = Mockito.mockStatic(SessionHelper.class)) { - User mockedUser = new User(); - String fileID = UUID.randomUUID().toString(); - File testFile = ResourceUtils.getFile("classpath:xls-test-files/test_file_1.xlsx"); - createTempFile(testFile, fileID); - - mockedImportMapper = createImportMapper(UUID.randomUUID().toString()); - when(userRepository.findById(any())).thenReturn(Optional.of(mockedUser)); - Team team1 = new Team(); - team1.setName("team1"); - Team team2 = new Team(); - team2.setName("team2"); - when(teamRepository.findAll()).thenReturn(List.of(team1)); - when(teamRepository.save(any())).thenReturn(team2); - - mockedScenario.setId(UUID.randomUUID().toString()); - - sessionHelper.when(SessionHelper::currentUser).thenReturn(new OpenBASOAuth2User(mockedUser)); - ImportTestSummary importTestSummary = - injectImportService.importInjectIntoScenarioFromXLS( - mockedScenario, mockedImportMapper, fileID, "CHECKLIST", 120, false); - - verify(teamRepository, times(1)).save(any()); - assertEquals( - 30 * 24 * 60 * 60, importTestSummary.getInjects().getLast().getDependsDuration()); - - ObjectNode jsonNodeMail = - (ObjectNode) - mapper.readTree( - "{\"message\":\"message1\",\"expectations\":[{\"expectation_description\":\"expectation\",\"expectation_name\":\"expectation done\",\"expectation_score\":100.0,\"expectation_type\":\"MANUAL\",\"expectation_expectation_group\":false}]}"); - assertEquals(jsonNodeMail, importTestSummary.getInjects().getFirst().getContent()); - - ObjectNode jsonNodeSms = - (ObjectNode) - mapper.readTree( - "{\"subject\":\"subject\",\"body\":\"message2\",\"expectations\":[{\"expectation_description\":\"expectation\",\"expectation_name\":\"expectation done\",\"expectation_score\":100.0,\"expectation_type\":\"MANUAL\",\"expectation_expectation_group\":false}]}"); - assertEquals(jsonNodeSms, importTestSummary.getInjects().getLast().getContent()); - } - } - - @DisplayName("Import a non existing XLS file") - @Test - void testImportXlsBadFile() throws IOException { - try (MockedStatic sessionHelper = Mockito.mockStatic(SessionHelper.class)) { - String fileID = UUID.randomUUID().toString(); - - mockedImportMapper = createImportMapper(UUID.randomUUID().toString()); - try { - injectImportService.importInjectIntoScenarioFromXLS( - mockedScenario, mockedImportMapper, fileID, "CHECKLIST", 120, true); - fail(); - } catch (Exception ex) { - assertTrue(ex instanceof BadRequestException); - } - } - } - - @DisplayName("Import an XLS file and have several matches of importer") - @Test - void testImportXlsSeveralMatches() throws IOException { - try (MockedStatic sessionHelper = Mockito.mockStatic(SessionHelper.class)) { - User mockedUser = new User(); - String fileID = UUID.randomUUID().toString(); - File testFile = ResourceUtils.getFile("classpath:xls-test-files/test_file_1.xlsx"); - createTempFile(testFile, fileID); - - mockedImportMapper = createImportMapper(UUID.randomUUID().toString()); - when(userRepository.findById(any())).thenReturn(Optional.of(mockedUser)); - Team team1 = new Team(); - team1.setName("team1"); - Team team2 = new Team(); - team2.setName("team2"); - when(teamRepository.findAll()).thenReturn(List.of(team1)); - lenient().when(teamRepository.save(any())).thenReturn(team2); - - sessionHelper.when(SessionHelper::currentUser).thenReturn(new OpenBASOAuth2User(mockedUser)); - - InjectImporter injectImporterMailCopy = new InjectImporter(); - injectImporterMailCopy.setId(UUID.randomUUID().toString()); - injectImporterMailCopy.setImportTypeValue(".*mail"); - injectImporterMailCopy.setRuleAttributes(new ArrayList<>()); - injectImporterMailCopy.setInjectorContract(createMailInjectorContract()); - - injectImporterMailCopy.getRuleAttributes().addAll(createRuleAttributeMail()); - mockedImportMapper.getInjectImporters().add(injectImporterMailCopy); - ImportTestSummary importTestSummary = - injectImportService.importInjectIntoScenarioFromXLS( - mockedScenario, mockedImportMapper, fileID, "CHECKLIST", 120, true); - assertTrue( - importTestSummary.getImportMessage().stream() - .anyMatch( - importMessage -> - importMessage.getMessageLevel().equals(ImportMessage.MessageLevel.WARN) - && importMessage - .getErrorCode() - .equals(ImportMessage.ErrorCode.SEVERAL_MATCHES))); - } - } - - @DisplayName("Import an XLS file with absolute date") - @Test - void testImportXlsAbsoluteDate() throws IOException { - try (MockedStatic sessionHelper = Mockito.mockStatic(SessionHelper.class)) { - User mockedUser = new User(); - String fileID = UUID.randomUUID().toString(); - File testFile = ResourceUtils.getFile("classpath:xls-test-files/test_file_2.xlsx"); - createTempFile(testFile, fileID); - - mockedScenario = new Scenario(); - mockedScenario.setId(UUID.randomUUID().toString()); - - mockedImportMapper = createImportMapper(UUID.randomUUID().toString()); - mockedImportMapper - .getInjectImporters() - .forEach( - injectImporter -> { - injectImporter.setRuleAttributes( - injectImporter.getRuleAttributes().stream() - .map( - ruleAttribute -> { - if ("trigger_time".equals(ruleAttribute.getName())) { - ruleAttribute.setAdditionalConfig( - Map.of("timePattern", "dd/MM/yyyy HH'h'mm")); - } - return ruleAttribute; - }) - .toList()); - }); - when(userRepository.findById(any())).thenReturn(Optional.of(mockedUser)); - Team team1 = new Team(); - team1.setName("team1"); - team1.setUsers(List.of(new User())); - Team team2 = new Team(); - team2.setName("team2"); - team1.setUsers(List.of(new User())); - when(teamRepository.findAll()).thenReturn(List.of(team1)); - when(teamRepository.save(any())).thenReturn(team2); - - when(injectRepository.saveAll(any())) - .thenReturn(List.of(createNewInject(List.of(team1)), new Inject())); - - sessionHelper.when(SessionHelper::currentUser).thenReturn(new OpenBASOAuth2User(mockedUser)); - ImportTestSummary importTestSummary = - injectImportService.importInjectIntoScenarioFromXLS( - mockedScenario, mockedImportMapper, fileID, "CHECKLIST", 120, true); - - assertTrue( - LocalDateTime.of(2024, Month.JUNE, 26, 0, 0) - .toInstant(ZoneOffset.of("Z")) - .equals(mockedScenario.getRecurrenceStart())); - assertTrue("0 0 7 * * *".equals(mockedScenario.getRecurrence())); - } - } - - @DisplayName("Import an XLS file with absolute date") - @Test - void testImportXlsAbsoluteDateWithExistingScenarioTeamUser() throws IOException { - try (MockedStatic sessionHelper = Mockito.mockStatic(SessionHelper.class)) { - User mockedUser = new User(); - String fileID = UUID.randomUUID().toString(); - File testFile = ResourceUtils.getFile("classpath:xls-test-files/test_file_2.xlsx"); - createTempFile(testFile, fileID); - - mockedScenario = new Scenario(); - mockedScenario.setId(UUID.randomUUID().toString()); - - mockedImportMapper = createImportMapper(UUID.randomUUID().toString()); - mockedImportMapper - .getInjectImporters() - .forEach( - injectImporter -> { - injectImporter.setRuleAttributes( - injectImporter.getRuleAttributes().stream() - .map( - ruleAttribute -> { - if ("trigger_time".equals(ruleAttribute.getName())) { - ruleAttribute.setAdditionalConfig( - Map.of("timePattern", "dd/MM/yyyy HH'h'mm")); - } - return ruleAttribute; - }) - .toList()); - }); - when(userRepository.findById(any())).thenReturn(Optional.of(mockedUser)); - Team team1 = new Team(); - team1.setName("team1"); - team1.setUsers(List.of(new User())); - Team team2 = new Team(); - team2.setName("team2"); - team1.setUsers(List.of(new User())); - when(scenarioTeamUserRepository.findById(any())) - .thenReturn(Optional.of(new ScenarioTeamUser())); - when(teamRepository.findAll()).thenReturn(List.of(team1)); - when(teamRepository.save(any())).thenReturn(team2); - - when(injectRepository.saveAll(any())) - .thenReturn(List.of(createNewInject(List.of(team1)), new Inject())); - - sessionHelper.when(SessionHelper::currentUser).thenReturn(new OpenBASOAuth2User(mockedUser)); - ImportTestSummary importTestSummary = - injectImportService.importInjectIntoScenarioFromXLS( - mockedScenario, mockedImportMapper, fileID, "CHECKLIST", 120, true); - - assertTrue( - LocalDateTime.of(2024, Month.JUNE, 26, 0, 0) - .toInstant(ZoneOffset.of("Z")) - .equals(mockedScenario.getRecurrenceStart())); - assertTrue("0 0 7 * * *".equals(mockedScenario.getRecurrence())); - } - } - - @DisplayName("Import an XLS file with relative and absolute dates") - @Test - void testImportXlsAbsoluteAndRelativeDates() throws IOException { - try (MockedStatic sessionHelper = Mockito.mockStatic(SessionHelper.class)) { - User mockedUser = new User(); - String fileID = UUID.randomUUID().toString(); - File testFile = ResourceUtils.getFile("classpath:xls-test-files/test_file_3.xlsx"); - createTempFile(testFile, fileID); - - mockedScenario = new Scenario(); - mockedScenario.setId(UUID.randomUUID().toString()); - - mockedImportMapper = createImportMapper(UUID.randomUUID().toString()); - mockedImportMapper - .getInjectImporters() - .forEach( - injectImporter -> { - injectImporter.setRuleAttributes( - injectImporter.getRuleAttributes().stream() - .map( - ruleAttribute -> { - if ("trigger_time".equals(ruleAttribute.getName())) { - ruleAttribute.setAdditionalConfig( - Map.of("timePattern", "dd/MM/yyyy HH'h'mm")); - } - return ruleAttribute; - }) - .toList()); - }); - when(userRepository.findById(any())).thenReturn(Optional.of(mockedUser)); - Team team1 = new Team(); - team1.setName("team1"); - Team team2 = new Team(); - team2.setName("team2"); - when(teamRepository.findAll()).thenReturn(List.of(team1)); - when(teamRepository.save(any())).thenReturn(team2); - - sessionHelper.when(SessionHelper::currentUser).thenReturn(new OpenBASOAuth2User(mockedUser)); - ImportTestSummary importTestSummary = - injectImportService.importInjectIntoScenarioFromXLS( - mockedScenario, mockedImportMapper, fileID, "CHECKLIST", 120, false); - - List sortedInjects = - importTestSummary.getInjects().stream() - .sorted(Comparator.comparing(Inject::getDependsDuration)) - .toList(); - - assertEquals(24 * 60 * 60, sortedInjects.get(1).getDependsDuration()); - assertEquals(24 * 60 * 60 + 5 * 60, sortedInjects.get(2).getDependsDuration()); - assertEquals(2 * 24 * 60 * 60, sortedInjects.get(3).getDependsDuration()); - } - } - - @DisplayName("Import an XLS file with relative dates and absolute hours") - @Test - void testImportXlsRelativeDatesAndAbsoluteHour() throws IOException { - try (MockedStatic sessionHelper = Mockito.mockStatic(SessionHelper.class)) { - User mockedUser = new User(); - String fileID = UUID.randomUUID().toString(); - File testFile = ResourceUtils.getFile("classpath:xls-test-files/test_file_4.xlsx"); - createTempFile(testFile, fileID); - - mockedScenario.setId(UUID.randomUUID().toString()); - mockedScenario.setRecurrenceStart( - LocalDateTime.of(2024, Month.JUNE, 26, 0, 0).toInstant(ZoneOffset.of("Z"))); - - mockedImportMapper = createImportMapper(UUID.randomUUID().toString()); - mockedImportMapper - .getInjectImporters() - .forEach( - injectImporter -> { - injectImporter.setRuleAttributes( - injectImporter.getRuleAttributes().stream() - .map( - ruleAttribute -> { - if ("trigger_time".equals(ruleAttribute.getName())) { - ruleAttribute.setAdditionalConfig(Map.of("timePattern", "HH'h'mm")); - } - return ruleAttribute; - }) - .toList()); - }); - when(userRepository.findById(any())).thenReturn(Optional.of(mockedUser)); - Team team1 = new Team(); - team1.setName("team1"); - Team team2 = new Team(); - team2.setName("team2"); - when(teamRepository.findAll()).thenReturn(List.of(team1)); - when(teamRepository.save(any())).thenReturn(team2); - - sessionHelper.when(SessionHelper::currentUser).thenReturn(new OpenBASOAuth2User(mockedUser)); - ImportTestSummary importTestSummary = - injectImportService.importInjectIntoScenarioFromXLS( - mockedScenario, mockedImportMapper, fileID, "CHECKLIST", 120, false); - - List sortedInjects = - importTestSummary.getInjects().stream() - .sorted(Comparator.comparing(Inject::getDependsDuration)) - .toList(); - - assertEquals(24 * 60 * 60, sortedInjects.get(1).getDependsDuration()); - assertEquals(2 * 24 * 60 * 60, sortedInjects.get(2).getDependsDuration()); - assertEquals(4 * 24 * 60 * 60 + 5 * 60, sortedInjects.get(3).getDependsDuration()); - } - } - - @DisplayName( - "Critical message when import an XLS file with relative dates " - + "and absolute hours but no date in scenario") - @Test - void testImportXlsRelativeDatesAndAbsoluteHourCriticalMessage() throws IOException { - try (MockedStatic sessionHelper = Mockito.mockStatic(SessionHelper.class)) { - User mockedUser = new User(); - String fileID = UUID.randomUUID().toString(); - File testFile = ResourceUtils.getFile("classpath:xls-test-files/test_file_4.xlsx"); - createTempFile(testFile, fileID); - - mockedScenario = new Scenario(); - mockedScenario.setId(UUID.randomUUID().toString()); - - mockedImportMapper = createImportMapper(UUID.randomUUID().toString()); - mockedImportMapper - .getInjectImporters() - .forEach( - injectImporter -> { - injectImporter.setRuleAttributes( - injectImporter.getRuleAttributes().stream() - .map( - ruleAttribute -> { - if ("trigger_time".equals(ruleAttribute.getName())) { - ruleAttribute.setAdditionalConfig(Map.of("timePattern", "HH'h'mm")); - } - return ruleAttribute; - }) - .toList()); - }); - when(userRepository.findById(any())).thenReturn(Optional.of(mockedUser)); - Team team1 = new Team(); - team1.setName("team1"); - Team team2 = new Team(); - team2.setName("team2"); - when(teamRepository.findAll()).thenReturn(List.of(team1)); - when(teamRepository.save(any())).thenReturn(team2); - - sessionHelper.when(SessionHelper::currentUser).thenReturn(new OpenBASOAuth2User(mockedUser)); - ImportTestSummary importTestSummary = - injectImportService.importInjectIntoScenarioFromXLS( - mockedScenario, mockedImportMapper, fileID, "CHECKLIST", 120, true); - - assertTrue( - importTestSummary.getImportMessage().stream() - .anyMatch( - importMessage -> - ImportMessage.MessageLevel.CRITICAL.equals(importMessage.getMessageLevel()) - && ImportMessage.ErrorCode.ABSOLUTE_TIME_WITHOUT_START_DATE.equals( - importMessage.getErrorCode()))); - } - } - - @DisplayName("Import an XLS file with relative dates, hours and minutes") - @Test - void testImportXlsRelativeDatesHoursAndMinutes() throws IOException { - try (MockedStatic sessionHelper = Mockito.mockStatic(SessionHelper.class)) { - User mockedUser = new User(); - String fileID = UUID.randomUUID().toString(); - File testFile = ResourceUtils.getFile("classpath:xls-test-files/test_file_5.xlsx"); - createTempFile(testFile, fileID); - mockedInjectsImportInput = new InjectsImportInput(); - mockedInjectsImportInput.setImportMapperId(fileID); - mockedInjectsImportInput.setName("CHECKLIST"); - mockedInjectsImportInput.setTimezoneOffset(120); - - mockedScenario = new Scenario(); - - mockedImportMapper = createImportMapper(UUID.randomUUID().toString()); - when(userRepository.findById(any())).thenReturn(Optional.of(mockedUser)); - Team team1 = new Team(); - team1.setName("team1"); - Team team2 = new Team(); - team2.setName("team2"); - when(teamRepository.findAll()).thenReturn(List.of(team1)); - when(teamRepository.save(any())).thenReturn(team2); - - sessionHelper.when(SessionHelper::currentUser).thenReturn(new OpenBASOAuth2User(mockedUser)); - ImportTestSummary importTestSummary = - injectImportService.importInjectIntoScenarioFromXLS( - mockedScenario, mockedImportMapper, fileID, "CHECKLIST", 120, false); - - List sortedInjects = - importTestSummary.getInjects().stream() - .sorted(Comparator.comparing(Inject::getDependsDuration)) - .toList(); - - assertEquals(24 * 60 * 60, sortedInjects.get(1).getDependsDuration()); - assertEquals(((((2 * 24) + 2) * 60) - 5) * 60, sortedInjects.get(2).getDependsDuration()); - assertEquals(4 * 24 * 60 * 60, sortedInjects.get(3).getDependsDuration()); - } - } - - @DisplayName("Import an XLS file with default values") - @Test - void testImportXlsDefaultValue() throws IOException { - try (MockedStatic sessionHelper = Mockito.mockStatic(SessionHelper.class)) { - User mockedUser = new User(); - String fileID = UUID.randomUUID().toString(); - File testFile = ResourceUtils.getFile("classpath:xls-test-files/test_file_5.xlsx"); - createTempFile(testFile, fileID); - - mockedScenario = new Scenario(); - mockedScenario.setId(UUID.randomUUID().toString()); - - mockedImportMapper = createImportMapper(UUID.randomUUID().toString()); - mockedImportMapper - .getInjectImporters() - .forEach( - injectImporter -> { - injectImporter.setRuleAttributes( - injectImporter.getRuleAttributes().stream() - .map( - ruleAttribute -> { - if ("title".equals(ruleAttribute.getName()) - || "trigger_time".equals(ruleAttribute.getName())) { - ruleAttribute.setColumns("A"); - } - return ruleAttribute; - }) - .toList()); - }); - when(userRepository.findById(any())).thenReturn(Optional.of(mockedUser)); - Team team1 = new Team(); - team1.setName("team1"); - Team team2 = new Team(); - team2.setName("team2"); - when(teamRepository.findAll()).thenReturn(List.of(team1)); - when(teamRepository.save(any())).thenReturn(team2); - - sessionHelper.when(SessionHelper::currentUser).thenReturn(new OpenBASOAuth2User(mockedUser)); - ImportTestSummary importTestSummary = - injectImportService.importInjectIntoScenarioFromXLS( - mockedScenario, mockedImportMapper, fileID, "CHECKLIST", 120, false); - - assertSame("title", importTestSummary.getInjects().getFirst().getTitle()); - } - } - - @DisplayName("Import an XLS file in an exercise") - @Test - void testImportXlsWithExercise() throws IOException { - try (MockedStatic sessionHelper = Mockito.mockStatic(SessionHelper.class)) { - User mockedUser = new User(); - String fileID = UUID.randomUUID().toString(); - File testFile = ResourceUtils.getFile("classpath:xls-test-files/test_file_2.xlsx"); - createTempFile(testFile, fileID); - - mockedExercise = new Exercise(); - mockedExercise.setId(UUID.randomUUID().toString()); - - mockedImportMapper = createImportMapper(UUID.randomUUID().toString()); - mockedImportMapper - .getInjectImporters() - .forEach( - injectImporter -> { - injectImporter.setRuleAttributes( - injectImporter.getRuleAttributes().stream() - .map( - ruleAttribute -> { - if ("trigger_time".equals(ruleAttribute.getName())) { - ruleAttribute.setAdditionalConfig( - Map.of("timePattern", "dd/MM/yyyy HH'h'mm")); - } - return ruleAttribute; - }) - .toList()); - }); - when(userRepository.findById(any())).thenReturn(Optional.of(mockedUser)); - Team team1 = new Team(); - team1.setName("team1"); - team1.setUsers(List.of(new User())); - Team team2 = new Team(); - team2.setName("team2"); - team1.setUsers(List.of(new User())); - when(teamRepository.findAll()).thenReturn(List.of(team1)); - when(teamRepository.save(any())).thenReturn(team2); - - when(injectRepository.saveAll(any())) - .thenReturn(List.of(createNewInject(List.of(team1)), new Inject())); - - sessionHelper.when(SessionHelper::currentUser).thenReturn(new OpenBASOAuth2User(mockedUser)); - ImportTestSummary importTestSummary = - injectImportService.importInjectIntoExerciseFromXLS( - mockedExercise, mockedImportMapper, fileID, "CHECKLIST", 120, true); - - assertTrue( - LocalDateTime.of(2024, Month.JUNE, 26, 0, 0) - .toInstant(ZoneOffset.of("Z")) - .equals(mockedExercise.getStart().get())); - } - } - - private void createTempFile(File testFile, String fileID) throws IOException { - InputStream in = new FileInputStream(testFile); - MockMultipartFile file = - new MockMultipartFile("file", "my-awesome-file.xls", "application/xlsx", in.readAllBytes()); - - // Writing the file in a temp dir - Path tempDir = Files.createDirectory(Path.of(System.getProperty("java.io.tmpdir"), fileID)); - Path tempFile = - Files.createTempFile( - tempDir, null, "." + FilenameUtils.getExtension(file.getOriginalFilename())); - Files.write(tempFile, file.getBytes()); - - // We're making sure the files are deleted when the test stops - tempDir.toFile().deleteOnExit(); - tempFile.toFile().deleteOnExit(); - } - - private ImportMapper createImportMapper(String id) throws JsonProcessingException { - ImportMapper importMapper = new ImportMapper(); - importMapper.setName("test import mapper"); - importMapper.setId(id); - importMapper.setInjectTypeColumn("B"); - importMapper.setInjectImporters(new ArrayList<>()); - - InjectImporter injectImporterSms = new InjectImporter(); - injectImporterSms.setId(UUID.randomUUID().toString()); - injectImporterSms.setImportTypeValue(".*(sms|SMS).*"); - injectImporterSms.setRuleAttributes(new ArrayList<>()); - injectImporterSms.setInjectorContract(createSmsInjectorContract()); - - injectImporterSms.getRuleAttributes().addAll(createRuleAttributeSms()); - - InjectImporter injectImporterMail = new InjectImporter(); - injectImporterMail.setId(UUID.randomUUID().toString()); - injectImporterMail.setImportTypeValue(".*mail.*"); - injectImporterMail.setRuleAttributes(new ArrayList<>()); - injectImporterMail.setInjectorContract(createMailInjectorContract()); - - injectImporterMail.getRuleAttributes().addAll(createRuleAttributeMail()); - - importMapper.getInjectImporters().add(injectImporterSms); - importMapper.getInjectImporters().add(injectImporterMail); - - return importMapper; - } - - private InjectorContract createSmsInjectorContract() throws JsonProcessingException { - InjectorContract injectorContract = new InjectorContract(); - ObjectNode jsonNode = - (ObjectNode) - mapper.readTree( - "{\"config\":{\"type\":\"openbas_ovh_sms\",\"expose\":true,\"label\":{\"en\":\"SMS (OVH)\"},\"color_dark\":\"#9c27b0\",\"color_light\":\"#9c27b0\"},\"label\":{\"en\":\"Send a SMS\",\"fr\":\"Envoyer un SMS\"},\"manual\":false,\"fields\":[{\"key\":\"teams\",\"label\":\"Teams\",\"mandatory\":true,\"readOnly\":false,\"mandatoryGroups\":null,\"linkedFields\":[],\"linkedValues\":[],\"cardinality\":\"n\",\"defaultValue\":[],\"type\":\"team\"},{\"key\":\"message\",\"label\":\"Message\",\"mandatory\":true,\"readOnly\":false,\"mandatoryGroups\":null,\"linkedFields\":[],\"linkedValues\":[],\"defaultValue\":\"\",\"richText\":false,\"type\":\"textarea\"},{\"key\":\"expectations\",\"label\":\"Expectations\",\"mandatory\":false,\"readOnly\":false,\"mandatoryGroups\":null,\"linkedFields\":[],\"linkedValues\":[],\"cardinality\":\"n\",\"defaultValue\":[],\"predefinedExpectations\":[],\"type\":\"expectation\"}],\"variables\":[{\"key\":\"user\",\"label\":\"User that will receive the injection\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[{\"key\":\"user.id\",\"label\":\"Id of the user in the platform\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]},{\"key\":\"user.email\",\"label\":\"Email of the user\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]},{\"key\":\"user.firstname\",\"label\":\"Firstname of the user\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]},{\"key\":\"user.lastname\",\"label\":\"Lastname of the user\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]},{\"key\":\"user.lang\",\"label\":\"Lang of the user\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]}]},{\"key\":\"exercise\",\"label\":\"Exercise of the current injection\",\"type\":\"Object\",\"cardinality\":\"1\",\"children\":[{\"key\":\"exercise.id\",\"label\":\"Id of the user in the platform\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]},{\"key\":\"exercise.name\",\"label\":\"Name of the exercise\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]},{\"key\":\"exercise.description\",\"label\":\"Description of the exercise\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]}]},{\"key\":\"teams\",\"label\":\"List of team name for the injection\",\"type\":\"String\",\"cardinality\":\"n\",\"children\":[]},{\"key\":\"player_uri\",\"label\":\"Player interface platform link\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]},{\"key\":\"challenges_uri\",\"label\":\"Challenges interface platform link\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]},{\"key\":\"scoreboard_uri\",\"label\":\"Scoreboard interface platform link\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]},{\"key\":\"lessons_uri\",\"label\":\"Lessons learned interface platform link\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]}],\"context\":{},\"contract_id\":\"e9e902bc-b03d-4223-89e1-fca093ac79dd\",\"contract_attack_patterns_external_ids\":[],\"is_atomic_testing\":true,\"needs_executor\":false,\"platforms\":[\"Service\"]}"); - injectorContract.setConvertedContent(jsonNode); - - return injectorContract; - } - - private InjectorContract createMailInjectorContract() throws JsonProcessingException { - InjectorContract injectorContract = new InjectorContract(); - ObjectNode jsonNode = - (ObjectNode) - mapper.readTree( - "{\"config\":{\"type\":\"openbas_email\",\"expose\":true,\"label\":{\"en\":\"Email\",\"fr\":\"Email\"},\"color_dark\":\"#cddc39\",\"color_light\":\"#cddc39\"},\"label\":{\"en\":\"Send individual mails\",\"fr\":\"Envoyer des mails individuels\"},\"manual\":false,\"fields\":[{\"key\":\"teams\",\"label\":\"Teams\",\"mandatory\":true,\"readOnly\":false,\"mandatoryGroups\":null,\"linkedFields\":[],\"linkedValues\":[],\"cardinality\":\"n\",\"defaultValue\":[],\"type\":\"team\"},{\"key\":\"subject\",\"label\":\"Subject\",\"mandatory\":true,\"readOnly\":false,\"mandatoryGroups\":null,\"linkedFields\":[],\"linkedValues\":[],\"defaultValue\":\"\",\"type\":\"text\"},{\"key\":\"body\",\"label\":\"Body\",\"mandatory\":true,\"readOnly\":false,\"mandatoryGroups\":null,\"linkedFields\":[],\"linkedValues\":[],\"defaultValue\":\"\",\"richText\":true,\"type\":\"textarea\"},{\"key\":\"encrypted\",\"label\":\"Encrypted\",\"mandatory\":false,\"readOnly\":false,\"mandatoryGroups\":null,\"linkedFields\":[],\"linkedValues\":[],\"defaultValue\":false,\"type\":\"checkbox\"},{\"key\":\"attachments\",\"label\":\"Attachments\",\"mandatory\":false,\"readOnly\":false,\"mandatoryGroups\":null,\"linkedFields\":[],\"linkedValues\":[],\"cardinality\":\"n\",\"defaultValue\":[],\"type\":\"attachment\"},{\"key\":\"expectations\",\"label\":\"Expectations\",\"mandatory\":false,\"readOnly\":false,\"mandatoryGroups\":null,\"linkedFields\":[],\"linkedValues\":[],\"cardinality\":\"n\",\"defaultValue\":[],\"predefinedExpectations\":[],\"type\":\"expectation\"}],\"variables\":[{\"key\":\"document_uri\",\"label\":\"Http user link to upload the document (only for document expectation)\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]},{\"key\":\"user\",\"label\":\"User that will receive the injection\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[{\"key\":\"user.id\",\"label\":\"Id of the user in the platform\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]},{\"key\":\"user.email\",\"label\":\"Email of the user\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]},{\"key\":\"user.firstname\",\"label\":\"Firstname of the user\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]},{\"key\":\"user.lastname\",\"label\":\"Lastname of the user\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]},{\"key\":\"user.lang\",\"label\":\"Lang of the user\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]}]},{\"key\":\"exercise\",\"label\":\"Exercise of the current injection\",\"type\":\"Object\",\"cardinality\":\"1\",\"children\":[{\"key\":\"exercise.id\",\"label\":\"Id of the user in the platform\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]},{\"key\":\"exercise.name\",\"label\":\"Name of the exercise\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]},{\"key\":\"exercise.description\",\"label\":\"Description of the exercise\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]}]},{\"key\":\"teams\",\"label\":\"List of team name for the injection\",\"type\":\"String\",\"cardinality\":\"n\",\"children\":[]},{\"key\":\"player_uri\",\"label\":\"Player interface platform link\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]},{\"key\":\"challenges_uri\",\"label\":\"Challenges interface platform link\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]},{\"key\":\"scoreboard_uri\",\"label\":\"Scoreboard interface platform link\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]},{\"key\":\"lessons_uri\",\"label\":\"Lessons learned interface platform link\",\"type\":\"String\",\"cardinality\":\"1\",\"children\":[]}],\"context\":{},\"contract_id\":\"138ad8f8-32f8-4a22-8114-aaa12322bd09\",\"contract_attack_patterns_external_ids\":[],\"is_atomic_testing\":true,\"needs_executor\":false,\"platforms\":[\"Service\"]}"); - injectorContract.setConvertedContent(jsonNode); - - return injectorContract; - } - - private List createRuleAttributeSms() { - List results = new ArrayList<>(); - RuleAttribute ruleAttributeTitle = new RuleAttribute(); - ruleAttributeTitle.setName("title"); - ruleAttributeTitle.setColumns("B"); - ruleAttributeTitle.setDefaultValue("title"); - - RuleAttribute ruleAttributeDescription = new RuleAttribute(); - ruleAttributeDescription.setName("description"); - ruleAttributeDescription.setColumns("G"); - ruleAttributeDescription.setDefaultValue("description"); - - RuleAttribute ruleAttributeTriggerTime = new RuleAttribute(); - ruleAttributeTriggerTime.setName("trigger_time"); - ruleAttributeTriggerTime.setColumns("C"); - ruleAttributeTriggerTime.setDefaultValue("trigger_time"); - ruleAttributeTriggerTime.setAdditionalConfig(Map.of("timePattern", "")); - - RuleAttribute ruleAttributeMessage = new RuleAttribute(); - ruleAttributeMessage.setName("message"); - ruleAttributeMessage.setColumns("F"); - ruleAttributeMessage.setDefaultValue("message"); - - RuleAttribute ruleAttributeTeams = new RuleAttribute(); - ruleAttributeTeams.setName("teams"); - ruleAttributeTeams.setColumns("D"); - ruleAttributeTeams.setDefaultValue("teams"); - - RuleAttribute ruleAttributeExpectationScore = new RuleAttribute(); - ruleAttributeExpectationScore.setName("expectation_score"); - ruleAttributeExpectationScore.setColumns("J"); - ruleAttributeExpectationScore.setDefaultValue("500.0"); - - RuleAttribute ruleAttributeExpectationName = new RuleAttribute(); - ruleAttributeExpectationName.setName("expectation_name"); - ruleAttributeExpectationName.setColumns("I"); - ruleAttributeExpectationName.setDefaultValue("name"); - - RuleAttribute ruleAttributeExpectationDescription = new RuleAttribute(); - ruleAttributeExpectationDescription.setName("expectation_description"); - ruleAttributeExpectationDescription.setColumns("H"); - ruleAttributeExpectationDescription.setDefaultValue("description"); - - results.add(ruleAttributeTitle); - results.add(ruleAttributeDescription); - results.add(ruleAttributeTriggerTime); - results.add(ruleAttributeMessage); - results.add(ruleAttributeTeams); - results.add(ruleAttributeExpectationScore); - results.add(ruleAttributeExpectationName); - results.add(ruleAttributeExpectationDescription); - - return results; - } - - private List createRuleAttributeMail() { - List results = new ArrayList<>(); - RuleAttribute ruleAttributeTitle = new RuleAttribute(); - ruleAttributeTitle.setName("title"); - ruleAttributeTitle.setColumns("B"); - ruleAttributeTitle.setDefaultValue("title"); - - RuleAttribute ruleAttributeDescription = new RuleAttribute(); - ruleAttributeDescription.setName("description"); - ruleAttributeDescription.setColumns("G"); - ruleAttributeDescription.setDefaultValue("description"); - - RuleAttribute ruleAttributeTriggerTime = new RuleAttribute(); - ruleAttributeTriggerTime.setName("trigger_time"); - ruleAttributeTriggerTime.setColumns("C"); - ruleAttributeTriggerTime.setDefaultValue("trigger_time"); - ruleAttributeTriggerTime.setAdditionalConfig(Map.of("timePattern", "")); - - RuleAttribute ruleAttributeMessage = new RuleAttribute(); - ruleAttributeMessage.setName("subject"); - ruleAttributeMessage.setColumns("E"); - ruleAttributeMessage.setDefaultValue("subject"); - - RuleAttribute ruleAttributeSubject = new RuleAttribute(); - ruleAttributeSubject.setName("body"); - ruleAttributeSubject.setColumns("F"); - ruleAttributeSubject.setDefaultValue("body"); - - RuleAttribute ruleAttributeTeams = new RuleAttribute(); - ruleAttributeTeams.setName("teams"); - ruleAttributeTeams.setColumns("D"); - ruleAttributeTeams.setDefaultValue("teams"); - - RuleAttribute ruleAttributeExpectationScore = new RuleAttribute(); - ruleAttributeExpectationScore.setName("expectation_score"); - ruleAttributeExpectationScore.setColumns("J"); - ruleAttributeExpectationScore.setDefaultValue("500.0"); - - RuleAttribute ruleAttributeExpectationName = new RuleAttribute(); - ruleAttributeExpectationName.setName("expectation_name"); - ruleAttributeExpectationName.setColumns("I"); - ruleAttributeExpectationName.setDefaultValue("name"); - - RuleAttribute ruleAttributeExpectationDescription = new RuleAttribute(); - ruleAttributeExpectationDescription.setName("expectation_description"); - ruleAttributeExpectationDescription.setColumns("H"); - ruleAttributeExpectationDescription.setDefaultValue("description"); - - results.add(ruleAttributeTitle); - results.add(ruleAttributeDescription); - results.add(ruleAttributeTriggerTime); - results.add(ruleAttributeMessage); - results.add(ruleAttributeSubject); - results.add(ruleAttributeTeams); - results.add(ruleAttributeExpectationScore); - results.add(ruleAttributeExpectationName); - results.add(ruleAttributeExpectationDescription); - - return results; - } - - private Object deepCopy(Object objectToCopy, Class classToCopy) throws JsonProcessingException { - ObjectMapper objectMapper = new ObjectMapper(); - - return objectMapper.readValue(objectMapper.writeValueAsString(objectToCopy), classToCopy); - } - - private Inject createNewInject(List teams) { - Inject inject = new Inject(); - inject.setTeams(teams); - inject.setId(UUID.randomUUID().toString()); - return inject; - } -} diff --git a/openbas-api/src/test/java/io/openbas/service/ScenarioServiceTest.java b/openbas-api/src/test/java/io/openbas/service/ScenarioServiceTest.java index ffde237c0e..8b9233e0e9 100644 --- a/openbas-api/src/test/java/io/openbas/service/ScenarioServiceTest.java +++ b/openbas-api/src/test/java/io/openbas/service/ScenarioServiceTest.java @@ -7,12 +7,17 @@ import static io.openbas.utils.fixtures.UserFixture.getUser; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.mockito.Mockito.*; import io.openbas.database.model.*; +import io.openbas.database.model.Tag; import io.openbas.database.repository.*; import io.openbas.rest.inject.service.InjectDuplicateService; +import io.openbas.rest.inject.service.InjectService; import io.openbas.utils.ExerciseMapper; +import io.openbas.utils.fixtures.AssetFixture; import io.openbas.utils.fixtures.ScenarioFixture; +import io.openbas.utils.fixtures.TagFixture; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -37,6 +42,7 @@ class ScenarioServiceTest { @Autowired InjectRepository injectRepository; + @Mock ScenarioRepository mockScenarioRepository; @Mock GrantService grantService; @Mock VariableService variableService; @Mock ChallengeService challengeService; @@ -46,7 +52,8 @@ class ScenarioServiceTest { @Autowired private ExerciseMapper exerciseMapper; @Autowired private InjectorContractRepository injectorContractRepository; @Autowired private LessonsCategoryRepository lessonsCategoryRepository; - + @Mock private InjectService injectService; + @Mock private TagRuleService tagRuleService; @InjectMocks private ScenarioService scenarioService; private static String USER_ID; @@ -70,6 +77,30 @@ void setUp() { teamService, fileService, injectDuplicateService, + tagRuleService, + injectService, + injectRepository, + lessonsCategoryRepository); + } + + void setUpWithMockRepository() { + scenarioService = + new ScenarioService( + mockScenarioRepository, + teamRepository, + userRepository, + documentRepository, + scenarioTeamUserRepository, + articleRepository, + exerciseMapper, + grantService, + variableService, + challengeService, + teamService, + fileService, + injectDuplicateService, + tagRuleService, + injectService, injectRepository, lessonsCategoryRepository); } @@ -171,4 +202,66 @@ void testRemoveTeams() { Inject injectAssert = this.injectRepository.findById(INJECT_ID).orElseThrow(); assertEquals(0, injectAssert.getTeams().size()); } + + @Test + public void testUpdateScenario_WITH_applyRule_true() { + setUpWithMockRepository(); + Asset asset1 = AssetFixture.createDefaultAsset("asset1"); + Asset asset2 = AssetFixture.createDefaultAsset("asset2"); + Asset asset3 = AssetFixture.createDefaultAsset("asset3"); + Tag tag1 = TagFixture.getTag("Tag1"); + Tag tag2 = TagFixture.getTag("Tag2"); + Tag tag3 = TagFixture.getTag("Tag3"); + Inject inject1 = new Inject(); + inject1.setId("1"); + Inject inject2 = new Inject(); + inject1.setId("2"); + Scenario scenario = ScenarioFixture.getScenario(null, Set.of(inject1, inject2)); + scenario.setTags(Set.of(tag1, tag2)); + Set currentTags = Set.of(tag2, tag3); + List assetsToAdd = List.of(asset1, asset2); + List assetsToRemove = List.of(asset3); + + when(tagRuleService.getAssetsFromTagIds(List.of(tag1.getId()))).thenReturn(assetsToAdd); + when(tagRuleService.getAssetsFromTagIds(List.of(tag3.getId()))).thenReturn(assetsToRemove); + when(mockScenarioRepository.save(scenario)).thenReturn(scenario); + + scenarioService.updateScenario(scenario, currentTags, true); + + scenario + .getInjects() + .forEach( + inject -> + verify(injectService) + .applyDefaultAssetsToInject(inject.getId(), assetsToAdd, assetsToRemove)); + verify(mockScenarioRepository).save(scenario); + } + + @Test + public void testUpdateScenario_WITH_applyRule_false() { + setUpWithMockRepository(); + Asset asset1 = AssetFixture.createDefaultAsset("asset1"); + Asset asset2 = AssetFixture.createDefaultAsset("asset2"); + Asset asset3 = AssetFixture.createDefaultAsset("asset3"); + Tag tag1 = TagFixture.getTag("Tag1"); + Tag tag2 = TagFixture.getTag("Tag2"); + Tag tag3 = TagFixture.getTag("Tag3"); + Inject inject1 = new Inject(); + inject1.setId("1"); + Inject inject2 = new Inject(); + inject1.setId("2"); + Scenario scenario = ScenarioFixture.getScenario(null, Set.of(inject1, inject2)); + scenario.setTags(Set.of(tag1, tag2)); + Set currentTags = Set.of(tag2, tag3); + List assetsToAdd = List.of(asset1, asset2); + List assetsToRemove = List.of(asset3); + + when(tagRuleService.getAssetsFromTagIds(List.of(tag1.getId()))).thenReturn(assetsToAdd); + when(tagRuleService.getAssetsFromTagIds(List.of(tag3.getId()))).thenReturn(assetsToRemove); + when(mockScenarioRepository.save(scenario)).thenReturn(scenario); + + scenarioService.updateScenario(scenario, currentTags, false); + + verify(injectService, never()).applyDefaultAssetsToInject(any(), any(), any()); + } } diff --git a/openbas-api/src/test/java/io/openbas/service/TagRuleServiceTest.java b/openbas-api/src/test/java/io/openbas/service/TagRuleServiceTest.java index 661deac76a..7ea1d98fe8 100644 --- a/openbas-api/src/test/java/io/openbas/service/TagRuleServiceTest.java +++ b/openbas-api/src/test/java/io/openbas/service/TagRuleServiceTest.java @@ -13,6 +13,7 @@ import io.openbas.database.repository.TagRepository; import io.openbas.database.repository.TagRuleRepository; import io.openbas.rest.exception.ElementNotFoundException; +import io.openbas.utils.fixtures.AssetFixture; import io.openbas.utils.fixtures.TagFixture; import io.openbas.utils.fixtures.TagRuleFixture; import java.util.HashSet; @@ -194,4 +195,38 @@ void testUpdateTagRule_WITH_non_existing_tag_rule() { expected.getAssets().stream().map(Asset::getId).toList()); }); } + + @Test + void testGetAssetsFromTagIds() { + List tagIds = List.of("tag1"); + TagRule tagRule = TagRuleFixture.createTagRule(TAG_RULE_ID); + when(tagRuleRepository.findByTags(tagIds)).thenReturn(List.of(tagRule)); + assertEquals( + new HashSet<>(tagRule.getAssets()), + new HashSet<>(tagRuleService.getAssetsFromTagIds(tagIds))); + } + + @Test + void testApplyTagRuleToInjectCreation() throws Exception { + Asset asset1 = AssetFixture.createDefaultAsset("asset1"); + Asset asset2 = AssetFixture.createDefaultAsset("asset2"); + Asset asset3 = AssetFixture.createDefaultAsset("asset3"); + Asset asset4 = AssetFixture.createDefaultAsset("asset4"); + + Tag tag1 = TagFixture.getTag("tag2"); + Tag tag2 = TagFixture.getTag("tag3"); + + List currentAssets = List.of(asset1, asset2); + List defaultAssets = List.of(asset2, asset3, asset4); + TagRule tagRule = TagRuleFixture.createTagRule("tag_rule1", defaultAssets); + + when(tagRuleRepository.findByTags(List.of(tag1.getId(), tag2.getId()))) + .thenReturn(List.of(tagRule)); + + List result = + tagRuleService.applyTagRuleToInjectCreation( + List.of(tag1.getId(), tag2.getId()), currentAssets); + List expected = List.of(asset1, asset2, asset3, asset4); + assertEquals(new HashSet<>(expected), new HashSet<>(result)); + } } diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/AssetFixture.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/AssetFixture.java new file mode 100644 index 0000000000..9104b5d3d9 --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/AssetFixture.java @@ -0,0 +1,17 @@ +package io.openbas.utils.fixtures; + +import io.openbas.database.model.Asset; +import java.time.Instant; +import org.jetbrains.annotations.NotNull; + +public class AssetFixture { + public static Asset createDefaultAsset(@NotNull final String id) { + Asset asset = new Asset(); + asset.setId(id); + asset.setCreatedAt(Instant.now()); + asset.setUpdatedAt(Instant.now()); + asset.setName("asset name"); + asset.setDescription("asset description"); + return asset; + } +} diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/TagFixture.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/TagFixture.java index 7f70146c18..d362a15fe1 100644 --- a/openbas-api/src/test/java/io/openbas/utils/fixtures/TagFixture.java +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/TagFixture.java @@ -13,4 +13,12 @@ public static Tag getTag() { tag.setColor("#FFFFFF"); return tag; } + + public static Tag getTag(final String id) { + Tag tag = new Tag(); + tag.setId(id); + tag.setName(TAG_NAME); + tag.setColor("#FFFFFF"); + return tag; + } } diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/TagRuleFixture.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/TagRuleFixture.java index e68f6205e2..d22ccdde9c 100644 --- a/openbas-api/src/test/java/io/openbas/utils/fixtures/TagRuleFixture.java +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/TagRuleFixture.java @@ -37,6 +37,18 @@ public static TagRule createTagRule(String tagRuleId) { return rule; } + public static TagRule createTagRule(String tagRuleId, List assets) { + Tag tag = new Tag(); + tag.setName(TAG_NAME); + + TagRule rule = new TagRule(); + rule.setAssets(assets); + rule.setTag(tag); + rule.setId(tagRuleId); + + return rule; + } + public static TagRuleOutput createTagRuleOutput() { return TagRuleOutput.builder() .tagName(TAG_NAME) diff --git a/openbas-model/src/main/java/io/openbas/database/repository/TagRuleRepository.java b/openbas-model/src/main/java/io/openbas/database/repository/TagRuleRepository.java index 79bb86edaa..11eee77eae 100644 --- a/openbas-model/src/main/java/io/openbas/database/repository/TagRuleRepository.java +++ b/openbas-model/src/main/java/io/openbas/database/repository/TagRuleRepository.java @@ -1,10 +1,13 @@ package io.openbas.database.repository; import io.openbas.database.model.TagRule; +import java.util.List; import java.util.Optional; import org.jetbrains.annotations.NotNull; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @Repository @@ -13,4 +16,7 @@ public interface TagRuleRepository @NotNull Optional findById(@NotNull String id); + + @Query("select tr from TagRule tr where tr.tag.id IN :tagids") + List findByTags(@Param("tagids") List tagIds); }