From 13d6e1b5e80139c63ea3e9f29c699bee2fae943a Mon Sep 17 00:00:00 2001 From: antoinemzs Date: Mon, 20 Jan 2025 14:42:38 +0100 Subject: [PATCH] [backend/tests] export of simulations, JSON (chunk 1) (#1489) Signed-off-by: Antoine MAZEAS --- openbas-api/pom.xml | 6 + .../io/openbas/rest/exercise/ExerciseApi.java | 129 +--- .../exercise/exports/ExerciseFileExport.java | 270 ++++++++- .../rest/exercise/exports/ExportOptions.java | 24 + .../rest/exercise/ExerciseApiExportTest.java | 570 ++++++++++++++++++ .../exercise/exports/ExportOptionsTest.java | 91 +++ .../test/java/io/openbas/utils/ZipUtils.java | 29 + .../utils/fixtures/ArticleFixture.java | 7 + .../utils/fixtures/ChannelFixture.java | 15 + .../openbas/utils/fixtures/InjectFixture.java | 8 + .../io/openbas/utils/fixtures/TagFixture.java | 9 + .../openbas/utils/fixtures/TeamFixture.java | 6 + .../utils/fixtures/VariableFixture.java | 13 + .../fixtures/composers/ArticleComposer.java | 43 ++ .../fixtures/composers/ChallengeComposer.java | 49 ++ .../fixtures/composers/ChannelComposer.java | 35 ++ .../fixtures/composers/ComposerBase.java | 12 + .../fixtures/composers/DocumentComposer.java | 49 ++ .../fixtures/composers/ExerciseComposer.java | 138 +++++ .../fixtures/composers/InjectComposer.java | 73 +++ .../fixtures/composers/InnerComposerBase.java | 7 + .../composers/LessonsCategoryComposer.java | 50 ++ .../composers/LessonsQuestionsComposer.java | 36 ++ .../fixtures/composers/ObjectiveComposer.java | 35 ++ .../composers/OrganizationComposer.java | 49 ++ .../utils/fixtures/composers/TagComposer.java | 35 ++ .../fixtures/composers/TeamComposer.java | 60 ++ .../fixtures/composers/UserComposer.java | 57 ++ .../fixtures/composers/VariableComposer.java | 35 ++ 29 files changed, 1811 insertions(+), 129 deletions(-) create mode 100644 openbas-api/src/main/java/io/openbas/rest/exercise/exports/ExportOptions.java create mode 100644 openbas-api/src/test/java/io/openbas/rest/exercise/ExerciseApiExportTest.java create mode 100644 openbas-api/src/test/java/io/openbas/rest/exercise/exports/ExportOptionsTest.java create mode 100644 openbas-api/src/test/java/io/openbas/utils/ZipUtils.java create mode 100644 openbas-api/src/test/java/io/openbas/utils/fixtures/ChannelFixture.java create mode 100644 openbas-api/src/test/java/io/openbas/utils/fixtures/VariableFixture.java create mode 100644 openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ArticleComposer.java create mode 100644 openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ChallengeComposer.java create mode 100644 openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ChannelComposer.java create mode 100644 openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ComposerBase.java create mode 100644 openbas-api/src/test/java/io/openbas/utils/fixtures/composers/DocumentComposer.java create mode 100644 openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ExerciseComposer.java create mode 100644 openbas-api/src/test/java/io/openbas/utils/fixtures/composers/InjectComposer.java create mode 100644 openbas-api/src/test/java/io/openbas/utils/fixtures/composers/InnerComposerBase.java create mode 100644 openbas-api/src/test/java/io/openbas/utils/fixtures/composers/LessonsCategoryComposer.java create mode 100644 openbas-api/src/test/java/io/openbas/utils/fixtures/composers/LessonsQuestionsComposer.java create mode 100644 openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ObjectiveComposer.java create mode 100644 openbas-api/src/test/java/io/openbas/utils/fixtures/composers/OrganizationComposer.java create mode 100644 openbas-api/src/test/java/io/openbas/utils/fixtures/composers/TagComposer.java create mode 100644 openbas-api/src/test/java/io/openbas/utils/fixtures/composers/TeamComposer.java create mode 100644 openbas-api/src/test/java/io/openbas/utils/fixtures/composers/UserComposer.java create mode 100644 openbas-api/src/test/java/io/openbas/utils/fixtures/composers/VariableComposer.java diff --git a/openbas-api/pom.xml b/openbas-api/pom.xml index 3dc7675a2e..791ad61133 100644 --- a/openbas-api/pom.xml +++ b/openbas-api/pom.xml @@ -253,6 +253,12 @@ logback-jackson 0.1.5 + + net.javacrumbs.json-unit + json-unit-assertj + 4.1.0 + test + 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 5d57179a33..a328e82f8a 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 @@ -22,10 +22,7 @@ import io.openbas.database.specification.*; import io.openbas.rest.exception.ElementNotFoundException; import io.openbas.rest.exception.InputValidationException; -import io.openbas.rest.exercise.exports.ExerciseExportMixins; -import io.openbas.rest.exercise.exports.ExerciseFileExport; -import io.openbas.rest.exercise.exports.VariableMixin; -import io.openbas.rest.exercise.exports.VariableWithValueMixin; +import io.openbas.rest.exercise.exports.*; import io.openbas.rest.exercise.form.*; import io.openbas.rest.exercise.response.ExercisesGlobalScoresOutput; import io.openbas.rest.exercise.service.ExerciseService; @@ -752,119 +749,15 @@ public void exerciseExport( @RequestParam(required = false) final boolean isWithVariableValues, HttpServletResponse response) throws IOException { - // Setup the mapper for export - List documentIds = new ArrayList<>(); ObjectMapper objectMapper = mapper.copy(); - if (!isWithPlayers) { - objectMapper.addMixIn( - ExerciseFileExport.class, ExerciseExportMixins.ExerciseFileExport.class); - } - // Start exporting exercise - ExerciseFileExport importExport = new ExerciseFileExport(); - importExport.setVersion(1); + Exercise exercise = exerciseRepository.findById(exerciseId).orElseThrow(ElementNotFoundException::new); - objectMapper.addMixIn(Exercise.class, ExerciseExportMixins.Exercise.class); - // Build the export - importExport.setExercise(exercise); - importExport.setDocuments(exercise.getDocuments()); - documentIds.addAll(exercise.getDocuments().stream().map(Document::getId).toList()); - objectMapper.addMixIn(Document.class, ExerciseExportMixins.Document.class); - List exerciseTags = new ArrayList<>(exercise.getTags()); - // Objectives - List objectives = exercise.getObjectives(); - importExport.setObjectives(objectives); - objectMapper.addMixIn(Objective.class, ExerciseExportMixins.Objective.class); - // Lessons categories - List lessonsCategories = exercise.getLessonsCategories(); - importExport.setLessonsCategories(lessonsCategories); - objectMapper.addMixIn(LessonsCategory.class, ExerciseExportMixins.LessonsCategory.class); - // Lessons questions - List lessonsQuestions = - lessonsCategories.stream().flatMap(category -> category.getQuestions().stream()).toList(); - importExport.setLessonsQuestions(lessonsQuestions); - objectMapper.addMixIn(LessonsQuestion.class, ExerciseExportMixins.LessonsQuestion.class); - if (isWithTeams) { - // Teams - List teams = exercise.getTeams(); - importExport.setTeams(teams); - objectMapper.addMixIn( - Team.class, - isWithPlayers ? ExerciseExportMixins.Team.class : ExerciseExportMixins.EmptyTeam.class); - exerciseTags.addAll(teams.stream().flatMap(team -> team.getTags().stream()).toList()); - } - if (isWithPlayers) { - // players - List players = - exercise.getTeams().stream() - .flatMap(team -> team.getUsers().stream()) - .distinct() - .toList(); - exerciseTags.addAll(players.stream().flatMap(user -> user.getTags().stream()).toList()); - importExport.setUsers(players); - objectMapper.addMixIn(User.class, ExerciseExportMixins.User.class); - // organizations - List organizations = - players.stream().map(User::getOrganization).filter(Objects::nonNull).distinct().toList(); - exerciseTags.addAll(organizations.stream().flatMap(org -> org.getTags().stream()).toList()); - importExport.setOrganizations(organizations); - objectMapper.addMixIn(Organization.class, ExerciseExportMixins.Organization.class); - } - // Injects - List injects = exercise.getInjects(); - injects.forEach( - inject -> { - exerciseTags.addAll(inject.getTags()); - inject - .getInjectorContract() - .ifPresent( - injectorContract -> { - if (injectorContract.getPayload() != null) { - exerciseTags.addAll(injectorContract.getPayload().getTags()); - } - }); - exerciseTags.addAll(inject.getTags()); - }); - exerciseTags.addAll(injects.stream().flatMap(inject -> inject.getTags().stream()).toList()); - importExport.setInjects(injects); - objectMapper.addMixIn(Inject.class, ExerciseExportMixins.Inject.class); - // Documents - exerciseTags.addAll( - exercise.getDocuments().stream().flatMap(doc -> doc.getTags().stream()).toList()); - // Articles / Channels - List
articles = exercise.getArticles(); - importExport.setArticles(articles); - objectMapper.addMixIn(Article.class, ExerciseExportMixins.Article.class); - List channels = articles.stream().map(Article::getChannel).distinct().toList(); - documentIds.addAll( - channels.stream() - .flatMap(channel -> channel.getLogos().stream()) - .map(Document::getId) - .toList()); - importExport.setChannels(channels); - objectMapper.addMixIn(Channel.class, ExerciseExportMixins.Channel.class); - // Challenges - List challenges = fromIterable(challengeService.getExerciseChallenges(exerciseId)); - importExport.setChallenges(challenges); - documentIds.addAll( - challenges.stream() - .flatMap(challenge -> challenge.getDocuments().stream()) - .map(Document::getId) - .toList()); - objectMapper.addMixIn(Challenge.class, ExerciseExportMixins.Challenge.class); - exerciseTags.addAll( - challenges.stream().flatMap(challenge -> challenge.getTags().stream()).toList()); - // Tags - importExport.setTags(exerciseTags.stream().distinct().toList()); - objectMapper.addMixIn(Tag.class, ExerciseExportMixins.Tag.class); - // -- Variables -- - List variables = this.variableService.variablesFromExercise(exerciseId); - importExport.setVariables(variables); - if (isWithVariableValues) { - objectMapper.addMixIn(Variable.class, VariableWithValueMixin.class); - } else { - objectMapper.addMixIn(Variable.class, VariableMixin.class); - } + ExerciseFileExport importExport = + ExerciseFileExport.fromExercise( + exercise, objectMapper, this.variableService, this.challengeService) + .withOptions(ExportOptions.mask(isWithPlayers, isWithTeams, isWithVariableValues)); + // Build the response String infos = "(" @@ -882,10 +775,14 @@ public void exerciseExport( ZipEntry zipEntry = new ZipEntry(exercise.getName() + ".json"); zipEntry.setComment(EXPORT_ENTRY_EXERCISE); zipExport.putNextEntry(zipEntry); - zipExport.write(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(importExport)); + zipExport.write( + importExport + .getObjectMapper() + .writerWithDefaultPrettyPrinter() + .writeValueAsBytes(importExport)); zipExport.closeEntry(); // Add the documents - documentIds.stream() + importExport.getAllDocumentIds().stream() .distinct() .forEach( docId -> { diff --git a/openbas-api/src/main/java/io/openbas/rest/exercise/exports/ExerciseFileExport.java b/openbas-api/src/main/java/io/openbas/rest/exercise/exports/ExerciseFileExport.java index fd768f459c..ffade46451 100644 --- a/openbas-api/src/main/java/io/openbas/rest/exercise/exports/ExerciseFileExport.java +++ b/openbas-api/src/main/java/io/openbas/rest/exercise/exports/ExerciseFileExport.java @@ -1,13 +1,18 @@ package io.openbas.rest.exercise.exports; import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; +import static io.openbas.helper.StreamHelper.fromIterable; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; import io.openbas.database.model.*; +import io.openbas.service.ChallengeService; +import io.openbas.service.VariableService; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import lombok.Getter; import lombok.Setter; @@ -16,50 +21,289 @@ @JsonInclude(NON_NULL) public class ExerciseFileExport { + @JsonIgnore private ChallengeService challengeService; + @JsonIgnore private VariableService variableService; + @JsonIgnore private ObjectMapper objectMapper; + @JsonProperty("export_version") - private int version; + private int version = 1; @JsonProperty("exercise_information") private Exercise exercise; @JsonProperty("exercise_teams") - private List teams = new ArrayList<>(); + private List teams; + + public List getTeams() { + if (teams == null) { + return this.exercise != null + && ExportOptions.has(ExportOptions.WITH_TEAMS, this.exportOptionsMask) + ? this.exercise.getTeams().stream().toList() + : new ArrayList<>(); + } + return teams; + } @JsonProperty("exercise_objectives") - private List objectives = new ArrayList<>(); + private List objectives; + + public List getObjectives() { + if (objectives == null) { + return this.exercise == null + ? new ArrayList<>() + : this.exercise.getObjectives().stream().toList(); + } + return objectives; + } @JsonProperty("exercise_users") - private List users = new ArrayList<>(); + private List users; + + public List getUsers() { + if (users == null) { + return this.exercise != null + && ExportOptions.has(ExportOptions.WITH_PLAYERS, this.exportOptionsMask) + ? this.exercise.getTeams().stream() + .flatMap(team -> team.getUsers().stream()) + .distinct() + .toList() + : new ArrayList<>(); + } + return users; + } @JsonProperty("exercise_organizations") - private List organizations = new ArrayList<>(); + private List organizations; + + public List getOrganizations() { + if (organizations == null) { + return this.exercise == null + ? new ArrayList<>() + : this.exercise.getUsers().stream() + .map(User::getOrganization) + .filter(Objects::nonNull) + .distinct() + .toList(); + } + return organizations; + } @JsonProperty("exercise_injects") - private List injects = new ArrayList<>(); + private List injects; + + public List getInjects() { + if (injects == null) { + return this.exercise == null ? new ArrayList<>() : this.exercise.getInjects(); + } + return injects; + } @JsonProperty("exercise_tags") - private List tags = new ArrayList<>(); + private List tags; + + public List getTags() { + if (tags == null) { + if (this.exercise == null) { + return new ArrayList<>(); + } else { + List allTags = new ArrayList<>(); + allTags.addAll(this.exercise.getTags().stream().toList()); + allTags.addAll(this.getTeams().stream().flatMap(team -> team.getTags().stream()).toList()); + allTags.addAll(this.getUsers().stream().flatMap(user -> user.getTags().stream()).toList()); + allTags.addAll( + this.getOrganizations().stream() + .flatMap(organization -> organization.getTags().stream()) + .toList()); + allTags.addAll( + this.getDocuments().stream().flatMap(doc -> doc.getTags().stream()).toList()); + allTags.addAll( + this.getChallenges().stream() + .flatMap(challenge -> challenge.getTags().stream()) + .toList()); + this.getInjects() + .forEach( + inject -> { + allTags.addAll(inject.getTags()); + inject + .getInjectorContract() + .ifPresent( + injectorContract -> { + if (injectorContract.getPayload() != null) { + allTags.addAll(injectorContract.getPayload().getTags()); + } + }); + }); + + return allTags; + } + } + return tags; + } @JsonProperty("exercise_documents") - private List documents = new ArrayList<>(); + private List documents; + + public List getDocuments() { + if (documents == null) { + return this.exercise == null ? new ArrayList<>() : this.exercise.getDocuments(); + } + return documents; + } @JsonProperty("exercise_channels") - private List channels = new ArrayList<>(); + private List channels; + + public List getChannels() { + if (channels == null) { + return this.exercise == null + ? new ArrayList<>() + : this.exercise.getArticles().stream().map(Article::getChannel).distinct().toList(); + } + return channels; + } @JsonProperty("exercise_articles") - private List
articles = new ArrayList<>(); + private List
articles; + + public List
getArticles() { + if (articles == null) { + return this.exercise == null ? new ArrayList<>() : this.exercise.getArticles(); + } + return articles; + } @JsonProperty("exercise_challenges") - private List challenges = new ArrayList<>(); + private List challenges; + + public List getChallenges() { + if (challenges == null) { + return this.exercise == null + ? new ArrayList<>() + : fromIterable(challengeService.getExerciseChallenges(this.exercise.getId())); + } + return challenges; + } @JsonProperty("exercise_lessons_categories") - private List lessonsCategories = new ArrayList<>(); + private List lessonsCategories; + + public List getLessonsCategories() { + if (lessonsCategories == null) { + return this.exercise == null + ? new ArrayList<>() + : this.exercise.getLessonsCategories().stream().toList(); + } + return lessonsCategories; + } @JsonProperty("exercise_lessons_questions") - private List lessonsQuestions = new ArrayList<>(); + private List lessonsQuestions; + + public List getLessonsQuestions() { + if (lessonsQuestions == null) { + return this.exercise == null + ? new ArrayList<>() + : this.exercise.getLessonsCategories().stream() + .flatMap(category -> category.getQuestions().stream()) + .toList(); + } + return lessonsQuestions; + } @JsonIgnore public static final String EXERCISE_VARIABLES = "exercise_variables"; @JsonProperty(EXERCISE_VARIABLES) private List variables; + + public List getVariables() { + if (variables == null) { + return this.exercise == null + ? new ArrayList<>() + : this.variableService.variablesFromExercise(this.exercise.getId()).stream().toList(); + } + return variables; + } + + @JsonIgnore + public List getAllDocumentIds() { + List documentIds = new ArrayList<>(); + documentIds.addAll(this.getDocuments().stream().map(Document::getId).toList()); + documentIds.addAll( + this.getChannels().stream() + .flatMap(channel -> channel.getLogos().stream()) + .map(Document::getId) + .toList()); + documentIds.addAll( + this.getChallenges().stream() + .flatMap(challenge -> challenge.getDocuments().stream()) + .map(Document::getId) + .toList()); + return documentIds; + } + + @JsonIgnore private int exportOptionsMask = 0; + + private ExerciseFileExport( + ObjectMapper objectMapper, + VariableService variableService, + ChallengeService challengeService) { + this.objectMapper = objectMapper; + this.variableService = variableService; + this.challengeService = challengeService; + + this.objectMapper.addMixIn(Exercise.class, ExerciseExportMixins.Exercise.class); + this.objectMapper.addMixIn(Document.class, ExerciseExportMixins.Document.class); + this.objectMapper.addMixIn(Objective.class, ExerciseExportMixins.Objective.class); + this.objectMapper.addMixIn(LessonsCategory.class, ExerciseExportMixins.LessonsCategory.class); + this.objectMapper.addMixIn(LessonsQuestion.class, ExerciseExportMixins.LessonsQuestion.class); + this.objectMapper.addMixIn(User.class, ExerciseExportMixins.User.class); + this.objectMapper.addMixIn(Organization.class, ExerciseExportMixins.Organization.class); + this.objectMapper.addMixIn(Inject.class, ExerciseExportMixins.Inject.class); + this.objectMapper.addMixIn(Article.class, ExerciseExportMixins.Article.class); + this.objectMapper.addMixIn(Channel.class, ExerciseExportMixins.Channel.class); + this.objectMapper.addMixIn(Challenge.class, ExerciseExportMixins.Challenge.class); + this.objectMapper.addMixIn(Tag.class, ExerciseExportMixins.Tag.class); + + // default options + // variables with no value + this.objectMapper.addMixIn(Variable.class, VariableMixin.class); + // empty teams + this.objectMapper.addMixIn(Team.class, ExerciseExportMixins.EmptyTeam.class); + } + + public static final ExerciseFileExport fromExercise( + Exercise exercise, + ObjectMapper objectMapper, + VariableService variableService, + ChallengeService challengeService) { + ExerciseFileExport efe = + new ExerciseFileExport(objectMapper, variableService, challengeService); + efe.setExercise(exercise); + return efe; + } + + public ExerciseFileExport withOptions(int exportOptionsMask) { + this.exportOptionsMask = exportOptionsMask; + + // disable users if not requested; note negation + if (!ExportOptions.has(ExportOptions.WITH_PLAYERS, this.exportOptionsMask)) { + this.objectMapper.addMixIn( + ExerciseFileExport.class, ExerciseExportMixins.ExerciseFileExport.class); + } + + if (ExportOptions.has(ExportOptions.WITH_TEAMS, this.exportOptionsMask)) { + this.objectMapper.addMixIn( + Team.class, + ExportOptions.has(ExportOptions.WITH_PLAYERS, this.exportOptionsMask) + ? ExerciseExportMixins.Team.class + : ExerciseExportMixins.EmptyTeam.class); + } + if (ExportOptions.has(ExportOptions.WITH_VARIABLE_VALUES, this.exportOptionsMask)) { + this.objectMapper.addMixIn(Variable.class, VariableWithValueMixin.class); + } else { + this.objectMapper.addMixIn(Variable.class, VariableMixin.class); + } + + return this; + } } diff --git a/openbas-api/src/main/java/io/openbas/rest/exercise/exports/ExportOptions.java b/openbas-api/src/main/java/io/openbas/rest/exercise/exports/ExportOptions.java new file mode 100644 index 0000000000..b4e764522e --- /dev/null +++ b/openbas-api/src/main/java/io/openbas/rest/exercise/exports/ExportOptions.java @@ -0,0 +1,24 @@ +package io.openbas.rest.exercise.exports; + +// note all values and future values must be an incremental power of 2 +public enum ExportOptions { + WITH_PLAYERS((int) Math.pow(2, 0)), + WITH_TEAMS((int) Math.pow(2, 1)), + WITH_VARIABLE_VALUES((int) Math.pow(2, 2)); + + private final int rank; + + ExportOptions(int rank) { + this.rank = rank; + } + + public static int mask(boolean with_players, boolean with_teams, boolean with_variable_values) { + return (with_players ? WITH_PLAYERS.rank : 0) + | (with_teams ? WITH_TEAMS.rank : 0) + | (with_variable_values ? WITH_VARIABLE_VALUES.rank : 0); + } + + public static boolean has(ExportOptions option, int mask) { + return (mask & option.rank) != 0; + } +} diff --git a/openbas-api/src/test/java/io/openbas/rest/exercise/ExerciseApiExportTest.java b/openbas-api/src/test/java/io/openbas/rest/exercise/ExerciseApiExportTest.java new file mode 100644 index 0000000000..9ee72a6509 --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/rest/exercise/ExerciseApiExportTest.java @@ -0,0 +1,570 @@ +package io.openbas.rest.exercise; + +import static io.openbas.rest.exercise.ExerciseApi.EXERCISE_URI; +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; +import static net.javacrumbs.jsonunit.core.Option.IGNORING_ARRAY_ORDER; +import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.openbas.IntegrationTest; +import io.openbas.database.model.*; +import io.openbas.database.model.Tag; +import io.openbas.rest.exercise.exports.ExerciseExportMixins; +import io.openbas.rest.exercise.exports.ExerciseFileExport; +import io.openbas.rest.exercise.exports.VariableMixin; +import io.openbas.rest.exercise.exports.VariableWithValueMixin; +import io.openbas.service.ChallengeService; +import io.openbas.service.VariableService; +import io.openbas.utils.ZipUtils; +import io.openbas.utils.fixtures.*; +import io.openbas.utils.fixtures.composers.*; +import io.openbas.utils.mockUser.WithMockAdminUser; +import jakarta.annotation.Resource; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@TestInstance(PER_CLASS) +public class ExerciseApiExportTest extends IntegrationTest { + @Autowired private MockMvc mvc; + @Autowired private ExerciseComposer exerciseComposer; + @Autowired private ArticleComposer articleComposer; + @Autowired private ChannelComposer channelComposer; + @Autowired private LessonsQuestionsComposer lessonsQuestionsComposer; + @Autowired private LessonsCategoryComposer lessonsCategoryComposer; + @Autowired private VariableComposer variableComposer; + @Autowired private TeamComposer teamComposer; + @Autowired private UserComposer userComposer; + @Autowired private OrganizationComposer organizationComposer; + @Autowired private InjectComposer injectComposer; + @Autowired private ChallengeComposer challengeComposer; + @Autowired private ObjectiveComposer objectiveComposer; + @Autowired private DocumentComposer documentComposer; + @Autowired private TagComposer tagComposer; + @Autowired private VariableService variableService; + @Autowired private ChallengeService challengeService; + @Resource protected ObjectMapper mapper; + + @BeforeEach + void before() { + lessonsQuestionsComposer.reset(); + lessonsCategoryComposer.reset(); + teamComposer.reset(); + userComposer.reset(); + variableComposer.reset(); + organizationComposer.reset(); + injectComposer.reset(); + challengeComposer.reset(); + channelComposer.reset(); + articleComposer.reset(); + objectiveComposer.reset(); + documentComposer.reset(); + tagComposer.reset(); + exerciseComposer.reset(); + } + + private Exercise getExercise() { + return exerciseComposer + .forExercise(ExerciseFixture.createDefaultCrisisExercise()) + .withArticle( + articleComposer + .forArticle(ArticleFixture.getArticleNoChannel()) + .withChannel(channelComposer.forChannel(ChannelFixture.getChannel()))) + .withLessonCategory( + lessonsCategoryComposer + .forLessonsCategory(LessonsCategoryFixture.createLessonCategory()) + .withLessonsQuestion( + lessonsQuestionsComposer.forLessonsQuestion( + LessonsQuestionFixture.createLessonsQuestion()))) + .withTeam( + teamComposer + .forTeam(TeamFixture.getEmptyTeam()) + .withTag(tagComposer.forTag(TagFixture.getTagWithText("Team tag"))) + .withUser( + userComposer + .forUser(UserFixture.getUser()) + .withTag(tagComposer.forTag(TagFixture.getTagWithText("User tag"))) + .withOrganization( + organizationComposer + .forOrganization(OrganizationFixture.createOrganization()) + .withTag( + tagComposer.forTag( + TagFixture.getTagWithText("Organization tag")))))) + .withTeamUsers() + .withInject( + injectComposer + .forInject(InjectFixture.getInjectWithoutContract()) + .withTag(tagComposer.forTag(TagFixture.getTagWithText("Inject tag"))) + .withChallenge( + challengeComposer + .forChallenge(ChallengeFixture.createDefaultChallenge()) + .withTag(tagComposer.forTag(TagFixture.getTagWithText("Challenge tag"))))) + .withDocument( + documentComposer + .forDocument(DocumentFixture.getDocumentJpeg()) + .withTag(tagComposer.forTag(TagFixture.getTagWithText("Document tag")))) + .withObjective(objectiveComposer.forObjective(ObjectiveFixture.getObjective())) + .withTag(tagComposer.forTag(TagFixture.getTagWithText("Exercise tag"))) + .withVariable(variableComposer.forVariable(VariableFixture.getVariable())) + .persist() + .get(); + } + + @DisplayName("Given a valid simulation, the export file is found in zip and correct") + @Test + @WithMockAdminUser + public void given_a_valid_simulation_the_export_file_is_found_in_zip_and_correct() + throws Exception { + Exercise ex = getExercise(); + byte[] response = + mvc.perform( + get(EXERCISE_URI + "/" + ex.getId() + "/export").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getResponse() + .getContentAsByteArray(); + + String actualJson = ZipUtils.getZipEntryAsString(response, "%s.json".formatted(ex.getName())); + ObjectMapper exportMapper = mapper.copy(); + String expectedJson = + exportMapper.writeValueAsString( + ExerciseFileExport.fromExercise(ex, exportMapper, variableService, challengeService) + .withOptions(0)); + + assertThatJson(expectedJson).isObject().isEqualTo(actualJson); + } + + @DisplayName( + "Given a valid simulation and full options, the export file is found in zip and correct") + @Test + @WithMockAdminUser + public void + given_a_valid_simulation_and_full_options_the_export_file_is_found_in_zip_and_correct() + throws Exception { + Exercise ex = getExercise(); + byte[] response = + mvc.perform( + get(EXERCISE_URI + "/" + ex.getId() + "/export") + .queryParam("isWithPlayers", "true") + .queryParam("isWithTeams", "true") + .queryParam("isWithVariableValues", "true") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getResponse() + .getContentAsByteArray(); + + String actualJson = ZipUtils.getZipEntryAsString(response, "%s.json".formatted(ex.getName())); + ObjectMapper exportMapper = mapper.copy(); + String expectedJson = + exportMapper.writeValueAsString( + ExerciseFileExport.fromExercise(ex, exportMapper, variableService, challengeService) + .withOptions(7)); + + assertThatJson(expectedJson).isObject().isEqualTo(actualJson); + } + + @DisplayName("Given a valid simulation and default options, exported tags are correct") + @Test + @WithMockAdminUser + public void given_a_valid_simulation_and_default_options_exported_tags_are_correct() + throws Exception { + ObjectMapper objectMapper = mapper.copy(); + Exercise ex = getExercise(); + byte[] response = + mvc.perform( + get(EXERCISE_URI + "/" + ex.getId() + "/export").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getResponse() + .getContentAsByteArray(); + + String actualJson = ZipUtils.getZipEntryAsString(response, "%s.json".formatted(ex.getName())); + + objectMapper.addMixIn(Tag.class, ExerciseExportMixins.Tag.class); + List expectedTags = + tagComposer.generatedItems.stream() + .filter( + tag -> + Arrays.asList( + "exercise tag", + "document tag", + "challenge tag", + "inject tag", + "organization tag") + .contains(tag.getName())) + .toList(); + String tagsJson = objectMapper.writeValueAsString(expectedTags); + + assertThatJson(actualJson).when(IGNORING_ARRAY_ORDER).node("exercise_tags").isEqualTo(tagsJson); + } + + @DisplayName("Given a valid simulation and default options, exported objectives are correct") + @Test + @WithMockAdminUser + public void given_a_valid_simulation_and_default_options_exported_objectives_are_correct() + throws Exception { + ObjectMapper objectMapper = mapper.copy(); + Exercise ex = getExercise(); + byte[] response = + mvc.perform( + get(EXERCISE_URI + "/" + ex.getId() + "/export").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getResponse() + .getContentAsByteArray(); + + String actualJson = ZipUtils.getZipEntryAsString(response, "%s.json".formatted(ex.getName())); + + objectMapper.addMixIn(Objective.class, ExerciseExportMixins.Objective.class); + String objectiveJson = objectMapper.writeValueAsString(objectiveComposer.generatedItems); + + assertThatJson(actualJson) + .when(IGNORING_ARRAY_ORDER) + .node("exercise_objectives") + .isEqualTo(objectiveJson); + } + + @DisplayName("Given a valid simulation and default options, exported challenges are correct") + @Test + @WithMockAdminUser + public void given_a_valid_simulation_and_default_options_exported_challenges_are_correct() + throws Exception { + ObjectMapper objectMapper = mapper.copy(); + Exercise ex = getExercise(); + byte[] response = + mvc.perform( + get(EXERCISE_URI + "/" + ex.getId() + "/export").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getResponse() + .getContentAsByteArray(); + + String actualJson = ZipUtils.getZipEntryAsString(response, "%s.json".formatted(ex.getName())); + + objectMapper.addMixIn(Challenge.class, ExerciseExportMixins.Challenge.class); + String challengeJson = objectMapper.writeValueAsString(challengeComposer.generatedItems); + + assertThatJson(actualJson) + .when(IGNORING_ARRAY_ORDER) + .node("exercise_challenges") + .isEqualTo(challengeJson); + } + + @DisplayName("Given a valid simulation and default options, exported articles are correct") + @Test + @WithMockAdminUser + public void given_a_valid_simulation_and_default_options_exported_articles_are_correct() + throws Exception { + ObjectMapper objectMapper = mapper.copy(); + Exercise ex = getExercise(); + byte[] response = + mvc.perform( + get(EXERCISE_URI + "/" + ex.getId() + "/export").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getResponse() + .getContentAsByteArray(); + + String actualJson = ZipUtils.getZipEntryAsString(response, "%s.json".formatted(ex.getName())); + + objectMapper.addMixIn(Article.class, ExerciseExportMixins.Article.class); + String articleJson = objectMapper.writeValueAsString(articleComposer.generatedItems); + + assertThatJson(actualJson) + .when(IGNORING_ARRAY_ORDER) + .node("exercise_articles") + .isEqualTo(articleJson); + } + + @DisplayName("Given a valid simulation and default options, exported channels are correct") + @Test + @WithMockAdminUser + public void given_a_valid_simulation_and_default_options_exported_channels_are_correct() + throws Exception { + ObjectMapper objectMapper = mapper.copy(); + Exercise ex = getExercise(); + byte[] response = + mvc.perform( + get(EXERCISE_URI + "/" + ex.getId() + "/export").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getResponse() + .getContentAsByteArray(); + + String actualJson = ZipUtils.getZipEntryAsString(response, "%s.json".formatted(ex.getName())); + + objectMapper.addMixIn(Channel.class, ExerciseExportMixins.Channel.class); + String channelJson = objectMapper.writeValueAsString(channelComposer.generatedItems); + + assertThatJson(actualJson) + .when(IGNORING_ARRAY_ORDER) + .node("exercise_channels") + .isEqualTo(channelJson); + } + + @DisplayName("Given a valid simulation and default options, exported documents are correct") + @Test + @WithMockAdminUser + public void given_a_valid_simulation_and_default_options_exported_documents_are_correct() + throws Exception { + ObjectMapper objectMapper = mapper.copy(); + Exercise ex = getExercise(); + byte[] response = + mvc.perform( + get(EXERCISE_URI + "/" + ex.getId() + "/export").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getResponse() + .getContentAsByteArray(); + + String actualJson = ZipUtils.getZipEntryAsString(response, "%s.json".formatted(ex.getName())); + + objectMapper.addMixIn(Document.class, ExerciseExportMixins.Document.class); + String documentJson = objectMapper.writeValueAsString(documentComposer.generatedItems); + + assertThatJson(actualJson) + .when(IGNORING_ARRAY_ORDER) + .node("exercise_documents") + .isEqualTo(documentJson); + } + + @DisplayName("Given a valid simulation and default options, exported exercise info are correct") + @Test + @WithMockAdminUser + public void given_a_valid_simulation_and_default_options_exported_exercise_info_are_correct() + throws Exception { + ObjectMapper objectMapper = mapper.copy(); + Exercise ex = getExercise(); + byte[] response = + mvc.perform( + get(EXERCISE_URI + "/" + ex.getId() + "/export").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getResponse() + .getContentAsByteArray(); + + String actualJson = ZipUtils.getZipEntryAsString(response, "%s.json".formatted(ex.getName())); + + objectMapper.addMixIn(Exercise.class, ExerciseExportMixins.Exercise.class); + String exerciseJson = objectMapper.writeValueAsString(ex); + + assertThatJson(actualJson) + .when(IGNORING_ARRAY_ORDER) + .node("exercise_information") + .isEqualTo(exerciseJson); + } + + @DisplayName("Given a valid simulation and default options, exported variables have no value") + @Test + @WithMockAdminUser + public void given_a_valid_simulation_and_default_options_exported_variables_have_no_value() + throws Exception { + ObjectMapper objectMapper = mapper.copy(); + Exercise ex = getExercise(); + byte[] response = + mvc.perform( + get(EXERCISE_URI + "/" + ex.getId() + "/export").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getResponse() + .getContentAsByteArray(); + + String actualJson = ZipUtils.getZipEntryAsString(response, "%s.json".formatted(ex.getName())); + + objectMapper.addMixIn(Variable.class, VariableMixin.class); + String variableJson = objectMapper.writeValueAsString(variableComposer.generatedItems); + + assertThatJson(actualJson) + .when(IGNORING_ARRAY_ORDER) + .node("exercise_variables") + .isEqualTo(variableJson); + } + + @DisplayName( + "Given a valid simulation, given isWithVariableValues options, exported variables have values") + @Test + @WithMockAdminUser + public void + given_a_valid_simulation_given_isWithVariableValues_option_exported_variables_have_values() + throws Exception { + ObjectMapper objectMapper = mapper.copy(); + Exercise ex = getExercise(); + byte[] response = + mvc.perform( + get(EXERCISE_URI + "/" + ex.getId() + "/export") + .queryParam("isWithVariableValues", "true") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getResponse() + .getContentAsByteArray(); + + String actualJson = ZipUtils.getZipEntryAsString(response, "%s.json".formatted(ex.getName())); + + objectMapper.addMixIn(Variable.class, VariableWithValueMixin.class); + String variableJson = objectMapper.writeValueAsString(variableComposer.generatedItems); + + assertThatJson(actualJson) + .when(IGNORING_ARRAY_ORDER) + .node("exercise_variables") + .isEqualTo(variableJson); + } + + @DisplayName("Given a valid simulation and default options, exported teams is empty array") + @Test + @WithMockAdminUser + public void given_a_valid_simulation_and_default_options_exported_teams_is_empty_array() + throws Exception { + Exercise ex = getExercise(); + byte[] response = + mvc.perform( + get(EXERCISE_URI + "/" + ex.getId() + "/export").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getResponse() + .getContentAsByteArray(); + + String actualJson = ZipUtils.getZipEntryAsString(response, "%s.json".formatted(ex.getName())); + + assertThatJson(actualJson).when(IGNORING_ARRAY_ORDER).node("exercise_teams").isEqualTo("[]"); + } + + @DisplayName( + "Given a valid simulation, given isWithTeams and NOT isWithPlayers options, exported teams have empty users") + @Test + @WithMockAdminUser + public void + given_a_valid_simulation_given_isWithTeams_and_not_isWithPlayers_options_exported_teams_have_empty_users() + throws Exception { + ObjectMapper objectMapper = mapper.copy(); + Exercise ex = getExercise(); + byte[] response = + mvc.perform( + get(EXERCISE_URI + "/" + ex.getId() + "/export") + .queryParam("isWithTeams", "true") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getResponse() + .getContentAsByteArray(); + + String actualJson = ZipUtils.getZipEntryAsString(response, "%s.json".formatted(ex.getName())); + + objectMapper.addMixIn(Team.class, ExerciseExportMixins.EmptyTeam.class); + String teamsJson = objectMapper.writeValueAsString(teamComposer.generatedItems); + + assertThatJson(actualJson) + .when(IGNORING_ARRAY_ORDER) + .node("exercise_teams") + .isEqualTo(teamsJson); + // users still not included + assertThatJson(actualJson).when(IGNORING_ARRAY_ORDER).node("exercise_users").isAbsent(); + } + + @DisplayName("Given a valid simulation and default options, exported users is absent key") + @Test + @WithMockAdminUser + public void given_a_valid_simulation_and_default_options_exported_users_is_absent_key() + throws Exception { + Exercise ex = getExercise(); + byte[] response = + mvc.perform( + get(EXERCISE_URI + "/" + ex.getId() + "/export").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getResponse() + .getContentAsByteArray(); + + String actualJson = ZipUtils.getZipEntryAsString(response, "%s.json".formatted(ex.getName())); + + assertThatJson(actualJson).when(IGNORING_ARRAY_ORDER).node("exercise_users").isAbsent(); + } + + @DisplayName( + "Given a valid simulation, given isWithPlayers and NOT isWithTeams option, exported users are correct") + @Test + @WithMockAdminUser + public void + given_a_valid_simulation_given_isWithPlayers_and_not_isWithTeams_options_exported_users_are_correct() + throws Exception { + ObjectMapper objectMapper = mapper.copy(); + Exercise ex = getExercise(); + byte[] response = + mvc.perform( + get(EXERCISE_URI + "/" + ex.getId() + "/export") + .queryParam("isWithPlayers", "true") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getResponse() + .getContentAsByteArray(); + + String actualJson = ZipUtils.getZipEntryAsString(response, "%s.json".formatted(ex.getName())); + + objectMapper.addMixIn(User.class, ExerciseExportMixins.User.class); + String usersJson = objectMapper.writeValueAsString(userComposer.generatedItems); + + assertThatJson(actualJson) + .when(IGNORING_ARRAY_ORDER) + .node("exercise_users") + .isEqualTo(usersJson); + assertThatJson(actualJson).when(IGNORING_ARRAY_ORDER).node("exercise_teams").isEqualTo("[]"); + } + + @DisplayName("Given a valid simulation and default options, exported organisations is absent key") + @Test + @WithMockAdminUser + public void given_a_valid_simulation_and_default_options_exported_organisations_is_absent_key() + throws Exception { + Exercise ex = getExercise(); + byte[] response = + mvc.perform( + get(EXERCISE_URI + "/" + ex.getId() + "/export").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getResponse() + .getContentAsByteArray(); + + String actualJson = ZipUtils.getZipEntryAsString(response, "%s.json".formatted(ex.getName())); + + assertThatJson(actualJson).when(IGNORING_ARRAY_ORDER).node("exercise_users").isAbsent(); + } + + @DisplayName( + "Given a valid simulation, given isWithPlayers option, exported organisations are correct") + @Test + @WithMockAdminUser + public void + given_a_valid_simulation_given_isWithPlayers_option_exported_organisations_are_correct() + throws Exception { + ObjectMapper objectMapper = mapper.copy(); + Exercise ex = getExercise(); + byte[] response = + mvc.perform( + get(EXERCISE_URI + "/" + ex.getId() + "/export") + .queryParam("isWithPlayers", "true") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getResponse() + .getContentAsByteArray(); + + String actualJson = ZipUtils.getZipEntryAsString(response, "%s.json".formatted(ex.getName())); + + objectMapper.addMixIn(Organization.class, ExerciseExportMixins.Organization.class); + String orgJson = objectMapper.writeValueAsString(organizationComposer.generatedItems); + + assertThatJson(actualJson) + .when(IGNORING_ARRAY_ORDER) + .node("exercise_organizations") + .isEqualTo(orgJson); + } +} diff --git a/openbas-api/src/test/java/io/openbas/rest/exercise/exports/ExportOptionsTest.java b/openbas-api/src/test/java/io/openbas/rest/exercise/exports/ExportOptionsTest.java new file mode 100644 index 0000000000..65fb7440e6 --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/rest/exercise/exports/ExportOptionsTest.java @@ -0,0 +1,91 @@ +package io.openbas.rest.exercise.exports; + +import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; + +import io.openbas.IntegrationTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +@TestInstance(PER_CLASS) +public class ExportOptionsTest extends IntegrationTest { + + @DisplayName("Given three boolean options, the resulting mask equals the correct decimal value") + @Test + public void given_three_boolean_options_the_resulting_mask_equals_correct_decimal_value() { + Assertions.assertEquals(0, ExportOptions.mask(false, false, false)); + Assertions.assertEquals(1, ExportOptions.mask(true, false, false)); + Assertions.assertEquals(2, ExportOptions.mask(false, true, false)); + Assertions.assertEquals(3, ExportOptions.mask(true, true, false)); + Assertions.assertEquals(4, ExportOptions.mask(false, false, true)); + Assertions.assertEquals(5, ExportOptions.mask(true, false, true)); + Assertions.assertEquals(6, ExportOptions.mask(false, true, true)); + Assertions.assertEquals(7, ExportOptions.mask(true, true, true)); + } + + @DisplayName( + "Given a mask and a standalone option, return correctly whether the option is within the mask or not") + @Test + public void + given_a_mask_and_a_standalone_option_return_correctly_whether_the_option_is_within_the_mask_or_not() { + Assertions.assertFalse( + ExportOptions.has(ExportOptions.WITH_PLAYERS, ExportOptions.mask(false, false, false))); + Assertions.assertFalse( + ExportOptions.has(ExportOptions.WITH_PLAYERS, ExportOptions.mask(false, true, false))); + Assertions.assertFalse( + ExportOptions.has(ExportOptions.WITH_PLAYERS, ExportOptions.mask(false, false, true))); + Assertions.assertFalse( + ExportOptions.has(ExportOptions.WITH_PLAYERS, ExportOptions.mask(false, true, true))); + Assertions.assertTrue( + ExportOptions.has(ExportOptions.WITH_PLAYERS, ExportOptions.mask(true, false, false))); + Assertions.assertTrue( + ExportOptions.has(ExportOptions.WITH_PLAYERS, ExportOptions.mask(true, true, false))); + Assertions.assertTrue( + ExportOptions.has(ExportOptions.WITH_PLAYERS, ExportOptions.mask(true, false, true))); + Assertions.assertTrue( + ExportOptions.has(ExportOptions.WITH_PLAYERS, ExportOptions.mask(true, true, true))); + + Assertions.assertFalse( + ExportOptions.has(ExportOptions.WITH_TEAMS, ExportOptions.mask(false, false, false))); + Assertions.assertTrue( + ExportOptions.has(ExportOptions.WITH_TEAMS, ExportOptions.mask(false, true, false))); + Assertions.assertFalse( + ExportOptions.has(ExportOptions.WITH_TEAMS, ExportOptions.mask(false, false, true))); + Assertions.assertTrue( + ExportOptions.has(ExportOptions.WITH_TEAMS, ExportOptions.mask(false, true, true))); + Assertions.assertFalse( + ExportOptions.has(ExportOptions.WITH_TEAMS, ExportOptions.mask(true, false, false))); + Assertions.assertTrue( + ExportOptions.has(ExportOptions.WITH_TEAMS, ExportOptions.mask(true, true, false))); + Assertions.assertFalse( + ExportOptions.has(ExportOptions.WITH_TEAMS, ExportOptions.mask(true, false, true))); + Assertions.assertTrue( + ExportOptions.has(ExportOptions.WITH_TEAMS, ExportOptions.mask(true, true, true))); + + Assertions.assertFalse( + ExportOptions.has( + ExportOptions.WITH_VARIABLE_VALUES, ExportOptions.mask(false, false, false))); + Assertions.assertFalse( + ExportOptions.has( + ExportOptions.WITH_VARIABLE_VALUES, ExportOptions.mask(false, true, false))); + Assertions.assertTrue( + ExportOptions.has( + ExportOptions.WITH_VARIABLE_VALUES, ExportOptions.mask(false, false, true))); + Assertions.assertTrue( + ExportOptions.has( + ExportOptions.WITH_VARIABLE_VALUES, ExportOptions.mask(false, true, true))); + Assertions.assertFalse( + ExportOptions.has( + ExportOptions.WITH_VARIABLE_VALUES, ExportOptions.mask(true, false, false))); + Assertions.assertFalse( + ExportOptions.has( + ExportOptions.WITH_VARIABLE_VALUES, ExportOptions.mask(true, true, false))); + Assertions.assertTrue( + ExportOptions.has( + ExportOptions.WITH_VARIABLE_VALUES, ExportOptions.mask(true, false, true))); + Assertions.assertTrue( + ExportOptions.has( + ExportOptions.WITH_VARIABLE_VALUES, ExportOptions.mask(true, true, true))); + } +} diff --git a/openbas-api/src/test/java/io/openbas/utils/ZipUtils.java b/openbas-api/src/test/java/io/openbas/utils/ZipUtils.java new file mode 100644 index 0000000000..d3dba84205 --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/utils/ZipUtils.java @@ -0,0 +1,29 @@ +package io.openbas.utils; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +public class ZipUtils { + // not this only works + public static String getZipEntryAsString(byte[] zipBlob, String entryName) throws IOException { + try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBlob))) { + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + if (entry.getName().equals(entryName)) { + StringBuilder sb = new StringBuilder(); + byte[] buffer = new byte[1024]; + int read = 0; + while ((read = zis.read(buffer, 0, 1024)) >= 0) { + sb.append(new String(buffer, 0, read)); + } + // force exit of test since we have found the correct entry + return sb.toString(); + } + } + // no zip entry corresponding to expected json + throw new IOException("Zip entry '%s' not found".formatted(entryName)); + } + } +} diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/ArticleFixture.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/ArticleFixture.java index 2320bd0009..74cceb8ea8 100644 --- a/openbas-api/src/test/java/io/openbas/utils/fixtures/ArticleFixture.java +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/ArticleFixture.java @@ -15,4 +15,11 @@ public static Article getArticle(@NotNull final Channel channel) { article.setChannel(channel); return article; } + + public static Article getArticleNoChannel() { + Article article = new Article(); + article.setName(ARTICLE_NAME); + article.setContent("Lorem"); + return article; + } } diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/ChannelFixture.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/ChannelFixture.java new file mode 100644 index 0000000000..09bf1435ef --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/ChannelFixture.java @@ -0,0 +1,15 @@ +package io.openbas.utils.fixtures; + +import io.openbas.database.model.Channel; + +public class ChannelFixture { + public static final String CHANNEL_NAME = "Very respected publication"; + + public static Channel getChannel() { + Channel channel = new Channel(); + channel.setName(CHANNEL_NAME); + channel.setType("Journal"); + channel.setDescription("This is a very respected publication and very serious"); + return channel; + } +} diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/InjectFixture.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/InjectFixture.java index 944696410c..b64ff2ff55 100644 --- a/openbas-api/src/test/java/io/openbas/utils/fixtures/InjectFixture.java +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/InjectFixture.java @@ -22,6 +22,14 @@ private static Inject createInject(InjectorContract injectorContract, String tit return inject; } + public static Inject getInjectWithoutContract() { + Inject inject = new Inject(); + inject.setTitle(INJECT_EMAIL_NAME); + inject.setEnabled(true); + inject.setDependsDuration(0L); + return inject; + } + public static Inject getInjectForEmailContract(InjectorContract injectorContract) { return createInject(injectorContract, INJECT_EMAIL_NAME); } 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 d362a15fe1..6edb282623 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 @@ -1,6 +1,7 @@ package io.openbas.utils.fixtures; import io.openbas.database.model.Tag; +import java.util.UUID; public class TagFixture { public static final String TAG_ID = "id"; @@ -14,6 +15,14 @@ public static Tag getTag() { return tag; } + public static Tag getTagWithText(String text) { + Tag tag = new Tag(); + tag.setId(UUID.randomUUID().toString()); + tag.setName(text); + tag.setColor("#FFFFFF"); + return tag; + } + public static Tag getTag(final String id) { Tag tag = new Tag(); tag.setId(id); diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/TeamFixture.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/TeamFixture.java index 4bdfd7799f..cd3267da68 100644 --- a/openbas-api/src/test/java/io/openbas/utils/fixtures/TeamFixture.java +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/TeamFixture.java @@ -40,6 +40,12 @@ public static Team getTeam(final User user) { return getTeam(user, TEAM_NAME, false); // Call the other method with default value } + public static Team getEmptyTeam() { + Team t = new Team(); + t.setName(TEAM_NAME); + return t; + } + public static Team getTeam(final User user, String name, Boolean isContextualTeam) { Team team = new Team(); team.setName(name); diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/VariableFixture.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/VariableFixture.java new file mode 100644 index 0000000000..40f0fcde72 --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/VariableFixture.java @@ -0,0 +1,13 @@ +package io.openbas.utils.fixtures; + +import io.openbas.database.model.Variable; + +public class VariableFixture { + public static Variable getVariable() { + Variable var = new Variable(); + var.setKey("variable_key"); + var.setValue("variable value"); + var.setDescription("variable description"); + return var; + } +} diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ArticleComposer.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ArticleComposer.java new file mode 100644 index 0000000000..da33d559af --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ArticleComposer.java @@ -0,0 +1,43 @@ +package io.openbas.utils.fixtures.composers; + +import io.openbas.database.model.Article; +import io.openbas.database.repository.ArticleRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ArticleComposer extends ComposerBase
{ + @Autowired private ArticleRepository articleRepository; + + public class Composer extends InnerComposerBase
{ + private final Article article; + private ChannelComposer.Composer channelComposer; + + public Composer(Article article) { + this.article = article; + } + + public Composer withChannel(ChannelComposer.Composer channelComposer) { + this.channelComposer = channelComposer; + this.article.setChannel(channelComposer.get()); + return this; + } + + @Override + public Composer persist() { + this.channelComposer.persist(); + articleRepository.save(article); + return this; + } + + @Override + public Article get() { + return article; + } + } + + public Composer forArticle(Article article) { + generatedItems.add(article); + return new Composer(article); + } +} diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ChallengeComposer.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ChallengeComposer.java new file mode 100644 index 0000000000..578d0b44af --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ChallengeComposer.java @@ -0,0 +1,49 @@ +package io.openbas.utils.fixtures.composers; + +import io.openbas.database.model.Challenge; +import io.openbas.database.model.Tag; +import io.openbas.database.repository.ChallengeRepository; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ChallengeComposer extends ComposerBase { + @Autowired private ChallengeRepository challengeRepository; + + public class Composer extends InnerComposerBase { + private final Challenge challenge; + private final List tagComposers = new ArrayList<>(); + + public Composer(Challenge challenge) { + this.challenge = challenge; + } + + public Composer withTag(TagComposer.Composer tagComposer) { + tagComposers.add(tagComposer); + Set tempTags = this.challenge.getTags(); + tempTags.add(tagComposer.get()); + this.challenge.setTags(tempTags); + return this; + } + + @Override + public Composer persist() { + this.tagComposers.forEach(TagComposer.Composer::persist); + challengeRepository.save(challenge); + return this; + } + + @Override + public Challenge get() { + return this.challenge; + } + } + + public Composer forChallenge(Challenge challenge) { + generatedItems.add(challenge); + return new Composer(challenge); + } +} diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ChannelComposer.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ChannelComposer.java new file mode 100644 index 0000000000..d504e56b8e --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ChannelComposer.java @@ -0,0 +1,35 @@ +package io.openbas.utils.fixtures.composers; + +import io.openbas.database.model.Channel; +import io.openbas.database.repository.ChannelRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ChannelComposer extends ComposerBase { + @Autowired private ChannelRepository channelRepository; + + public class Composer extends InnerComposerBase { + private final Channel channel; + + public Composer(Channel channel) { + this.channel = channel; + } + + @Override + public Composer persist() { + channelRepository.save(channel); + return this; + } + + @Override + public Channel get() { + return channel; + } + } + + public Composer forChannel(Channel channel) { + generatedItems.add(channel); + return new Composer(channel); + } +} diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ComposerBase.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ComposerBase.java new file mode 100644 index 0000000000..55ee79983c --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ComposerBase.java @@ -0,0 +1,12 @@ +package io.openbas.utils.fixtures.composers; + +import java.util.ArrayList; +import java.util.List; + +public class ComposerBase { + public List generatedItems = new ArrayList<>(); + + public void reset() { + generatedItems.clear(); + } +} diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/DocumentComposer.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/DocumentComposer.java new file mode 100644 index 0000000000..a090150993 --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/DocumentComposer.java @@ -0,0 +1,49 @@ +package io.openbas.utils.fixtures.composers; + +import io.openbas.database.model.Document; +import io.openbas.database.model.Tag; +import io.openbas.database.repository.DocumentRepository; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class DocumentComposer extends ComposerBase { + @Autowired private DocumentRepository documentRepository; + + public class Composer extends InnerComposerBase { + private final Document document; + private final List tagComposers = new ArrayList<>(); + + public Composer(Document document) { + this.document = document; + } + + public Composer withTag(TagComposer.Composer tagComposer) { + tagComposers.add(tagComposer); + Set tempTags = this.document.getTags(); + tempTags.add(tagComposer.get()); + this.document.setTags(tempTags); + return this; + } + + @Override + public Composer persist() { + this.tagComposers.forEach(TagComposer.Composer::persist); + documentRepository.save(document); + return this; + } + + @Override + public Document get() { + return this.document; + } + } + + public Composer forDocument(Document document) { + generatedItems.add(document); + return new Composer(document); + } +} diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ExerciseComposer.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ExerciseComposer.java new file mode 100644 index 0000000000..7931e40003 --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ExerciseComposer.java @@ -0,0 +1,138 @@ +package io.openbas.utils.fixtures.composers; + +import io.openbas.database.model.*; +import io.openbas.database.model.Article; +import io.openbas.database.repository.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ExerciseComposer extends ComposerBase { + @Autowired private ExerciseRepository exerciseRepository; + + public class Composer extends InnerComposerBase { + private final Exercise exercise; + private final List injectComposers = new ArrayList<>(); + private final List categoryComposers = new ArrayList<>(); + private final List teamComposers = new ArrayList<>(); + private final List articleComposers = new ArrayList<>(); + private final List objectiveComposers = new ArrayList<>(); + private final List tagComposers = new ArrayList<>(); + private final List documentComposers = new ArrayList<>(); + private final List variableComposers = new ArrayList<>(); + + public Composer(Exercise exercise) { + this.exercise = exercise; + } + + public Composer withVariable(VariableComposer.Composer variableComposer) { + variableComposers.add(variableComposer); + variableComposer.get().setExercise(exercise); + return this; + } + + public Composer withInject(InjectComposer.Composer injectComposer) { + injectComposers.add(injectComposer); + List injects = exercise.getInjects(); + injects.add(injectComposer.get()); + this.exercise.setInjects(injects); + return this; + } + + public Composer withLessonCategory(LessonsCategoryComposer.Composer categoryComposer) { + this.categoryComposers.add(categoryComposer); + List tempCategories = this.exercise.getLessonsCategories(); + tempCategories.add(categoryComposer.get()); + this.exercise.setLessonsCategories(tempCategories); + return this; + } + + public Composer withTeam(TeamComposer.Composer teamComposer) { + this.teamComposers.add(teamComposer); + List tempTeams = this.exercise.getTeams(); + tempTeams.add(teamComposer.get()); + this.exercise.setTeams(tempTeams); + return this; + } + + // special composition that injects the currently set users in currently set teams as + // "ExerciseTeamUsers" + public Composer withTeamUsers() { + List tempTeamUsers = new ArrayList<>(); + this.exercise + .getTeams() + .forEach( + team -> + team.getUsers() + .forEach( + user -> { + ExerciseTeamUser exerciseTeamUser = new ExerciseTeamUser(); + exerciseTeamUser.setExercise(exercise); + exerciseTeamUser.setUser(user); + exerciseTeamUser.setTeam(team); + tempTeamUsers.add(exerciseTeamUser); + })); + this.exercise.setTeamUsers(tempTeamUsers); + return this; + } + + public Composer withArticle(ArticleComposer.Composer articleComposer) { + this.articleComposers.add(articleComposer); + List
tempArticles = this.exercise.getArticles(); + tempArticles.add(articleComposer.get()); + this.exercise.setArticles(tempArticles); + return this; + } + + public Composer withObjective(ObjectiveComposer.Composer objectiveComposer) { + this.objectiveComposers.add(objectiveComposer); + List tempObjectives = this.exercise.getObjectives(); + tempObjectives.add(objectiveComposer.get()); + this.exercise.setObjectives(tempObjectives); + return this; + } + + public Composer withTag(TagComposer.Composer tagComposer) { + this.tagComposers.add(tagComposer); + Set tempTags = this.exercise.getTags(); + tempTags.add(tagComposer.get()); + this.exercise.setTags(tempTags); + return this; + } + + public Composer withDocument(DocumentComposer.Composer documentComposer) { + this.documentComposers.add(documentComposer); + List tempDocuments = this.exercise.getDocuments(); + tempDocuments.add(documentComposer.get()); + this.exercise.setDocuments(tempDocuments); + return this; + } + + @Override + public Composer persist() { + this.articleComposers.forEach(ArticleComposer.Composer::persist); + this.categoryComposers.forEach(LessonsCategoryComposer.Composer::persist); + this.teamComposers.forEach(TeamComposer.Composer::persist); + this.injectComposers.forEach(InjectComposer.Composer::persist); + this.objectiveComposers.forEach(ObjectiveComposer.Composer::persist); + this.tagComposers.forEach(TagComposer.Composer::persist); + this.documentComposers.forEach(DocumentComposer.Composer::persist); + exerciseRepository.save(exercise); + this.variableComposers.forEach(VariableComposer.Composer::persist); + return this; + } + + @Override + public Exercise get() { + return this.exercise; + } + } + + public Composer forExercise(Exercise exercise) { + generatedItems.add(exercise); + return new Composer(exercise); + } +} diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/InjectComposer.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/InjectComposer.java new file mode 100644 index 0000000000..3591a45375 --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/InjectComposer.java @@ -0,0 +1,73 @@ +package io.openbas.utils.fixtures.composers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.openbas.database.model.Inject; +import io.openbas.database.model.InjectorContract; +import io.openbas.database.model.Tag; +import io.openbas.database.repository.InjectRepository; +import io.openbas.database.repository.InjectorContractRepository; +import io.openbas.injectors.challenge.ChallengeContract; +import io.openbas.injectors.challenge.model.ChallengeContent; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +// TODO: injector contract, payloads... +@Component +public class InjectComposer extends ComposerBase { + @Autowired private InjectRepository injectRepository; + @Autowired private InjectorContractRepository injectorContractRepository; + @Autowired private ObjectMapper objectMapper; + + public class Composer extends InnerComposerBase { + private final Inject inject; + private final List challengeComposers = new ArrayList<>(); + private final List tagComposers = new ArrayList<>(); + + public Composer(Inject inject) { + this.inject = inject; + } + + // note this sets the inject's injector contract to Challenge Publish + public Composer withChallenge(ChallengeComposer.Composer challengeComposer) { + challengeComposers.add(challengeComposer); + InjectorContract injectorContract = + injectorContractRepository.findById(ChallengeContract.CHALLENGE_PUBLISH).orElseThrow(); + this.inject.setInjectorContract(injectorContract); + return this; + } + + public Composer withTag(TagComposer.Composer tagComposer) { + tagComposers.add(tagComposer); + Set tempTags = this.inject.getTags(); + tempTags.add(tagComposer.get()); + this.inject.setTags(tempTags); + return this; + } + + @Override + public Composer persist() { + tagComposers.forEach(TagComposer.Composer::persist); + challengeComposers.forEach(ChallengeComposer.Composer::persist); + // replace the inject content if applicable, after persisting the challenges + ChallengeContent cc = new ChallengeContent(); + cc.setChallenges( + challengeComposers.stream().map(composer -> composer.get().getId()).toList()); + this.inject.setContent(objectMapper.valueToTree(cc)); + injectRepository.save(inject); + return this; + } + + @Override + public Inject get() { + return this.inject; + } + } + + public Composer forInject(Inject inject) { + generatedItems.add(inject); + return new Composer(inject); + } +} diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/InnerComposerBase.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/InnerComposerBase.java new file mode 100644 index 0000000000..f899353525 --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/InnerComposerBase.java @@ -0,0 +1,7 @@ +package io.openbas.utils.fixtures.composers; + +public abstract class InnerComposerBase { + public abstract InnerComposerBase persist(); + + public abstract T get(); +} diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/LessonsCategoryComposer.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/LessonsCategoryComposer.java new file mode 100644 index 0000000000..0ff0cce52c --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/LessonsCategoryComposer.java @@ -0,0 +1,50 @@ +package io.openbas.utils.fixtures.composers; + +import io.openbas.database.model.LessonsCategory; +import io.openbas.database.repository.LessonsCategoryRepository; +import java.util.ArrayList; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class LessonsCategoryComposer extends ComposerBase { + + @Autowired private LessonsCategoryRepository lessonsCategoryRepository; + + public class Composer extends InnerComposerBase { + private final LessonsCategory lessonsCategory; + private final List lessonsQuestionComposers = + new ArrayList<>(); + + public Composer(LessonsCategory lessonsCategory) { + this.lessonsCategory = lessonsCategory; + } + + public Composer withLessonsQuestion(LessonsQuestionsComposer.Composer lessonsQuestionComposer) { + lessonsQuestionComposers.add(lessonsQuestionComposer); + return this; + } + + @Override + public Composer persist() { + lessonsCategoryRepository.save(lessonsCategory); + lessonsQuestionComposers.forEach( + composer -> { + composer.get().setCategory(lessonsCategory); + composer.persist(); + }); + return this; + } + + @Override + public LessonsCategory get() { + return lessonsCategory; + } + } + + public Composer forLessonsCategory(LessonsCategory lessonsCategory) { + generatedItems.add(lessonsCategory); + return new Composer(lessonsCategory); + } +} diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/LessonsQuestionsComposer.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/LessonsQuestionsComposer.java new file mode 100644 index 0000000000..d3b94d3152 --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/LessonsQuestionsComposer.java @@ -0,0 +1,36 @@ +package io.openbas.utils.fixtures.composers; + +import io.openbas.database.model.LessonsQuestion; +import io.openbas.database.repository.LessonsQuestionRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class LessonsQuestionsComposer extends ComposerBase { + + @Autowired private LessonsQuestionRepository lessonsQuestionRepository; + + public class Composer extends InnerComposerBase { + private final LessonsQuestion lessonsQuestion; + + public Composer(LessonsQuestion lessonsQuestion) { + this.lessonsQuestion = lessonsQuestion; + } + + @Override + public Composer persist() { + lessonsQuestionRepository.save(lessonsQuestion); + return this; + } + + @Override + public LessonsQuestion get() { + return this.lessonsQuestion; + } + } + + public Composer forLessonsQuestion(LessonsQuestion lessonsQuestion) { + generatedItems.add(lessonsQuestion); + return new Composer(lessonsQuestion); + } +} diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ObjectiveComposer.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ObjectiveComposer.java new file mode 100644 index 0000000000..d42d3e57c5 --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ObjectiveComposer.java @@ -0,0 +1,35 @@ +package io.openbas.utils.fixtures.composers; + +import io.openbas.database.model.Objective; +import io.openbas.database.repository.ObjectiveRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ObjectiveComposer extends ComposerBase { + @Autowired private ObjectiveRepository objectiveRepository; + + public class Composer extends InnerComposerBase { + private final Objective objective; + + public Composer(Objective objective) { + this.objective = objective; + } + + @Override + public Composer persist() { + objectiveRepository.save(objective); + return this; + } + + @Override + public Objective get() { + return this.objective; + } + } + + public Composer forObjective(Objective objective) { + generatedItems.add(objective); + return new Composer(objective); + } +} diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/OrganizationComposer.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/OrganizationComposer.java new file mode 100644 index 0000000000..494c2a7a42 --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/OrganizationComposer.java @@ -0,0 +1,49 @@ +package io.openbas.utils.fixtures.composers; + +import io.openbas.database.model.Organization; +import io.openbas.database.model.Tag; +import io.openbas.database.repository.OrganizationRepository; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class OrganizationComposer extends ComposerBase { + @Autowired OrganizationRepository organizationRepository; + + public class Composer extends InnerComposerBase { + private final Organization organization; + private final List tagComposers = new ArrayList<>(); + + public Composer(Organization organization) { + this.organization = organization; + } + + public Composer withTag(TagComposer.Composer tagComposer) { + tagComposers.add(tagComposer); + Set tempTags = this.organization.getTags(); + tempTags.add(tagComposer.get()); + this.organization.setTags(tempTags); + return this; + } + + @Override + public Composer persist() { + this.tagComposers.forEach(TagComposer.Composer::persist); + organizationRepository.save(organization); + return this; + } + + @Override + public Organization get() { + return this.organization; + } + } + + public Composer forOrganization(Organization organization) { + generatedItems.add(organization); + return new Composer(organization); + } +} diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/TagComposer.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/TagComposer.java new file mode 100644 index 0000000000..3324209ee6 --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/TagComposer.java @@ -0,0 +1,35 @@ +package io.openbas.utils.fixtures.composers; + +import io.openbas.database.model.Tag; +import io.openbas.database.repository.TagRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class TagComposer extends ComposerBase { + @Autowired private TagRepository tagRepository; + + public class Composer extends InnerComposerBase { + private final Tag tag; + + public Composer(Tag tag) { + this.tag = tag; + } + + @Override + public Composer persist() { + tagRepository.save(tag); + return this; + } + + @Override + public Tag get() { + return this.tag; + } + } + + public Composer forTag(Tag tag) { + generatedItems.add(tag); + return new Composer(tag); + } +} diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/TeamComposer.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/TeamComposer.java new file mode 100644 index 0000000000..751985de32 --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/TeamComposer.java @@ -0,0 +1,60 @@ +package io.openbas.utils.fixtures.composers; + +import io.openbas.database.model.Tag; +import io.openbas.database.model.Team; +import io.openbas.database.model.User; +import io.openbas.database.repository.TeamRepository; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class TeamComposer extends ComposerBase { + @Autowired private TeamRepository teamRepository; + + public class Composer extends InnerComposerBase { + private final Team team; + private final List userComposers = new ArrayList<>(); + private final List tagComposers = new ArrayList<>(); + + public Composer(Team team) { + this.team = team; + } + + public Composer withUser(UserComposer.Composer userComposer) { + userComposers.add(userComposer); + List tempUsers = this.team.getUsers(); + tempUsers.add(userComposer.get()); + this.team.setUsers(tempUsers); + return this; + } + + public Composer withTag(TagComposer.Composer tagComposer) { + tagComposers.add(tagComposer); + Set tempTags = this.team.getTags(); + tempTags.add(tagComposer.get()); + this.team.setTags(tempTags); + return this; + } + + @Override + public Composer persist() { + userComposers.forEach(UserComposer.Composer::persist); + tagComposers.forEach(TagComposer.Composer::persist); + teamRepository.save(team); + return this; + } + + @Override + public Team get() { + return this.team; + } + } + + public Composer forTeam(Team team) { + generatedItems.add(team); + return new Composer(team); + } +} diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/UserComposer.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/UserComposer.java new file mode 100644 index 0000000000..e9742ec3ce --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/UserComposer.java @@ -0,0 +1,57 @@ +package io.openbas.utils.fixtures.composers; + +import io.openbas.database.model.Tag; +import io.openbas.database.model.User; +import io.openbas.database.repository.UserRepository; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class UserComposer extends ComposerBase { + @Autowired UserRepository userRepository; + + public class Composer extends InnerComposerBase { + private final User user; + private OrganizationComposer.Composer organizationComposer; + private final List tagComposers = new ArrayList<>(); + + public Composer(User user) { + this.user = user; + } + + public Composer withOrganization(OrganizationComposer.Composer organizationComposer) { + this.organizationComposer = organizationComposer; + this.user.setOrganization(organizationComposer.get()); + return this; + } + + public Composer withTag(TagComposer.Composer tagComposer) { + tagComposers.add(tagComposer); + Set tempTags = this.user.getTags(); + tempTags.add(tagComposer.get()); + this.user.setTags(tempTags); + return this; + } + + @Override + public Composer persist() { + this.tagComposers.forEach(TagComposer.Composer::persist); + organizationComposer.persist(); + userRepository.save(user); + return this; + } + + @Override + public User get() { + return this.user; + } + } + + public Composer forUser(User user) { + generatedItems.add(user); + return new Composer(user); + } +} diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/VariableComposer.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/VariableComposer.java new file mode 100644 index 0000000000..b7153b352f --- /dev/null +++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/VariableComposer.java @@ -0,0 +1,35 @@ +package io.openbas.utils.fixtures.composers; + +import io.openbas.database.model.Variable; +import io.openbas.database.repository.VariableRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class VariableComposer extends ComposerBase { + @Autowired private VariableRepository variableRepository; + + public class Composer extends InnerComposerBase { + private final Variable variable; + + public Composer(Variable variable) { + this.variable = variable; + } + + @Override + public Composer persist() { + variableRepository.save(variable); + return this; + } + + @Override + public Variable get() { + return this.variable; + } + } + + public Composer forVariable(Variable variable) { + generatedItems.add(variable); + return new Composer(variable); + } +}