diff --git a/build.gradle b/build.gradle index e6f7cd99be1b..30502ab85ec7 100644 --- a/build.gradle +++ b/build.gradle @@ -257,6 +257,7 @@ dependencies { implementation "de.jplag:rust:${jplag_version}" implementation "de.jplag:swift:${jplag_version}" implementation "de.jplag:text:${jplag_version}" + implementation "de.jplag:typescript:${jplag_version}" // those are transitive dependencies of JPlag Text --> Stanford NLP // Note: ideally we would exclude them, but for some reason this does not work diff --git a/docs/user/exercises/programming-exercise-features.inc b/docs/user/exercises/programming-exercise-features.inc index e1ce98df80f1..19eb1e02a680 100644 --- a/docs/user/exercises/programming-exercise-features.inc +++ b/docs/user/exercises/programming-exercise-features.inc @@ -41,6 +41,8 @@ Instructors can still use those templates to generate programming exercises and +----------------------+----------+---------+ | C++ | yes | yes | +----------------------+----------+---------+ + | TypeScript | yes | yes | + +----------------------+----------+---------+ - Not all ``templates`` support the same feature set and supported features can also change depending on the continuous integration system setup. Depending on the feature set, some options might not be available during the creation of the programming exercise. @@ -79,6 +81,8 @@ Instructors can still use those templates to generate programming exercises and +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ | C++ | no | no | yes | no | n/a | no | no | L: yes, J: no | +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ + | TypeScript | no | no | yes | no | n/a | no | no | L: yes, J: no | + +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ - *Sequential Test Runs*: ``Artemis`` can generate a build plan which first executes structural and then behavioral tests. This feature can help students to better concentrate on the immediate challenge at hand. - *Static Code Analysis*: ``Artemis`` can generate a build plan which additionally executes static code analysis tools. diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/dto/UpdateCourseCompetencyRelationDTO.java b/src/main/java/de/tum/cit/aet/artemis/atlas/dto/UpdateCourseCompetencyRelationDTO.java new file mode 100644 index 000000000000..d1bac2f5b3ca --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/dto/UpdateCourseCompetencyRelationDTO.java @@ -0,0 +1,9 @@ +package de.tum.cit.aet.artemis.atlas.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import de.tum.cit.aet.artemis.atlas.domain.competency.RelationType; + +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public record UpdateCourseCompetencyRelationDTO(RelationType newRelationType) { +} diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyService.java index 9ec942846bf8..fbe46aa979b0 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyService.java @@ -23,6 +23,7 @@ import de.tum.cit.aet.artemis.atlas.service.LearningObjectImportService; import de.tum.cit.aet.artemis.atlas.service.learningpath.LearningPathService; import de.tum.cit.aet.artemis.core.domain.Course; +import de.tum.cit.aet.artemis.core.repository.CourseRepository; import de.tum.cit.aet.artemis.core.service.AuthorizationCheckService; import de.tum.cit.aet.artemis.exercise.service.ExerciseService; import de.tum.cit.aet.artemis.lecture.repository.LectureUnitCompletionRepository; @@ -41,9 +42,9 @@ public CompetencyService(CompetencyRepository competencyRepository, Authorizatio LearningPathService learningPathService, CompetencyProgressService competencyProgressService, LectureUnitService lectureUnitService, CompetencyProgressRepository competencyProgressRepository, LectureUnitCompletionRepository lectureUnitCompletionRepository, StandardizedCompetencyRepository standardizedCompetencyRepository, CourseCompetencyRepository courseCompetencyRepository, ExerciseService exerciseService, - LearningObjectImportService learningObjectImportService) { + LearningObjectImportService learningObjectImportService, CourseRepository courseRepository) { super(competencyProgressRepository, courseCompetencyRepository, competencyRelationRepository, competencyProgressService, exerciseService, lectureUnitService, - learningPathService, authCheckService, standardizedCompetencyRepository, lectureUnitCompletionRepository, learningObjectImportService); + learningPathService, authCheckService, standardizedCompetencyRepository, lectureUnitCompletionRepository, learningObjectImportService, courseRepository); this.competencyRepository = competencyRepository; } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java index 01eb37cf8271..ea31bff10fad 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java @@ -27,6 +27,7 @@ import de.tum.cit.aet.artemis.atlas.dto.CompetencyImportOptionsDTO; import de.tum.cit.aet.artemis.atlas.dto.CompetencyRelationDTO; import de.tum.cit.aet.artemis.atlas.dto.CompetencyWithTailRelationDTO; +import de.tum.cit.aet.artemis.atlas.dto.UpdateCourseCompetencyRelationDTO; import de.tum.cit.aet.artemis.atlas.repository.CompetencyProgressRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyRelationRepository; import de.tum.cit.aet.artemis.atlas.repository.CourseCompetencyRepository; @@ -39,6 +40,7 @@ import de.tum.cit.aet.artemis.core.dto.pageablesearch.CompetencyPageableSearchDTO; import de.tum.cit.aet.artemis.core.exception.BadRequestAlertException; import de.tum.cit.aet.artemis.core.exception.EntityNotFoundException; +import de.tum.cit.aet.artemis.core.repository.CourseRepository; import de.tum.cit.aet.artemis.core.service.AuthorizationCheckService; import de.tum.cit.aet.artemis.core.util.PageUtil; import de.tum.cit.aet.artemis.exercise.domain.Exercise; @@ -77,11 +79,13 @@ public class CourseCompetencyService { private final LearningObjectImportService learningObjectImportService; + private final CourseRepository courseRepository; + public CourseCompetencyService(CompetencyProgressRepository competencyProgressRepository, CourseCompetencyRepository courseCompetencyRepository, CompetencyRelationRepository competencyRelationRepository, CompetencyProgressService competencyProgressService, ExerciseService exerciseService, LectureUnitService lectureUnitService, LearningPathService learningPathService, AuthorizationCheckService authCheckService, StandardizedCompetencyRepository standardizedCompetencyRepository, LectureUnitCompletionRepository lectureUnitCompletionRepository, - LearningObjectImportService learningObjectImportService) { + LearningObjectImportService learningObjectImportService, CourseRepository courseRepository) { this.competencyProgressRepository = competencyProgressRepository; this.courseCompetencyRepository = courseCompetencyRepository; this.competencyRelationRepository = competencyRelationRepository; @@ -93,6 +97,7 @@ public CourseCompetencyService(CompetencyProgressRepository competencyProgressRe this.standardizedCompetencyRepository = standardizedCompetencyRepository; this.lectureUnitCompletionRepository = lectureUnitCompletionRepository; this.learningObjectImportService = learningObjectImportService; + this.courseRepository = courseRepository; } /** @@ -123,6 +128,28 @@ public List findCourseCompetenciesWithProgressForUserByCourseI return findProgressForCompetenciesAndUser(competencies, userId); } + /** + * Updates the type of a course competency relation. + * + * @param courseId The id of the course for which to fetch the competencies + * @param courseCompetencyRelationId The id of the course competency relation to update + * @param updateCourseCompetencyRelationDTO The DTO containing the new relation type + * + */ + public void updateCourseCompetencyRelation(long courseId, long courseCompetencyRelationId, UpdateCourseCompetencyRelationDTO updateCourseCompetencyRelationDTO) { + var relation = competencyRelationRepository.findByIdElseThrow(courseCompetencyRelationId); + var course = courseRepository.findByIdElseThrow(courseId); + var headCompetency = relation.getHeadCompetency(); + var tailCompetency = relation.getTailCompetency(); + + if (!course.getId().equals(headCompetency.getCourse().getId()) || !course.getId().equals(tailCompetency.getCourse().getId())) { + throw new BadRequestAlertException("The relation does not belong to the course", ENTITY_NAME, "relationWrongCourse"); + } + + relation.setType(updateCourseCompetencyRelationDTO.newRelationType()); + competencyRelationRepository.save(relation); + } + /** * Search for all course competencies fitting a {@link CompetencyPageableSearchDTO search query}. The result is paged. * diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/PrerequisiteService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/PrerequisiteService.java index 3fc520a21378..4bf07e6e42ca 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/PrerequisiteService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/PrerequisiteService.java @@ -23,6 +23,7 @@ import de.tum.cit.aet.artemis.atlas.service.LearningObjectImportService; import de.tum.cit.aet.artemis.atlas.service.learningpath.LearningPathService; import de.tum.cit.aet.artemis.core.domain.Course; +import de.tum.cit.aet.artemis.core.repository.CourseRepository; import de.tum.cit.aet.artemis.core.service.AuthorizationCheckService; import de.tum.cit.aet.artemis.exercise.service.ExerciseService; import de.tum.cit.aet.artemis.lecture.repository.LectureUnitCompletionRepository; @@ -41,9 +42,9 @@ public PrerequisiteService(PrerequisiteRepository prerequisiteRepository, Author LearningPathService learningPathService, CompetencyProgressService competencyProgressService, LectureUnitService lectureUnitService, CompetencyProgressRepository competencyProgressRepository, LectureUnitCompletionRepository lectureUnitCompletionRepository, StandardizedCompetencyRepository standardizedCompetencyRepository, CourseCompetencyRepository courseCompetencyRepository, ExerciseService exerciseService, - LearningObjectImportService learningObjectImportService) { + LearningObjectImportService learningObjectImportService, CourseRepository courseRepository) { super(competencyProgressRepository, courseCompetencyRepository, competencyRelationRepository, competencyProgressService, exerciseService, lectureUnitService, - learningPathService, authCheckService, standardizedCompetencyRepository, lectureUnitCompletionRepository, learningObjectImportService); + learningPathService, authCheckService, standardizedCompetencyRepository, lectureUnitCompletionRepository, learningObjectImportService, courseRepository); this.prerequisiteRepository = prerequisiteRepository; } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/web/CourseCompetencyResource.java b/src/main/java/de/tum/cit/aet/artemis/atlas/web/CourseCompetencyResource.java index 449a92a1d171..8e93c6c73090 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/web/CourseCompetencyResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/web/CourseCompetencyResource.java @@ -10,6 +10,7 @@ import java.util.Set; import java.util.stream.Collectors; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import org.slf4j.Logger; @@ -18,6 +19,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; @@ -32,6 +34,7 @@ import de.tum.cit.aet.artemis.atlas.dto.CompetencyJolPairDTO; import de.tum.cit.aet.artemis.atlas.dto.CompetencyRelationDTO; import de.tum.cit.aet.artemis.atlas.dto.CompetencyWithTailRelationDTO; +import de.tum.cit.aet.artemis.atlas.dto.UpdateCourseCompetencyRelationDTO; import de.tum.cit.aet.artemis.atlas.repository.CompetencyProgressRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyRelationRepository; import de.tum.cit.aet.artemis.atlas.repository.CourseCompetencyRepository; @@ -350,6 +353,23 @@ public ResponseEntity generateCompetenciesFromCourseDescription(@PathVaria return ResponseEntity.accepted().build(); } + /** + * PATCH courses/:courseId/course-competencies/relations/:competencyRelationId update a relation type of an existing relation + * + * @param courseId the id of the course to which the competencies belong + * @param competencyRelationId the id of the competency relation to update + * @param updateCourseCompetencyRelationDTO the new relation type + * @return the ResponseEntity with status 200 (OK) + */ + @PatchMapping("courses/{courseId}/course-competencies/relations/{competencyRelationId}") + @EnforceAtLeastInstructorInCourse + public ResponseEntity updateCompetencyRelation(@PathVariable long courseId, @PathVariable long competencyRelationId, + @RequestBody @Valid UpdateCourseCompetencyRelationDTO updateCourseCompetencyRelationDTO) { + log.info("REST request to update a competency relation: {}", competencyRelationId); + courseCompetencyService.updateCourseCompetencyRelation(courseId, competencyRelationId, updateCourseCompetencyRelationDTO); + return ResponseEntity.noContent().build(); + } + /** * PUT courses/:courseId/course-competencies/:competencyId/jol/:jolValue : Sets the judgement of learning for a competency * diff --git a/src/main/java/de/tum/cit/aet/artemis/buildagent/service/BuildJobExecutionService.java b/src/main/java/de/tum/cit/aet/artemis/buildagent/service/BuildJobExecutionService.java index 9c968c453e47..75bbaf826b00 100644 --- a/src/main/java/de/tum/cit/aet/artemis/buildagent/service/BuildJobExecutionService.java +++ b/src/main/java/de/tum/cit/aet/artemis/buildagent/service/BuildJobExecutionService.java @@ -9,8 +9,11 @@ import java.io.IOException; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.time.Duration; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.List; @@ -27,7 +30,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.annotation.Profile; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import com.github.dockerjava.api.command.CreateContainerResponse; @@ -71,6 +77,8 @@ public class BuildJobExecutionService { @Value("${artemis.version-control.default-branch:main}") private String defaultBranch; + private static final Duration TEMP_DIR_RETENTION_PERIOD = Duration.ofMinutes(5); + public BuildJobExecutionService(BuildJobContainerService buildJobContainerService, BuildJobGitService buildJobGitService, BuildAgentDockerService buildAgentDockerService, BuildLogsMap buildLogsMap) { this.buildJobContainerService = buildJobContainerService; @@ -79,6 +87,38 @@ public BuildJobExecutionService(BuildJobContainerService buildJobContainerServic this.buildLogsMap = buildLogsMap; } + /** + * This method is responsible for cleaning up temporary directories that were used for checking out repositories. + * It is triggered when the application is ready and runs asynchronously. + */ + @EventListener(ApplicationReadyEvent.class) + @Async + public void initAsync() { + final ZonedDateTime currentTime = ZonedDateTime.now(); + cleanUpTempDirectoriesAsync(currentTime); + } + + private void cleanUpTempDirectoriesAsync(ZonedDateTime currentTime) { + log.info("Cleaning up temporary directories in {}", CHECKED_OUT_REPOS_TEMP_DIR); + try (DirectoryStream directoryStream = Files.newDirectoryStream(Path.of(CHECKED_OUT_REPOS_TEMP_DIR))) { + for (Path path : directoryStream) { + try { + ZonedDateTime lastModifiedTime = ZonedDateTime.ofInstant(Files.getLastModifiedTime(path).toInstant(), currentTime.getZone()); + if (Files.isDirectory(path) && lastModifiedTime.isBefore(currentTime.minus(TEMP_DIR_RETENTION_PERIOD))) { + FileUtils.deleteDirectory(path.toFile()); + } + } + catch (IOException e) { + log.error("Could not delete temporary directory {}", path, e); + } + } + } + catch (IOException e) { + log.error("Could not delete temporary directories", e); + } + log.info("Clean up of temporary directories in {} completed.", CHECKED_OUT_REPOS_TEMP_DIR); + } + /** * Orchestrates the execution of a build job in a Docker container. This method handles the preparation and configuration of the container, * including cloning the necessary repositories, checking out the appropriate branches, and preparing the environment for the build. @@ -512,15 +552,16 @@ private void deleteCloneRepo(VcsRepositoryUri repositoryUri, @Nullable String co } buildJobGitService.deleteLocalRepository(repository); } + // Do not throw an exception if deletion fails. If an exception occurs, clean up will happen in the next server start. catch (EntityNotFoundException e) { msg = "Error while checking out repository"; buildLogsMap.appendBuildLogEntry(buildJobId, msg); - throw new LocalCIException(msg, e); + log.error("Error while deleting repository with URI {} and Path {}", repositoryUri, repositoryPath, e); } catch (IOException e) { msg = "Error while deleting repository"; buildLogsMap.appendBuildLogEntry(buildJobId, msg); - throw new LocalCIException(msg, e); + log.error("Error while deleting repository with URI {} and Path {}", repositoryUri, repositoryPath, e); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/plagiarism/service/ProgrammingPlagiarismDetectionService.java b/src/main/java/de/tum/cit/aet/artemis/plagiarism/service/ProgrammingPlagiarismDetectionService.java index 30e568ac85a5..e0707ca62144 100644 --- a/src/main/java/de/tum/cit/aet/artemis/plagiarism/service/ProgrammingPlagiarismDetectionService.java +++ b/src/main/java/de/tum/cit/aet/artemis/plagiarism/service/ProgrammingPlagiarismDetectionService.java @@ -41,6 +41,7 @@ import de.jplag.rlang.RLanguage; import de.jplag.rust.RustLanguage; import de.jplag.swift.SwiftLanguage; +import de.jplag.typescript.TypeScriptLanguage; import de.tum.cit.aet.artemis.core.exception.BadRequestAlertException; import de.tum.cit.aet.artemis.core.exception.GitException; import de.tum.cit.aet.artemis.core.service.FileService; @@ -321,7 +322,8 @@ private Language getJPlagProgrammingLanguage(ProgrammingExercise programmingExer case R -> new RLanguage(); case RUST -> new RustLanguage(); case SWIFT -> new SwiftLanguage(); - case EMPTY, PHP, DART, HASKELL, ASSEMBLER, OCAML, C_SHARP, SQL, TYPESCRIPT, GO, MATLAB, BASH, VHDL, RUBY, POWERSHELL, ADA -> throw new BadRequestAlertException( + case TYPESCRIPT -> new TypeScriptLanguage(); + case EMPTY, PHP, DART, HASKELL, ASSEMBLER, OCAML, C_SHARP, SQL, GO, MATLAB, BASH, VHDL, RUBY, POWERSHELL, ADA -> throw new BadRequestAlertException( "Programming language " + programmingExercise.getProgrammingLanguage() + " not supported for plagiarism check.", "ProgrammingExercise", "notSupported"); }; } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingLanguage.java b/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingLanguage.java index 0fdc216122b9..71f00210c2df 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingLanguage.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingLanguage.java @@ -50,6 +50,7 @@ public enum ProgrammingLanguage { R, RUST, SWIFT, + TYPESCRIPT, VHDL, EMPTY ); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/TemplateUpgradePolicyService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/TemplateUpgradePolicyService.java index cc018ae1eb61..07dc44fada0e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/TemplateUpgradePolicyService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/TemplateUpgradePolicyService.java @@ -32,8 +32,8 @@ public TemplateUpgradePolicyService(JavaTemplateUpgradeService javaRepositoryUpg public TemplateUpgradeService getUpgradeService(ProgrammingLanguage programmingLanguage) { return switch (programmingLanguage) { case JAVA -> javaRepositoryUpgradeService; - case KOTLIN, PYTHON, C, HASKELL, VHDL, ASSEMBLER, SWIFT, OCAML, EMPTY, RUST, JAVASCRIPT, R, C_PLUS_PLUS -> defaultRepositoryUpgradeService; - case C_SHARP, SQL, TYPESCRIPT, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> + case KOTLIN, PYTHON, C, HASKELL, VHDL, ASSEMBLER, SWIFT, OCAML, EMPTY, RUST, JAVASCRIPT, R, C_PLUS_PLUS, TYPESCRIPT -> defaultRepositoryUpgradeService; + case C_SHARP, SQL, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> throw new UnsupportedOperationException("Unsupported programming language: " + programmingLanguage); }; } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/ContinuousIntegrationService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/ContinuousIntegrationService.java index f2f53467d261..ec8e2165c46a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/ContinuousIntegrationService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/ContinuousIntegrationService.java @@ -219,8 +219,8 @@ enum RepositoryCheckoutPath implements CustomizableCheckoutPath { @Override public String forProgrammingLanguage(ProgrammingLanguage language) { return switch (language) { - case JAVA, PYTHON, C, HASKELL, KOTLIN, VHDL, ASSEMBLER, SWIFT, OCAML, EMPTY, RUST, JAVASCRIPT, R, C_PLUS_PLUS -> "assignment"; - case C_SHARP, SQL, TYPESCRIPT, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> + case JAVA, PYTHON, C, HASKELL, KOTLIN, VHDL, ASSEMBLER, SWIFT, OCAML, EMPTY, RUST, JAVASCRIPT, R, C_PLUS_PLUS, TYPESCRIPT -> "assignment"; + case C_SHARP, SQL, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> throw new UnsupportedOperationException("Unsupported programming language: " + language); }; } @@ -230,9 +230,9 @@ public String forProgrammingLanguage(ProgrammingLanguage language) { @Override public String forProgrammingLanguage(ProgrammingLanguage language) { return switch (language) { - case JAVA, PYTHON, HASKELL, KOTLIN, SWIFT, EMPTY, RUST, JAVASCRIPT, R, C_PLUS_PLUS -> ""; + case JAVA, PYTHON, HASKELL, KOTLIN, SWIFT, EMPTY, RUST, JAVASCRIPT, R, C_PLUS_PLUS, TYPESCRIPT -> ""; case C, VHDL, ASSEMBLER, OCAML -> "tests"; - case C_SHARP, SQL, TYPESCRIPT, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> + case C_SHARP, SQL, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> throw new UnsupportedOperationException("Unsupported programming language: " + language); }; } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsProgrammingLanguageFeatureService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsProgrammingLanguageFeatureService.java index 8b948e8f0073..9c318267953e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsProgrammingLanguageFeatureService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsProgrammingLanguageFeatureService.java @@ -11,6 +11,7 @@ import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.R; import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.RUST; import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.SWIFT; +import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.TYPESCRIPT; import static de.tum.cit.aet.artemis.programming.domain.ProjectType.FACT; import static de.tum.cit.aet.artemis.programming.domain.ProjectType.GCC; import static de.tum.cit.aet.artemis.programming.domain.ProjectType.GRADLE_GRADLE; @@ -47,5 +48,6 @@ public JenkinsProgrammingLanguageFeatureService() { programmingLanguageFeatures.put(RUST, new ProgrammingLanguageFeature(RUST, false, false, true, false, false, List.of(), false, false)); // Jenkins is not supporting XCODE at the moment programmingLanguageFeatures.put(SWIFT, new ProgrammingLanguageFeature(SWIFT, false, true, true, true, false, List.of(PLAIN), false, false)); + programmingLanguageFeatures.put(TYPESCRIPT, new ProgrammingLanguageFeature(TYPESCRIPT, false, false, true, false, false, List.of(), false, false)); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/build_plan/JenkinsBuildPlanService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/build_plan/JenkinsBuildPlanService.java index dc4ff7a8178a..6dc40e173e4e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/build_plan/JenkinsBuildPlanService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/build_plan/JenkinsBuildPlanService.java @@ -184,8 +184,8 @@ private JenkinsXmlConfigBuilder builderFor(ProgrammingLanguage programmingLangua throw new UnsupportedOperationException("Xcode templates are not available for Jenkins."); } return switch (programmingLanguage) { - case JAVA, KOTLIN, PYTHON, C, HASKELL, SWIFT, EMPTY, RUST, JAVASCRIPT, R, C_PLUS_PLUS -> jenkinsBuildPlanCreator; - case VHDL, ASSEMBLER, OCAML, C_SHARP, SQL, TYPESCRIPT, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> + case JAVA, KOTLIN, PYTHON, C, HASKELL, SWIFT, EMPTY, RUST, JAVASCRIPT, R, C_PLUS_PLUS, TYPESCRIPT -> jenkinsBuildPlanCreator; + case VHDL, ASSEMBLER, OCAML, C_SHARP, SQL, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> throw new UnsupportedOperationException(programmingLanguage + " templates are not available for Jenkins."); }; } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java index 704755528d3c..d86199310720 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java @@ -14,6 +14,7 @@ import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.R; import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.RUST; import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.SWIFT; +import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.TYPESCRIPT; import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.VHDL; import static de.tum.cit.aet.artemis.programming.domain.ProjectType.FACT; import static de.tum.cit.aet.artemis.programming.domain.ProjectType.GCC; @@ -54,6 +55,7 @@ public LocalCIProgrammingLanguageFeatureService() { programmingLanguageFeatures.put(R, new ProgrammingLanguageFeature(R, false, false, true, false, false, List.of(), false, true)); programmingLanguageFeatures.put(RUST, new ProgrammingLanguageFeature(RUST, false, false, true, false, false, List.of(), false, true)); programmingLanguageFeatures.put(SWIFT, new ProgrammingLanguageFeature(SWIFT, false, false, true, true, false, List.of(PLAIN), false, true)); + programmingLanguageFeatures.put(TYPESCRIPT, new ProgrammingLanguageFeature(TYPESCRIPT, false, false, true, false, false, List.of(), false, true)); programmingLanguageFeatures.put(VHDL, new ProgrammingLanguageFeature(VHDL, false, false, false, false, false, List.of(), false, true)); } } diff --git a/src/main/resources/config/application.yml b/src/main/resources/config/application.yml index 6645696acc2e..85965bb400e0 100644 --- a/src/main/resources/config/application.yml +++ b/src/main/resources/config/application.yml @@ -95,6 +95,8 @@ artemis: default: "ghcr.io/ls1intum/artemis-r-docker:v1.0.0" c_plus_plus: default: "ghcr.io/ls1intum/artemis-cpp-docker:v1.0.0" + typescript: + default: "ghcr.io/ls1intum/artemis-javascript-docker:v1.0.0" management: endpoints: diff --git a/src/main/resources/templates/aeolus/typescript/default.sh b/src/main/resources/templates/aeolus/typescript/default.sh new file mode 100644 index 000000000000..6b6dceabd179 --- /dev/null +++ b/src/main/resources/templates/aeolus/typescript/default.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -e +export AEOLUS_INITIAL_DIRECTORY=${PWD} +install_dependencies () { + echo '⚙️ executing install_dependencies' + npm ci --prefer-offline --no-audit +} + +build () { + echo '⚙️ executing build' + npm run build +} + +test () { + echo '⚙️ executing test' + npm run test:ci +} + +main () { + if [[ "${1}" == "aeolus_sourcing" ]]; then + return 0 # just source to use the methods in the subshell, no execution + fi + local _script_name + _script_name=${BASH_SOURCE[0]:-$0} + cd "${AEOLUS_INITIAL_DIRECTORY}" + bash -c "source ${_script_name} aeolus_sourcing; install_dependencies" + cd "${AEOLUS_INITIAL_DIRECTORY}" + bash -c "source ${_script_name} aeolus_sourcing; build" + cd "${AEOLUS_INITIAL_DIRECTORY}" + bash -c "source ${_script_name} aeolus_sourcing; test" +} + +main "${@}" diff --git a/src/main/resources/templates/aeolus/typescript/default.yaml b/src/main/resources/templates/aeolus/typescript/default.yaml new file mode 100644 index 000000000000..de335d090617 --- /dev/null +++ b/src/main/resources/templates/aeolus/typescript/default.yaml @@ -0,0 +1,16 @@ +api: v0.0.1 +metadata: + name: TypeScript + description: Run tests using Jest +actions: + - name: install_dependencies + script: 'npm ci --prefer-offline --no-audit' + - name: build + script: 'npm run build' + - name: test + script: 'npm run test:ci' + runAlways: false + results: + - name: junit + path: 'junit.xml' + type: junit diff --git a/src/main/resources/templates/jenkins/typescript/regularRuns/pipeline.groovy b/src/main/resources/templates/jenkins/typescript/regularRuns/pipeline.groovy new file mode 100644 index 000000000000..1ba259ab3553 --- /dev/null +++ b/src/main/resources/templates/jenkins/typescript/regularRuns/pipeline.groovy @@ -0,0 +1,62 @@ +/* + * This file configures the actual build steps for the automatic grading. + * + * !!! + * For regular exercises, there is no need to make changes to this file. + * Only this base configuration is actively supported by the Artemis maintainers + * and/or your Artemis instance administrators. + * !!! + */ + +dockerImage = '#dockerImage' +dockerFlags = '#dockerArgs' + +/** + * Main function called by Jenkins. + */ +void testRunner() { + docker.image(dockerImage).inside(dockerFlags) { c -> + runTestSteps() + } +} + +private void runTestSteps() { + test() +} + +/** + * Run unit tests + */ +private void test() { + stage('Install Dependencies') { + sh 'npm ci --prefer-offline --no-audit' + } + stage('Build') { + sh 'npm run build' + } + stage('Test') { + sh 'npm run test:ci' + } +} + +/** + * Script of the post build tasks aggregating all JUnit files in $WORKSPACE/results. + * + * Called by Jenkins. + */ +void postBuildTasks() { + sh ''' + rm -rf results + mkdir results + if [ -e junit.xml ] + then + sed -i 's/]*>//g ; s/<\\/testsuites>/<\\/testsuite>/g' junit.xml + fi + cp junit.xml $WORKSPACE/results/ || true + sed -i 's/[^[:print:]\t]/�/g' $WORKSPACE/results/*.xml || true + ''' +} + +// very important, do not remove +// required so that Jenkins finds the methods defined in this script +return this diff --git a/src/main/resources/templates/typescript/exercise/.gitignore b/src/main/resources/templates/typescript/exercise/.gitignore new file mode 100644 index 000000000000..c6ce4cc9ff34 --- /dev/null +++ b/src/main/resources/templates/typescript/exercise/.gitignore @@ -0,0 +1,132 @@ +# NodeJS .gitignore from https://github.com/github/gitignore/blob/main/Node.gitignore + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/src/main/resources/templates/typescript/exercise/package-lock.json b/src/main/resources/templates/typescript/exercise/package-lock.json new file mode 100644 index 000000000000..4c093b19263f --- /dev/null +++ b/src/main/resources/templates/typescript/exercise/package-lock.json @@ -0,0 +1,53 @@ +{ + "name": "artemis-exercise", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "artemis-exercise", + "devDependencies": { + "@tsconfig/node20": "^20.1.4", + "@types/node": "^20.15.0", + "typescript": "^5.6.2" + } + }, + "node_modules/@tsconfig/node20": { + "version": "20.1.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.4.tgz", + "integrity": "sha512-sqgsT69YFeLWf5NtJ4Xq/xAF8p4ZQHlmGW74Nu2tD4+g5fAsposc4ZfaaPixVu4y01BEiDCWLRDCvDM5JOsRxg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.16.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.11.tgz", + "integrity": "sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/src/main/resources/templates/typescript/exercise/package.json b/src/main/resources/templates/typescript/exercise/package.json new file mode 100644 index 000000000000..6d49a1f95773 --- /dev/null +++ b/src/main/resources/templates/typescript/exercise/package.json @@ -0,0 +1,19 @@ +{ + "name": "artemis-exercise", + "private": true, + "scripts": { + "build": "tsc", + "start": "node ./dist/client.js" + }, + "exports": { + "./*": { + "types": "./dist/*.d.ts", + "default": "./dist/*.js" + } + }, + "devDependencies": { + "@tsconfig/node20": "^20.1.4", + "@types/node": "^20.15.0", + "typescript": "^5.6.2" + } +} diff --git a/src/main/resources/templates/typescript/exercise/src/bubblesort.ts b/src/main/resources/templates/typescript/exercise/src/bubblesort.ts new file mode 100644 index 000000000000..36f8463102ef --- /dev/null +++ b/src/main/resources/templates/typescript/exercise/src/bubblesort.ts @@ -0,0 +1,3 @@ +export default class BubbleSort { + // TODO: implement in performSort(Array) +} diff --git a/src/main/resources/templates/typescript/exercise/src/client.ts b/src/main/resources/templates/typescript/exercise/src/client.ts new file mode 100644 index 000000000000..ad9475b976ad --- /dev/null +++ b/src/main/resources/templates/typescript/exercise/src/client.ts @@ -0,0 +1,66 @@ +const ITERATIONS = 10; +const DATES_LENGTH_MIN = 5; +const DATES_LENGTH_MAX = 15; + +/** + * Main function. + * Add code to demonstrate your implementation here. + */ +function main() { + // TODO: Init Context and Policy + + // Run multiple times to simulate different sorting strategies + for (let i = 0; i < ITERATIONS; i++) { + const dates = createRandomDates(); + + // TODO: Configure context + + console.log('Unsorted Array of dates:'); + console.log(dates); + + // TODO: Sort dates + + console.log('Sorted Array of dates:'); + console.log(dates); + } +} + +/** + * Generates an Array of random Date objects with random Array length between + * {@link DATES_LENGTH_MIN} and {@link DATES_LENGTH_MAX}. + * + * @return an Array of random Date objects + */ +function createRandomDates(): Array { + const length = randomIntegerWithin(DATES_LENGTH_MIN, DATES_LENGTH_MAX); + + const lowestDate = new Date('2024-09-15'); + const highestDate = new Date('2025-01-15'); + + return Array.from(Array(length), () => randomDateWithin(lowestDate, highestDate)); +} + +/** + * Creates a random Date within the given range. + * + * @param low {Date} the lower bound + * @param high {Date} the upper bound + * @return {Date} random Date within the given range + */ +function randomDateWithin(low: Date, high: Date): Date { + const randomTimestamp = randomIntegerWithin(low.valueOf(), high.valueOf()); + return new Date(randomTimestamp); +} + +/** + * Creates a random int within the given range. + * + * @param low {number} the lower bound + * @param high {number} the upper bound + * @returns {number} random int within the given range + */ +function randomIntegerWithin(low: number, high: number): number { + return Math.floor(Math.random() * (high - low + 1)) + low; +} + +main(); diff --git a/src/main/resources/templates/typescript/exercise/src/context.ts b/src/main/resources/templates/typescript/exercise/src/context.ts new file mode 100644 index 000000000000..a667a10bb29e --- /dev/null +++ b/src/main/resources/templates/typescript/exercise/src/context.ts @@ -0,0 +1,3 @@ +export default class Context { + // TODO: Create and implement a Context class according to the UML class diagram +} diff --git a/src/main/resources/templates/typescript/exercise/src/mergesort.ts b/src/main/resources/templates/typescript/exercise/src/mergesort.ts new file mode 100644 index 000000000000..4b07a80b4c31 --- /dev/null +++ b/src/main/resources/templates/typescript/exercise/src/mergesort.ts @@ -0,0 +1,3 @@ +export default class MergeSort { + // TODO: implement in performSort(Array) +} diff --git a/src/main/resources/templates/typescript/exercise/src/policy.ts b/src/main/resources/templates/typescript/exercise/src/policy.ts new file mode 100644 index 000000000000..7c8723feb1a9 --- /dev/null +++ b/src/main/resources/templates/typescript/exercise/src/policy.ts @@ -0,0 +1,3 @@ +export default class Policy { + // TODO: Create and implement a Policy class as described in the problem statement +} diff --git a/src/main/resources/templates/typescript/exercise/src/sortstrategy.ts b/src/main/resources/templates/typescript/exercise/src/sortstrategy.ts new file mode 100644 index 000000000000..40723e61965c --- /dev/null +++ b/src/main/resources/templates/typescript/exercise/src/sortstrategy.ts @@ -0,0 +1,3 @@ +export default interface SortStrategy { + // TODO: Create a SortStrategy interface according to the UML class diagram +} diff --git a/src/main/resources/templates/typescript/exercise/tsconfig.json b/src/main/resources/templates/typescript/exercise/tsconfig.json new file mode 100644 index 000000000000..b26f243b6e4d --- /dev/null +++ b/src/main/resources/templates/typescript/exercise/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@tsconfig/node20/tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src", + "composite": true + } +} diff --git a/src/main/resources/templates/typescript/readme b/src/main/resources/templates/typescript/readme new file mode 100644 index 000000000000..a536c0d80cc9 --- /dev/null +++ b/src/main/resources/templates/typescript/readme @@ -0,0 +1,86 @@ +# Sorting with the Strategy Pattern + +In this exercise, we want to implement sorting algorithms and choose them based on runtime specific variables. + +### Part 1: Sorting + +First, we need to implement two sorting algorithms, in this case `MergeSort` and `BubbleSort`. + +**You have the following tasks:** + +1. [task][Implement Bubble Sort](structural_BubbleSort_has_method,behavior_BubbleSort_should_sort_correctly) +Implement the method `performSort(Array)` in the class `BubbleSort`. Make sure to follow the Bubble Sort algorithm exactly. + +2. [task][Implement Merge Sort](structural_MergeSort_has_method,behavior_MergeSort_should_sort_correctly) +Implement the method `performSort(Array)` in the class `MergeSort`. Make sure to follow the Merge Sort algorithm exactly. + +### Part 2: Strategy Pattern + +We want the application to apply different algorithms for sorting an Array of `Date` objects. +Use the strategy pattern to select the right sorting algorithm at runtime. + +**You have the following tasks:** + +1. SortStrategy Interface +Create a `SortStrategy` interface and adjust the sorting algorithms so that they implement this interface. + +2. [task][Context Class](structural_Context_has_properties,structural_Context_has_methods) +Create and implement a `Context` class following the below class diagram. +Add `get` and `set` accessors for the attribute. + +3. [task][Context Policy](structural_Policy_has_properties,structural_Policy_has_methods) +Create and implement a `Policy` class following the below class diagram. +Add `get` and `set` accessors for the attribute. +`Policy` should implement a simple configuration mechanism: + + 1. [task][Select MergeSort](behavior_Policy_uses_MergeSort_for_big_list) + Select `MergeSort` when the List has more than 10 dates. + + 2. [task][Select BubbleSort](behavior_Policy_uses_BubbleSort_for_small_list) + Select `BubbleSort` when the List has less or equal 10 dates. + +4. Complete the `main()` function which demonstrates switching between two strategies at runtime. + +@startuml + +class Policy { + +Policy(Context) <> + +configure() +} + +class Context { + -dates: Array + +sort() +} + +interface SortStrategy { + +performSort(Array) +} + +class BubbleSort { + +performSort(Array) +} + +class MergeSort { + +performSort(Array) +} + +MergeSort -up-|> SortStrategy #testsColor(structural_MergeSort_has_method) +BubbleSort -up-|> SortStrategy #testsColor(structural_BubbleSort_has_method) +Policy -right-> Context #testsColor(structural_Policy_has_properties): context +Context -right-> SortStrategy #testsColor(structural_Context_has_properties): sortAlgorithm + +hide empty fields +hide empty methods + +@enduml + + +### Part 3: Optional Challenges + +(These are not tested) + +1. Create a new class `QuickSort` that implements `SortStrategy` and implement the Quick Sort algorithm. +2. Make the method `performSort(List)` generic, so that other objects can also be sorted by the same method. + **Hint:** Create a `Comparable` interface. +3. Think about a useful decision in `Policy` when to use the new `QuickSort` algorithm. diff --git a/src/main/resources/templates/typescript/solution/.gitignore b/src/main/resources/templates/typescript/solution/.gitignore new file mode 100644 index 000000000000..c6ce4cc9ff34 --- /dev/null +++ b/src/main/resources/templates/typescript/solution/.gitignore @@ -0,0 +1,132 @@ +# NodeJS .gitignore from https://github.com/github/gitignore/blob/main/Node.gitignore + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/src/main/resources/templates/typescript/solution/package-lock.json b/src/main/resources/templates/typescript/solution/package-lock.json new file mode 100644 index 000000000000..4c093b19263f --- /dev/null +++ b/src/main/resources/templates/typescript/solution/package-lock.json @@ -0,0 +1,53 @@ +{ + "name": "artemis-exercise", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "artemis-exercise", + "devDependencies": { + "@tsconfig/node20": "^20.1.4", + "@types/node": "^20.15.0", + "typescript": "^5.6.2" + } + }, + "node_modules/@tsconfig/node20": { + "version": "20.1.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.4.tgz", + "integrity": "sha512-sqgsT69YFeLWf5NtJ4Xq/xAF8p4ZQHlmGW74Nu2tD4+g5fAsposc4ZfaaPixVu4y01BEiDCWLRDCvDM5JOsRxg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.16.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.11.tgz", + "integrity": "sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/src/main/resources/templates/typescript/solution/package.json b/src/main/resources/templates/typescript/solution/package.json new file mode 100644 index 000000000000..6d49a1f95773 --- /dev/null +++ b/src/main/resources/templates/typescript/solution/package.json @@ -0,0 +1,19 @@ +{ + "name": "artemis-exercise", + "private": true, + "scripts": { + "build": "tsc", + "start": "node ./dist/client.js" + }, + "exports": { + "./*": { + "types": "./dist/*.d.ts", + "default": "./dist/*.js" + } + }, + "devDependencies": { + "@tsconfig/node20": "^20.1.4", + "@types/node": "^20.15.0", + "typescript": "^5.6.2" + } +} diff --git a/src/main/resources/templates/typescript/solution/src/bubblesort.ts b/src/main/resources/templates/typescript/solution/src/bubblesort.ts new file mode 100644 index 000000000000..f894bfc3fdd9 --- /dev/null +++ b/src/main/resources/templates/typescript/solution/src/bubblesort.ts @@ -0,0 +1,21 @@ +import SortStrategy from './sortstrategy'; +import Comparable from './comparable'; + +export default class BubbleSort implements SortStrategy { + /** + * Sorts objects with BubbleSort. + * + * @param input {Array} the array of objects to be sorted + */ + performSort(input: Array) { + for (let i = input.length - 1; i >= 0; i--) { + for (let j = 0; j < i; j++) { + if (input[j].valueOf() > input[j + 1].valueOf()) { + const temp = input[j]; + input[j] = input[j + 1]; + input[j + 1] = temp; + } + } + } + } +} diff --git a/src/main/resources/templates/typescript/solution/src/client.ts b/src/main/resources/templates/typescript/solution/src/client.ts new file mode 100644 index 000000000000..7c27a4411760 --- /dev/null +++ b/src/main/resources/templates/typescript/solution/src/client.ts @@ -0,0 +1,72 @@ +import Context from './context'; +import Policy from './policy'; + +const ITERATIONS = 10; +const DATES_LENGTH_MIN = 5; +const DATES_LENGTH_MAX = 15; + +/** + * Main function. + * Add code to demonstrate your implementation here. + */ +function main() { + // Init Context and Policy + const context = new Context(); + const policy = new Policy(context); + + // Run multiple times to simulate different sorting strategies + for (let i = 0; i < ITERATIONS; i++) { + const dates = createRandomDates(); + + context.dates = dates; + policy.configure(); + + console.log('Unsorted Array of dates:'); + console.log(dates); + + context.sort(); + + console.log('Sorted Array of dates:'); + console.log(dates); + } +} + +/** + * Generates an Array of random Date objects with random Array length between + * {@link DATES_LENGTH_MIN} and {@link DATES_LENGTH_MAX}. + * + * @return an Array of random Date objects + */ +function createRandomDates(): Array { + const length = randomIntegerWithin(DATES_LENGTH_MIN, DATES_LENGTH_MAX); + + const lowestDate = new Date('2024-09-15'); + const highestDate = new Date('2025-01-15'); + + return Array.from(Array(length), () => randomDateWithin(lowestDate, highestDate)); +} + +/** + * Creates a random Date within the given range. + * + * @param low {Date} the lower bound + * @param high {Date} the upper bound + * @return {Date} random Date within the given range + */ +function randomDateWithin(low: Date, high: Date): Date { + const randomTimestamp = randomIntegerWithin(low.valueOf(), high.valueOf()); + return new Date(randomTimestamp); +} + +/** + * Creates a random int within the given range. + * + * @param low {number} the lower bound + * @param high {number} the upper bound + * @returns {number} random int within the given range + */ +function randomIntegerWithin(low: number, high: number): number { + return Math.floor(Math.random() * (high - low + 1)) + low; +} + +main(); diff --git a/src/main/resources/templates/typescript/solution/src/comparable.ts b/src/main/resources/templates/typescript/solution/src/comparable.ts new file mode 100644 index 000000000000..eade48028180 --- /dev/null +++ b/src/main/resources/templates/typescript/solution/src/comparable.ts @@ -0,0 +1,3 @@ +export default interface Comparable { + valueOf(): number; +} diff --git a/src/main/resources/templates/typescript/solution/src/context.ts b/src/main/resources/templates/typescript/solution/src/context.ts new file mode 100644 index 000000000000..731a630aa235 --- /dev/null +++ b/src/main/resources/templates/typescript/solution/src/context.ts @@ -0,0 +1,30 @@ +import type SortStrategy from './sortstrategy'; + +export default class Context { + private _sortAlgorithm: SortStrategy | null = null; + + private _dates: Array = []; + + /** + * Runs the configured sort algorithm. + */ + sort() { + this._sortAlgorithm?.performSort(this._dates); + } + + get sortAlgorithm(): SortStrategy | null { + return this._sortAlgorithm; + } + + set sortAlgorithm(sortAlgorithm: SortStrategy) { + this._sortAlgorithm = sortAlgorithm; + } + + get dates(): Array { + return this._dates; + } + + set dates(dates: Array) { + this._dates = dates; + } +} diff --git a/src/main/resources/templates/typescript/solution/src/mergesort.ts b/src/main/resources/templates/typescript/solution/src/mergesort.ts new file mode 100644 index 000000000000..383d84a8826c --- /dev/null +++ b/src/main/resources/templates/typescript/solution/src/mergesort.ts @@ -0,0 +1,69 @@ +import SortStrategy from './sortstrategy'; +import Comparable from './comparable'; + +export default class MergeSort implements SortStrategy { + /** + * Wrapper method for the real MergeSort algorithm. + * + * @template T + * @param input {Array} the array of objects to be sorted + */ + performSort(input: Array) { + mergesort(input, 0, input.length - 1); + } +} + +/** + * Recursive merge sort function + * + * @template T + * @param input {Array} + * @param low {number} + * @param high {number} + */ +function mergesort(input: Array, low: number, high: number) { + if (low >= high) { + return; + } + const mid = Math.floor((low + high) / 2); + mergesort(input, low, mid); + mergesort(input, mid + 1, high); + merge(input, low, mid, high); +} + +/** + * Merge function + * + * @template T + * @param input {Array} + * @param low {number} + * @param middle {number} + * @param high {number} + */ +function merge(input: Array, low: number, middle: number, high: number) { + const temp = new Array(high - low + 1); + + let leftIndex = low; + let rightIndex = middle + 1; + let wholeIndex = 0; + + while (leftIndex <= middle && rightIndex <= high) { + if (input[leftIndex].valueOf() <= input[rightIndex].valueOf()) { + temp[wholeIndex] = input[leftIndex++]; + } else { + temp[wholeIndex] = input[rightIndex++]; + } + wholeIndex++; + } + + while (leftIndex <= middle) { + temp[wholeIndex++] = input[leftIndex++]; + } + while (rightIndex <= high) { + temp[wholeIndex++] = input[rightIndex++]; + } + + for (wholeIndex = 0; wholeIndex < temp.length; wholeIndex++) { + input[wholeIndex + low] = temp[wholeIndex]; + } +} diff --git a/src/main/resources/templates/typescript/solution/src/policy.ts b/src/main/resources/templates/typescript/solution/src/policy.ts new file mode 100644 index 000000000000..19bf88b07911 --- /dev/null +++ b/src/main/resources/templates/typescript/solution/src/policy.ts @@ -0,0 +1,28 @@ +import BubbleSort from './bubblesort'; +import MergeSort from './mergesort'; +import Context from './context'; + +const DATES_LENGTH_THRESHOLD = 10; + +export default class Policy { + constructor(private _context: Context) {} + + /** + * Chooses a strategy depending on the number of date objects. + */ + configure() { + if (this._context.dates.length > DATES_LENGTH_THRESHOLD) { + this._context.sortAlgorithm = new MergeSort(); + } else { + this._context.sortAlgorithm = new BubbleSort(); + } + } + + get context(): Context { + return this._context; + } + + set context(context: Context) { + this._context = context; + } +} diff --git a/src/main/resources/templates/typescript/solution/src/sortstrategy.ts b/src/main/resources/templates/typescript/solution/src/sortstrategy.ts new file mode 100644 index 000000000000..f658b5b53705 --- /dev/null +++ b/src/main/resources/templates/typescript/solution/src/sortstrategy.ts @@ -0,0 +1,5 @@ +import Comparable from './comparable'; + +export default interface SortStrategy { + performSort(dates: Array): void; +} diff --git a/src/main/resources/templates/typescript/solution/tsconfig.json b/src/main/resources/templates/typescript/solution/tsconfig.json new file mode 100644 index 000000000000..b26f243b6e4d --- /dev/null +++ b/src/main/resources/templates/typescript/solution/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@tsconfig/node20/tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src", + "composite": true + } +} diff --git a/src/main/resources/templates/typescript/test/.gitignore b/src/main/resources/templates/typescript/test/.gitignore new file mode 100644 index 000000000000..9de920749a26 --- /dev/null +++ b/src/main/resources/templates/typescript/test/.gitignore @@ -0,0 +1,135 @@ +/${studentParentWorkingDirectoryName} +/junit.xml + +# NodeJS .gitignore from https://github.com/github/gitignore/blob/main/Node.gitignore + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/src/main/resources/templates/typescript/test/jest.config.js b/src/main/resources/templates/typescript/test/jest.config.js new file mode 100644 index 000000000000..f5d30d13b959 --- /dev/null +++ b/src/main/resources/templates/typescript/test/jest.config.js @@ -0,0 +1,7 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} **/ +module.exports = { + testEnvironment: "node", + transform: { + "^.+.tsx?$": ["ts-jest",{}], + }, +}; \ No newline at end of file diff --git a/src/main/resources/templates/typescript/test/package-lock.json b/src/main/resources/templates/typescript/test/package-lock.json new file mode 100644 index 000000000000..1db85e84a58f --- /dev/null +++ b/src/main/resources/templates/typescript/test/package-lock.json @@ -0,0 +1,4039 @@ +{ + "name": "artemis-test", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "artemis-test", + "workspaces": [ + "${studentParentWorkingDirectoryName}" + ], + "devDependencies": { + "@tsconfig/node20": "^20.1.4", + "@types/jest": "^29.5.12", + "jest": "^29.7.0", + "jest-junit": "^16.0.0", + "ts-jest": "^29.2.5", + "typescript": "^5.6.2" + } + }, + "${studentParentWorkingDirectoryName}": { + "name": "artemis-exercise", + "devDependencies": { + "@tsconfig/node20": "^20.1.4", + "@types/node": "^20.15.0", + "typescript": "^5.6.2" + } + }, + "${studentParentWorkingDirectoryName}/node_modules/@types/node": { + "version": "20.16.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.11.tgz", + "integrity": "sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", + "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.25.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.8.tgz", + "integrity": "sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.8.tgz", + "integrity": "sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.25.7", + "@babel/generator": "^7.25.7", + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helpers": "^7.25.7", + "@babel/parser": "^7.25.8", + "@babel/template": "^7.25.7", + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.8", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", + "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.7", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz", + "integrity": "sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.25.7", + "@babel/helper-validator-option": "^7.25.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", + "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.7.tgz", + "integrity": "sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.7", + "@babel/helper-simple-access": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "@babel/traverse": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz", + "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", + "integrity": "sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", + "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", + "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz", + "integrity": "sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.7.tgz", + "integrity": "sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", + "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.8.tgz", + "integrity": "sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.7.tgz", + "integrity": "sha512-AqVo+dguCgmpi/3mYBdu9lkngOBlQ2w2vnNpa6gfiCxQZLzV4ZbhsXitJ2Yblkoe1VQwtHSaNmIaGll/26YWRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.7.tgz", + "integrity": "sha512-ruZOnKO+ajVL/MVx+PwNBPOkrnXTXoWMtte1MBpegfCArhqOe3Bj52avVj1huLLxNKYKXYaSxZ2F+woK1ekXfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.7.tgz", + "integrity": "sha512-rR+5FDjpCHqqZN2bzZm18bVYGaejGq5ZkpVCJLXor/+zlSrSoc4KWcHI0URVWjl/68Dyr1uwZUz/1njycEAv9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", + "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", + "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.25.7", + "@babel/generator": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/template": "^7.25.7", + "@babel/types": "^7.25.7", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz", + "integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tsconfig/node20": { + "version": "20.1.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.4.tgz", + "integrity": "sha512-sqgsT69YFeLWf5NtJ4Xq/xAF8p4ZQHlmGW74Nu2tD4+g5fAsposc4ZfaaPixVu4y01BEiDCWLRDCvDM5JOsRxg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.13", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.13.tgz", + "integrity": "sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/node": { + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/artemis-exercise": { + "resolved": "${studentParentWorkingDirectoryName}", + "link": true + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", + "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001663", + "electron-to-chromium": "^1.5.28", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001668", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001668.tgz", + "integrity": "sha512-nWLrdxqCdblixUO+27JtGJJE/txpJlyUy5YN1u53wLZkP0emYCo5zgS6QYft7VUYR42LGgi/S5hdLZTrnyIddw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", + "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.36", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.36.tgz", + "integrity": "sha512-HYTX8tKge/VNp6FGO+f/uVDmUkq+cEfcxYhKf15Akc4M5yxt5YmorwlAitKWjWhWQnKcDRBAQKXkhqqXMqcrjw==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-junit": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-16.0.0.tgz", + "integrity": "sha512-A94mmw6NfJab4Fg/BlvVOUXzXgF0XIH6EmTgJ5NDPp4xoKq0Kr7sErb+4Xs9nZvu58pJojz5RFGpqnZYJTrRfQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mkdirp": "^1.0.4", + "strip-ansi": "^6.0.1", + "uuid": "^8.3.2", + "xml": "^1.0.1" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-jest": { + "version": "29.2.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", + "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "ejs": "^3.1.10", + "fast-json-stable-stringify": "^2.1.0", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.6.3", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", + "dev": true, + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/src/main/resources/templates/typescript/test/package.json b/src/main/resources/templates/typescript/test/package.json new file mode 100644 index 000000000000..d7f50e3d33d3 --- /dev/null +++ b/src/main/resources/templates/typescript/test/package.json @@ -0,0 +1,25 @@ +{ + "name": "artemis-test", + "private": true, + "scripts": { + "build": "tsc -b", + "test": "jest", + "test:ci": "jest --ci --reporters=default --reporters=jest-junit" + }, + "workspaces": [ + "${studentParentWorkingDirectoryName}" + ], + "devDependencies": { + "@tsconfig/node20": "^20.1.4", + "@types/jest": "^29.5.12", + "jest": "^29.7.0", + "jest-junit": "^16.0.0", + "ts-jest": "^29.2.5", + "typescript": "^5.6.2" + }, + "jest-junit": { + "classNameTemplate": "{classname}_{title}", + "titleTemplate": "{classname}_{title}", + "ancestorSeparator": "_" + } +} diff --git a/src/main/resources/templates/typescript/test/src/behavior.test.ts b/src/main/resources/templates/typescript/test/src/behavior.test.ts new file mode 100644 index 000000000000..d18092bff9aa --- /dev/null +++ b/src/main/resources/templates/typescript/test/src/behavior.test.ts @@ -0,0 +1,77 @@ +import MergeSort from 'artemis-exercise/mergesort'; +import BubbleSort from 'artemis-exercise/bubblesort'; +import Context from 'artemis-exercise/context'; +import Policy from 'artemis-exercise/policy'; + +// incorrect type structure should fail with runtime errors +const _MergeSort: any = MergeSort; +const _BubbleSort: any = BubbleSort; +const _Context: any = Context; +const _Policy: any = Policy; + +// prettier-ignore +const datesWithCorrectOrder = [ + new Date('2016-02-15'), + new Date('2017-04-15'), + new Date('2017-09-15'), + new Date('2018-11-08'), +]; + +describe('behavior', () => { + let dates: Array; + beforeEach(() => { + // prettier-ignore + dates = [ + new Date('2018-11-08'), + new Date('2017-04-15'), + new Date('2016-02-15'), + new Date('2017-09-15'), + ]; + }); + + describe('BubbleSort', () => { + it('should_sort_correctly', () => { + const bubbleSort = new _BubbleSort(); + bubbleSort.performSort(dates); + expect(dates).toEqual(datesWithCorrectOrder); + }); + }); + + describe('MergeSort', () => { + it('should_sort_correctly', () => { + const mergeSort = new _MergeSort(); + mergeSort.performSort(dates); + expect(dates).toEqual(datesWithCorrectOrder); + }); + }); + + describe('Policy', () => { + it('uses_MergeSort_for_big_list', () => { + const bigList: Array = []; + for (let i = 0; i < 11; i++) { + bigList.push(new Date()); + } + + const context = new _Context(); + context.dates = bigList; + const policy = new _Policy(context); + policy.configure(); + const chosenSortStrategy = context.sortAlgorithm; + expect(chosenSortStrategy).toBeInstanceOf(_MergeSort); + }); + + it('uses_BubbleSort_for_small_list', () => { + const smallList: Array = []; + for (let i = 0; i < 3; i++) { + smallList.push(new Date()); + } + + const context = new _Context(); + context.dates = smallList; + const policy = new _Policy(context); + policy.configure(); + const chosenSortStrategy = context.sortAlgorithm; + expect(chosenSortStrategy).toBeInstanceOf(_BubbleSort); + }); + }); +}); diff --git a/src/main/resources/templates/typescript/test/src/structural.test.ts b/src/main/resources/templates/typescript/test/src/structural.test.ts new file mode 100644 index 000000000000..e6048d0e3f40 --- /dev/null +++ b/src/main/resources/templates/typescript/test/src/structural.test.ts @@ -0,0 +1,50 @@ +import MergeSort from 'artemis-exercise/mergesort'; +import BubbleSort from 'artemis-exercise/bubblesort'; +import Context from 'artemis-exercise/context'; +import Policy from 'artemis-exercise/policy'; + +// incorrect type structure should fail with runtime errors +const _MergeSort: any = MergeSort; +const _BubbleSort: any = BubbleSort; +const _Context: any = Context; +const _Policy: any = Policy; + +describe('structural', () => { + describe('Context', () => { + const context = new _Context(); + + it('has_properties', () => { + expect(context).toHaveProperty('dates'); + expect(context).toHaveProperty('sortAlgorithm'); + }); + + it('has_methods', () => { + expect(context).toHaveProperty('sort', expect.any(Function)); + }); + }); + + describe('Policy', () => { + const context = new _Context(); + const policy = new _Policy(context); + + it('has_properties', () => { + expect(policy).toHaveProperty('context'); + }); + + it('has_methods', () => { + expect(policy).toHaveProperty('configure', expect.any(Function)); + }); + }); + + describe('BubbleSort', () => { + it('has_method', () => { + expect(_BubbleSort.prototype).toHaveProperty('performSort', expect.any(Function)); + }); + }); + + describe('MergeSort', () => { + it('has_method', () => { + expect(_MergeSort.prototype).toHaveProperty('performSort', expect.any(Function)); + }); + }); +}); diff --git a/src/main/resources/templates/typescript/test/tsconfig.json b/src/main/resources/templates/typescript/test/tsconfig.json new file mode 100644 index 000000000000..d7b28c1a1dbf --- /dev/null +++ b/src/main/resources/templates/typescript/test/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@tsconfig/node20/tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "references": [ + { + "path": "${studentParentWorkingDirectoryName}" + } + ] +} diff --git a/src/main/webapp/app/course/competencies/competency-management/competency-management-table.component.ts b/src/main/webapp/app/course/competencies/competency-management/competency-management-table.component.ts index e60ec966042a..0ee37dd08169 100644 --- a/src/main/webapp/app/course/competencies/competency-management/competency-management-table.component.ts +++ b/src/main/webapp/app/course/competencies/competency-management/competency-management-table.component.ts @@ -1,15 +1,7 @@ import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, inject } from '@angular/core'; import { CompetencyService } from 'app/course/competencies/competency.service'; import { AlertService } from 'app/core/util/alert.service'; -import { - CompetencyRelation, - CompetencyRelationDTO, - CompetencyWithTailRelationDTO, - CourseCompetency, - CourseCompetencyType, - dtoToCompetencyRelation, - getIcon, -} from 'app/entities/competency.model'; +import { CompetencyWithTailRelationDTO, CourseCompetency, CourseCompetencyType, getIcon } from 'app/entities/competency.model'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { filter, map } from 'rxjs/operators'; import { onError } from 'app/shared/util/global.utils'; @@ -30,7 +22,6 @@ import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; export class CompetencyManagementTableComponent implements OnInit, OnDestroy { @Input() courseId: number; @Input() courseCompetencies: CourseCompetency[]; - @Input() relations: CompetencyRelation[]; @Input() competencyType: CourseCompetencyType; @Input() standardizedCompetenciesEnabled: boolean; @@ -103,14 +94,7 @@ export class CompetencyManagementTableComponent implements OnInit, OnDestroy { */ updateDataAfterImportAll(res: Array) { const importedCompetencies = res.map((dto) => dto.competency).filter((element): element is CourseCompetency => !!element); - - const importedRelations = res - .map((dto) => dto.tailRelations) - .flat() - .filter((element): element is CompetencyRelationDTO => !!element) - .map((dto) => dtoToCompetencyRelation(dto)); this.courseCompetencies.push(...importedCompetencies); - this.relations.push(...importedRelations); } /** diff --git a/src/main/webapp/app/course/competencies/competency-management/competency-management.component.html b/src/main/webapp/app/course/competencies/competency-management/competency-management.component.html index e541845c4ede..a70934974f15 100644 --- a/src/main/webapp/app/course/competencies/competency-management/competency-management.component.html +++ b/src/main/webapp/app/course/competencies/competency-management/competency-management.component.html @@ -14,6 +14,10 @@

} + - -
-
- -
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- -
-
-
- @if (relationError) { - - } - -
-
- - - - - - - - - - - {{ node.label }} - - - - - - - - - {{ ('artemisApp.competency.relation.type.' + link.label | artemisTranslate).toUpperCase() }} - - - - - -
-
-
- - - diff --git a/src/main/webapp/app/course/competencies/competency-management/competency-relation-graph.component.scss b/src/main/webapp/app/course/competencies/competency-management/competency-relation-graph.component.scss deleted file mode 100644 index e639f3e1bbfe..000000000000 --- a/src/main/webapp/app/course/competencies/competency-management/competency-relation-graph.component.scss +++ /dev/null @@ -1,28 +0,0 @@ -.accordion-body { - overflow: hidden; - max-height: 60vh; -} - -.node { - text { - fill: var(--body-color); - } - - rect { - fill: var(--primary); - } -} - -.edge { - stroke: var(--body-color) !important; - marker-end: url(#arrow); -} - -#arrow { - stroke: var(--body-color); - fill: var(--body-color); -} - -.text-path { - fill: var(--body-color); -} diff --git a/src/main/webapp/app/course/competencies/competency-management/competency-relation-graph.component.ts b/src/main/webapp/app/course/competencies/competency-management/competency-relation-graph.component.ts deleted file mode 100644 index 2fc93e4a8e63..000000000000 --- a/src/main/webapp/app/course/competencies/competency-management/competency-relation-graph.component.ts +++ /dev/null @@ -1,319 +0,0 @@ -import { Component, EventEmitter, Output, computed, input } from '@angular/core'; -import { faArrowsToEye } from '@fortawesome/free-solid-svg-icons'; -import { Edge, NgxGraphZoomOptions, Node } from '@swimlane/ngx-graph'; -import { CompetencyRelation, CompetencyRelationError, CompetencyRelationType, CourseCompetency } from 'app/entities/competency.model'; -import { Subject } from 'rxjs'; - -@Component({ - selector: 'jhi-competency-relation-graph', - templateUrl: './competency-relation-graph.component.html', - styleUrls: ['./competency-relation-graph.component.scss'], -}) -export class CompetencyRelationGraphComponent { - competencies = input([]); - relations = input([]); - - @Output() onRemoveRelation = new EventEmitter(); - @Output() onCreateRelation = new EventEmitter(); - - nodes = computed(() => { - this.update$.next(true); - return this.competencies().map((competency): Node => { - return { - id: `${competency.id}`, - label: competency.title, - }; - }); - }); - - edges = computed(() => { - this.update$.next(true); - return this.relations().map( - (relation): Edge => ({ - id: `edge${relation.id}`, - source: `${relation.tailCompetency?.id}`, - target: `${relation.headCompetency?.id}`, - label: relation.type, - data: { - id: relation.id, - }, - }), - ); - }); - - tailCompetencyId?: number; - headCompetencyId?: number; - relationType?: CompetencyRelationType; - relationError?: CompetencyRelationError = undefined; - update$: Subject = new Subject(); - center$: Subject = new Subject(); - zoomToFit$: Subject = new Subject(); - - // icons - protected readonly faArrowsToEye = faArrowsToEye; - - // constants - protected readonly competencyRelationType = CompetencyRelationType; - protected readonly errorMessage: Record = { - CIRCULAR: 'artemisApp.competency.relation.createsCircularRelation', - EXISTING: 'artemisApp.competency.relation.relationAlreadyExists', - SELF: 'artemisApp.competency.relation.selfRelation', - }; - - /** - * creates a relation with the currently entered data if it would not cause an error - */ - createRelation() { - this.validate(); - if (this.relationError) { - return; - } - const relation: CompetencyRelation = { - tailCompetency: { id: this.tailCompetencyId }, - headCompetency: { id: this.headCompetencyId }, - type: this.relationType, - }; - this.onCreateRelation.emit(relation); - } - - /** - * removes the relation - * @param edge the edge symbolizing the relation - */ - removeRelation(edge: Edge) { - this.onRemoveRelation.emit(edge.data.id); - } - - centerView() { - this.zoomToFit$.next({ autoCenter: true }); - this.center$.next(true); - } - - /** - * Validates if the currently entered data would cause an error and sets relationError accordingly - */ - validate(): void { - if (!this.tailCompetencyId || !this.headCompetencyId || !this.relationType) { - this.relationError = undefined; - return; - } - if (this.headCompetencyId === this.tailCompetencyId) { - this.relationError = CompetencyRelationError.SELF; - return; - } - if (this.doesRelationAlreadyExist()) { - this.relationError = CompetencyRelationError.EXISTING; - return; - } - if (this.containsCircularRelation()) { - this.relationError = CompetencyRelationError.CIRCULAR; - return; - } - this.relationError = undefined; - } - - /** - * checks if the currently entered data is equal to an existing relation - * @private - */ - private doesRelationAlreadyExist(): boolean { - return !!this.edges().find((edge) => edge.source === this.tailCompetencyId?.toString() && edge.target === this.headCompetencyId?.toString()); - } - - /** - * Checks if the currently entered data would create a circular relation - * - * @private - */ - private containsCircularRelation(): boolean { - if (!this.tailCompetencyId || !this.headCompetencyId || !this.relationType) { - return false; - } - return this.doesCreateCircularRelation(this.nodes(), this.edges(), { - source: this.tailCompetencyId! + '', - target: this.headCompetencyId! + '', - label: this.relationType!, - } as Edge); - } - - /** - * Checks if adding an edge would create a circular relation - * @param {Node[]} nodes an array of all existing nodes of a graph - * @param {Edge[]} edges an array of all existing edges of a graph - * @param {Edge} edgeToAdd the edge that you try to add to the graph - * - * @returns {boolean} whether or not adding the provided edge would result in a circle in the graph - */ - private doesCreateCircularRelation(nodes: Node[], edges: Edge[], edgeToAdd: Edge): boolean { - const edgesWithNewEdge = JSON.parse(JSON.stringify(edges)); - edgesWithNewEdge.push(edgeToAdd); - const graph = new Graph(); - for (const node of nodes) { - graph.addVertex(new Vertex(node.id)); - } - for (const edge of edgesWithNewEdge) { - const headVertex = graph.vertices.find((vertex: Vertex) => vertex.getLabel() === edge.target); - const tailVertex = graph.vertices.find((vertex: Vertex) => vertex.getLabel() === edge.source); - if (headVertex === undefined || tailVertex === undefined) { - throw new TypeError('Every edge needs a source or a target.'); - } - // only extends and assumes relations are considered when checking for circles because only they don't make sense - // MATCHES relations are considered in the next step by merging the edges and combining the adjacencyLists - switch (edge.label) { - case 'EXTENDS': - case 'ASSUMES': { - graph.addEdge(tailVertex, headVertex); - break; - } - } - } - // combine vertices that are connected through MATCHES - for (const edge of edgesWithNewEdge) { - if (edge.label === 'MATCHES') { - const headVertex = graph.vertices.find((vertex: Vertex) => vertex.getLabel() === edge.target); - const tailVertex = graph.vertices.find((vertex: Vertex) => vertex.getLabel() === edge.source); - if (headVertex === undefined || tailVertex === undefined) { - throw new TypeError('Every edge needs a source or a target.'); - } - if (headVertex.getAdjacencyList().includes(tailVertex) || tailVertex.getAdjacencyList().includes(headVertex)) { - return true; - } - // create a merged vertex - const mergedVertex = new Vertex(tailVertex.getLabel() + ', ' + headVertex.getLabel()); - // add all neighbours to merged vertex - mergedVertex.getAdjacencyList().push(...headVertex.getAdjacencyList()); - mergedVertex.getAdjacencyList().push(...tailVertex.getAdjacencyList()); - // update every vertex that initially had one of the two merged vertices as neighbours to now reference the merged vertex - for (const vertex of graph.vertices) { - for (const adjacentVertex of vertex.getAdjacencyList()) { - if (adjacentVertex.getLabel() === headVertex.getLabel() || adjacentVertex.getLabel() === tailVertex.getLabel()) { - const index = vertex.getAdjacencyList().indexOf(adjacentVertex, 0); - if (index > -1) { - vertex.getAdjacencyList().splice(index, 1); - } - vertex.getAdjacencyList().push(mergedVertex); - } - } - } - } - } - return graph.hasCycle(); - } - - /** - * Keeps order of elements as-is in the keyvalue pipe - */ - keepOrder = () => { - return 0; - }; -} - -/** - * A class that represents a vertex in a graph - * @class - * - * @constructor - * - * @property label a label to identify the vertex (we use the node id) - * @property beingVisited is the vertex the one that is currently being visited during the graph traversal - * @property visited has this vertex been visited before - * @property adjacencyList an array that contains all adjacent vertices - */ -class Vertex { - private readonly label: string; - private beingVisited: boolean; - private visited: boolean; - private readonly adjacencyList: Vertex[]; - - constructor(label: string) { - this.label = label; - this.adjacencyList = []; - } - - getLabel(): string { - return this.label; - } - - addNeighbor(adjacent: Vertex): void { - this.adjacencyList.push(adjacent); - } - - getAdjacencyList(): Vertex[] { - return this.adjacencyList; - } - - isBeingVisited(): boolean { - return this.beingVisited; - } - - setBeingVisited(beingVisited: boolean): void { - this.beingVisited = beingVisited; - } - - isVisited(): boolean { - return this.visited; - } - - setVisited(visited: boolean) { - this.visited = visited; - } -} - -/** - * A class that represents a graph - * @class - * - * @constructor - * - * @property vertices an array of all vertices in the graph (edges are represented by the adjacent vertices property of each vertex) - */ -class Graph { - vertices: Vertex[]; - - constructor() { - this.vertices = []; - } - - public addVertex(vertex: Vertex): void { - this.vertices.push(vertex); - } - - public addEdge(from: Vertex, to: Vertex): void { - from.addNeighbor(to); - } - - /** - * Checks if the graph contains a circle - * - * @returns {boolean} whether or not the graph contains a circle - */ - public hasCycle(): boolean { - // we have to check for every vertex if it is part of a cycle in case the graph is not connected - for (const vertex of this.vertices) { - if (!vertex.isVisited() && this.vertexHasCycle(vertex)) { - return true; - } - } - return false; - } - - /** - * Checks if a vertex is part of a circle - * - * @returns {boolean} whether or not the vertex is part of a circle - */ - private vertexHasCycle(sourceVertex: Vertex): boolean { - sourceVertex.setBeingVisited(true); - - for (const neighbor of sourceVertex.getAdjacencyList()) { - if (neighbor.isBeingVisited() || (!neighbor.isVisited() && this.vertexHasCycle(neighbor))) { - // backward edge exists - return true; - } - } - - sourceVertex.setBeingVisited(false); - sourceVertex.setVisited(true); - return false; - } -} diff --git a/src/main/webapp/app/course/competencies/competency.module.ts b/src/main/webapp/app/course/competencies/competency.module.ts index d7b4c6da30b5..6065b7e90ccb 100644 --- a/src/main/webapp/app/course/competencies/competency.module.ts +++ b/src/main/webapp/app/course/competencies/competency.module.ts @@ -3,7 +3,6 @@ import { RouterModule } from '@angular/router'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { CompetencyManagementComponent } from './competency-management/competency-management.component'; import { CompetencyCardComponent } from 'app/course/competencies/competency-card/competency-card.component'; import { CompetenciesPopoverComponent } from './competencies-popover/competencies-popover.component'; import { NgxGraphModule } from '@swimlane/ngx-graph'; @@ -15,7 +14,6 @@ import { CourseDescriptionFormComponent } from 'app/course/competencies/generate import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; import { IrisModule } from 'app/iris/iris.module'; import { TaxonomySelectComponent } from 'app/course/competencies/taxonomy-select/taxonomy-select.component'; -import { CompetencyRelationGraphComponent } from 'app/course/competencies/competency-management/competency-relation-graph.component'; import { CompetencyAccordionComponent } from 'app/course/competencies/competency-accordion/competency-accordion.component'; import { ArtemisCourseExerciseRowModule } from 'app/overview/course-exercises/course-exercise-row.module'; import { RatingModule } from 'app/exercises/shared/rating/rating.module'; @@ -49,13 +47,11 @@ import { ArtemisMarkdownEditorModule } from 'app/shared/markdown-editor/markdown CompetencySearchComponent, CompetencyRecommendationDetailComponent, CourseDescriptionFormComponent, - CompetencyManagementComponent, CompetencyCardComponent, CompetencyAccordionComponent, CompetenciesPopoverComponent, ImportCompetenciesTableComponent, TaxonomySelectComponent, - CompetencyRelationGraphComponent, ], exports: [ CompetencyCardComponent, diff --git a/src/main/webapp/app/course/competencies/components/course-competencies-relation-graph/course-competencies-relation-graph.component.html b/src/main/webapp/app/course/competencies/components/course-competencies-relation-graph/course-competencies-relation-graph.component.html new file mode 100644 index 000000000000..d90c81fd3714 --- /dev/null +++ b/src/main/webapp/app/course/competencies/components/course-competencies-relation-graph/course-competencies-relation-graph.component.html @@ -0,0 +1,36 @@ +
+ + + + + + + + + + + + + + + + + + + {{ ('artemisApp.courseCompetency.relations.relationTypes.' + link.label | artemisTranslate).toUpperCase() }} + + + + + +
diff --git a/src/main/webapp/app/course/competencies/components/course-competencies-relation-graph/course-competencies-relation-graph.component.scss b/src/main/webapp/app/course/competencies/components/course-competencies-relation-graph/course-competencies-relation-graph.component.scss new file mode 100644 index 000000000000..add2bfbd3928 --- /dev/null +++ b/src/main/webapp/app/course/competencies/components/course-competencies-relation-graph/course-competencies-relation-graph.component.scss @@ -0,0 +1,14 @@ +.course-competencies-graph-container { + #arrow { + stroke: var(--body-color); + fill: var(--body-color); + } + + .selected { + stroke: var(--bs-primary); + } + + .text-path { + fill: var(--body-color); + } +} diff --git a/src/main/webapp/app/course/competencies/components/course-competencies-relation-graph/course-competencies-relation-graph.component.ts b/src/main/webapp/app/course/competencies/components/course-competencies-relation-graph/course-competencies-relation-graph.component.ts new file mode 100644 index 000000000000..448d2e4b1d77 --- /dev/null +++ b/src/main/webapp/app/course/competencies/components/course-competencies-relation-graph/course-competencies-relation-graph.component.ts @@ -0,0 +1,80 @@ +import { Component, computed, effect, input, model, output, signal } from '@angular/core'; +import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; +import { faFileImport } from '@fortawesome/free-solid-svg-icons'; +import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'; +import { CompetencyRelationDTO, CourseCompetency } from 'app/entities/competency.model'; +import { ArtemisSharedModule } from 'app/shared/shared.module'; +import { Edge, NgxGraphModule, Node } from '@swimlane/ngx-graph'; +import { Subject } from 'rxjs'; +import { SizeUpdate } from 'app/course/learning-paths/components/competency-node/competency-node.component'; +import { CourseCompetencyRelationNodeComponent } from 'app/course/competencies/components/course-competency-relation-node/course-competency-relation-node.component'; + +@Component({ + selector: 'jhi-course-competencies-relation-graph', + standalone: true, + imports: [FontAwesomeModule, NgbAccordionModule, NgxGraphModule, ArtemisSharedModule, CourseCompetencyRelationNodeComponent], + templateUrl: './course-competencies-relation-graph.component.html', + styleUrl: './course-competencies-relation-graph.component.scss', +}) +export class CourseCompetenciesRelationGraphComponent { + protected readonly faFileImport = faFileImport; + + readonly courseCompetencies = input.required(); + readonly relations = input.required(); + + readonly selectedRelationId = model.required(); + + readonly onCourseCompetencySelection = output(); + + readonly update$ = new Subject(); + readonly center$ = new Subject(); + + readonly nodes = signal([]); + + readonly edges = computed(() => { + return this.relations().map((relation) => ({ + id: `edge-${relation.id}`, + source: `${relation.headCompetencyId}`, + target: `${relation.tailCompetencyId}`, + label: relation.relationType, + data: { + id: relation.id, + }, + })); + }); + + constructor() { + effect( + () => { + return this.nodes.set( + this.courseCompetencies().map( + (courseCompetency): Node => ({ + id: courseCompetency.id!.toString(), + label: courseCompetency.title, + data: { + id: courseCompetency.id, + type: courseCompetency.type, + }, + }), + ), + ); + }, + { allowSignalWrites: true }, + ); + } + + protected selectRelation(relationId: number): void { + this.selectedRelationId.set(relationId); + } + + protected setNodeDimension(sizeUpdate: SizeUpdate): void { + this.nodes.update((nodes) => + nodes.map((node) => { + if (node.id === sizeUpdate.id) { + node.dimension = sizeUpdate.dimension; + } + return node; + }), + ); + } +} diff --git a/src/main/webapp/app/course/competencies/components/course-competencies-relation-modal/course-competencies-relation-modal.component.html b/src/main/webapp/app/course/competencies/components/course-competencies-relation-modal/course-competencies-relation-modal.component.html new file mode 100644 index 000000000000..5e14b7fb7680 --- /dev/null +++ b/src/main/webapp/app/course/competencies/components/course-competencies-relation-modal/course-competencies-relation-modal.component.html @@ -0,0 +1,33 @@ +
+
+
+ + +
+
+
+
+ @if (isLoading()) { +
+
+ +
+
+ } @else { +
+ +
+ + } +
+
diff --git a/src/main/webapp/app/course/competencies/components/course-competencies-relation-modal/course-competencies-relation-modal.component.scss b/src/main/webapp/app/course/competencies/components/course-competencies-relation-modal/course-competencies-relation-modal.component.scss new file mode 100644 index 000000000000..bc1c8d310a04 --- /dev/null +++ b/src/main/webapp/app/course/competencies/components/course-competencies-relation-modal/course-competencies-relation-modal.component.scss @@ -0,0 +1,5 @@ +.course-competencies-graph-modal { + height: 90vh; + max-height: 700px; + overflow: hidden; +} diff --git a/src/main/webapp/app/course/competencies/components/course-competencies-relation-modal/course-competencies-relation-modal.component.ts b/src/main/webapp/app/course/competencies/components/course-competencies-relation-modal/course-competencies-relation-modal.component.ts new file mode 100644 index 000000000000..df0547c57a44 --- /dev/null +++ b/src/main/webapp/app/course/competencies/components/course-competencies-relation-modal/course-competencies-relation-modal.component.ts @@ -0,0 +1,57 @@ +import { Component, effect, inject, input, signal, viewChild } from '@angular/core'; +import { CourseCompetencyApiService } from 'app/course/competencies/services/course-competency-api.service'; +import { CompetencyRelationDTO, CourseCompetency } from 'app/entities/competency.model'; +import { AlertService } from 'app/core/util/alert.service'; +import { onError } from 'app/shared/util/global.utils'; +import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; +import { CompetencyGraphComponent } from 'app/course/learning-paths/components/competency-graph/competency-graph.component'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { CourseCompetencyRelationFormComponent } from 'app/course/competencies/components/course-competency-relation-form/course-competency-relation-form.component'; +import { CourseCompetenciesRelationGraphComponent } from '../course-competencies-relation-graph/course-competencies-relation-graph.component'; + +@Component({ + selector: 'jhi-course-competencies-relation-modal', + standalone: true, + imports: [ArtemisSharedCommonModule, CompetencyGraphComponent, CourseCompetenciesRelationGraphComponent, CourseCompetencyRelationFormComponent], + templateUrl: './course-competencies-relation-modal.component.html', + styleUrl: './course-competencies-relation-modal.component.scss', +}) +export class CourseCompetenciesRelationModalComponent { + private readonly courseCompetencyApiService = inject(CourseCompetencyApiService); + private readonly alertService = inject(AlertService); + private readonly activeModal = inject(NgbActiveModal); + + private readonly courseCompetencyRelationFormComponent = viewChild.required(CourseCompetencyRelationFormComponent); + + readonly courseId = input.required(); + readonly courseCompetencies = input.required(); + + readonly selectedRelationId = signal(undefined); + + readonly isLoading = signal(false); + readonly relations = signal([]); + + constructor() { + effect(() => this.loadRelations(this.courseId()), { allowSignalWrites: true }); + } + + private async loadRelations(courseId: number): Promise { + try { + this.isLoading.set(true); + const relations = await this.courseCompetencyApiService.getCourseCompetencyRelationsByCourseId(courseId); + this.relations.set(relations); + } catch (error) { + onError(this.alertService, error); + } finally { + this.isLoading.set(false); + } + } + + protected selectCourseCompetency(courseCompetencyId: number) { + this.courseCompetencyRelationFormComponent().selectCourseCompetency(courseCompetencyId); + } + + protected closeModal(): void { + this.activeModal.close(); + } +} diff --git a/src/main/webapp/app/course/competencies/components/course-competency-relation-form/course-competency-relation-form.component.html b/src/main/webapp/app/course/competencies/components/course-competency-relation-form/course-competency-relation-form.component.html new file mode 100644 index 000000000000..5d8be2495c7c --- /dev/null +++ b/src/main/webapp/app/course/competencies/components/course-competency-relation-form/course-competency-relation-form.component.html @@ -0,0 +1,70 @@ +
+
+ + +
+
+ + +
+
+ + +
+
+ @if (exactRelationAlreadyExists()) { + + } @else if (relationAlreadyExists()) { + + } @else { + + } +
+ @if (showCircularDependencyError()) { + + } +
diff --git a/src/main/webapp/app/course/competencies/components/course-competency-relation-form/course-competency-relation-form.component.scss b/src/main/webapp/app/course/competencies/components/course-competency-relation-form/course-competency-relation-form.component.scss new file mode 100644 index 000000000000..84ed3ff63b6a --- /dev/null +++ b/src/main/webapp/app/course/competencies/components/course-competency-relation-form/course-competency-relation-form.component.scss @@ -0,0 +1,4 @@ +.course-competency-relation-form-container { + background-color: var(--bs-body-bg); + border-radius: var(--bs-border-radius-lg); +} diff --git a/src/main/webapp/app/course/competencies/components/course-competency-relation-form/course-competency-relation-form.component.ts b/src/main/webapp/app/course/competencies/components/course-competency-relation-form/course-competency-relation-form.component.ts new file mode 100644 index 000000000000..7f173f4968a3 --- /dev/null +++ b/src/main/webapp/app/course/competencies/components/course-competency-relation-form/course-competency-relation-form.component.ts @@ -0,0 +1,306 @@ +import { Component, computed, effect, inject, input, model, signal } from '@angular/core'; +import { CompetencyRelationDTO, CompetencyRelationType, CourseCompetency, UpdateCourseCompetencyRelationDTO } from 'app/entities/competency.model'; +import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; +import { CourseCompetencyApiService } from 'app/course/competencies/services/course-competency-api.service'; +import { AlertService } from 'app/core/util/alert.service'; +import { faSpinner } from '@fortawesome/free-solid-svg-icons'; + +@Component({ + selector: 'jhi-course-competency-relation-form', + standalone: true, + imports: [ArtemisSharedCommonModule], + templateUrl: './course-competency-relation-form.component.html', + styleUrl: './course-competency-relation-form.component.scss', +}) +export class CourseCompetencyRelationFormComponent { + protected readonly faSpinner = faSpinner; + + protected readonly competencyRelationType = CompetencyRelationType; + + private readonly courseCompetencyApiService = inject(CourseCompetencyApiService); + private readonly alertService = inject(AlertService); + + readonly courseId = input.required(); + readonly courseCompetencies = input.required(); + readonly relations = model.required(); + readonly selectedRelationId = model.required(); + + readonly headCompetencyId = signal(undefined); + readonly tailCompetencyId = signal(undefined); + readonly relationType = model(undefined); + + readonly isLoading = signal(false); + + readonly relationAlreadyExists = computed(() => this.getRelation(this.headCompetencyId(), this.tailCompetencyId()) !== undefined); + readonly exactRelationAlreadyExists = computed(() => this.getExactRelation(this.headCompetencyId(), this.tailCompetencyId(), this.relationType()) !== undefined); + + private readonly selectableTailCourseCompetencyIds = computed(() => { + if (this.headCompetencyId() && this.relationType()) { + return this.getSelectableTailCompetencyIds(this.headCompetencyId()!, this.relationType()!); + } + return this.courseCompetencies().map(({ id }) => id!); + }); + + readonly showCircularDependencyError = computed(() => this.tailCompetencyId() && !this.selectableTailCourseCompetencyIds().includes(this.tailCompetencyId()!)); + + constructor() { + effect(() => this.selectRelation(this.selectedRelationId()), { allowSignalWrites: true }); + } + + protected isCourseCompetencySelectable(courseCompetencyId: number): boolean { + return this.selectableTailCourseCompetencyIds().includes(courseCompetencyId); + } + + private selectRelation(relationId?: number): void { + const relation = this.relations().find(({ id }) => id === relationId); + if (relation) { + this.headCompetencyId.set(relation?.headCompetencyId); + this.tailCompetencyId.set(relation?.tailCompetencyId); + this.relationType.set(relation?.relationType); + } + } + + public selectCourseCompetency(courseCompetencyId: number): void { + if (!this.headCompetencyId()) { + this.selectHeadCourseCompetency(courseCompetencyId); + } else if (!this.tailCompetencyId()) { + this.selectTailCourseCompetency(courseCompetencyId); + } else { + this.selectHeadCourseCompetency(courseCompetencyId); + } + } + + protected selectHeadCourseCompetency(headId: number) { + this.headCompetencyId.set(headId); + this.tailCompetencyId.set(undefined); + this.selectedRelationId.set(undefined); + } + + protected selectTailCourseCompetency(tailId: number) { + this.tailCompetencyId.set(tailId); + const existingRelation = this.getRelation(this.headCompetencyId(), this.tailCompetencyId()); + if (existingRelation) { + this.selectedRelationId.set(existingRelation.id); + } else { + this.selectedRelationId.set(undefined); + } + } + + protected async createRelation(): Promise { + try { + this.isLoading.set(true); + const courseCompetencyRelation = await this.courseCompetencyApiService.createCourseCompetencyRelation(this.courseId(), { + headCompetencyId: this.headCompetencyId()!, + tailCompetencyId: Number(this.tailCompetencyId()!), + relationType: this.relationType()!, + }); + this.relations.update((relations) => [...relations, courseCompetencyRelation]); + this.selectedRelationId.set(courseCompetencyRelation.id!); + } catch (error) { + this.alertService.error(error.message); + } finally { + this.isLoading.set(false); + } + } + + protected getExactRelation(headCompetencyId?: number, tailCompetencyId?: number, relationType?: CompetencyRelationType): CompetencyRelationDTO | undefined { + return this.relations().find( + (relation) => relation.headCompetencyId === headCompetencyId && relation.tailCompetencyId === tailCompetencyId && relation.relationType === relationType, + ); + } + + protected getRelation(headCompetencyId?: number, tailCompetencyId?: number): CompetencyRelationDTO | undefined { + return this.relations().find((relation) => relation.headCompetencyId === headCompetencyId && relation.tailCompetencyId === tailCompetencyId); + } + + protected async updateRelation(): Promise { + try { + this.isLoading.set(true); + const newRelationType = this.relationType()!; + await this.courseCompetencyApiService.updateCourseCompetencyRelation(this.courseId(), this.selectedRelationId()!, { + newRelationType: newRelationType, + }); + this.relations.update((relations) => + relations.map((relation) => { + if (relation.id === this.selectedRelationId()) { + return { ...relation, relationType: newRelationType }; + } + return relation; + }), + ); + } catch (error) { + this.alertService.error(error.message); + } finally { + this.isLoading.set(false); + } + } + + protected async deleteRelation(): Promise { + try { + this.isLoading.set(true); + const deletedRelation = this.relations().find( + ({ headCompetencyId, tailCompetencyId, relationType }) => + headCompetencyId == this.headCompetencyId() && tailCompetencyId == this.tailCompetencyId() && relationType === this.relationType(), + ); + await this.courseCompetencyApiService.deleteCourseCompetencyRelation(this.courseId(), deletedRelation!.id!); + this.relations.update((relations) => relations.filter(({ id }) => id !== deletedRelation!.id)); + this.selectedRelationId.set(undefined); + } catch (error) { + this.alertService.error(error.message); + } finally { + this.isLoading.set(false); + } + } + + /** + * Function to get the selectable tail competency ids for the given head + * competency and relation type without creating a cyclic dependency + * + * @param headCompetencyId The selected head competency id + * @param relationType The selected relation type + * @private + * + * @returns The selectable tail competency ids + */ + private getSelectableTailCompetencyIds(headCompetencyId: number, relationType: CompetencyRelationType): number[] { + return this.courseCompetencies() + .map(({ id }) => id!) + .filter((id) => id !== headCompetencyId) // Exclude the head itself + .filter((id) => { + let relations = this.relations(); + const existingRelation = this.getRelation(headCompetencyId, id); + if (existingRelation) { + relations = relations.filter((relation) => relation.id !== existingRelation.id); + } + const potentialRelation: CompetencyRelationDTO = { + headCompetencyId: headCompetencyId, + tailCompetencyId: id, + relationType: relationType, + }; + return !this.detectCycleInRelations(relations.concat(potentialRelation), this.courseCompetencies().length); + }); + } + + /** + * Function to detect cycles in the competency relations + * @param relations The list of competency relations + * @param numOfCompetencies The total number of competencies + * @private + * + * @returns True if a cycle is detected, false otherwise + */ + private detectCycleInRelations(relations: CompetencyRelationDTO[], numOfCompetencies: number): boolean { + // Create a map to store the competency IDs and map them to incremental indices + const idToIndexMap = new Map(); + let currentIndex = 0; + + // map the competency IDs to incremental indices + relations.forEach((relation) => { + const tail = relation.tailCompetencyId!; + const head = relation.headCompetencyId!; + + if (!idToIndexMap.has(tail)) { + idToIndexMap.set(tail, currentIndex++); + } + if (!idToIndexMap.has(head)) { + idToIndexMap.set(head, currentIndex++); + } + }); + + const unionFind = new UnionFind(numOfCompetencies); + + // Apply Union-Find based on the MATCHES relations + relations.forEach((relation) => { + if (relation.relationType === CompetencyRelationType.MATCHES) { + const tailIndex = idToIndexMap.get(relation.tailCompetencyId!); + const headIndex = idToIndexMap.get(relation.headCompetencyId!); + + if (tailIndex !== undefined && headIndex !== undefined) { + // Perform union operation to group matching course competencies into sets + unionFind.union(tailIndex, headIndex); + } + } + }); + + // Build the reduced graph for EXTENDS and ASSUMES relations + const reducedGraph: number[][] = Array.from({ length: numOfCompetencies }, () => []); + + relations.forEach((relation) => { + const tail = unionFind.find(idToIndexMap.get(relation.tailCompetencyId!)!); + const head = unionFind.find(idToIndexMap.get(relation.headCompetencyId!)!); + + if (relation.relationType === CompetencyRelationType.EXTENDS || relation.relationType === CompetencyRelationType.ASSUMES) { + reducedGraph[tail].push(head); + } + }); + + return this.hasCycle(reducedGraph, numOfCompetencies); + } + + private hasCycle(graph: number[][], noOfCourseCompetencies: number): boolean { + const visited: boolean[] = Array(noOfCourseCompetencies).fill(false); + const recursionStack: boolean[] = Array(noOfCourseCompetencies).fill(false); + + // Depth-first search to detect cycles + const depthFirstSearch = (v: number): boolean => { + visited[v] = true; + recursionStack[v] = true; + + for (const neighbor of graph[v] || []) { + if (!visited[neighbor]) { + if (depthFirstSearch(neighbor)) return true; + } else if (recursionStack[neighbor]) { + return true; + } + } + + recursionStack[v] = false; + return false; + }; + + for (let node = 0; node < noOfCourseCompetencies; node++) { + if (!visited[node]) { + if (depthFirstSearch(node)) { + return true; + } + } + } + return false; + } +} + +// Union-Find (Disjoint Set) class (https://en.wikipedia.org/wiki/Disjoint-set_data_structure -> union by rank) +export class UnionFind { + parent: number[]; + rank: number[]; + + constructor(size: number) { + this.parent = Array.from({ length: size }, (_, index) => index); + this.rank = Array(size).fill(1); + } + + // Find the representative of the set that contains the `competencyId` + public find(competencyId: number): number { + if (this.parent[competencyId] !== competencyId) { + this.parent[competencyId] = this.find(this.parent[competencyId]); // Path compression + } + return this.parent[competencyId]; + } + + // Union the sets containing `tailCompetencyId` and `headCompetencyId` + public union(tailCompetencyId: number, headCompetencyId: number) { + const rootU = this.find(tailCompetencyId); + const rootV = this.find(headCompetencyId); + if (rootU !== rootV) { + // Union by rank + if (this.rank[rootU] > this.rank[rootV]) { + this.parent[rootV] = rootU; + } else if (this.rank[rootU] < this.rank[rootV]) { + this.parent[rootU] = rootV; + } else { + this.parent[rootV] = rootU; + this.rank[rootU] += 1; + } + } + } +} diff --git a/src/main/webapp/app/course/competencies/components/course-competency-relation-node/course-competency-relation-node.component.html b/src/main/webapp/app/course/competencies/components/course-competency-relation-node/course-competency-relation-node.component.html new file mode 100644 index 000000000000..c7c8821f577f --- /dev/null +++ b/src/main/webapp/app/course/competencies/components/course-competency-relation-node/course-competency-relation-node.component.html @@ -0,0 +1,17 @@ +
+
+ + + +
+ {{ courseCompetencyNode().label }} +
diff --git a/src/main/webapp/app/course/competencies/components/course-competency-relation-node/course-competency-relation-node.component.scss b/src/main/webapp/app/course/competencies/components/course-competency-relation-node/course-competency-relation-node.component.scss new file mode 100644 index 000000000000..baa3d06976c5 --- /dev/null +++ b/src/main/webapp/app/course/competencies/components/course-competency-relation-node/course-competency-relation-node.component.scss @@ -0,0 +1,20 @@ +.competency-node { + white-space: nowrap; + background-color: var(--bs-body-bg); + border-radius: calc(var(--bs-border-radius-lg) + 6px); + padding: 10px 12px; + + .progress-container { + color: var(--bs-white); + padding: 2px 8px; + border-radius: var(--bs-border-radius-lg); + } + + .competency-container { + background-color: var(--bs-green); + } + + .prerequisite-container { + background-color: var(--bs-yellow); + } +} diff --git a/src/main/webapp/app/course/competencies/components/course-competency-relation-node/course-competency-relation-node.component.ts b/src/main/webapp/app/course/competencies/components/course-competency-relation-node/course-competency-relation-node.component.ts new file mode 100644 index 000000000000..48708b6ed23c --- /dev/null +++ b/src/main/webapp/app/course/competencies/components/course-competency-relation-node/course-competency-relation-node.component.ts @@ -0,0 +1,38 @@ +import { AfterViewInit, Component, ElementRef, computed, inject, input, output } from '@angular/core'; +import { SizeUpdate } from 'app/course/learning-paths/components/competency-node/competency-node.component'; +import { Node } from '@swimlane/ngx-graph'; +import { CourseCompetencyType } from 'app/entities/competency.model'; +import { NgClass } from '@angular/common'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; +import { ArtemisSharedModule } from 'app/shared/shared.module'; + +@Component({ + selector: 'jhi-course-competency-relation-node', + standalone: true, + imports: [NgClass, TranslateDirective, NgbTooltipModule, ArtemisSharedModule], + templateUrl: './course-competency-relation-node.component.html', + styleUrl: './course-competency-relation-node.component.scss', +}) +export class CourseCompetencyRelationNodeComponent implements AfterViewInit { + protected readonly CourseCompetencyType = CourseCompetencyType; + // height of node element in pixels + private readonly nodeHeight = 45.59; + + private readonly element = inject(ElementRef); + + readonly courseCompetencyNode = input.required(); + readonly courseCompetencyType = computed(() => this.courseCompetencyNode().data.type!); + + readonly onSizeSet = output(); + + ngAfterViewInit(): void { + this.setDimensions(this.element); + } + + setDimensions(element: ElementRef): void { + const width: number = element.nativeElement.offsetWidth; + const height = this.nodeHeight; + this.onSizeSet.emit({ id: `${this.courseCompetencyNode().id}`, dimension: { height, width } }); + } +} diff --git a/src/main/webapp/app/course/competencies/services/course-competency-api.service.ts b/src/main/webapp/app/course/competencies/services/course-competency-api.service.ts index c7d69c3674c9..26dbb518ba68 100644 --- a/src/main/webapp/app/course/competencies/services/course-competency-api.service.ts +++ b/src/main/webapp/app/course/competencies/services/course-competency-api.service.ts @@ -1,6 +1,12 @@ import { Injectable } from '@angular/core'; import { BaseApiHttpService } from 'app/course/learning-paths/services/base-api-http.service'; -import { CompetencyRelationDTO, CompetencyWithTailRelationDTO, CourseCompetency, CourseCompetencyImportOptionsDTO } from 'app/entities/competency.model'; +import { + CompetencyRelationDTO, + CompetencyWithTailRelationDTO, + CourseCompetency, + CourseCompetencyImportOptionsDTO, + UpdateCourseCompetencyRelationDTO, +} from 'app/entities/competency.model'; @Injectable({ providedIn: 'root' }) export class CourseCompetencyApiService extends BaseApiHttpService { @@ -10,23 +16,31 @@ export class CourseCompetencyApiService extends BaseApiHttpService { return this.basePath.replace('$courseId', courseId.toString()); } - importAllByCourseId(courseId: number, courseCompetencyImportOptions: CourseCompetencyImportOptionsDTO): Promise { - return this.post(`${this.getPath(courseId)}/import-all`, courseCompetencyImportOptions); + async importAllByCourseId(courseId: number, courseCompetencyImportOptions: CourseCompetencyImportOptionsDTO): Promise { + return await this.post(`${this.getPath(courseId)}/import-all`, courseCompetencyImportOptions); } - createCourseCompetencyRelation(courseId: number, relation: CompetencyRelationDTO): Promise { - return this.post(`${this.getPath(courseId)}/relations`, relation); + async createCourseCompetencyRelation(courseId: number, relation: CompetencyRelationDTO): Promise { + return await this.post(`${this.getPath(courseId)}/relations`, relation); } - deleteCourseCompetencyRelation(courseId: number, relationId: number): Promise { - return this.delete(`${this.getPath(courseId)}/relations/${relationId}`); + async updateCourseCompetencyRelation(courseId: number, relationId: number, updateCourseCompetencyRelationDTO: UpdateCourseCompetencyRelationDTO): Promise { + return await this.patch(`${this.getPath(courseId)}/relations/${relationId}`, updateCourseCompetencyRelationDTO); } - getCourseCompetencyRelations(courseId: number): Promise { - return this.get(`${this.getPath(courseId)}/relations`); + async deleteCourseCompetencyRelation(courseId: number, relationId: number): Promise { + return await this.delete(`${this.getPath(courseId)}/relations/${relationId}`); } - getCourseCompetenciesByCourseId(courseId: number): Promise { - return this.get(`${this.getPath(courseId)}`); + async getCourseCompetencyRelationsByCourseId(courseId: number): Promise { + return await this.get(`${this.getPath(courseId)}/relations`); + } + + async getCourseCompetencyRelations(courseId: number): Promise { + return await this.get(`${this.getPath(courseId)}/relations`); + } + + async getCourseCompetenciesByCourseId(courseId: number): Promise { + return await this.get(`${this.getPath(courseId)}`); } } diff --git a/src/main/webapp/app/entities/competency.model.ts b/src/main/webapp/app/entities/competency.model.ts index 2a24f304e04c..b4bd6321b458 100644 --- a/src/main/webapp/app/entities/competency.model.ts +++ b/src/main/webapp/app/entities/competency.model.ts @@ -53,6 +53,10 @@ export abstract class BaseCompetency implements BaseEntity { taxonomy?: CompetencyTaxonomy; } +export interface UpdateCourseCompetencyRelationDTO { + newRelationType: CompetencyRelationType; +} + export abstract class CourseCompetency extends BaseCompetency { softDueDate?: dayjs.Dayjs; masteryThreshold?: number; diff --git a/src/main/webapp/app/entities/programming/programming-exercise.model.ts b/src/main/webapp/app/entities/programming/programming-exercise.model.ts index d1d039f7cd38..f00dc636caca 100644 --- a/src/main/webapp/app/entities/programming/programming-exercise.model.ts +++ b/src/main/webapp/app/entities/programming/programming-exercise.model.ts @@ -26,6 +26,7 @@ export enum ProgrammingLanguage { R = 'R', RUST = 'RUST', SWIFT = 'SWIFT', + TYPESCRIPT = 'TYPESCRIPT', VHDL = 'VHDL', } diff --git a/src/main/webapp/i18n/de/competency.json b/src/main/webapp/i18n/de/competency.json index cb07c639df52..aaef36b9f34a 100644 --- a/src/main/webapp/i18n/de/competency.json +++ b/src/main/webapp/i18n/de/competency.json @@ -172,8 +172,9 @@ "softDueDate": "Empfohlen bis", "masteryThreshold": "Schwellwert zum Erreichen der Kompetenz", "manage": { - "helpButton": "Hilfe", - "importAllButton": "Alles eines Kurses importieren" + "importAllButton": "Alles eines Kurses importieren", + "editRelationsButton": "Beziehungen bearbeiten", + "helpButton": "Hilfe" }, "importSettings": { "importRelationsLabel": "Beziehungen importieren", @@ -242,6 +243,36 @@ "participationRate": "Teilnahmerate" } }, + "relations": { + "modalTitle": "Beziehungen zwischen Kurskompetenzen", + "relationTypes": { + "ASSUMES": "Setzt voraus", + "EXTENDS": "Erweitert", + "MATCHES": "Stimmt überein mit" + }, + "form": { + "headCourseCompetencyLabel": "Startkurskompetenz", + "headCourseCompetencyDefaultOption": "Wähle Startkurskompetenz", + "tailCourseCompetencyLabel": "Schlusskurskompetenz", + "tailCourseCompetencyDefaultOption": "Wähle Schlusskurskompetenz", + "relationTypeLabel": "Beziehungstyp", + "relationTypeDefaultOption": "Wähle Beziehungstyp", + "deleteRelationButtonLabel": "Beziehung löschen", + "updateRelationButtonLabel": "Beziehung aktualisieren", + "createRelationButtonLabel": "Beziehung erstellen", + "cyclicDependencyError": "Du kannst keine zyklischen Beziehungen zwischen Kurskompetenzen erstellen." + }, + "graph": { + "nodeTypes": { + "competency": "K", + "prerequisite": "V" + }, + "tooltips": { + "competency": "Dieser Knoten repräsentiert eine Kompetenz", + "prerequisite": "Dieser Knoten repräsentiert eine Voraussetzung" + } + } + }, "featureExplanation": { "title": "Einführung in Kurskompetenzen", "adaptiveLearning": { diff --git a/src/main/webapp/i18n/en/competency.json b/src/main/webapp/i18n/en/competency.json index c056c0d50d94..e4aedbe82c77 100644 --- a/src/main/webapp/i18n/en/competency.json +++ b/src/main/webapp/i18n/en/competency.json @@ -172,8 +172,9 @@ "softDueDate": "Recommended until", "masteryThreshold": "Mastery threshold", "manage": { - "helpButton": "Help", - "importAllButton": "Import all of a course" + "importAllButton": "Import all of a course", + "editRelationsButton": "Edit relations", + "helpButton": "Help" }, "importSettings": { "importRelationsLabel": "Import relations", @@ -242,6 +243,36 @@ "EVALUATE": "appraise, argue, choose, defend, judge, select, support, value, critique", "CREATE": "design, formulate, hypothesize, invent, plan, propose, write, assemble, construct, develop" }, + "relations": { + "modalTitle": "Course competency relations", + "relationTypes": { + "ASSUMES": "Assumes", + "EXTENDS": "Extends", + "MATCHES": "Matches" + }, + "form": { + "headCourseCompetencyLabel": "Head course competency", + "headCourseCompetencyDefaultOption": "Select head course competency", + "tailCourseCompetencyLabel": "Tail course competency", + "tailCourseCompetencyDefaultOption": "Select tail course competency", + "relationTypeLabel": "Relation Type", + "relationTypeDefaultOption": "Select relation type", + "deleteRelationButtonLabel": "Delete Relation", + "updateRelationButtonLabel": "Update Relation", + "createRelationButtonLabel": "Create Relation", + "cyclicDependencyError": "You cannot create a cyclic dependency between course competencies." + }, + "graph": { + "nodeTypes": { + "competency": "C", + "prerequisite": "P" + }, + "tooltips": { + "competency": "This node represents a Competency", + "prerequisite": "This node represents a Prerequisite" + } + } + }, "featureExplanation": { "title": "Introduction to course competencies", "adaptiveLearning": { diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/architecture/AtlasTestArchitectureTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/architecture/AtlasTestArchitectureTest.java index 96bdd6b27d58..ff479619c209 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/architecture/AtlasTestArchitectureTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/architecture/AtlasTestArchitectureTest.java @@ -1,9 +1,11 @@ package de.tum.cit.aet.artemis.atlas.architecture; +import java.util.Set; + import de.tum.cit.aet.artemis.atlas.AbstractAtlasIntegrationTest; import de.tum.cit.aet.artemis.shared.architecture.module.AbstractModuleTestArchitectureTest; -class AtlasTestArchitectureTest extends AbstractModuleTestArchitectureTest { +class AtlasTestArchitectureTest extends AbstractModuleTestArchitectureTest { @Override public String getModulePackage() { @@ -11,7 +13,7 @@ public String getModulePackage() { } @Override - protected Class getAbstractModuleIntegrationTestClass() { - return AbstractAtlasIntegrationTest.class; + protected Set> getAbstractModuleIntegrationTestClasses() { + return Set.of(AbstractAtlasIntegrationTest.class); } } diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.java index df2c561bda7e..bccff7083c8c 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.java @@ -27,6 +27,7 @@ import de.tum.cit.aet.artemis.atlas.dto.CompetencyImportResponseDTO; import de.tum.cit.aet.artemis.atlas.dto.CompetencyRelationDTO; import de.tum.cit.aet.artemis.atlas.dto.CompetencyWithTailRelationDTO; +import de.tum.cit.aet.artemis.atlas.dto.UpdateCourseCompetencyRelationDTO; import de.tum.cit.aet.artemis.core.domain.User; import de.tum.cit.aet.artemis.core.dto.CourseCompetencyProgressDTO; import de.tum.cit.aet.artemis.core.dto.SearchResultPageDTO; @@ -467,6 +468,31 @@ void shouldReturnBadRequestForCircularRelations() throws Exception { request.post("/api/courses/" + course.getId() + "/course-competencies/relations", CompetencyRelationDTO.of(relation), HttpStatus.BAD_REQUEST); } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void shouldUpdateForInstructor() throws Exception { + var headCompetency = competencyUtilService.createCompetency(course); + var relationToCreate = new CompetencyRelation(); + relationToCreate.setTailCompetency(courseCompetency); + relationToCreate.setHeadCompetency(headCompetency); + relationToCreate.setType(RelationType.EXTENDS); + + request.postWithResponseBody("/api/courses/" + course.getId() + "/course-competencies/relations", CompetencyRelationDTO.of(relationToCreate), CompetencyRelation.class, + HttpStatus.OK); + + var relations = competencyRelationRepository.findAllWithHeadAndTailByCourseId(course.getId()); + assertThat(relations).hasSize(1); + var relation = relations.stream().findFirst().get(); + assertThat(relation.getType()).isEqualTo(RelationType.EXTENDS); + + request.patch("/api/courses/" + course.getId() + "/course-competencies/relations/" + relation.getId(), new UpdateCourseCompetencyRelationDTO(RelationType.MATCHES), + HttpStatus.NO_CONTENT); + + relations = competencyRelationRepository.findAllWithHeadAndTailByCourseId(course.getId()); + assertThat(relations).hasSize(1); + assertThat(relations.stream().findFirst().get().getType()).isEqualTo(RelationType.MATCHES); + } } @Test diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/learningpath/LearningPathIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/learningpath/LearningPathIntegrationTest.java index b217564b469f..7ec124ecc537 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/learningpath/LearningPathIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/learningpath/LearningPathIntegrationTest.java @@ -379,7 +379,7 @@ void testUpdateLearningPathProgress() throws Exception { * This only tests if the end point successfully retrieves the health status. The correctness of the health status is tested in LearningPathServiceTest. * * @throws Exception the request failed - * @see de.tum.cit.aet.artemis.service.LearningPathServiceTest + * @see de.tum.cit.aet.artemis.atlas.service.LearningPathServiceTest */ @Test @WithMockUser(username = INSTRUCTOR_OF_COURSE, roles = "INSTRUCTOR") @@ -468,7 +468,7 @@ void testGetLearningPathNgxForOtherStudent(LearningPathResource.NgxRequestType t * This only tests if the end point successfully retrieves the graph representation. The correctness of the response is tested in LearningPathServiceTest. * * @throws Exception the request failed - * @see de.tum.cit.aet.artemis.service.LearningPathServiceTest + * @see de.tum.cit.aet.artemis.atlas.service.LearningPathServiceTest */ @ParameterizedTest(name = "{displayName} [{index}] {argumentsWithNames}") @EnumSource(LearningPathResource.NgxRequestType.class) @@ -484,7 +484,7 @@ void testGetLearningPathNgxAsStudent(LearningPathResource.NgxRequestType type) t * This only tests if the end point successfully retrieves the graph representation. The correctness of the response is tested in LearningPathServiceTest. * * @throws Exception the request failed - * @see de.tum.cit.aet.artemis.service.LearningPathServiceTest + * @see de.tum.cit.aet.artemis.atlas.service.LearningPathServiceTest */ @ParameterizedTest(name = "{displayName} [{index}] {argumentsWithNames}") @EnumSource(LearningPathResource.NgxRequestType.class) @@ -500,7 +500,7 @@ void testGetLearningPathNgxAsTutor(LearningPathResource.NgxRequestType type) thr * This only tests if the end point successfully retrieves the graph representation. The correctness of the response is tested in LearningPathServiceTest. * * @throws Exception the request failed - * @see de.tum.cit.aet.artemis.service.LearningPathServiceTest + * @see de.tum.cit.aet.artemis.atlas.service.LearningPathServiceTest */ @ParameterizedTest(name = "{displayName} [{index}] {argumentsWithNames}") @EnumSource(LearningPathResource.NgxRequestType.class) @@ -516,7 +516,7 @@ void testGetLearningPathNgxAsEditor(LearningPathResource.NgxRequestType type) th * This only tests if the end point successfully retrieves the graph representation. The correctness of the response is tested in LearningPathServiceTest. * * @throws Exception the request failed - * @see de.tum.cit.aet.artemis.service.LearningPathServiceTest + * @see de.tum.cit.aet.artemis.atlas.service.LearningPathServiceTest */ @ParameterizedTest(name = "{displayName} [{index}] {argumentsWithNames}") @EnumSource(LearningPathResource.NgxRequestType.class) diff --git a/src/test/java/de/tum/cit/aet/artemis/fileupload/AbstractFileUploadIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/fileupload/AbstractFileUploadIntegrationTest.java index 6c6ff7719640..f85aa92027b7 100644 --- a/src/test/java/de/tum/cit/aet/artemis/fileupload/AbstractFileUploadIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/fileupload/AbstractFileUploadIntegrationTest.java @@ -21,7 +21,7 @@ import de.tum.cit.aet.artemis.modeling.util.ModelingExerciseUtilService; import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; -public class AbstractFileUploadIntegrationTest extends AbstractSpringIntegrationIndependentTest { +public abstract class AbstractFileUploadIntegrationTest extends AbstractSpringIntegrationIndependentTest { // Repositories @Autowired diff --git a/src/test/java/de/tum/cit/aet/artemis/fileupload/architecture/FileUploadTestArchitectureTest.java b/src/test/java/de/tum/cit/aet/artemis/fileupload/architecture/FileUploadTestArchitectureTest.java index f67db93f311f..8e36ca4a74d1 100644 --- a/src/test/java/de/tum/cit/aet/artemis/fileupload/architecture/FileUploadTestArchitectureTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/fileupload/architecture/FileUploadTestArchitectureTest.java @@ -1,9 +1,11 @@ package de.tum.cit.aet.artemis.fileupload.architecture; +import java.util.Set; + import de.tum.cit.aet.artemis.fileupload.AbstractFileUploadIntegrationTest; import de.tum.cit.aet.artemis.shared.architecture.module.AbstractModuleTestArchitectureTest; -class FileUploadTestArchitectureTest extends AbstractModuleTestArchitectureTest { +class FileUploadTestArchitectureTest extends AbstractModuleTestArchitectureTest { @Override public String getModulePackage() { @@ -11,7 +13,7 @@ public String getModulePackage() { } @Override - protected Class getAbstractModuleIntegrationTestClass() { - return AbstractFileUploadIntegrationTest.class; + protected Set> getAbstractModuleIntegrationTestClasses() { + return Set.of(AbstractFileUploadIntegrationTest.class); } } diff --git a/src/test/java/de/tum/cit/aet/artemis/lti/architecture/LtiTestArchitectureTest.java b/src/test/java/de/tum/cit/aet/artemis/lti/architecture/LtiTestArchitectureTest.java index ceb15210ada3..4d18fa50b636 100644 --- a/src/test/java/de/tum/cit/aet/artemis/lti/architecture/LtiTestArchitectureTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/lti/architecture/LtiTestArchitectureTest.java @@ -1,9 +1,11 @@ package de.tum.cit.aet.artemis.lti.architecture; +import java.util.Set; + import de.tum.cit.aet.artemis.lti.AbstractLtiIntegrationTest; import de.tum.cit.aet.artemis.shared.architecture.module.AbstractModuleTestArchitectureTest; -class LtiTestArchitectureTest extends AbstractModuleTestArchitectureTest { +class LtiTestArchitectureTest extends AbstractModuleTestArchitectureTest { @Override public String getModulePackage() { @@ -11,7 +13,7 @@ public String getModulePackage() { } @Override - protected Class getAbstractModuleIntegrationTestClass() { - return AbstractLtiIntegrationTest.class; + protected Set> getAbstractModuleIntegrationTestClasses() { + return Set.of(AbstractLtiIntegrationTest.class); } } diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationGitlabCIGitlabSamlTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationGitlabCIGitlabSamlTest.java new file mode 100644 index 000000000000..cb8866b2160a --- /dev/null +++ b/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationGitlabCIGitlabSamlTest.java @@ -0,0 +1,82 @@ +package de.tum.cit.aet.artemis.programming; + +import java.net.URL; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; + +import de.tum.cit.aet.artemis.core.service.messaging.InstanceMessageReceiveService; +import de.tum.cit.aet.artemis.core.user.util.UserUtilService; +import de.tum.cit.aet.artemis.exam.repository.ExamRepository; +import de.tum.cit.aet.artemis.exam.test_repository.StudentExamTestRepository; +import de.tum.cit.aet.artemis.exam.util.ExamUtilService; +import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; +import de.tum.cit.aet.artemis.exercise.test_repository.ParticipationTestRepository; +import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; +import de.tum.cit.aet.artemis.programming.repository.BuildLogStatisticsEntryRepository; +import de.tum.cit.aet.artemis.programming.repository.BuildPlanRepository; +import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; +import de.tum.cit.aet.artemis.programming.service.gitlabci.GitLabCIResultService; +import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; +import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; +import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; +import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationGitlabCIGitlabSamlTest; + +public abstract class AbstractProgrammingIntegrationGitlabCIGitlabSamlTest extends AbstractSpringIntegrationGitlabCIGitlabSamlTest { + + // Config + @Value("${artemis.version-control.url}") + protected URL gitlabServerUrl; + + // Repositories + @Autowired + protected BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository; + + @Autowired + protected BuildPlanRepository buildPlanRepository; + + @Autowired + protected ParticipationTestRepository participationRepository; + + @Autowired + protected ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository; + + @Autowired + protected ProgrammingExerciseTestCaseTestRepository programmingExerciseTestCaseRepository; + + @Autowired + protected ProgrammingExerciseTestRepository programmingExerciseRepository; + + // External Repositories + @Autowired + protected ExamRepository examRepository; + + @Autowired + protected StudentExamTestRepository studentExamRepository; + + // Services + @Autowired + protected GitLabCIResultService gitLabCIResultService; + + // External Services + @Autowired + protected InstanceMessageReceiveService instanceMessageReceiveService; + + // Util Services + @Autowired + protected ProgrammingExerciseUtilService programmingExerciseUtilService; + + // External Util Services + @Autowired + protected ExamUtilService examUtilService; + + @Autowired + protected ExerciseUtilService exerciseUtilService; + + @Autowired + protected ParticipationUtilService participationUtilService; + + @Autowired + protected UserUtilService userUtilService; + +} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationIndependentTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationIndependentTest.java new file mode 100644 index 000000000000..46749fbcd3ec --- /dev/null +++ b/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationIndependentTest.java @@ -0,0 +1,183 @@ +package de.tum.cit.aet.artemis.programming; + +import org.springframework.beans.factory.annotation.Autowired; + +import de.tum.cit.aet.artemis.assessment.repository.ComplaintRepository; +import de.tum.cit.aet.artemis.assessment.util.ComplaintUtilService; +import de.tum.cit.aet.artemis.core.test_repository.UserTestRepository; +import de.tum.cit.aet.artemis.core.user.util.UserUtilService; +import de.tum.cit.aet.artemis.core.util.CourseUtilService; +import de.tum.cit.aet.artemis.exam.repository.ExamRepository; +import de.tum.cit.aet.artemis.exam.util.ExamUtilService; +import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; +import de.tum.cit.aet.artemis.exercise.repository.ExerciseTestRepository; +import de.tum.cit.aet.artemis.exercise.test_repository.ParticipationTestRepository; +import de.tum.cit.aet.artemis.exercise.test_repository.StudentParticipationTestRepository; +import de.tum.cit.aet.artemis.exercise.test_repository.SubmissionTestRepository; +import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; +import de.tum.cit.aet.artemis.programming.repository.AuxiliaryRepositoryRepository; +import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; +import de.tum.cit.aet.artemis.programming.repository.SolutionProgrammingExerciseParticipationRepository; +import de.tum.cit.aet.artemis.programming.repository.StaticCodeAnalysisCategoryRepository; +import de.tum.cit.aet.artemis.programming.repository.hestia.CodeHintRepository; +import de.tum.cit.aet.artemis.programming.repository.hestia.CoverageFileReportRepository; +import de.tum.cit.aet.artemis.programming.repository.hestia.CoverageReportRepository; +import de.tum.cit.aet.artemis.programming.repository.hestia.ExerciseHintActivationRepository; +import de.tum.cit.aet.artemis.programming.repository.hestia.ExerciseHintRepository; +import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseSolutionEntryRepository; +import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseTaskRepository; +import de.tum.cit.aet.artemis.programming.repository.hestia.TestwiseCoverageReportEntryRepository; +import de.tum.cit.aet.artemis.programming.repository.settings.IdeRepository; +import de.tum.cit.aet.artemis.programming.repository.settings.UserIdeMappingRepository; +import de.tum.cit.aet.artemis.programming.service.AuxiliaryRepositoryService; +import de.tum.cit.aet.artemis.programming.service.BuildLogEntryService; +import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseFeedbackCreationService; +import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseGradingService; +import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseRepositoryService; +import de.tum.cit.aet.artemis.programming.service.hestia.CodeHintService; +import de.tum.cit.aet.artemis.programming.service.hestia.ExerciseHintService; +import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseTaskService; +import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseStudentParticipationTestRepository; +import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; +import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; +import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingSubmissionTestRepository; +import de.tum.cit.aet.artemis.programming.util.GitUtilService; +import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; +import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; + +public abstract class AbstractProgrammingIntegrationIndependentTest extends AbstractSpringIntegrationIndependentTest { + + // Repositories + @Autowired + protected AuxiliaryRepositoryRepository auxiliaryRepositoryRepository; + + @Autowired + protected CodeHintRepository codeHintRepository; + + @Autowired + protected CoverageFileReportRepository coverageFileReportRepository; + + @Autowired + protected CoverageReportRepository coverageReportRepository; + + @Autowired + protected ExerciseHintActivationRepository exerciseHintActivationRepository; + + @Autowired + protected ExerciseHintRepository exerciseHintRepository; + + @Autowired + protected IdeRepository ideRepository; + + @Autowired + protected ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository; + + @Autowired + protected ProgrammingExerciseSolutionEntryRepository programmingExerciseSolutionEntryRepository; + + @Autowired + protected ProgrammingExerciseStudentParticipationTestRepository programmingExerciseStudentParticipationRepository; + + @Autowired + protected ProgrammingExerciseTaskRepository taskRepository; + + @Autowired + protected ProgrammingExerciseTestCaseTestRepository testCaseRepository; + + @Autowired + protected ProgrammingExerciseTestRepository programmingExerciseRepository; + + @Autowired + protected ProgrammingSubmissionTestRepository programmingSubmissionRepository; + + @Autowired + protected SolutionProgrammingExerciseParticipationRepository solutionEntryRepository; + + @Autowired + protected StaticCodeAnalysisCategoryRepository staticCodeAnalysisCategoryRepository; + + @Autowired + protected TestwiseCoverageReportEntryRepository testwiseCoverageReportEntryRepository; + + @Autowired + protected UserIdeMappingRepository userIdeMappingRepository; + + // External Repositories + @Autowired + protected ComplaintRepository complaintRepo; + + @Autowired + protected ExamRepository examRepository; + + @Autowired + protected ExerciseTestRepository exerciseRepository; + + @Autowired + protected ParticipationTestRepository participationRepository; + + @Autowired + protected StudentParticipationTestRepository studentParticipationRepository; + + @Autowired + protected SubmissionTestRepository submissionRepository; + + @Autowired + protected UserTestRepository userRepository; + + // Services + @Autowired + protected AuxiliaryRepositoryService auxiliaryRepositoryService; + + @Autowired + protected BuildLogEntryService buildLogEntryService; + + @Autowired + protected CodeHintService codeHintService; + + @Autowired + protected ExerciseHintService exerciseHintService; + + @Autowired + protected GitUtilService gitUtilService; + + @Autowired + protected ProgrammingExerciseFeedbackCreationService feedbackCreationService; + + @Autowired + protected ProgrammingExerciseGradingService gradingService; + + @Autowired + protected ProgrammingExerciseRepositoryService programmingExerciseRepositoryService; + + @Autowired + protected ProgrammingExerciseTaskService programmingExerciseTaskService; + + // External Services + + // Util Services + @Autowired + protected ProgrammingExerciseIntegrationTestService programmingExerciseIntegrationTestService; + + @Autowired + protected ProgrammingExerciseUtilService programmingExerciseUtilService; + + // External Util Services + @Autowired + protected ComplaintUtilService complaintUtilService; + + @Autowired + protected CourseUtilService courseUtilService; + + @Autowired + protected ExamUtilService examUtilService; + + @Autowired + protected ExerciseUtilService exerciseUtilService; + + @Autowired + protected ParticipationUtilService participationUtilService; + + @Autowired + protected UserUtilService userUtilService; + +} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationJenkinsGitlabTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationJenkinsGitlabTest.java new file mode 100644 index 000000000000..780343412cf9 --- /dev/null +++ b/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationJenkinsGitlabTest.java @@ -0,0 +1,209 @@ +package de.tum.cit.aet.artemis.programming; + +import java.net.URL; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.client.RestTemplate; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import de.tum.cit.aet.artemis.communication.repository.conversation.ChannelRepository; +import de.tum.cit.aet.artemis.communication.test_repository.PostTestRepository; +import de.tum.cit.aet.artemis.core.test_repository.UserTestRepository; +import de.tum.cit.aet.artemis.core.user.util.UserUtilService; +import de.tum.cit.aet.artemis.core.util.CourseTestService; +import de.tum.cit.aet.artemis.core.util.CourseUtilService; +import de.tum.cit.aet.artemis.exam.repository.ExamRepository; +import de.tum.cit.aet.artemis.exam.test_repository.StudentExamTestRepository; +import de.tum.cit.aet.artemis.exam.util.ExamUtilService; +import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; +import de.tum.cit.aet.artemis.exercise.test_repository.StudentParticipationTestRepository; +import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; +import de.tum.cit.aet.artemis.modeling.util.ModelingExerciseUtilService; +import de.tum.cit.aet.artemis.plagiarism.repository.PlagiarismCaseRepository; +import de.tum.cit.aet.artemis.plagiarism.repository.PlagiarismComparisonRepository; +import de.tum.cit.aet.artemis.programming.repository.BuildLogStatisticsEntryRepository; +import de.tum.cit.aet.artemis.programming.repository.BuildPlanRepository; +import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; +import de.tum.cit.aet.artemis.programming.service.BuildLogEntryService; +import de.tum.cit.aet.artemis.programming.service.ConsistencyCheckTestService; +import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseGradingService; +import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseImportService; +import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseService; +import de.tum.cit.aet.artemis.programming.service.ProgrammingLanguageFeatureService; +import de.tum.cit.aet.artemis.programming.service.RepositoryAccessService; +import de.tum.cit.aet.artemis.programming.service.gitlab.GitLabPersonalAccessTokenManagementService; +import de.tum.cit.aet.artemis.programming.service.jenkins.JenkinsAuthorizationInterceptor; +import de.tum.cit.aet.artemis.programming.service.jenkins.JenkinsInternalUrlService; +import de.tum.cit.aet.artemis.programming.service.jenkins.build_plan.JenkinsPipelineScriptCreator; +import de.tum.cit.aet.artemis.programming.service.jenkins.jobs.JenkinsJobPermissionsService; +import de.tum.cit.aet.artemis.programming.service.jenkins.jobs.JenkinsJobService; +import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseStudentParticipationTestRepository; +import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; +import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; +import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingSubmissionTestRepository; +import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseResultTestService; +import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseTestService; +import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; +import de.tum.cit.aet.artemis.programming.util.ProgrammingSubmissionAndResultIntegrationTestService; +import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; +import de.tum.cit.aet.artemis.text.util.TextExerciseUtilService; + +public abstract class AbstractProgrammingIntegrationJenkinsGitlabTest extends AbstractSpringIntegrationJenkinsGitlabTest { + + // Config + @Value("${artemis.continuous-integration.artemis-authentication-token-value}") + protected String ARTEMIS_AUTHENTICATION_TOKEN_VALUE; + + @Value("${artemis.version-control.url}") + protected URL gitlabServerUrl; + + @Value("${artemis.git.name}") + protected String artemisGitName; + + @Value("${artemis.git.email}") + protected String artemisGitEmail; + + @Value("${artemis.continuous-integration.url}") + protected URL jenkinsServerUrl; + + @Autowired + protected RestTemplate restTemplate; + + @Autowired + protected ObjectMapper objectMapper; + + // Repositories + @Autowired + protected BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository; + + @Autowired + protected BuildPlanRepository buildPlanRepository; + + @Autowired + protected ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository; + + @Autowired + protected ProgrammingExerciseStudentParticipationTestRepository participationRepository; + + @Autowired + protected ProgrammingExerciseTestCaseTestRepository testCaseRepository; + + @Autowired + protected ProgrammingExerciseTestRepository programmingExerciseRepository; + + @Autowired + protected ProgrammingSubmissionTestRepository submissionRepository; + + // External Repositories + @Autowired + protected ChannelRepository channelRepository; + + @Autowired + protected ExamRepository examRepository; + + @Autowired + protected PlagiarismCaseRepository plagiarismCaseRepository; + + @Autowired + protected PlagiarismComparisonRepository plagiarismComparisonRepository; + + @Autowired + protected PostTestRepository postRepository; + + @Autowired + protected StudentExamTestRepository studentExamRepository; + + @Autowired + protected StudentParticipationTestRepository studentParticipationRepository; + + @Autowired + protected UserTestRepository userRepository; + + // Services + @Autowired + protected BuildLogEntryService buildLogEntryService; + + @Autowired + protected ConsistencyCheckTestService consistencyCheckTestService; + + @Autowired + protected GitLabPersonalAccessTokenManagementService gitLabPersonalAccessTokenManagementService; + + @Autowired + protected JenkinsAuthorizationInterceptor jenkinsAuthorizationInterceptor; + + @Autowired + protected JenkinsInternalUrlService jenkinsInternalUrlService; + + @Autowired + protected JenkinsJobPermissionsService jenkinsJobPermissionsService; + + @Autowired + protected JenkinsJobService jenkinsJobService; + + @Autowired + protected JenkinsPipelineScriptCreator jenkinsPipelineScriptCreator; + + @Autowired + protected ProgrammingExerciseGradingService gradingService; + + @Autowired + protected ProgrammingExerciseImportService programmingExerciseImportService; + + @Autowired + protected ProgrammingExerciseIntegrationTestService programmingExerciseIntegrationTestService; + + @Autowired + protected ProgrammingExerciseService programmingExerciseService; + + @Autowired + protected ProgrammingLanguageFeatureService programmingLanguageFeatureService; + + @Autowired + protected ProgrammingSubmissionAndResultIntegrationTestService testService; + + @Autowired + protected RepositoryAccessService repositoryAccessService; + + // External Services + + // Util Services + @Autowired + protected ContinuousIntegrationTestService continuousIntegrationTestService; + + @Autowired + protected ProgrammingExerciseResultTestService programmingExerciseResultTestService; + + @Autowired + protected ProgrammingExerciseTestService programmingExerciseTestService; + + @Autowired + protected ProgrammingExerciseUtilService programmingExerciseUtilService; + + // External Util Services + @Autowired + protected CourseTestService courseTestService; + + @Autowired + protected CourseUtilService courseUtilService; + + @Autowired + protected ExamUtilService examUtilService; + + @Autowired + protected ExerciseUtilService exerciseUtilService; + + @Autowired + protected ModelingExerciseUtilService modelingExerciseUtilService; + + @Autowired + protected ParticipationUtilService participationUtilService; + + @Autowired + protected TextExerciseUtilService textExerciseUtilService; + + @Autowired + protected UserUtilService userUtilService; +} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationLocalCILocalVCTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationLocalCILocalVCTest.java new file mode 100644 index 000000000000..fcbdc0bdef78 --- /dev/null +++ b/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationLocalCILocalVCTest.java @@ -0,0 +1,103 @@ +package de.tum.cit.aet.artemis.programming; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; + +import com.hazelcast.core.HazelcastInstance; + +import de.tum.cit.aet.artemis.atlas.competency.util.CompetencyUtilService; +import de.tum.cit.aet.artemis.buildagent.service.SharedQueueProcessingService; +import de.tum.cit.aet.artemis.core.connector.AeolusRequestMockProvider; +import de.tum.cit.aet.artemis.core.util.PageableSearchUtilService; +import de.tum.cit.aet.artemis.exam.util.ExamUtilService; +import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; +import de.tum.cit.aet.artemis.exercise.util.ExerciseIntegrationTestService; +import de.tum.cit.aet.artemis.programming.repository.StaticCodeAnalysisCategoryRepository; +import de.tum.cit.aet.artemis.programming.repository.VcsAccessLogRepository; +import de.tum.cit.aet.artemis.programming.service.BuildScriptProviderService; +import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseFeedbackCreationService; +import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseImportBasicService; +import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseTestCaseService; +import de.tum.cit.aet.artemis.programming.service.StaticCodeAnalysisService; +import de.tum.cit.aet.artemis.programming.service.aeolus.AeolusTemplateService; +import de.tum.cit.aet.artemis.programming.service.localci.SharedQueueManagementService; +import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; +import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; +import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationLocalCILocalVCTest; + +public abstract class AbstractProgrammingIntegrationLocalCILocalVCTest extends AbstractSpringIntegrationLocalCILocalVCTest { + + // Config + @Autowired + @Qualifier("hazelcastInstance") + protected HazelcastInstance hazelcastInstance; + + @Value("${artemis.user-management.internal-admin.username}") + protected String localVCUsername; + + @Value("${artemis.user-management.internal-admin.password}") + protected String localVCPassword; + + // Repositories + @Autowired + protected ProgrammingExerciseTestCaseTestRepository testCaseRepository; + + @Autowired + protected StaticCodeAnalysisCategoryRepository staticCodeAnalysisCategoryRepository; + + @Autowired + protected VcsAccessLogRepository vcsAccessLogRepository; + + // External Repositories + + // Services + @Autowired + protected AeolusRequestMockProvider aeolusRequestMockProvider; + + @Autowired + protected AeolusTemplateService aeolusTemplateService; + + @Autowired + protected BuildScriptProviderService buildScriptProviderService; + + @Autowired + protected ProgrammingExerciseFeedbackCreationService feedbackCreationService; + + @Autowired + protected ProgrammingExerciseImportBasicService programmingExerciseImportBasicService; + + @Autowired + protected ProgrammingExerciseTestCaseService testCaseService; + + @Autowired + protected SharedQueueManagementService sharedQueueManagementService; + + @Autowired + protected SharedQueueProcessingService sharedQueueProcessingService; + + @Autowired + protected StaticCodeAnalysisService staticCodeAnalysisService; + + // External Services + + // Util Services + @Autowired + protected ProgrammingExerciseUtilService programmingExerciseUtilService; + + // External Util Services + @Autowired + protected CompetencyUtilService competencyUtilService; + + @Autowired + protected ExamUtilService examUtilService; + + @Autowired + protected ExerciseIntegrationTestService exerciseIntegrationTestService; + + @Autowired + protected PageableSearchUtilService pageableSearchUtilService; + + @Autowired + protected ParticipationUtilService participationUtilService; +} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/AbstractLocalCILocalVCIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationLocalCILocalVCTestBase.java similarity index 63% rename from src/test/java/de/tum/cit/aet/artemis/programming/icl/AbstractLocalCILocalVCIntegrationTest.java rename to src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationLocalCILocalVCTestBase.java index 0f559f0c3a50..56a168f7e776 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/AbstractLocalCILocalVCIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationLocalCILocalVCTestBase.java @@ -1,9 +1,10 @@ -package de.tum.cit.aet.artemis.programming.icl; +package de.tum.cit.aet.artemis.programming; import java.time.ZonedDateTime; import java.util.List; import java.util.Set; +import org.apache.sshd.server.SshServer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; @@ -18,23 +19,76 @@ import de.tum.cit.aet.artemis.core.user.util.UserUtilService; import de.tum.cit.aet.artemis.exam.repository.ExamRepository; import de.tum.cit.aet.artemis.exam.test_repository.StudentExamTestRepository; -import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; import de.tum.cit.aet.artemis.exercise.repository.TeamRepository; +import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProjectType; import de.tum.cit.aet.artemis.programming.domain.SolutionProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.domain.TemplateProgrammingExerciseParticipation; +import de.tum.cit.aet.artemis.programming.hestia.util.HestiaUtilTestService; import de.tum.cit.aet.artemis.programming.repository.AuxiliaryRepositoryRepository; -import de.tum.cit.aet.artemis.programming.service.StaticCodeAnalysisService; -import de.tum.cit.aet.artemis.programming.service.aeolus.AeolusTemplateService; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationLocalCILocalVCTest; +import de.tum.cit.aet.artemis.programming.repository.SolutionProgrammingExerciseParticipationRepository; +import de.tum.cit.aet.artemis.programming.repository.hestia.CoverageFileReportRepository; +import de.tum.cit.aet.artemis.programming.repository.hestia.CoverageReportRepository; +import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseGitDiffReportRepository; +import de.tum.cit.aet.artemis.programming.repository.hestia.TestwiseCoverageReportEntryRepository; +import de.tum.cit.aet.artemis.programming.service.BuildLogEntryService; +import de.tum.cit.aet.artemis.programming.service.ParticipationVcsAccessTokenService; +import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseGitDiffReportService; +import de.tum.cit.aet.artemis.programming.service.hestia.TestwiseCoverageService; +import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralTestCaseService; +import de.tum.cit.aet.artemis.programming.service.hestia.structural.StructuralTestCaseService; +import de.tum.cit.aet.artemis.programming.service.localci.LocalCIResultService; +import de.tum.cit.aet.artemis.programming.service.localci.LocalCITriggerService; +import de.tum.cit.aet.artemis.programming.service.localvc.LocalVCServletService; +import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; +import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingSubmissionTestRepository; + +/** + * This adds upon the {@link AbstractProgrammingIntegrationLocalCILocalVCTest} by providing additional + *
    + *
  • test data on test startup
  • + *
  • services and repositories that are needed for the tests.
  • + *
+ */ +public abstract class AbstractProgrammingIntegrationLocalCILocalVCTestBase extends AbstractProgrammingIntegrationLocalCILocalVCTest { + + // Config + @Value("${artemis.version-control.user}") + protected String localVCBaseUsername; -public abstract class AbstractLocalCILocalVCIntegrationTest extends AbstractSpringIntegrationLocalCILocalVCTest { + @LocalServerPort + protected int port; @Autowired - protected TeamRepository teamRepository; + protected SshServer sshServer; + + // Repositories + @Autowired + protected AuxiliaryRepositoryRepository auxiliaryRepositoryRepository; + + @Autowired + protected CoverageFileReportRepository coverageFileReportRepository; + + @Autowired + protected CoverageReportRepository coverageReportRepository; + + @Autowired + protected ProgrammingExerciseGitDiffReportRepository reportRepository; + + @Autowired + protected ProgrammingExerciseTestRepository programmingExerciseRepository; + + @Autowired + protected ProgrammingSubmissionTestRepository programmingSubmissionRepository; + + @Autowired + protected SolutionProgrammingExerciseParticipationRepository solutionProgrammingExerciseRepository; + + @Autowired + protected TestwiseCoverageReportEntryRepository testwiseCoverageReportEntryRepository; + // External Repositories @Autowired protected ExamRepository examRepository; @@ -42,28 +96,49 @@ public abstract class AbstractLocalCILocalVCIntegrationTest extends AbstractSpri protected StudentExamTestRepository studentExamRepository; @Autowired - protected UserUtilService userUtilService; + protected TeamRepository teamRepository; + // Services @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; + protected BehavioralTestCaseService behavioralTestCaseService; @Autowired - protected ParticipationUtilService participationUtilService; + protected BuildLogEntryService buildLogEntryService; @Autowired - private StaticCodeAnalysisService staticCodeAnalysisService; + protected HestiaUtilTestService hestiaUtilTestService; @Autowired - protected AuxiliaryRepositoryRepository auxiliaryRepositoryRepository; + protected LocalCIResultService localCIResultService; @Autowired - private AeolusTemplateService aeolusTemplateService; + protected LocalVCServletService localVCServletService; - @Value("${artemis.version-control.user}") - protected String localVCBaseUsername; + @Autowired + protected LocalCITriggerService localCITriggerService; - @LocalServerPort - protected int port; + @Autowired + protected ParticipationVcsAccessTokenService participationVcsAccessTokenService; + + @Autowired + protected ProgrammingExerciseGitDiffReportService reportService; + + @Autowired + protected StructuralTestCaseService structuralTestCaseService; + + @Autowired + protected TestwiseCoverageService testwiseCoverageService; + + // External Services + + // Util Services + + // External Util services + @Autowired + protected ExerciseUtilService exerciseUtilService; + + @Autowired + protected UserUtilService userUtilService; // The error messages returned by JGit contain these Strings that correspond to the HTTP status codes. protected static final String NOT_FOUND = "not found"; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/AuxiliaryRepositoryServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/AuxiliaryRepositoryServiceTest.java index 6bb1268ff527..7108644c2fe3 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/AuxiliaryRepositoryServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/AuxiliaryRepositoryServiceTest.java @@ -8,39 +8,17 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import de.tum.cit.aet.artemis.core.exception.BadRequestAlertException; -import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; import de.tum.cit.aet.artemis.programming.domain.AuxiliaryRepository; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.repository.AuxiliaryRepositoryRepository; -import de.tum.cit.aet.artemis.programming.service.AuxiliaryRepositoryService; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; -class AuxiliaryRepositoryServiceTest extends AbstractSpringIntegrationIndependentTest { +class AuxiliaryRepositoryServiceTest extends AbstractProgrammingIntegrationIndependentTest { private static final String TEST_INVALID_LENGTH_STRING = "a".repeat(AuxiliaryRepository.MAX_NAME_LENGTH + 1); private static final String TEST_INVALID_DESCRIPTION_LENGTH_STRING = "a".repeat(AuxiliaryRepository.MAX_DESCRIPTION_LENGTH + 1); - @Autowired - private AuxiliaryRepositoryRepository auxiliaryRepositoryRepository; - - @Autowired - private AuxiliaryRepositoryService auxiliaryRepositoryService; - - @Autowired - private ExerciseUtilService exerciseUtilService; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - private static ProgrammingExercise programmingExerciseBeforeUpdate; private static ProgrammingExercise updatedProgrammingExercise; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/BuildPlanIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/BuildPlanIntegrationTest.java index adab0997cd23..82809c5875e2 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/BuildPlanIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/BuildPlanIntegrationTest.java @@ -5,7 +5,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; @@ -14,28 +13,11 @@ import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.ProjectType; import de.tum.cit.aet.artemis.programming.domain.build.BuildPlan; -import de.tum.cit.aet.artemis.programming.repository.BuildPlanRepository; -import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; -class BuildPlanIntegrationTest extends AbstractSpringIntegrationJenkinsGitlabTest { +class BuildPlanIntegrationTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { private static final String TEST_PREFIX = "buildplanintegration"; - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository; - - @Autowired - private BuildPlanRepository buildPlanRepository; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - private ProgrammingExercise programmingExercise; @BeforeEach diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ConsistencyCheckGitlabJenkinsIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ConsistencyCheckGitlabJenkinsIntegrationTest.java index 38b1447bc645..5f11ce522fc4 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ConsistencyCheckGitlabJenkinsIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ConsistencyCheckGitlabJenkinsIntegrationTest.java @@ -3,16 +3,9 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.test.context.support.WithMockUser; -import de.tum.cit.aet.artemis.programming.service.ConsistencyCheckTestService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; - -class ConsistencyCheckGitlabJenkinsIntegrationTest extends AbstractSpringIntegrationJenkinsGitlabTest { - - @Autowired - private ConsistencyCheckTestService consistencyCheckTestService; +class ConsistencyCheckGitlabJenkinsIntegrationTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { @BeforeEach void setup() throws Exception { diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/CourseGitlabJenkinsIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/CourseGitlabJenkinsIntegrationTest.java index f23887c80d43..ffe1a7bf34a6 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/CourseGitlabJenkinsIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/CourseGitlabJenkinsIntegrationTest.java @@ -13,37 +13,18 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.servlet.MvcResult; -import com.fasterxml.jackson.databind.ObjectMapper; - import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.domain.User; import de.tum.cit.aet.artemis.core.util.CourseFactory; -import de.tum.cit.aet.artemis.core.util.CourseTestService; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; -class CourseGitlabJenkinsIntegrationTest extends AbstractSpringIntegrationJenkinsGitlabTest { +class CourseGitlabJenkinsIntegrationTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { private static final String TEST_PREFIX = "courseegitlabjenkins"; - @Autowired - private CourseTestService courseTestService; - - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ObjectMapper objectMapper; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - @BeforeEach void setup() { courseTestService.setup(TEST_PREFIX, this); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/GitServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/GitServiceTest.java index f03dc054e698..123d6cf796d3 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/GitServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/GitServiceTest.java @@ -31,7 +31,6 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.beans.factory.annotation.Autowired; import de.tum.cit.aet.artemis.core.exception.GitException; import de.tum.cit.aet.artemis.core.user.util.UserFactory; @@ -39,12 +38,8 @@ import de.tum.cit.aet.artemis.programming.domain.FileType; import de.tum.cit.aet.artemis.programming.domain.Repository; import de.tum.cit.aet.artemis.programming.util.GitUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; -class GitServiceTest extends AbstractSpringIntegrationIndependentTest { - - @Autowired - private GitUtilService gitUtilService; +class GitServiceTest extends AbstractProgrammingIntegrationIndependentTest { private static final String TEST_PREFIX = "gitservice"; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/GitlabServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/GitlabServiceTest.java index 8e857fed08c2..6d220e121508 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/GitlabServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/GitlabServiceTest.java @@ -8,7 +8,6 @@ import static org.mockito.Mockito.verify; import java.net.URISyntaxException; -import java.net.URL; import java.util.Optional; import java.util.stream.Stream; @@ -20,8 +19,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; @@ -33,24 +30,8 @@ import de.tum.cit.aet.artemis.programming.domain.Commit; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.VcsRepositoryUri; -import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; -class GitlabServiceTest extends AbstractSpringIntegrationJenkinsGitlabTest { - - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Value("${artemis.version-control.url}") - private URL gitlabServerUrl; +class GitlabServiceTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { @BeforeEach void initTestCase() { diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/IdePreferencesIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/IdePreferencesIntegrationTest.java index 888424193c1e..acec92af2f2b 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/IdePreferencesIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/IdePreferencesIntegrationTest.java @@ -7,7 +7,6 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.util.LinkedMultiValueMap; @@ -19,20 +18,11 @@ import de.tum.cit.aet.artemis.programming.domain.ide.UserIdeMapping; import de.tum.cit.aet.artemis.programming.dto.IdeDTO; import de.tum.cit.aet.artemis.programming.dto.IdeMappingDTO; -import de.tum.cit.aet.artemis.programming.repository.settings.IdeRepository; -import de.tum.cit.aet.artemis.programming.repository.settings.UserIdeMappingRepository; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; -class IdePreferencesIntegrationTest extends AbstractSpringIntegrationIndependentTest { +class IdePreferencesIntegrationTest extends AbstractProgrammingIntegrationIndependentTest { private static final String TEST_PREFIX = "idepreferencesintegration"; - @Autowired - private UserIdeMappingRepository userIdeMappingRepository; - - @Autowired - private IdeRepository ideRepository; - private Ide VsCode; private Ide IntelliJ; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/PlantUmlIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/PlantUmlIntegrationTest.java index 304ab40f1351..58e167265d15 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/PlantUmlIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/PlantUmlIntegrationTest.java @@ -17,11 +17,10 @@ import org.springframework.security.test.context.support.WithMockUser; import org.springframework.util.LinkedMultiValueMap; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; import net.sourceforge.plantuml.SourceStringReader; import net.sourceforge.plantuml.core.DiagramDescription; -class PlantUmlIntegrationTest extends AbstractSpringIntegrationIndependentTest { +class PlantUmlIntegrationTest extends AbstractProgrammingIntegrationIndependentTest { private static final String TEST_PREFIX = "plantumlintegration"; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingAssessmentIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingAssessmentIntegrationTest.java index b39e0c0bc704..137bbab18fb0 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingAssessmentIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingAssessmentIntegrationTest.java @@ -20,7 +20,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.util.LinkedMultiValueMap; @@ -34,16 +33,12 @@ import de.tum.cit.aet.artemis.assessment.domain.LongFeedbackText; import de.tum.cit.aet.artemis.assessment.domain.Result; import de.tum.cit.aet.artemis.assessment.dto.AssessmentUpdateDTO; -import de.tum.cit.aet.artemis.assessment.repository.ComplaintRepository; -import de.tum.cit.aet.artemis.assessment.util.ComplaintUtilService; import de.tum.cit.aet.artemis.core.config.Constants; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.domain.User; import de.tum.cit.aet.artemis.core.util.TestResourceUtils; import de.tum.cit.aet.artemis.exam.domain.Exam; import de.tum.cit.aet.artemis.exam.domain.ExerciseGroup; -import de.tum.cit.aet.artemis.exam.repository.ExamRepository; -import de.tum.cit.aet.artemis.exam.util.ExamUtilService; import de.tum.cit.aet.artemis.exercise.domain.Exercise; import de.tum.cit.aet.artemis.exercise.domain.IncludedInOverallScore; import de.tum.cit.aet.artemis.exercise.domain.InitializationState; @@ -51,21 +46,13 @@ import de.tum.cit.aet.artemis.exercise.domain.SubmissionType; import de.tum.cit.aet.artemis.exercise.domain.participation.StudentParticipation; import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationFactory; -import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; -import de.tum.cit.aet.artemis.exercise.test_repository.StudentParticipationTestRepository; -import de.tum.cit.aet.artemis.exercise.test_repository.SubmissionTestRepository; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; import de.tum.cit.aet.artemis.programming.dto.ResultDTO; -import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingSubmissionTestRepository; import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseFactory; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; -class ProgrammingAssessmentIntegrationTest extends AbstractSpringIntegrationIndependentTest { +class ProgrammingAssessmentIntegrationTest extends AbstractProgrammingIntegrationIndependentTest { private static final String TEST_PREFIX = "programmingassessment"; @@ -73,39 +60,6 @@ class ProgrammingAssessmentIntegrationTest extends AbstractSpringIntegrationInde private final Double offsetByTenThousandth = 0.0001; - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository; - - @Autowired - private ComplaintRepository complaintRepo; - - @Autowired - private ProgrammingSubmissionTestRepository programmingSubmissionRepository; - - @Autowired - private ExamRepository examRepository; - - @Autowired - private StudentParticipationTestRepository studentParticipationRepository; - - @Autowired - private SubmissionTestRepository submissionRepository; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ParticipationUtilService participationUtilService; - - @Autowired - private ExamUtilService examUtilService; - - @Autowired - private ComplaintUtilService complaintUtilService; - private ProgrammingExercise programmingExercise; private ProgrammingSubmission programmingSubmission; @@ -1011,7 +965,7 @@ void overrideProgrammingAssessmentAfterComplaint() throws Exception { void unlockFeedbackRequestAfterAssessment() throws Exception { programmingExercise.setAllowFeedbackRequests(true); programmingExercise.setDueDate(ZonedDateTime.now().plusDays(1)); - exerciseRepository.save(programmingExercise); + programmingExerciseRepository.save(programmingExercise); var participation = programmingExerciseStudentParticipation; participation.setIndividualDueDate(ZonedDateTime.now().minusDays(1)); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseBuildPlanTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseBuildPlanTest.java index 074fe70e54b9..0bde4cd2fa51 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseBuildPlanTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseBuildPlanTest.java @@ -4,23 +4,12 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.util.LinkedMultiValueMap; -import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationGitlabCIGitlabSamlTest; -class ProgrammingExerciseBuildPlanTest extends AbstractSpringIntegrationGitlabCIGitlabSamlTest { - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ExerciseUtilService exerciseUtilService; +class ProgrammingExerciseBuildPlanTest extends AbstractProgrammingIntegrationGitlabCIGitlabSamlTest { private static final String BUILD_PLAN = """ image: ubuntu:20.04 @@ -32,9 +21,6 @@ class ProgrammingExerciseBuildPlanTest extends AbstractSpringIntegrationGitlabCI - echo "Test" """; - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - private Long programmingExerciseId; @BeforeEach diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseGitIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseGitIntegrationTest.java index 0fb13f48c51a..5882a3517915 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseGitIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseGitIntegrationTest.java @@ -22,39 +22,21 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.core.exception.EntityNotFoundException; -import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.VcsRepositoryUri; import de.tum.cit.aet.artemis.programming.service.GitService; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.programming.util.GitUtilService; import de.tum.cit.aet.artemis.programming.util.LocalRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; -class ProgrammingExerciseGitIntegrationTest extends AbstractSpringIntegrationIndependentTest { +class ProgrammingExerciseGitIntegrationTest extends AbstractProgrammingIntegrationIndependentTest { private static final String TEST_PREFIX = "progexgitintegration"; private static final String COMBINE_COMMITS_ENDPOINT = "/api/programming-exercises/{exerciseId}/combine-template-commits"; - @Autowired - private GitUtilService gitUtilService; - - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ParticipationUtilService participationUtilService; - private File localRepoFile; private Git localGit; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseGitlabJenkinsIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseGitlabJenkinsIntegrationTest.java index 8918d1190bef..53ff9474ac5c 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseGitlabJenkinsIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseGitlabJenkinsIntegrationTest.java @@ -32,7 +32,6 @@ import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.MockedStatic; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; @@ -44,20 +43,11 @@ import de.tum.cit.aet.artemis.exercise.domain.SubmissionType; import de.tum.cit.aet.artemis.programming.domain.AeolusTarget; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; -import de.tum.cit.aet.artemis.programming.service.ProgrammingLanguageFeatureService; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseTestService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; -class ProgrammingExerciseGitlabJenkinsIntegrationTest extends AbstractSpringIntegrationJenkinsGitlabTest { +class ProgrammingExerciseGitlabJenkinsIntegrationTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { private static final String TEST_PREFIX = "progexgitlabjenkins"; - @Autowired - private ProgrammingExerciseTestService programmingExerciseTestService; - - @Autowired - private ProgrammingLanguageFeatureService programmingLanguageFeatureService; - @BeforeEach void setup() throws Exception { programmingExerciseTestService.setupTestUsers(TEST_PREFIX, 0, 0, 0, 0); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseGradingServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseGradingServiceTest.java index b38a7248ae5f..2993b02caccb 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseGradingServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseGradingServiceTest.java @@ -23,7 +23,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.test.context.TestSecurityContextHolder; @@ -36,19 +35,12 @@ import de.tum.cit.aet.artemis.assessment.domain.Result; import de.tum.cit.aet.artemis.assessment.domain.Visibility; import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.core.user.util.UserUtilService; -import de.tum.cit.aet.artemis.core.util.CourseUtilService; import de.tum.cit.aet.artemis.core.util.RoundingUtil; import de.tum.cit.aet.artemis.exam.domain.Exam; import de.tum.cit.aet.artemis.exam.domain.ExerciseGroup; -import de.tum.cit.aet.artemis.exam.repository.ExamRepository; -import de.tum.cit.aet.artemis.exam.util.ExamUtilService; import de.tum.cit.aet.artemis.exercise.domain.Exercise; import de.tum.cit.aet.artemis.exercise.domain.participation.Participation; import de.tum.cit.aet.artemis.exercise.domain.participation.StudentParticipation; -import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; -import de.tum.cit.aet.artemis.exercise.test_repository.StudentParticipationTestRepository; -import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; @@ -56,14 +48,8 @@ import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; import de.tum.cit.aet.artemis.programming.domain.SolutionProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.dto.ProgrammingExerciseGradingStatisticsDTO; -import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; -import de.tum.cit.aet.artemis.programming.repository.StaticCodeAnalysisCategoryRepository; import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseGradingService; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseFactory; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; /** * Tests the {@link ProgrammingExerciseGradingService}. @@ -76,49 +62,10 @@ *
  • {@link ExamProgrammingExerciseGradingServiceTest} - for exercises in an exam setting.
  • * */ -abstract class ProgrammingExerciseGradingServiceTest extends AbstractSpringIntegrationIndependentTest { +abstract class ProgrammingExerciseGradingServiceTest extends AbstractProgrammingIntegrationIndependentTest { private static final String TEST_PREFIX = "progexgradingservice"; - @Autowired - private ProgrammingExerciseTestCaseTestRepository testCaseRepository; - - @Autowired - private StudentParticipationTestRepository studentParticipationRepository; - - @Autowired - private StaticCodeAnalysisCategoryRepository staticCodeAnalysisCategoryRepository; - - @Autowired - private ExamRepository examRepository; - - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository; - - @Autowired - private ProgrammingExerciseGradingService gradingService; - - @Autowired - private UserUtilService userUtilService; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ParticipationUtilService participationUtilService; - - @Autowired - private CourseUtilService courseUtilService; - - @Autowired - private ExerciseUtilService exerciseUtilService; - - @Autowired - private ExamUtilService examUtilService; - private ProgrammingExercise programmingExerciseSCAEnabled; private ProgrammingExercise programmingExercise; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationJenkinsGitlabTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationJenkinsGitlabTest.java index 6906106f8fac..5812c2923e0e 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationJenkinsGitlabTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationJenkinsGitlabTest.java @@ -15,26 +15,17 @@ import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.util.LinkedMultiValueMap; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; -import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseService; import de.tum.cit.aet.artemis.programming.util.ArgumentSources; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; -class ProgrammingExerciseIntegrationJenkinsGitlabTest extends AbstractSpringIntegrationJenkinsGitlabTest { +class ProgrammingExerciseIntegrationJenkinsGitlabTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { private static final String TEST_PREFIX = "progexjenkgitlab"; - @Autowired - private ProgrammingExerciseIntegrationTestService programmingExerciseIntegrationTestService; - - @Autowired - private ProgrammingExerciseService programmingExerciseService; - @BeforeEach void initTestCase() throws Exception { gitlabRequestMockProvider.enableMockingOfRequests(); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationTestService.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationTestService.java index 49696af61d96..23739bdaacd3 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationTestService.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationTestService.java @@ -128,7 +128,7 @@ * 1) Jenkins + Gitlab */ @Service -class ProgrammingExerciseIntegrationTestService { +public class ProgrammingExerciseIntegrationTestService { private static final String NON_EXISTING_ID = Integer.toString(Integer.MAX_VALUE); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java index 1e47b62e649b..843cb7e0f378 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java @@ -25,7 +25,6 @@ import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.util.LinkedMultiValueMap; @@ -37,9 +36,6 @@ import de.tum.cit.aet.artemis.exercise.domain.Submission; import de.tum.cit.aet.artemis.exercise.domain.participation.Participation; import de.tum.cit.aet.artemis.exercise.domain.participation.StudentParticipation; -import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; -import de.tum.cit.aet.artemis.exercise.test_repository.ParticipationTestRepository; -import de.tum.cit.aet.artemis.exercise.test_repository.StudentParticipationTestRepository; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; @@ -49,13 +45,8 @@ import de.tum.cit.aet.artemis.programming.domain.TemplateProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.domain.VcsRepositoryUri; import de.tum.cit.aet.artemis.programming.dto.CommitInfoDTO; -import de.tum.cit.aet.artemis.programming.repository.AuxiliaryRepositoryRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseStudentParticipationTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; -class ProgrammingExerciseParticipationIntegrationTest extends AbstractSpringIntegrationIndependentTest { +class ProgrammingExerciseParticipationIntegrationTest extends AbstractProgrammingIntegrationIndependentTest { private static final String TEST_PREFIX = "programmingexerciseparticipation"; @@ -63,31 +54,6 @@ class ProgrammingExerciseParticipationIntegrationTest extends AbstractSpringInte private final String exercisesBaseUrl = "/api/programming-exercises/"; - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private StudentParticipationTestRepository studentParticipationRepository; - - @Autowired - private ParticipationTestRepository participationRepository; - - @Autowired - private ProgrammingExerciseStudentParticipationTestRepository programmingExerciseStudentParticipationRepository; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ParticipationUtilService participationUtilService; - - @Autowired - private AuxiliaryRepositoryRepository auxiliaryRepositoryRepository; - - // TODO remove again after refactoring and cleanup - @Autowired - private ProgrammingExerciseIntegrationTestService programmingExerciseIntegrationTestService; - private ProgrammingExercise programmingExercise; private Participation programmingExerciseParticipation; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseRepositoryServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseRepositoryServiceTest.java index c045fe08c76b..1a7f91039e94 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseRepositoryServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseRepositoryServiceTest.java @@ -10,40 +10,14 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Autowired; -import de.tum.cit.aet.artemis.core.user.util.UserUtilService; -import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; -import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; -import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseRepositoryService; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; -class ProgrammingExerciseRepositoryServiceTest extends AbstractSpringIntegrationIndependentTest { +class ProgrammingExerciseRepositoryServiceTest extends AbstractProgrammingIntegrationIndependentTest { private static final String TEST_PREFIX = "progexreposervice"; - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ProgrammingExerciseRepositoryService programmingExerciseRepositoryService; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ExerciseUtilService exerciseUtilService; - - @Autowired - private ParticipationUtilService participationUtilService; - - @Autowired - private UserUtilService userUtilService; - private ProgrammingExercise programmingExerciseBeforeUpdate; private ProgrammingExercise updatedProgrammingExercise; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseResultJenkinsIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseResultJenkinsIntegrationTest.java index 7308c6a6dae5..9e0650403412 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseResultJenkinsIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseResultJenkinsIntegrationTest.java @@ -16,7 +16,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.mockito.ArgumentMatchers; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.core.config.Constants; @@ -25,16 +24,11 @@ import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.service.ci.notification.dto.CommitDTO; import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseFactory; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseResultTestService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; -class ProgrammingExerciseResultJenkinsIntegrationTest extends AbstractSpringIntegrationJenkinsGitlabTest { +class ProgrammingExerciseResultJenkinsIntegrationTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { private static final String TEST_PREFIX = "progexresultjenk"; - @Autowired - private ProgrammingExerciseResultTestService programmingExerciseResultTestService; - @BeforeEach void setup() { programmingExerciseResultTestService.setup(TEST_PREFIX); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseScheduleServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseScheduleServiceTest.java index 3fd82c87864c..b386fb22d033 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseScheduleServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseScheduleServiceTest.java @@ -25,73 +25,27 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.InOrder; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.assessment.domain.AssessmentType; import de.tum.cit.aet.artemis.assessment.domain.Visibility; import de.tum.cit.aet.artemis.core.config.Constants; import de.tum.cit.aet.artemis.core.domain.User; -import de.tum.cit.aet.artemis.core.service.messaging.InstanceMessageReceiveService; -import de.tum.cit.aet.artemis.core.user.util.UserUtilService; import de.tum.cit.aet.artemis.exam.domain.Exam; import de.tum.cit.aet.artemis.exam.domain.StudentExam; -import de.tum.cit.aet.artemis.exam.repository.ExamRepository; -import de.tum.cit.aet.artemis.exam.test_repository.StudentExamTestRepository; -import de.tum.cit.aet.artemis.exam.util.ExamUtilService; import de.tum.cit.aet.artemis.exercise.domain.ExerciseLifecycle; import de.tum.cit.aet.artemis.exercise.domain.participation.StudentParticipation; -import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; -import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; import de.tum.cit.aet.artemis.programming.domain.ParticipationLifecycle; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; import de.tum.cit.aet.artemis.programming.domain.VcsRepositoryUri; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseStudentParticipationTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; import de.tum.cit.aet.artemis.programming.util.LocalRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationGitlabCIGitlabSamlTest; -class ProgrammingExerciseScheduleServiceTest extends AbstractSpringIntegrationGitlabCIGitlabSamlTest { +class ProgrammingExerciseScheduleServiceTest extends AbstractProgrammingIntegrationGitlabCIGitlabSamlTest { private static final String TEST_PREFIX = "programmingexercisescheduleservice"; - @Autowired - private InstanceMessageReceiveService instanceMessageReceiveService; - - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ProgrammingExerciseStudentParticipationTestRepository participationRepository; - - @Autowired - private ProgrammingExerciseTestCaseTestRepository programmingExerciseTestCaseRepository; - - @Autowired - private ExamRepository examRepository; - - @Autowired - private StudentExamTestRepository studentExamRepository; - - @Autowired - private UserUtilService userUtilService; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ExerciseUtilService exerciseUtilService; - - @Autowired - private ParticipationUtilService participationUtilService; - - @Autowired - private ExamUtilService examUtilService; - private ProgrammingExercise programmingExercise; private final LocalRepository studentRepository = new LocalRepository(defaultBranch); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseServiceIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseServiceIntegrationTest.java index 4ab18f1ce22a..e32956ac6bd8 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseServiceIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseServiceIntegrationTest.java @@ -12,13 +12,10 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.core.util.PageableSearchUtilService; -import de.tum.cit.aet.artemis.exercise.util.ExerciseIntegrationTestService; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; import de.tum.cit.aet.artemis.programming.domain.StaticCodeAnalysisCategory; @@ -27,33 +24,14 @@ import de.tum.cit.aet.artemis.programming.domain.submissionpolicy.LockRepositoryPolicy; import de.tum.cit.aet.artemis.programming.domain.submissionpolicy.SubmissionPenaltyPolicy; import de.tum.cit.aet.artemis.programming.domain.submissionpolicy.SubmissionPolicy; -import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseImportBasicService; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseFactory; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationLocalCILocalVCTest; -class ProgrammingExerciseServiceIntegrationTest extends AbstractSpringIntegrationLocalCILocalVCTest { +class ProgrammingExerciseServiceIntegrationTest extends AbstractProgrammingIntegrationLocalCILocalVCTest { private static final String TEST_PREFIX = "progexserviceintegration"; private static final String BASE_RESOURCE = "/api/programming-exercises"; - @Autowired - ProgrammingExerciseImportBasicService programmingExerciseImportBasicService; - - @Autowired - ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ExerciseIntegrationTestService exerciseIntegrationTestService; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private PageableSearchUtilService pageableSearchUtilService; - private Course additionalEmptyCourse; private ProgrammingExercise programmingExercise; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseServiceTest.java index 028ac2405428..353461ecf77a 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseServiceTest.java @@ -7,35 +7,14 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.test.context.support.WithMockUser; -import de.tum.cit.aet.artemis.core.user.util.UserUtilService; -import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; -class ProgrammingExerciseServiceTest extends AbstractSpringIntegrationIndependentTest { +class ProgrammingExerciseServiceTest extends AbstractProgrammingIntegrationIndependentTest { private static final String TEST_PREFIX = "progexservice"; - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private UserUtilService userUtilService; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ExerciseUtilService exerciseUtilService; - - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseTestRepository; - private ProgrammingExercise programmingExercise1; private ProgrammingExercise programmingExercise2; @@ -63,7 +42,7 @@ void shouldFindProgrammingExerciseWithBuildAndTestDateInFuture() { programmingExercise2.setBuildAndTestStudentSubmissionsAfterDueDate(ZonedDateTime.now().minusHours(1)); programmingExerciseRepository.save(programmingExercise2); - List programmingExercises = programmingExerciseTestRepository.findAllWithBuildAndTestAfterDueDateInFuture(); + List programmingExercises = programmingExerciseRepository.findAllWithBuildAndTestAfterDueDateInFuture(); assertThat(programmingExercises).contains(programmingExercise1).doesNotContain(programmingExercise2); } } diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseTemplateIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseTemplateIntegrationTest.java index 618437fd78b9..909814b1e829 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseTemplateIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseTemplateIntegrationTest.java @@ -47,7 +47,6 @@ import org.junit.jupiter.params.provider.MethodSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; @@ -55,14 +54,11 @@ import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.ProjectType; -import de.tum.cit.aet.artemis.programming.service.ProgrammingLanguageFeatureService; import de.tum.cit.aet.artemis.programming.util.LocalRepository; import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseFactory; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseTestService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; @TestInstance(TestInstance.Lifecycle.PER_CLASS) -class ProgrammingExerciseTemplateIntegrationTest extends AbstractSpringIntegrationJenkinsGitlabTest { +class ProgrammingExerciseTemplateIntegrationTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { private static final Logger log = LoggerFactory.getLogger(ProgrammingExerciseTemplateIntegrationTest.class); @@ -70,12 +66,6 @@ class ProgrammingExerciseTemplateIntegrationTest extends AbstractSpringIntegrati private static File java17Home; - @Autowired - private ProgrammingExerciseTestService programmingExerciseTestService; - - @Autowired - private ProgrammingLanguageFeatureService programmingLanguageFeatureService; - private ProgrammingExercise exercise; private final LocalRepository exerciseRepo = new LocalRepository(defaultBranch); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseTest.java index 587478746a89..0679190ab7b9 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseTest.java @@ -18,64 +18,30 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.assessment.domain.AssessmentType; import de.tum.cit.aet.artemis.communication.domain.conversation.Channel; -import de.tum.cit.aet.artemis.communication.repository.conversation.ChannelRepository; import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.core.user.util.UserUtilService; import de.tum.cit.aet.artemis.exam.domain.StudentExam; -import de.tum.cit.aet.artemis.exam.util.ExamUtilService; import de.tum.cit.aet.artemis.exercise.domain.Exercise; import de.tum.cit.aet.artemis.exercise.domain.InitializationState; import de.tum.cit.aet.artemis.exercise.domain.Submission; import de.tum.cit.aet.artemis.exercise.domain.SubmissionType; import de.tum.cit.aet.artemis.exercise.domain.participation.StudentParticipation; -import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseStudentParticipationTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; -class ProgrammingExerciseTest extends AbstractSpringIntegrationJenkinsGitlabTest { +class ProgrammingExerciseTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { private static final String TEST_PREFIX = "peinttest"; - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ProgrammingExerciseTestCaseTestRepository programmingExerciseTestCaseRepository; - - @Autowired - private ProgrammingExerciseStudentParticipationTestRepository participationRepository; - - @Autowired - private UserUtilService userUtilService; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ExerciseUtilService exerciseUtilService; - - @Autowired - private ExamUtilService examUtilService; - private Long programmingExerciseId; - @Autowired - private ChannelRepository channelRepository; - @BeforeEach void init() { userUtilService.addUsers(TEST_PREFIX, 2, 2, 0, 2); @@ -170,7 +136,7 @@ void updateExerciseAutomaticFeedbackNoTestCases() throws Exception { ProgrammingExercise programmingExercise = programmingExerciseRepository .findWithTemplateAndSolutionParticipationTeamAssignmentConfigCategoriesAndBuildConfigById(programmingExerciseId).orElseThrow(); - Set testCases = programmingExerciseTestCaseRepository.findByExerciseId(programmingExercise.getId()); + Set testCases = testCaseRepository.findByExerciseId(programmingExercise.getId()); assertThat(testCases).isEmpty(); // no test cases, changing to automatic feedback: update should work @@ -198,9 +164,9 @@ void updateExerciseTestCasesZeroWeight(AssessmentType assessmentType) throws Exc .findWithTemplateAndSolutionParticipationTeamAssignmentConfigCategoriesAndBuildConfigById(programmingExerciseId).orElseThrow(); programmingExerciseUtilService.addTestCasesToProgrammingExercise(programmingExercise); - Set testCases = programmingExerciseTestCaseRepository.findByExerciseId(programmingExercise.getId()); + Set testCases = testCaseRepository.findByExerciseId(programmingExercise.getId()); testCases.forEach(testCase -> testCase.setWeight(0D)); - programmingExerciseTestCaseRepository.saveAll(testCases); + testCaseRepository.saveAll(testCases); programmingExercise.setAssessmentType(assessmentType); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseTestCaseServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseTestCaseServiceTest.java index cf293bf0740c..504d895a9200 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseTestCaseServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseTestCaseServiceTest.java @@ -17,55 +17,21 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.assessment.domain.AssessmentType; import de.tum.cit.aet.artemis.assessment.domain.Visibility; import de.tum.cit.aet.artemis.core.security.SecurityUtils; -import de.tum.cit.aet.artemis.core.user.util.UserUtilService; import de.tum.cit.aet.artemis.exam.domain.ExerciseGroup; -import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; -import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; import de.tum.cit.aet.artemis.programming.dto.ProgrammingExerciseTestCaseDTO; -import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseFeedbackCreationService; -import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseTestCaseService; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseFactory; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationLocalCILocalVCTest; -class ProgrammingExerciseTestCaseServiceTest extends AbstractSpringIntegrationLocalCILocalVCTest { +class ProgrammingExerciseTestCaseServiceTest extends AbstractProgrammingIntegrationLocalCILocalVCTest { private static final String TEST_PREFIX = "progextestcase"; - @Autowired - private ProgrammingExerciseTestCaseTestRepository testCaseRepository; - - @Autowired - private ProgrammingExerciseTestCaseService testCaseService; - - @Autowired - private ProgrammingExerciseFeedbackCreationService feedbackCreationService; - - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private UserUtilService userUtilService; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ExerciseUtilService exerciseUtilService; - - @Autowired - private ParticipationUtilService participationUtilService; - private ProgrammingExercise programmingExercise; @BeforeEach diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionAndResultGitlabJenkinsIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionAndResultGitlabJenkinsIntegrationTest.java index 354a3e9b65c5..102643f76237 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionAndResultGitlabJenkinsIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionAndResultGitlabJenkinsIntegrationTest.java @@ -16,8 +16,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; @@ -29,55 +27,23 @@ import de.tum.cit.aet.artemis.assessment.domain.Result; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.security.SecurityUtils; -import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; import de.tum.cit.aet.artemis.programming.domain.ProjectType; import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry; -import de.tum.cit.aet.artemis.programming.repository.BuildLogStatisticsEntryRepository; -import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; import de.tum.cit.aet.artemis.programming.service.ci.notification.dto.CommitDTO; import de.tum.cit.aet.artemis.programming.service.ci.notification.dto.TestCaseDTO; import de.tum.cit.aet.artemis.programming.service.ci.notification.dto.TestCaseDetailMessageDTO; import de.tum.cit.aet.artemis.programming.service.ci.notification.dto.TestResultsDTO; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingSubmissionTestRepository; import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseFactory; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.programming.util.ProgrammingSubmissionAndResultIntegrationTestService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; -class ProgrammingSubmissionAndResultGitlabJenkinsIntegrationTest extends AbstractSpringIntegrationJenkinsGitlabTest { +class ProgrammingSubmissionAndResultGitlabJenkinsIntegrationTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { private static final String TEST_PREFIX = "progsubresgitlabjen"; - @Value("${artemis.continuous-integration.artemis-authentication-token-value}") - private String ARTEMIS_AUTHENTICATION_TOKEN_VALUE; - - @Autowired - private ProgrammingSubmissionTestRepository submissionRepository; - - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository; - - @Autowired - private BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ParticipationUtilService participationUtilService; - private ProgrammingExercise exercise; - @Autowired - private ProgrammingSubmissionAndResultIntegrationTestService testService; - @BeforeEach void setUp() { jenkinsRequestMockProvider.enableMockingOfRequests(jenkinsServer); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java index 4d53882cd461..6e6ba01f1c5c 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java @@ -27,8 +27,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; @@ -50,53 +48,18 @@ import de.tum.cit.aet.artemis.exercise.domain.participation.StudentParticipation; import de.tum.cit.aet.artemis.exercise.dto.SubmissionDTO; import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationFactory; -import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; -import de.tum.cit.aet.artemis.exercise.test_repository.StudentParticipationTestRepository; import de.tum.cit.aet.artemis.modeling.domain.ModelingExercise; import de.tum.cit.aet.artemis.modeling.domain.ModelingSubmission; -import de.tum.cit.aet.artemis.modeling.util.ModelingExerciseUtilService; import de.tum.cit.aet.artemis.programming.domain.Commit; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseStudentParticipationTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingSubmissionTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; -class ProgrammingSubmissionIntegrationTest extends AbstractSpringIntegrationJenkinsGitlabTest { +class ProgrammingSubmissionIntegrationTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { private static final String TEST_PREFIX = "programmingsubmission"; - @Value("${artemis.git.name}") - private String artemisGitName; - - @Value("${artemis.git.email}") - private String artemisGitEmail; - - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ProgrammingSubmissionTestRepository submissionRepository; - - @Autowired - private ProgrammingExerciseStudentParticipationTestRepository programmingExerciseStudentParticipationRepository; - - @Autowired - private StudentParticipationTestRepository studentParticipationRepository; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ParticipationUtilService participationUtilService; - - @Autowired - private ModelingExerciseUtilService modelingExerciseUtilService; - private ProgrammingExercise exercise; private ProgrammingExerciseStudentParticipation programmingExerciseStudentParticipation; @@ -394,7 +357,7 @@ void triggerFailedBuildResultPresentInCIOk() throws Exception { submission.setCommitHash(TestConstants.COMMIT_HASH_STRING); submission.setType(SubmissionType.MANUAL); submission = programmingExerciseUtilService.addProgrammingSubmission(exercise, submission, TEST_PREFIX + "student1"); - var optionalParticipation = programmingExerciseStudentParticipationRepository.findById(submission.getParticipation().getId()); + var optionalParticipation = participationRepository.findById(submission.getParticipation().getId()); assertThat(optionalParticipation).isPresent(); final var participation = optionalParticipation.get(); jenkinsRequestMockProvider.enableMockingOfRequests(jenkinsServer); @@ -446,11 +409,11 @@ private ProgrammingExerciseStudentParticipation createExerciseWithSubmissionAndP var submission = new ProgrammingSubmission(); submission.setType(SubmissionType.MANUAL); submission = programmingExerciseUtilService.addProgrammingSubmission(exercise, submission, user.getLogin()); - var optionalParticipation = programmingExerciseStudentParticipationRepository.findById(submission.getParticipation().getId()); + var optionalParticipation = participationRepository.findById(submission.getParticipation().getId()); assertThat(optionalParticipation).isPresent(); var participation = optionalParticipation.get(); participation.setBuildPlanId(null); - participation = programmingExerciseStudentParticipationRepository.save(participation); + participation = participationRepository.save(participation); return participation; } @@ -842,7 +805,7 @@ void testGetProgrammingSubmissionWithoutAssessmentWithIndividualDueDate(boolean else { submission.getParticipation().setIndividualDueDate(ZonedDateTime.now().minusDays(1)); } - programmingExerciseStudentParticipationRepository.save((ProgrammingExerciseStudentParticipation) submission.getParticipation()); + participationRepository.save((ProgrammingExerciseStudentParticipation) submission.getParticipation()); participationUtilService.addResultToSubmission(submission, AssessmentType.AUTOMATIC, null); String url = "/api/exercises/" + exercise.getId() + "/programming-submission-without-assessment"; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/RepositoryIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/RepositoryIntegrationTest.java index cc602c83f30d..64307de24282 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/RepositoryIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/RepositoryIntegrationTest.java @@ -43,7 +43,6 @@ import org.mockito.MockedStatic; import org.mockito.stubbing.Answer; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.util.LinkedMultiValueMap; @@ -54,25 +53,17 @@ import ch.qos.logback.core.read.ListAppender; import de.tum.cit.aet.artemis.assessment.domain.AssessmentType; import de.tum.cit.aet.artemis.communication.domain.Post; -import de.tum.cit.aet.artemis.communication.test_repository.PostTestRepository; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.util.TestConstants; import de.tum.cit.aet.artemis.exam.domain.Exam; -import de.tum.cit.aet.artemis.exam.repository.ExamRepository; -import de.tum.cit.aet.artemis.exam.test_repository.StudentExamTestRepository; -import de.tum.cit.aet.artemis.exam.util.ExamUtilService; import de.tum.cit.aet.artemis.exercise.domain.InitializationState; import de.tum.cit.aet.artemis.exercise.domain.SubmissionType; import de.tum.cit.aet.artemis.exercise.domain.participation.StudentParticipation; -import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; -import de.tum.cit.aet.artemis.exercise.test_repository.StudentParticipationTestRepository; import de.tum.cit.aet.artemis.plagiarism.domain.PlagiarismCase; import de.tum.cit.aet.artemis.plagiarism.domain.PlagiarismComparison; import de.tum.cit.aet.artemis.plagiarism.domain.PlagiarismStatus; import de.tum.cit.aet.artemis.plagiarism.domain.PlagiarismSubmission; import de.tum.cit.aet.artemis.plagiarism.domain.text.TextSubmissionElement; -import de.tum.cit.aet.artemis.plagiarism.repository.PlagiarismCaseRepository; -import de.tum.cit.aet.artemis.plagiarism.repository.PlagiarismComparisonRepository; import de.tum.cit.aet.artemis.programming.domain.FileType; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; @@ -81,20 +72,14 @@ import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry; import de.tum.cit.aet.artemis.programming.dto.FileMove; import de.tum.cit.aet.artemis.programming.dto.RepositoryStatusDTO; -import de.tum.cit.aet.artemis.programming.service.BuildLogEntryService; import de.tum.cit.aet.artemis.programming.service.GitService; import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseParticipationService; import de.tum.cit.aet.artemis.programming.service.vcs.VersionControlRepositoryPermission; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseStudentParticipationTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; import de.tum.cit.aet.artemis.programming.util.GitUtilService; import de.tum.cit.aet.artemis.programming.util.LocalRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; import de.tum.cit.aet.artemis.programming.web.repository.FileSubmission; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; -import de.tum.cit.aet.artemis.text.util.TextExerciseUtilService; -class RepositoryIntegrationTest extends AbstractSpringIntegrationJenkinsGitlabTest { +class RepositoryIntegrationTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { private static final String TEST_PREFIX = "repositoryintegration"; @@ -102,45 +87,6 @@ class RepositoryIntegrationTest extends AbstractSpringIntegrationJenkinsGitlabTe private final String filesContentBaseUrl = "/api/repository-files-content/"; - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private StudentParticipationTestRepository studentParticipationRepository; - - @Autowired - private ExamRepository examRepository; - - @Autowired - private StudentExamTestRepository studentExamRepository; - - @Autowired - private PlagiarismComparisonRepository plagiarismComparisonRepository; - - @Autowired - private PlagiarismCaseRepository plagiarismCaseRepository; - - @Autowired - private PostTestRepository postRepository; - - @Autowired - private BuildLogEntryService buildLogEntryService; - - @Autowired - private ProgrammingExerciseStudentParticipationTestRepository programmingExerciseStudentParticipationRepository; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ParticipationUtilService participationUtilService; - - @Autowired - private TextExerciseUtilService textExerciseUtilService; - - @Autowired - private ExamUtilService examUtilService; - private ProgrammingExercise programmingExercise; private final String currentLocalFileName = "currentFileName"; @@ -1081,7 +1027,7 @@ void testCommitChangesNotAllowedForLockedParticipation() throws Exception { programmingExercise.setReleaseDate(ZonedDateTime.now().minusHours(2)); programmingExercise.setDueDate(ZonedDateTime.now().minusHours(1)); programmingExerciseRepository.save(programmingExercise); - this.programmingExerciseStudentParticipationRepository.updateLockedById(participation.getId(), true); + participationRepository.updateLockedById(participation.getId(), true); // Committing is not allowed var receivedStatusBeforeCommit = request.get(studentRepoBaseUrl + participation.getId(), HttpStatus.OK, RepositoryStatusDTO.class); @@ -1104,7 +1050,7 @@ void testResetNotAllowedForLockedParticipation() throws Exception { programmingExercise.setReleaseDate(ZonedDateTime.now().minusHours(2)); programmingExercise.setDueDate(ZonedDateTime.now().minusHours(1)); programmingExerciseRepository.save(programmingExercise); - this.programmingExerciseStudentParticipationRepository.updateLockedById(participation.getId(), true); + participationRepository.updateLockedById(participation.getId(), true); assertUnchangedRepositoryStatusForForbiddenReset(); } diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/RepositoryProgrammingExerciseParticipationJenkinsIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/RepositoryProgrammingExerciseParticipationJenkinsIntegrationTest.java index 0616cda099c4..325c4520311b 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/RepositoryProgrammingExerciseParticipationJenkinsIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/RepositoryProgrammingExerciseParticipationJenkinsIntegrationTest.java @@ -13,7 +13,6 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; @@ -22,27 +21,14 @@ import de.tum.cit.aet.artemis.core.util.TestConstants; import de.tum.cit.aet.artemis.exercise.domain.SubmissionType; -import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; -class RepositoryProgrammingExerciseParticipationJenkinsIntegrationTest extends AbstractSpringIntegrationJenkinsGitlabTest { +class RepositoryProgrammingExerciseParticipationJenkinsIntegrationTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { private static final String TEST_PREFIX = "repoprogexpartjenk"; - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ParticipationUtilService participationUtilService; - @BeforeEach void setup() throws Exception { userUtilService.addUsers(TEST_PREFIX, 1, 1, 0, 1); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/StaticCodeAnalysisIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/StaticCodeAnalysisIntegrationTest.java index 06f673f808ee..2a6cccd7d15e 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/StaticCodeAnalysisIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/StaticCodeAnalysisIntegrationTest.java @@ -15,7 +15,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; @@ -34,33 +33,12 @@ import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.StaticCodeAnalysisCategory; import de.tum.cit.aet.artemis.programming.dto.StaticCodeAnalysisIssue; -import de.tum.cit.aet.artemis.programming.repository.StaticCodeAnalysisCategoryRepository; -import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseFeedbackCreationService; -import de.tum.cit.aet.artemis.programming.service.StaticCodeAnalysisService; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseFactory; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationLocalCILocalVCTest; -class StaticCodeAnalysisIntegrationTest extends AbstractSpringIntegrationLocalCILocalVCTest { +class StaticCodeAnalysisIntegrationTest extends AbstractProgrammingIntegrationLocalCILocalVCTest { private static final String TEST_PREFIX = "staticcodeanalysis"; - @Autowired - private StaticCodeAnalysisService staticCodeAnalysisService; - - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private StaticCodeAnalysisCategoryRepository staticCodeAnalysisCategoryRepository; - - @Autowired - private ProgrammingExerciseFeedbackCreationService feedbackCreationService; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - private ProgrammingExercise programmingExerciseSCAEnabled; private ProgrammingExercise programmingExercise; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/SubmissionPolicyIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/SubmissionPolicyIntegrationTest.java index 660eec38c4f6..6fe76f858ff9 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/SubmissionPolicyIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/SubmissionPolicyIntegrationTest.java @@ -14,7 +14,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; @@ -22,7 +21,6 @@ import de.tum.cit.aet.artemis.core.domain.User; import de.tum.cit.aet.artemis.exercise.domain.Submission; import de.tum.cit.aet.artemis.exercise.domain.SubmissionType; -import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; @@ -30,29 +28,13 @@ import de.tum.cit.aet.artemis.programming.domain.submissionpolicy.LockRepositoryPolicy; import de.tum.cit.aet.artemis.programming.domain.submissionpolicy.SubmissionPenaltyPolicy; import de.tum.cit.aet.artemis.programming.domain.submissionpolicy.SubmissionPolicy; -import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseGradingService; import de.tum.cit.aet.artemis.programming.service.ci.notification.dto.CommitDTO; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseFactory; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; -class SubmissionPolicyIntegrationTest extends AbstractSpringIntegrationJenkinsGitlabTest { +class SubmissionPolicyIntegrationTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { private static final String TEST_PREFIX = "submissionpolicyintegration"; - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ProgrammingExerciseGradingService gradingService; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ParticipationUtilService participationUtilService; - private Long programmingExerciseId; private ProgrammingExercise programmingExercise; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java index dfaf90733fac..1a43719cc32b 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java @@ -25,7 +25,6 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.util.LinkedMultiValueMap; @@ -38,25 +37,16 @@ import de.tum.cit.aet.artemis.programming.dto.FileMove; import de.tum.cit.aet.artemis.programming.dto.RepositoryStatusDTO; import de.tum.cit.aet.artemis.programming.dto.RepositoryStatusDTOType; -import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; import de.tum.cit.aet.artemis.programming.service.GitService; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; import de.tum.cit.aet.artemis.programming.util.GitUtilService; import de.tum.cit.aet.artemis.programming.util.LocalRepository; import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseFactory; import de.tum.cit.aet.artemis.programming.web.repository.FileSubmission; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; -class TestRepositoryResourceIntegrationTest extends AbstractSpringIntegrationJenkinsGitlabTest { +class TestRepositoryResourceIntegrationTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { private static final String TEST_PREFIX = "testrepositoryresourceint"; - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository; - private final String testRepoBaseUrl = "/api/test-repository/"; private ProgrammingExercise programmingExercise; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/architecture/ProgrammingTestArchitectureTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/architecture/ProgrammingTestArchitectureTest.java new file mode 100644 index 000000000000..b434af7971bd --- /dev/null +++ b/src/test/java/de/tum/cit/aet/artemis/programming/architecture/ProgrammingTestArchitectureTest.java @@ -0,0 +1,31 @@ +package de.tum.cit.aet.artemis.programming.architecture; + +import java.util.Set; + +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationGitlabCIGitlabSamlTest; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationJenkinsGitlabTest; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTest; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTestBase; +import de.tum.cit.aet.artemis.shared.architecture.module.AbstractModuleTestArchitectureTest; + +class ProgrammingTestArchitectureTest extends AbstractModuleTestArchitectureTest { + + @Override + public String getModulePackage() { + return ARTEMIS_PACKAGE + ".programming"; + } + + @Override + protected Set> getAbstractModuleIntegrationTestClasses() { + // @formatter:off + return Set.of( + AbstractProgrammingIntegrationGitlabCIGitlabSamlTest.class, + AbstractProgrammingIntegrationIndependentTest.class, + AbstractProgrammingIntegrationJenkinsGitlabTest.class, + AbstractProgrammingIntegrationLocalCILocalVCTest.class, + AbstractProgrammingIntegrationLocalCILocalVCTestBase.class + ); + // @formatter:on + } +} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/CodeHintIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/CodeHintIntegrationTest.java index a4d326fe43a8..a49fe18c881c 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/CodeHintIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/CodeHintIntegrationTest.java @@ -9,37 +9,20 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.core.domain.Course; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; import de.tum.cit.aet.artemis.programming.domain.hestia.CodeHint; import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; -import de.tum.cit.aet.artemis.programming.repository.hestia.CodeHintRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseSolutionEntryRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; -class CodeHintIntegrationTest extends AbstractSpringIntegrationIndependentTest { +class CodeHintIntegrationTest extends AbstractProgrammingIntegrationIndependentTest { private static final String TEST_PREFIX = "codehint"; - @Autowired - private CodeHintRepository codeHintRepository; - - @Autowired - private ProgrammingExerciseTestCaseTestRepository testCaseRepository; - - @Autowired - private ProgrammingExerciseSolutionEntryRepository solutionEntryRepository; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - private ProgrammingExercise exercise; private CodeHint codeHint; @@ -143,7 +126,7 @@ void updateSolutionEntriesOnSaving() throws Exception { newEntry.setPreviousCode("New previous code"); var testCase = testCases.get("test1"); newEntry.setTestCase(testCase); - var savedNewEntry = solutionEntryRepository.save(newEntry); + var savedNewEntry = programmingExerciseSolutionEntryRepository.save(newEntry); savedNewEntry.setTestCase(testCase); codeHint.setSolutionEntries(new HashSet<>(Set.of(changedEntry, savedNewEntry))); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/CodeHintServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/CodeHintServiceTest.java index 634e86de81b9..05a72ac9a78d 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/CodeHintServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/CodeHintServiceTest.java @@ -12,57 +12,24 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.assessment.domain.Visibility; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.exception.BadRequestAlertException; -import de.tum.cit.aet.artemis.core.user.util.UserUtilService; -import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; import de.tum.cit.aet.artemis.programming.domain.hestia.CodeHint; import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTestCaseType; -import de.tum.cit.aet.artemis.programming.repository.hestia.CodeHintRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseSolutionEntryRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseTaskRepository; -import de.tum.cit.aet.artemis.programming.service.hestia.CodeHintService; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; @SuppressWarnings("ArraysAsListWithZeroOrOneArgument") -class CodeHintServiceTest extends AbstractSpringIntegrationIndependentTest { +class CodeHintServiceTest extends AbstractProgrammingIntegrationIndependentTest { private static final String TEST_PREFIX = "codehintservice"; - @Autowired - private CodeHintService codeHintService; - - @Autowired - private CodeHintRepository codeHintRepository; - - @Autowired - private ProgrammingExerciseTaskRepository taskRepository; - - @Autowired - private ProgrammingExerciseTestCaseTestRepository testCaseRepository; - - @Autowired - private ProgrammingExerciseSolutionEntryRepository solutionEntryRepository; - - @Autowired - private UserUtilService userUtilService; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ExerciseUtilService exerciseUtilService; - private ProgrammingExercise exercise; @BeforeEach @@ -88,7 +55,7 @@ private ProgrammingExerciseSolutionEntry addSolutionEntryToTestCase(ProgrammingE solutionEntry.setTestCase(testCase); solutionEntry.setLine(1); solutionEntry.setCode("code"); - return solutionEntryRepository.save(solutionEntry); + return programmingExerciseSolutionEntryRepository.save(solutionEntry); } private ProgrammingExerciseTask addTaskToExercise(String name, List testCases) { @@ -114,7 +81,7 @@ private CodeHint addCodeHintToTask(String name, ProgrammingExerciseTask task, Se solutionEntries.forEach(entry -> entry.setCodeHint(codeHint)); var createdHint = codeHintRepository.save(codeHint); - solutionEntryRepository.saveAll(solutionEntries); + programmingExerciseSolutionEntryRepository.saveAll(solutionEntries); return createdHint; } @@ -195,7 +162,7 @@ void testUpdateTestCaseOfSolutionEntry() { entryToUpdate.setTestCase(testCase2); codeHintService.updateSolutionEntriesForCodeHint(codeHint); - var allEntries = solutionEntryRepository.findByExerciseIdWithTestCases(exercise.getId()); + var allEntries = programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(exercise.getId()); assertThat(allEntries).hasSize(1); assertThat(allEntries.stream().findAny().orElseThrow().getTestCase().getId()).isEqualTo(testCase2.getId()); } @@ -216,7 +183,7 @@ void testUpdatedContentOfSolutionEntry() { entry.setFilePath("Updated file path"); codeHintService.updateSolutionEntriesForCodeHint(codeHint); - var allEntries = solutionEntryRepository.findByExerciseIdWithTestCases(exercise.getId()); + var allEntries = programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(exercise.getId()); assertThat(allEntries).hasSize(1); assertThat(allEntries.stream().findAny().orElseThrow()).isEqualTo(entryToUpdate); } @@ -233,7 +200,7 @@ void testSaveWithNewSolutionEntry() { codeHint.setSolutionEntries(new HashSet<>(Set.of(manuallyCreatedEntry))); codeHintService.updateSolutionEntriesForCodeHint(codeHint); - var allEntries = solutionEntryRepository.findByExerciseIdWithTestCases(exercise.getId()); + var allEntries = programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(exercise.getId()); assertThat(allEntries).containsExactly(manuallyCreatedEntry); } @@ -249,10 +216,10 @@ void testSaveWithRemovedSolutionEntry() { codeHint.setSolutionEntries(new HashSet<>(Collections.emptySet())); codeHintService.updateSolutionEntriesForCodeHint(codeHint); - var entriesForHint = solutionEntryRepository.findByCodeHintId(codeHint.getId()); + var entriesForHint = programmingExerciseSolutionEntryRepository.findByCodeHintId(codeHint.getId()); assertThat(entriesForHint).isEmpty(); - var allEntries = solutionEntryRepository.findByExerciseIdWithTestCases(exercise.getId()); + var allEntries = programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(exercise.getId()); assertThat(allEntries).containsExactly(entryToRemove); } @@ -273,10 +240,10 @@ void testSaveEntryWithTestCaseUnrelatedToHintTask() { codeHint.setSolutionEntries(new HashSet<>(Set.of(invalidSolutionEntry))); assertThatExceptionOfType(BadRequestAlertException.class).isThrownBy(() -> codeHintService.updateSolutionEntriesForCodeHint(codeHint)); - var entriesForHint = solutionEntryRepository.findByCodeHintId(codeHint.getId()); + var entriesForHint = programmingExerciseSolutionEntryRepository.findByCodeHintId(codeHint.getId()); assertThat(entriesForHint).isEmpty(); - var allEntries = solutionEntryRepository.findByExerciseIdWithTestCases(exercise.getId()); + var allEntries = programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(exercise.getId()); assertThat(allEntries).isEmpty(); } } diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ExerciseHintIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ExerciseHintIntegrationTest.java index adaa6e600832..6a44f54820fe 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ExerciseHintIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ExerciseHintIntegrationTest.java @@ -11,7 +11,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; @@ -21,7 +20,7 @@ import de.tum.cit.aet.artemis.assessment.domain.Result; import de.tum.cit.aet.artemis.assessment.domain.Visibility; import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; @@ -29,43 +28,11 @@ import de.tum.cit.aet.artemis.programming.domain.hestia.ExerciseHint; import de.tum.cit.aet.artemis.programming.domain.hestia.ExerciseHintActivation; import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; -import de.tum.cit.aet.artemis.programming.repository.hestia.ExerciseHintActivationRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ExerciseHintRepository; -import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseTaskService; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingSubmissionTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; -class ExerciseHintIntegrationTest extends AbstractSpringIntegrationIndependentTest { +class ExerciseHintIntegrationTest extends AbstractProgrammingIntegrationIndependentTest { private static final String TEST_PREFIX = "exercisehintintegration"; - @Autowired - private ExerciseHintRepository exerciseHintRepository; - - @Autowired - private ProgrammingExerciseTestRepository exerciseRepository; - - @Autowired - private ProgrammingExerciseTaskService programmingExerciseTaskService; - - @Autowired - private ProgrammingSubmissionTestRepository programmingSubmissionRepository; - - @Autowired - private ExerciseHintActivationRepository exerciseHintActivationRepository; - - @Autowired - private ProgrammingExerciseTestCaseTestRepository programmingExerciseTestCaseRepository; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ParticipationUtilService participationUtilService; - private ProgrammingExercise exercise; private ProgrammingExercise exerciseLite; @@ -85,9 +52,8 @@ void initTestCase() { userUtilService.addUsers(TEST_PREFIX, 2, 2, 1, 2); - programmingExerciseTestCaseRepository - .saveAll(programmingExerciseTestCaseRepository.findByExerciseId(programmingExercise.getId()).stream().peek(testCase -> testCase.setActive(true)).toList()); - exerciseLite = exerciseRepository.findByIdElseThrow(programmingExercise.getId()); + testCaseRepository.saveAll(testCaseRepository.findByExerciseId(programmingExercise.getId()).stream().peek(testCase -> testCase.setActive(true)).toList()); + exerciseLite = programmingExerciseRepository.findByIdElseThrow(programmingExercise.getId()); exercise = programmingExerciseUtilService.loadProgrammingExerciseWithEagerReferences(exerciseLite); programmingExerciseUtilService.addHintsToExercise(exercise); programmingExerciseUtilService.addTasksToProgrammingExercise(exercise); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ExerciseHintServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ExerciseHintServiceTest.java index 69279ac37859..99b3d357f73f 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ExerciseHintServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ExerciseHintServiceTest.java @@ -10,7 +10,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import de.tum.cit.aet.artemis.assessment.domain.AssessmentType; import de.tum.cit.aet.artemis.assessment.domain.Feedback; @@ -19,69 +18,17 @@ import de.tum.cit.aet.artemis.assessment.domain.Visibility; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.domain.User; -import de.tum.cit.aet.artemis.core.test_repository.UserTestRepository; -import de.tum.cit.aet.artemis.core.user.util.UserUtilService; -import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; -import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; import de.tum.cit.aet.artemis.programming.domain.hestia.ExerciseHint; import de.tum.cit.aet.artemis.programming.domain.hestia.ExerciseHintActivation; import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; -import de.tum.cit.aet.artemis.programming.repository.hestia.ExerciseHintActivationRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ExerciseHintRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseTaskRepository; -import de.tum.cit.aet.artemis.programming.service.hestia.ExerciseHintService; -import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseTaskService; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingSubmissionTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; - -class ExerciseHintServiceTest extends AbstractSpringIntegrationIndependentTest { - private static final String TEST_PREFIX = "exercisehintservice"; - - @Autowired - private UserTestRepository userRepository; - - @Autowired - private ExerciseHintService exerciseHintService; - - @Autowired - private ExerciseHintRepository exerciseHintRepository; - - @Autowired - private ProgrammingExerciseTestRepository exerciseRepository; - - @Autowired - private ProgrammingExerciseTaskService programmingExerciseTaskService; - - @Autowired - private ProgrammingSubmissionTestRepository programmingSubmissionRepository; +class ExerciseHintServiceTest extends AbstractProgrammingIntegrationIndependentTest { - @Autowired - private ExerciseHintActivationRepository exerciseHintActivationRepository; - - @Autowired - private ProgrammingExerciseTaskRepository programmingExerciseTaskRepository; - - @Autowired - private ProgrammingExerciseTestCaseTestRepository programmingExerciseTestCaseRepository; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ExerciseUtilService exerciseUtilService; - - @Autowired - private UserUtilService userUtilService; - - @Autowired - private ParticipationUtilService participationUtilService; + private static final String TEST_PREFIX = "exercisehintservice"; private ProgrammingExercise exercise; @@ -107,9 +54,9 @@ void initTestCase() { student = userRepository.getUserWithGroupsAndAuthorities(TEST_PREFIX + "student1"); userUtilService.changeUser(TEST_PREFIX + "student1"); - var activatedTestCases = programmingExerciseTestCaseRepository.findByExerciseId(programmingExercise.getId()).stream().peek(testCase -> testCase.setActive(true)).toList(); - programmingExerciseTestCaseRepository.saveAll(activatedTestCases); - exercise = exerciseRepository.findByIdElseThrow(programmingExercise.getId()); + var activatedTestCases = testCaseRepository.findByExerciseId(programmingExercise.getId()).stream().peek(testCase -> testCase.setActive(true)).toList(); + testCaseRepository.saveAll(activatedTestCases); + exercise = programmingExerciseRepository.findByIdElseThrow(programmingExercise.getId()); exercise = programmingExerciseUtilService.loadProgrammingExerciseWithEagerReferences(exercise); programmingExerciseUtilService.addHintsToExercise(exercise); programmingExerciseUtilService.addTasksToProgrammingExercise(exercise); @@ -133,10 +80,10 @@ void testGetAvailableExerciseHintsTasksWithoutTestCases() { addResultWithFailedTestCases(exercise.getTestCases()); for (ProgrammingExerciseTask sortedTask : sortedTasks) { sortedTask.getTestCases().clear(); - programmingExerciseTaskRepository.save(sortedTask); + taskRepository.save(sortedTask); } exercise.setProblemStatement(exercise.getProblemStatement().replaceAll("\\([^()]+\\)", "()")); - exerciseRepository.save(exercise); + programmingExerciseRepository.save(exercise); var availableExerciseHints = exerciseHintService.getAvailableExerciseHints(exercise, student); assertThat(availableExerciseHints).isEmpty(); } diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/HestiaDatabaseTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/HestiaDatabaseTest.java index 2267bf5acaae..040547ae9cf9 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/HestiaDatabaseTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/HestiaDatabaseTest.java @@ -8,57 +8,24 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.core.user.util.UserUtilService; -import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; import de.tum.cit.aet.artemis.programming.domain.hestia.CodeHint; import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; -import de.tum.cit.aet.artemis.programming.repository.hestia.CodeHintRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseSolutionEntryRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseTaskRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; /** * This class tests the database relations of the Hestia domain models. * This currently includes ProgrammingExerciseTask, ProgrammingExerciseSolutionEntry and CodeHint. * It tests if the addition and deletion of these models works as expected. */ -class HestiaDatabaseTest extends AbstractSpringIntegrationIndependentTest { +class HestiaDatabaseTest extends AbstractProgrammingIntegrationIndependentTest { private static final String TEST_PREFIX = "hestiadatabase"; - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ProgrammingExerciseTestCaseTestRepository programmingExerciseTestCaseRepository; - - @Autowired - private ProgrammingExerciseTaskRepository programmingExerciseTaskRepository; - - @Autowired - private ProgrammingExerciseSolutionEntryRepository programmingExerciseSolutionEntryRepository; - - @Autowired - private CodeHintRepository codeHintRepository; - - @Autowired - private UserUtilService userUtilService; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ExerciseUtilService exerciseUtilService; - private Long programmingExerciseId; @BeforeEach @@ -72,7 +39,7 @@ ProgrammingExerciseTask addTaskToProgrammingExercise(String taskName) { var task = new ProgrammingExerciseTask(); task.setTaskName(taskName); task.setExercise(programmingExerciseRepository.getReferenceById(programmingExerciseId)); - task = programmingExerciseTaskRepository.save(task); + task = taskRepository.save(task); return task; } @@ -92,21 +59,21 @@ ProgrammingExerciseSolutionEntry[] addSolutionEntriesToTestCase(int count, Progr @Test void addOneTaskToProgrammingExercise() { var task = addTaskToProgrammingExercise("Task 1"); - assertThat(programmingExerciseTaskRepository.findByExerciseIdWithTestCases(programmingExerciseId)).containsExactly(task); + assertThat(taskRepository.findByExerciseIdWithTestCases(programmingExerciseId)).containsExactly(task); } @Test void deleteProgrammingExerciseWithTask() { addOneTaskToProgrammingExercise(); programmingExerciseRepository.deleteById(programmingExerciseId); - assertThat(programmingExerciseTaskRepository.findByExerciseId(programmingExerciseId)).isEmpty(); + assertThat(taskRepository.findByExerciseId(programmingExerciseId)).isEmpty(); } @Test void addTestCasesWithSolutionEntriesToProgrammingExercise() { var programmingExercise = programmingExerciseRepository.findByIdElseThrow(programmingExerciseId); programmingExerciseUtilService.addTestCasesToProgrammingExercise(programmingExercise); - var testCases = programmingExerciseTestCaseRepository.findByExerciseId(programmingExerciseId); + var testCases = testCaseRepository.findByExerciseId(programmingExerciseId); assertThat(testCases).isNotEmpty(); for (ProgrammingExerciseTestCase testCase : testCases) { var solutionEntries = addSolutionEntriesToTestCase(2, testCase); @@ -118,7 +85,7 @@ void addTestCasesWithSolutionEntriesToProgrammingExercise() { void deleteProgrammingExerciseWithTestCasesAndSolutionEntries() { addTestCasesWithSolutionEntriesToProgrammingExercise(); programmingExerciseRepository.deleteById(programmingExerciseId); - assertThat(programmingExerciseTestCaseRepository.findByExerciseId(programmingExerciseId)).isEmpty(); + assertThat(testCaseRepository.findByExerciseId(programmingExerciseId)).isEmpty(); assertThat(programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(programmingExerciseId)).isEmpty(); } @@ -126,24 +93,24 @@ void deleteProgrammingExerciseWithTestCasesAndSolutionEntries() { void deleteTaskWithTestCases() { var programmingExercise = programmingExerciseRepository.findByIdElseThrow(programmingExerciseId); programmingExerciseUtilService.addTestCasesToProgrammingExercise(programmingExercise); - var testCases = programmingExerciseTestCaseRepository.findByExerciseId(programmingExerciseId); + var testCases = testCaseRepository.findByExerciseId(programmingExerciseId); assertThat(testCases).isNotEmpty(); var task = addTaskToProgrammingExercise("Task 1"); task.setTestCases(testCases); - task = programmingExerciseTaskRepository.save(task); - programmingExerciseTaskRepository.delete(task); - assertThat(programmingExerciseTestCaseRepository.findByExerciseId(programmingExerciseId)).isEqualTo(testCases); + task = taskRepository.save(task); + taskRepository.delete(task); + assertThat(testCaseRepository.findByExerciseId(programmingExerciseId)).isEqualTo(testCases); } @Test void addCodeHintToProgrammingExercise() { var programmingExercise = programmingExerciseRepository.findByIdElseThrow(programmingExerciseId); programmingExerciseUtilService.addTestCasesToProgrammingExercise(programmingExercise); - var testCases = programmingExerciseTestCaseRepository.findByExerciseId(programmingExerciseId); + var testCases = testCaseRepository.findByExerciseId(programmingExerciseId); assertThat(testCases).isNotEmpty(); var task = addTaskToProgrammingExercise("Task 1"); task.setTestCases(testCases); - task = programmingExerciseTaskRepository.save(task); + task = taskRepository.save(task); Set allSolutionEntries = new HashSet<>(); for (ProgrammingExerciseTestCase testCase : testCases) { var solutionEntries = addSolutionEntriesToTestCase(2, testCase); @@ -162,7 +129,7 @@ void addCodeHintToProgrammingExercise() { codeHint.setSolutionEntries(allSolutionEntries); codeHint = codeHintRepository.save(codeHint); task.setExerciseHints(Set.of(codeHint)); - programmingExerciseTaskRepository.save(task); + taskRepository.save(task); assertThat(programmingExerciseSolutionEntryRepository.findByCodeHintId(codeHint.getId())).isEqualTo(allSolutionEntries); assertThat(codeHintRepository.findByExerciseId(programmingExerciseId)).containsExactly(codeHint); } @@ -172,7 +139,7 @@ void deleteCodeHint() { addCodeHintToProgrammingExercise(); var codeHint = codeHintRepository.findByExerciseId(programmingExerciseId).stream().findAny().orElseThrow(); codeHintRepository.delete(codeHint); - assertThat(programmingExerciseTaskRepository.findByExerciseId(programmingExerciseId)).hasSize(1); + assertThat(taskRepository.findByExerciseId(programmingExerciseId)).hasSize(1); assertThat(programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(programmingExerciseId)).hasSize(6); } @@ -180,19 +147,19 @@ void deleteCodeHint() { void deleteProgrammingExerciseWithCodeHint() { addCodeHintToProgrammingExercise(); programmingExerciseRepository.deleteById(programmingExerciseId); - assertThat(programmingExerciseTaskRepository.findByExerciseId(programmingExerciseId)).isEmpty(); + assertThat(taskRepository.findByExerciseId(programmingExerciseId)).isEmpty(); assertThat(programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(programmingExerciseId)).isEmpty(); assertThat(codeHintRepository.findByExerciseId(programmingExerciseId)).isEmpty(); - assertThat(programmingExerciseTestCaseRepository.findByExerciseId(programmingExerciseId)).isEmpty(); + assertThat(testCaseRepository.findByExerciseId(programmingExerciseId)).isEmpty(); } @Test void deleteTaskWithCodeHint() { addCodeHintToProgrammingExercise(); - var task = programmingExerciseTaskRepository.findByExerciseId(programmingExerciseId).stream().findAny().orElseThrow(); - programmingExerciseTaskRepository.delete(task); + var task = taskRepository.findByExerciseId(programmingExerciseId).stream().findAny().orElseThrow(); + taskRepository.delete(task); assertThat(codeHintRepository.findByExerciseId(programmingExerciseId)).isEmpty(); - assertThat(programmingExerciseTestCaseRepository.findByExerciseId(programmingExerciseId)).hasSize(3); + assertThat(testCaseRepository.findByExerciseId(programmingExerciseId)).hasSize(3); assertThat(programmingExerciseSolutionEntryRepository.findByExerciseIdWithTestCases(programmingExerciseId)).hasSize(6); } } diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseGitDiffReportIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseGitDiffReportIntegrationTest.java index 6d5bf267139a..df54e4dc10f5 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseGitDiffReportIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseGitDiffReportIntegrationTest.java @@ -7,24 +7,21 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.core.domain.Course; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTestBase; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffEntry; import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffReport; -import de.tum.cit.aet.artemis.programming.hestia.util.HestiaUtilTestService; -import de.tum.cit.aet.artemis.programming.icl.AbstractLocalCILocalVCIntegrationTest; -import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseGitDiffReportService; import de.tum.cit.aet.artemis.programming.util.LocalRepository; import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseFactory; /** * Tests for the ProgrammingExerciseGitDiffReportResource */ -class ProgrammingExerciseGitDiffReportIntegrationTest extends AbstractLocalCILocalVCIntegrationTest { +class ProgrammingExerciseGitDiffReportIntegrationTest extends AbstractProgrammingIntegrationLocalCILocalVCTestBase { private static final String TEST_PREFIX = "progexgitdiffreport"; @@ -40,12 +37,6 @@ class ProgrammingExerciseGitDiffReportIntegrationTest extends AbstractLocalCILoc private ProgrammingExercise exercise; - @Autowired - private HestiaUtilTestService hestiaUtilTestService; - - @Autowired - private ProgrammingExerciseGitDiffReportService reportService; - @BeforeEach void initTestCase() throws Exception { Course course = courseUtilService.addEmptyCourse(); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseGitDiffReportServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseGitDiffReportServiceTest.java index 70e3c6fb8301..2516a2f416fc 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseGitDiffReportServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseGitDiffReportServiceTest.java @@ -9,38 +9,22 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.core.user.util.UserUtilService; -import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTestBase; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffEntry; import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseGitDiffReport; -import de.tum.cit.aet.artemis.programming.hestia.util.HestiaUtilTestService; -import de.tum.cit.aet.artemis.programming.icl.AbstractLocalCILocalVCIntegrationTest; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseGitDiffReportRepository; -import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseGitDiffReportService; import de.tum.cit.aet.artemis.programming.util.LocalRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; /** * Tests for the ProgrammingExerciseGitDiffReportService */ -class ProgrammingExerciseGitDiffReportServiceTest extends AbstractLocalCILocalVCIntegrationTest { +class ProgrammingExerciseGitDiffReportServiceTest extends AbstractProgrammingIntegrationLocalCILocalVCTestBase { private static final String TEST_PREFIX = "progexgitdiffreportservice"; - @Autowired - private UserUtilService userUtilService; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ExerciseUtilService exerciseUtilService; - private static final String FILE_NAME = "test.java"; private final LocalRepository solutionRepo = new LocalRepository("main"); @@ -49,15 +33,6 @@ class ProgrammingExerciseGitDiffReportServiceTest extends AbstractLocalCILocalVC private ProgrammingExercise exercise; - @Autowired - private HestiaUtilTestService hestiaUtilTestService; - - @Autowired - private ProgrammingExerciseGitDiffReportService reportService; - - @Autowired - private ProgrammingExerciseGitDiffReportRepository reportRepository; - @Override protected String getTestPrefix() { return TEST_PREFIX; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseSolutionEntryIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseSolutionEntryIntegrationTest.java index 4bf515d2e4db..17d326cfc221 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseSolutionEntryIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseSolutionEntryIntegrationTest.java @@ -8,41 +8,20 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; import de.tum.cit.aet.artemis.programming.domain.hestia.CodeHint; import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; -import de.tum.cit.aet.artemis.programming.repository.hestia.CodeHintRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseSolutionEntryRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseTaskRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; -class ProgrammingExerciseSolutionEntryIntegrationTest extends AbstractSpringIntegrationIndependentTest { +class ProgrammingExerciseSolutionEntryIntegrationTest extends AbstractProgrammingIntegrationIndependentTest { private static final String TEST_PREFIX = "progexsolutionentry"; - @Autowired - private ProgrammingExerciseSolutionEntryRepository programmingExerciseSolutionEntryRepository; - - @Autowired - private ProgrammingExerciseTestCaseTestRepository programmingExerciseTestCaseRepository; - - @Autowired - private ProgrammingExerciseTaskRepository programmingExerciseTaskRepository; - - @Autowired - private CodeHintRepository codeHintRepository; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - private ProgrammingExercise programmingExercise; private CodeHint codeHint; @@ -53,7 +32,7 @@ void initTestCase() { userUtilService.addUsers(TEST_PREFIX, 2, 2, 1, 2); programmingExercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); - Set testCases = programmingExerciseTestCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()); + Set testCases = testCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()); codeHint = new CodeHint(); codeHint.setExercise(programmingExercise); @@ -76,7 +55,7 @@ void initTestCase() { task.setExercise(programmingExercise); task.setTaskName("Task"); task.setTestCases(new HashSet<>(testCases)); - task = programmingExerciseTaskRepository.save(task); + task = taskRepository.save(task); codeHint.setProgrammingExerciseTask(task); codeHintRepository.save(codeHint); } @@ -119,8 +98,7 @@ void testGetSolutionEntriesByCodeHintId() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") void testGetSolutionEntriesByTestCaseId() throws Exception { - ProgrammingExerciseTestCase testCase = programmingExerciseTestCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst() - .orElseThrow(); + ProgrammingExerciseTestCase testCase = testCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst().orElseThrow(); final var solutionEntries = new HashSet<>( request.getList("/api/programming-exercises/" + programmingExercise.getId() + "/test-cases/" + testCase.getId() + "/solution-entries", HttpStatus.OK, ProgrammingExerciseSolutionEntry.class)); @@ -139,8 +117,7 @@ void testGetAllSolutionEntries() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") void testDeleteSolutionEntry() throws Exception { - ProgrammingExerciseTestCase testCase = programmingExerciseTestCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst() - .orElseThrow(); + ProgrammingExerciseTestCase testCase = testCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst().orElseThrow(); Long entryId = testCase.getSolutionEntries().stream().findFirst().orElseThrow().getId(); request.delete("/api/programming-exercises/" + programmingExercise.getId() + "/test-cases/" + testCase.getId() + "/solution-entries/" + entryId, HttpStatus.NO_CONTENT); assertThat(programmingExerciseSolutionEntryRepository.findById(entryId)).isEmpty(); @@ -149,8 +126,7 @@ void testDeleteSolutionEntry() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "student1", roles = "STUDENT") void testDeleteSolutionEntryAsStudent() throws Exception { - ProgrammingExerciseTestCase testCase = programmingExerciseTestCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst() - .orElseThrow(); + ProgrammingExerciseTestCase testCase = testCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst().orElseThrow(); Long entryId = testCase.getSolutionEntries().stream().findFirst().orElseThrow().getId(); request.delete("/api/programming-exercises/" + programmingExercise.getId() + "/test-cases/" + testCase.getId() + "/solution-entries/" + entryId, HttpStatus.FORBIDDEN); } @@ -158,8 +134,7 @@ void testDeleteSolutionEntryAsStudent() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") void testDeleteSolutionEntryAsTutor() throws Exception { - ProgrammingExerciseTestCase testCase = programmingExerciseTestCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst() - .orElseThrow(); + ProgrammingExerciseTestCase testCase = testCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst().orElseThrow(); Long entryId = testCase.getSolutionEntries().stream().findFirst().orElseThrow().getId(); request.delete("/api/programming-exercises/" + programmingExercise.getId() + "/test-cases/" + testCase.getId() + "/solution-entries/" + entryId, HttpStatus.FORBIDDEN); } @@ -174,8 +149,7 @@ void testDeleteAllSolutionEntriesForExercise() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") void testUpdateSolutionEntry() throws Exception { - ProgrammingExerciseTestCase testCase = programmingExerciseTestCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst() - .orElseThrow(); + ProgrammingExerciseTestCase testCase = testCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst().orElseThrow(); ProgrammingExerciseSolutionEntry entry = testCase.getSolutionEntries().stream().findFirst().orElseThrow(); Long entryId = entry.getId(); String updatedFilePath = "NewPath.java"; @@ -190,8 +164,7 @@ void testUpdateSolutionEntry() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") void testUpdateSolutionEntryWithInvalidId() throws Exception { - ProgrammingExerciseTestCase testCase = programmingExerciseTestCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst() - .orElseThrow(); + ProgrammingExerciseTestCase testCase = testCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst().orElseThrow(); ProgrammingExerciseSolutionEntry entry = testCase.getSolutionEntries().stream().findFirst().orElseThrow(); Long entryId = entry.getId(); String updatedFilePath = "NewPath.java"; @@ -206,8 +179,7 @@ void testUpdateSolutionEntryWithInvalidId() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "student1", roles = "STUDENT") void testUpdateSolutionEntryAsStudent() throws Exception { - ProgrammingExerciseTestCase testCase = programmingExerciseTestCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst() - .orElseThrow(); + ProgrammingExerciseTestCase testCase = testCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst().orElseThrow(); ProgrammingExerciseSolutionEntry entry = testCase.getSolutionEntries().stream().findFirst().orElseThrow(); Long entryId = entry.getId(); @@ -217,8 +189,7 @@ void testUpdateSolutionEntryAsStudent() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "tutor1", roles = "TA") void testUpdateSolutionEntryAsTutor() throws Exception { - ProgrammingExerciseTestCase testCase = programmingExerciseTestCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst() - .orElseThrow(); + ProgrammingExerciseTestCase testCase = testCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()).stream().findFirst().orElseThrow(); ProgrammingExerciseSolutionEntry entry = testCase.getSolutionEntries().stream().findFirst().orElseThrow(); Long entryId = entry.getId(); @@ -259,7 +230,7 @@ void testCreateManualSolutionEntry() throws Exception { manualEntry.setLine(1); manualEntry.setFilePath("src/de/tum/in/ase/BubbleSort.java"); - var testCase = programmingExerciseTestCaseRepository.findByExerciseId(programmingExercise.getId()).stream().findFirst().orElseThrow(); + var testCase = testCaseRepository.findByExerciseId(programmingExercise.getId()).stream().findFirst().orElseThrow(); manualEntry.setTestCase(testCase); request.postWithoutLocation("/api/programming-exercises/" + programmingExercise.getId() + "/test-cases/" + testCase.getId() + "/solution-entries", manualEntry, diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseTaskIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseTaskIntegrationTest.java index c302c5945314..c4def2a98eb6 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseTaskIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseTaskIntegrationTest.java @@ -10,46 +10,21 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.domain.DomainObject; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseSolutionEntryRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseTaskRepository; -import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseTaskService; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; -class ProgrammingExerciseTaskIntegrationTest extends AbstractSpringIntegrationIndependentTest { +class ProgrammingExerciseTaskIntegrationTest extends AbstractProgrammingIntegrationIndependentTest { private static final String TEST_PREFIX = "progextask"; - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ProgrammingExerciseTaskRepository programmingExerciseTaskRepository; - - @Autowired - private ProgrammingExerciseTestCaseTestRepository programmingExerciseTestCaseRepository; - - @Autowired - private ProgrammingExerciseSolutionEntryRepository programmingExerciseSolutionEntryRepository; - - @Autowired - private ProgrammingExerciseTaskService programmingExerciseTaskService; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - private ProgrammingExercise programmingExercise; private Set testCases; @@ -60,7 +35,7 @@ void initTestCases() { final Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExerciseAndSpecificTestCases(); programmingExercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); - this.testCases = programmingExerciseTestCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()); + this.testCases = testCaseRepository.findByExerciseIdWithSolutionEntries(programmingExercise.getId()); for (ProgrammingExerciseTestCase testCase : testCases) { var solutionEntry = new ProgrammingExerciseSolutionEntry(); solutionEntry.setTestCase(testCase); @@ -102,10 +77,10 @@ void testDeleteAllTasksAndSolutionEntriesForProgrammingExercise() throws Excepti task.setExercise(programmingExercise); task.setTaskName("Task"); task.setTestCases(new HashSet<>(testCases)); - programmingExerciseTaskRepository.save(task); + taskRepository.save(task); request.delete("/api/programming-exercises/" + programmingExercise.getId() + "/tasks", HttpStatus.NO_CONTENT); - assertThat(programmingExerciseTaskRepository.findByExerciseId(programmingExercise.getId())).isEmpty(); + assertThat(taskRepository.findByExerciseId(programmingExercise.getId())).isEmpty(); assertThat(programmingExerciseSolutionEntryRepository.findAllById(solutionEntryIdsBeforeDeleting)).isEmpty(); } @@ -139,7 +114,7 @@ void testTaskExtractionForProgrammingExercise() throws Exception { programmingExerciseTaskService.updateTasksFromProblemStatement(programmingExercise); request.get("/api/programming-exercises/" + programmingExercise.getId() + "/tasks", HttpStatus.OK, Set.class); - Set extractedTasks = programmingExerciseTaskRepository.findByExerciseIdWithTestCaseAndSolutionEntriesElseThrow(programmingExercise.getId()); + Set extractedTasks = taskRepository.findByExerciseIdWithTestCaseAndSolutionEntriesElseThrow(programmingExercise.getId()); Optional task1Optional = extractedTasks.stream().filter(task -> task.getTaskName().equals(taskName1)).findFirst(); Optional task2Optional = extractedTasks.stream().filter(task -> task.getTaskName().equals(taskName2)).findFirst(); assertThat(task1Optional).isPresent(); @@ -164,7 +139,7 @@ void testTaskExtractionForEmptyProblemStatement() throws Exception { request.get("/api/programming-exercises/" + programmingExercise.getId() + "/tasks", HttpStatus.OK, Set.class); - assertThat(programmingExerciseTaskRepository.findByExerciseId(programmingExercise.getId())).isEmpty(); + assertThat(taskRepository.findByExerciseId(programmingExercise.getId())).isEmpty(); } @Test diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseTaskServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseTaskServiceTest.java index b2fcb4b0bf31..a322fa44ba44 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseTaskServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/ProgrammingExerciseTaskServiceTest.java @@ -9,53 +9,20 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.domain.DomainObject; -import de.tum.cit.aet.artemis.core.user.util.UserUtilService; -import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; import de.tum.cit.aet.artemis.programming.domain.hestia.CodeHint; import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTask; -import de.tum.cit.aet.artemis.programming.repository.hestia.CodeHintRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseTaskRepository; -import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseTaskService; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; -class ProgrammingExerciseTaskServiceTest extends AbstractSpringIntegrationIndependentTest { +class ProgrammingExerciseTaskServiceTest extends AbstractProgrammingIntegrationIndependentTest { private static final String TEST_PREFIX = "progextaskservice"; - @Autowired - private ProgrammingExerciseTaskService programmingExerciseTaskService; - - @Autowired - private ProgrammingExerciseTaskRepository programmingExerciseTaskRepository; - - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ProgrammingExerciseTestCaseTestRepository programmingExerciseTestCaseRepository; - - @Autowired - private CodeHintRepository codeHintRepository; - - @Autowired - private UserUtilService userUtilService; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ExerciseUtilService exerciseUtilService; - private ProgrammingExercise programmingExercise; @BeforeEach @@ -78,24 +45,23 @@ private void updateProblemStatement(String problemStatement) { @Test void testNewExercise() { - assertThat(programmingExerciseTaskRepository.findByExerciseId(programmingExercise.getId())).hasSize(2); - var tasks = programmingExerciseTaskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()); + assertThat(taskRepository.findByExerciseId(programmingExercise.getId())).hasSize(2); + var tasks = taskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()); assertThat(tasks).hasSize(2).anyMatch(programmingExerciseTask -> checkTaskEqual(programmingExerciseTask, "Task 1", "testClass[BubbleSort]")) .anyMatch(programmingExerciseTask -> checkTaskEqual(programmingExerciseTask, "Task 2", "testMethods[Context]")); } @Test void testAddTask() { - var previousTaskIds = programmingExerciseTaskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()).stream().map(ProgrammingExerciseTask::getId) - .collect(Collectors.toSet()); + var previousTaskIds = taskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()).stream().map(ProgrammingExerciseTask::getId).collect(Collectors.toSet()); updateProblemStatement(""" [task][Task 1](testClass[BubbleSort]) [task][Task 2](testMethods[Context]) [task][Task 3](testMethods[Policy]) """); - assertThat(programmingExerciseTaskRepository.findByExerciseId(programmingExercise.getId())).hasSize(3); - var tasks = programmingExerciseTaskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()); + assertThat(taskRepository.findByExerciseId(programmingExercise.getId())).hasSize(3); + var tasks = taskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()); assertThat(tasks).hasSize(3).anyMatch(programmingExerciseTask -> checkTaskEqual(programmingExerciseTask, "Task 1", "testClass[BubbleSort]")) .anyMatch(programmingExerciseTask -> checkTaskEqual(programmingExerciseTask, "Task 2", "testMethods[Context]")) .anyMatch(programmingExerciseTask -> checkTaskEqual(programmingExerciseTask, "Task 3", "testMethods[Policy]")); @@ -108,14 +74,14 @@ void testAddTask() { @Test void testRemoveAllTasks() { updateProblemStatement("Empty"); - assertThat(programmingExerciseTaskRepository.findByExerciseId(programmingExercise.getId())).isEmpty(); + assertThat(taskRepository.findByExerciseId(programmingExercise.getId())).isEmpty(); } @Test void testReduceToOneTask() { updateProblemStatement("[task][Task 1](testClass[BubbleSort],testMethods[Context], testMethods[Policy])"); - assertThat(programmingExerciseTaskRepository.findByExerciseId(programmingExercise.getId())).hasSize(1); - var tasks = programmingExerciseTaskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()); + assertThat(taskRepository.findByExerciseId(programmingExercise.getId())).hasSize(1); + var tasks = taskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()); assertThat(tasks).hasSize(1); var task = tasks.stream().findFirst().orElseThrow(); assertThat(task.getTaskName()).isEqualTo("Task 1"); @@ -130,21 +96,20 @@ void testReduceToOneTask() { */ @Test void testRenameTask() { - var previousTaskIds = programmingExerciseTaskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()).stream().map(ProgrammingExerciseTask::getId) - .collect(Collectors.toSet()); + var previousTaskIds = taskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()).stream().map(ProgrammingExerciseTask::getId).collect(Collectors.toSet()); updateProblemStatement(""" [task][Task 1a](testClass[BubbleSort]) [task][Task 2](testMethods[Context]) """); - assertThat(programmingExerciseTaskRepository.findByExerciseId(programmingExercise.getId())).hasSize(2); - var tasks = programmingExerciseTaskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()); + assertThat(taskRepository.findByExerciseId(programmingExercise.getId())).hasSize(2); + var tasks = taskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()); var newTaskIds = tasks.stream().map(ProgrammingExerciseTask::getId).collect(Collectors.toSet()); assertThat(previousTaskIds).isEqualTo(newTaskIds); - assertThat(programmingExerciseTaskRepository.findByExerciseIdWithTestCases(programmingExercise.getId())).isEqualTo(tasks); + assertThat(taskRepository.findByExerciseIdWithTestCases(programmingExercise.getId())).isEqualTo(tasks); assertThat(tasks).anyMatch(programmingExerciseTask -> checkTaskEqual(programmingExerciseTask, "Task 1a", "testClass[BubbleSort]")) .anyMatch(programmingExerciseTask -> checkTaskEqual(programmingExerciseTask, "Task 2", "testMethods[Context]")); @@ -155,8 +120,7 @@ void testRenameTask() { */ @Test void testNoChanges() { - var previousTaskIds = programmingExerciseTaskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()).stream().map(ProgrammingExerciseTask::getId) - .collect(Collectors.toSet()); + var previousTaskIds = taskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()).stream().map(ProgrammingExerciseTask::getId).collect(Collectors.toSet()); updateProblemStatement(""" Test @@ -164,17 +128,15 @@ void testNoChanges() { [task][Task 2](testMethods[Context]) """); - assertThat(programmingExerciseTaskRepository.findByExerciseId(programmingExercise.getId())).hasSize(2); + assertThat(taskRepository.findByExerciseId(programmingExercise.getId())).hasSize(2); - var newTaskIds = programmingExerciseTaskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()).stream().map(ProgrammingExerciseTask::getId) - .collect(Collectors.toSet()); + var newTaskIds = taskRepository.findByExerciseIdWithTestCases(programmingExercise.getId()).stream().map(ProgrammingExerciseTask::getId).collect(Collectors.toSet()); assertThat(previousTaskIds).isEqualTo(newTaskIds); } @Test void testDeleteWithCodeHints() { - var task = programmingExerciseTaskRepository.findByExerciseId(programmingExercise.getId()).stream().filter(task1 -> "Task 1".equals(task1.getTaskName())).findFirst() - .orElse(null); + var task = taskRepository.findByExerciseId(programmingExercise.getId()).stream().filter(task1 -> "Task 1".equals(task1.getTaskName())).findFirst().orElse(null); assertThat(task).isNotNull(); var codeHint = new CodeHint(); @@ -183,8 +145,8 @@ void testDeleteWithCodeHints() { codeHintRepository.save(codeHint); programmingExerciseTaskService.delete(task); - assertThat(programmingExerciseTaskRepository.findByExerciseId(programmingExercise.getId())).hasSize(1); - assertThat(programmingExerciseTaskRepository.findById(task.getId())).isEmpty(); + assertThat(taskRepository.findByExerciseId(programmingExercise.getId())).hasSize(1); + assertThat(taskRepository.findById(task.getId())).isEmpty(); assertThat(codeHintRepository.findByExerciseId(programmingExercise.getId())).isEmpty(); } @@ -194,7 +156,7 @@ void getTasksWithoutInactiveFiltersOutInactive() { programmingExercise = programmingExerciseRepository .findByIdWithEagerTestCasesStaticCodeAnalysisCategoriesHintsAndTemplateAndSolutionParticipationsAndAuxReposAndBuildConfig(programmingExercise.getId()) .orElseThrow(); - programmingExerciseTestCaseRepository.deleteAll(programmingExercise.getTestCases()); + testCaseRepository.deleteAll(programmingExercise.getTestCases()); String[] testCaseNames = { "testClass[BubbleSort]", "testParametrized(Parameter1, 2)[1]" }; for (var name : testCaseNames) { @@ -202,14 +164,14 @@ void getTasksWithoutInactiveFiltersOutInactive() { testCase.setExercise(programmingExercise); testCase.setTestName(name); testCase.setActive(true); - programmingExerciseTestCaseRepository.save(testCase); + testCaseRepository.save(testCase); } var testCase = new ProgrammingExerciseTestCase(); testCase.setExercise(programmingExercise); testCase.setTestName("testWithBraces()"); testCase.setActive(false); - programmingExerciseTestCaseRepository.save(testCase); + testCaseRepository.save(testCase); programmingExercise = programmingExerciseRepository .findByIdWithEagerTestCasesStaticCodeAnalysisCategoriesHintsAndTemplateAndSolutionParticipationsAndAuxReposAndBuildConfig(programmingExercise.getId()) @@ -232,7 +194,7 @@ void testParseTestCaseNames() { programmingExercise = programmingExerciseRepository .findByIdWithEagerTestCasesStaticCodeAnalysisCategoriesHintsAndTemplateAndSolutionParticipationsAndAuxReposAndBuildConfig(programmingExercise.getId()) .orElseThrow(); - programmingExerciseTestCaseRepository.deleteAll(programmingExercise.getTestCases()); + testCaseRepository.deleteAll(programmingExercise.getTestCases()); String[] testCaseNames = new String[] { "testClass[BubbleSort]", "testWithBraces()", "testParametrized(Parameter1, 2)[1]" }; for (var name : testCaseNames) { @@ -240,7 +202,7 @@ void testParseTestCaseNames() { testCase.setExercise(programmingExercise); testCase.setTestName(name); testCase.setActive(true); - programmingExerciseTestCaseRepository.save(testCase); + testCaseRepository.save(testCase); } programmingExercise = programmingExerciseRepository .findByIdWithEagerTestCasesStaticCodeAnalysisCategoriesHintsAndTemplateAndSolutionParticipationsAndAuxReposAndBuildConfig(programmingExercise.getId()) @@ -250,10 +212,10 @@ void testParseTestCaseNames() { [task][Task 1](testClass[BubbleSort],testWithBraces(),testParametrized(Parameter1, 2)[1]) """); - var actualTasks = programmingExerciseTaskRepository.findByExerciseId(programmingExercise.getId()); + var actualTasks = taskRepository.findByExerciseId(programmingExercise.getId()); assertThat(actualTasks).hasSize(1); final var actualTask = actualTasks.iterator().next().getId(); - var actualTaskWithTestCases = programmingExerciseTaskRepository.findByIdWithTestCaseAndSolutionEntriesElseThrow(actualTask); + var actualTaskWithTestCases = taskRepository.findByIdWithTestCaseAndSolutionEntriesElseThrow(actualTask); assertThat(actualTaskWithTestCases.getTaskName()).isEqualTo("Task 1"); var actualTestCaseNames = actualTaskWithTestCases.getTestCases().stream().map(ProgrammingExerciseTestCase::getTestName).toList(); assertThat(actualTestCaseNames).containsExactlyInAnyOrder(testCaseNames); @@ -262,15 +224,15 @@ void testParseTestCaseNames() { @Test @WithMockUser(username = "instructor1", roles = "INSTRUCTOR") void testExtractTasksFromTestIds() { - var test1 = programmingExerciseTestCaseRepository.findByExerciseIdAndTestName(programmingExercise.getId(), "testClass[BubbleSort]").orElseThrow(); - var test2 = programmingExerciseTestCaseRepository.findByExerciseIdAndTestName(programmingExercise.getId(), "testMethods[Context]").orElseThrow(); + var test1 = testCaseRepository.findByExerciseIdAndTestName(programmingExercise.getId(), "testClass[BubbleSort]").orElseThrow(); + var test2 = testCaseRepository.findByExerciseIdAndTestName(programmingExercise.getId(), "testMethods[Context]").orElseThrow(); updateProblemStatement("[task][Task 1](%s,%s)".formatted(test1.getId(), test2.getId())); - var actualTasks = programmingExerciseTaskRepository.findByExerciseId(programmingExercise.getId()); + var actualTasks = taskRepository.findByExerciseId(programmingExercise.getId()); assertThat(actualTasks).hasSize(1); final var actualTask = actualTasks.iterator().next().getId(); - var actualTaskWithTestCases = programmingExerciseTaskRepository.findByIdWithTestCaseAndSolutionEntriesElseThrow(actualTask); + var actualTaskWithTestCases = taskRepository.findByIdWithTestCaseAndSolutionEntriesElseThrow(actualTask); assertThat(actualTaskWithTestCases.getTaskName()).isEqualTo("Task 1"); var actualTestCaseNames = actualTaskWithTestCases.getTestCases().stream().map(ProgrammingExerciseTestCase::getTestName).toList(); assertThat(actualTestCaseNames).containsExactlyInAnyOrder("testClass[BubbleSort]", "testMethods[Context]"); @@ -283,7 +245,7 @@ private boolean checkTaskEqual(ProgrammingExerciseTask task, String expectedName @Test void testNameReplacement() { - Map testCases = programmingExerciseTestCaseRepository.findByExerciseId(programmingExercise.getId()).stream() + Map testCases = testCaseRepository.findByExerciseId(programmingExercise.getId()).stream() .collect(Collectors.toMap(ProgrammingExerciseTestCase::getTestName, ProgrammingExerciseTestCase::getId)); programmingExerciseTaskService.replaceTestNamesWithIds(programmingExercise); @@ -300,9 +262,9 @@ void testNameReplacement() { void testNameReplacementKeepsInactiveTests() { // Task 1 is inactive, task 2 does not exist updateProblemStatement("[task][Task 1](testClass[BubbleSort])\n[task][Task 2](nonExistingTask)"); - var testCase = programmingExerciseTestCaseRepository.findByExerciseIdAndTestName(programmingExercise.getId(), "testClass[BubbleSort]").orElseThrow(); + var testCase = testCaseRepository.findByExerciseIdAndTestName(programmingExercise.getId(), "testClass[BubbleSort]").orElseThrow(); testCase.setActive(false); - programmingExerciseTestCaseRepository.save(testCase); + testCaseRepository.save(testCase); programmingExerciseTaskService.replaceTestNamesWithIds(programmingExercise); String problemStatement = programmingExercise.getProblemStatement(); @@ -313,7 +275,7 @@ void testNameReplacementKeepsInactiveTests() { @Test void testNameReplacementSpecialNames() { - var bubbleSort = programmingExerciseTestCaseRepository.findByExerciseIdAndTestName(programmingExercise.getId(), "testClass[BubbleSort]").orElseThrow(); + var bubbleSort = testCaseRepository.findByExerciseIdAndTestName(programmingExercise.getId(), "testClass[BubbleSort]").orElseThrow(); var braces = programmingExerciseUtilService.addTestCaseToProgrammingExercise(programmingExercise, "testWithBraces()"); var parameterized = programmingExerciseUtilService.addTestCaseToProgrammingExercise(programmingExercise, "testParametrized(Parameter1, 2)[1]"); updateProblemStatement(""" @@ -414,10 +376,10 @@ class LinkedList { @Test void testIdReplacementWithNames() { - var bubbleSort = programmingExerciseTestCaseRepository.findByExerciseIdAndTestName(programmingExercise.getId(), "testClass[BubbleSort]").orElseThrow(); + var bubbleSort = testCaseRepository.findByExerciseIdAndTestName(programmingExercise.getId(), "testClass[BubbleSort]").orElseThrow(); var inactiveTest = programmingExerciseUtilService.addTestCaseToProgrammingExercise(programmingExercise, "testName"); inactiveTest.setActive(false); - programmingExerciseTestCaseRepository.save(inactiveTest); + testCaseRepository.save(inactiveTest); updateProblemStatement("[task][Taskname](%s,%s)".formatted(bubbleSort.getId(), inactiveTest.getId())); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/StructuralTestCaseServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/StructuralTestCaseServiceTest.java index 08d6f5994ac7..747032881267 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/StructuralTestCaseServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/StructuralTestCaseServiceTest.java @@ -9,21 +9,15 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.assessment.domain.Visibility; import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.core.user.util.UserUtilService; -import de.tum.cit.aet.artemis.core.util.CourseUtilService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTestBase; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTestCaseType; -import de.tum.cit.aet.artemis.programming.hestia.util.HestiaUtilTestService; -import de.tum.cit.aet.artemis.programming.icl.AbstractLocalCILocalVCIntegrationTest; import de.tum.cit.aet.artemis.programming.service.hestia.structural.StructuralSolutionEntryGenerationException; -import de.tum.cit.aet.artemis.programming.service.hestia.structural.StructuralTestCaseService; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; import de.tum.cit.aet.artemis.programming.util.LocalRepository; import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseFactory; @@ -31,29 +25,14 @@ * Tests for the StructuralTestCaseService * Test if solution entries are generated as expected for structural tests */ -class StructuralTestCaseServiceTest extends AbstractLocalCILocalVCIntegrationTest { +class StructuralTestCaseServiceTest extends AbstractProgrammingIntegrationLocalCILocalVCTestBase { private static final String TEST_PREFIX = "structuraltestcaseservice"; - @Autowired - private CourseUtilService courseUtilService; - - @Autowired - private UserUtilService userUtilService; - private final LocalRepository solutionRepo = new LocalRepository("main"); private final LocalRepository testRepo = new LocalRepository("main"); - @Autowired - private HestiaUtilTestService hestiaUtilTestService; - - @Autowired - private StructuralTestCaseService structuralTestCaseService; - - @Autowired - private ProgrammingExerciseTestCaseTestRepository testCaseRepository; - private ProgrammingExercise exercise; @Override diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/TestwiseCoverageIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/TestwiseCoverageIntegrationTest.java index 6319f8395fd4..a9544af19d89 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/TestwiseCoverageIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/TestwiseCoverageIntegrationTest.java @@ -7,11 +7,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.core.domain.Course; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; @@ -19,40 +19,11 @@ import de.tum.cit.aet.artemis.programming.domain.hestia.CoverageFileReport; import de.tum.cit.aet.artemis.programming.domain.hestia.CoverageReport; import de.tum.cit.aet.artemis.programming.domain.hestia.TestwiseCoverageReportEntry; -import de.tum.cit.aet.artemis.programming.repository.SolutionProgrammingExerciseParticipationRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.CoverageFileReportRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.CoverageReportRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.TestwiseCoverageReportEntryRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingSubmissionTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; -class TestwiseCoverageIntegrationTest extends AbstractSpringIntegrationIndependentTest { +class TestwiseCoverageIntegrationTest extends AbstractProgrammingIntegrationIndependentTest { private static final String TEST_PREFIX = "testwisecoverageint"; - @Autowired - private ProgrammingExerciseTestCaseTestRepository programmingExerciseTestCaseRepository; - - @Autowired - private CoverageReportRepository coverageReportRepository; - - @Autowired - private CoverageFileReportRepository coverageFileReportRepository; - - @Autowired - private TestwiseCoverageReportEntryRepository testwiseCoverageReportEntryRepository; - - @Autowired - private ProgrammingSubmissionTestRepository programmingSubmissionRepository; - - @Autowired - private SolutionProgrammingExerciseParticipationRepository solutionProgrammingExerciseRepository; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - private ProgrammingExercise programmingExercise; private ProgrammingSubmission latestSolutionSubmission; @@ -64,7 +35,7 @@ void setup() { userUtilService.addUsers(TEST_PREFIX, 1, 1, 0, 0); final Course course = programmingExerciseUtilService.addCourseWithOneProgrammingExercise(false, true, ProgrammingLanguage.JAVA); programmingExercise = exerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class); - var solutionParticipation = solutionProgrammingExerciseRepository.findWithEagerResultsAndSubmissionsByProgrammingExerciseId(programmingExercise.getId()).orElseThrow(); + var solutionParticipation = solutionEntryRepository.findWithEagerResultsAndSubmissionsByProgrammingExerciseId(programmingExercise.getId()).orElseThrow(); var unsavedPreviousSubmission = new ProgrammingSubmission(); unsavedPreviousSubmission.setParticipation(solutionParticipation); unsavedPreviousSubmission.setSubmissionDate(ZonedDateTime.of(2022, 4, 5, 12, 0, 0, 0, ZoneId.of("Europe/Berlin"))); @@ -74,8 +45,8 @@ void setup() { unsavedLatestSubmission.setSubmissionDate(ZonedDateTime.of(2022, 4, 5, 13, 0, 0, 0, ZoneId.of("Europe/Berlin"))); latestSolutionSubmission = programmingSubmissionRepository.save(unsavedLatestSubmission); - var testCase1 = programmingExerciseTestCaseRepository.save(new ProgrammingExerciseTestCase().exercise(programmingExercise).testName("test1()")); - var testCase2 = programmingExerciseTestCaseRepository.save(new ProgrammingExerciseTestCase().exercise(programmingExercise).testName("test2()")); + var testCase1 = testCaseRepository.save(new ProgrammingExerciseTestCase().exercise(programmingExercise).testName("test1()")); + var testCase2 = testCaseRepository.save(new ProgrammingExerciseTestCase().exercise(programmingExercise).testName("test2()")); generateAndSaveSimpleReport(0.3, "src/de/tum/in/ase/BubbleSort.java", 15, 5, 1, 5, testCase1, previousSolutionSubmission); latestReport = generateAndSaveSimpleReport(0.4, "src/de/tum/in/ase/BubbleSort.java", 20, 8, 1, 8, testCase2, latestSolutionSubmission); } diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/TestwiseCoverageReportServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/TestwiseCoverageReportServiceTest.java index ba89b52dc804..18addd11453e 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/TestwiseCoverageReportServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/TestwiseCoverageReportServiceTest.java @@ -9,60 +9,23 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Pageable; import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.core.user.util.UserUtilService; -import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTestBase; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; import de.tum.cit.aet.artemis.programming.domain.hestia.TestwiseCoverageReportEntry; -import de.tum.cit.aet.artemis.programming.hestia.util.HestiaUtilTestService; import de.tum.cit.aet.artemis.programming.hestia.util.TestwiseCoverageTestUtil; -import de.tum.cit.aet.artemis.programming.icl.AbstractLocalCILocalVCIntegrationTest; -import de.tum.cit.aet.artemis.programming.repository.SolutionProgrammingExerciseParticipationRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.CoverageReportRepository; -import de.tum.cit.aet.artemis.programming.service.hestia.TestwiseCoverageService; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; import de.tum.cit.aet.artemis.programming.util.LocalRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -class TestwiseCoverageReportServiceTest extends AbstractLocalCILocalVCIntegrationTest { +class TestwiseCoverageReportServiceTest extends AbstractProgrammingIntegrationLocalCILocalVCTestBase { private static final String TEST_PREFIX = "testwisecoveragereportservice"; - @Autowired - private TestwiseCoverageService testwiseCoverageService; - - @Autowired - private CoverageReportRepository coverageReportRepository; - - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ProgrammingExerciseTestCaseTestRepository programmingExerciseTestCaseRepository; - - @Autowired - private SolutionProgrammingExerciseParticipationRepository solutionProgrammingExerciseRepository; - - @Autowired - private HestiaUtilTestService hestiaUtilTestService; - - @Autowired - private UserUtilService userUtilService; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ExerciseUtilService exerciseUtilService; - private ProgrammingExercise programmingExercise; private ProgrammingSubmission solutionSubmission; @@ -85,9 +48,9 @@ void setup() throws Exception { solutionRepo); var testCase1 = new ProgrammingExerciseTestCase().testName("test1()").exercise(programmingExercise).active(true).weight(1.0); - programmingExerciseTestCaseRepository.save(testCase1); + testCaseRepository.save(testCase1); var testCase2 = new ProgrammingExerciseTestCase().testName("test2()").exercise(programmingExercise).active(true).weight(1.0); - programmingExerciseTestCaseRepository.save(testCase2); + testCaseRepository.save(testCase2); var solutionParticipation = solutionProgrammingExerciseRepository.findWithEagerResultsAndSubmissionsByProgrammingExerciseId(programmingExercise.getId()).orElseThrow(); solutionSubmission = programmingExerciseUtilService.createProgrammingSubmission(solutionParticipation, false); programmingExercise = programmingExerciseRepository.findByIdElseThrow(programmingExercise.getId()); @@ -110,7 +73,7 @@ void shouldCreateFullTestwiseCoverageReport() { // 18/50 lines covered = 32% assertThat(report.getCoveredLineRatio()).isEqualTo(0.32); - var testCases = programmingExerciseTestCaseRepository.findByExerciseId(programmingExercise.getId()); + var testCases = testCaseRepository.findByExerciseId(programmingExercise.getId()); var testCase1 = testCases.stream().filter(testCase -> "test1()".equals(testCase.getTestName())).findFirst().orElseThrow(); var testCase2 = testCases.stream().filter(testCase -> "test2()".equals(testCase.getTestName())).findFirst().orElseThrow(); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/BehavioralTestCaseServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/BehavioralTestCaseServiceLocalCILocalVCTest.java similarity index 74% rename from src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/BehavioralTestCaseServiceTest.java rename to src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/BehavioralTestCaseServiceLocalCILocalVCTest.java index f646dd652ded..d4de36c789e9 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/BehavioralTestCaseServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/hestia/behavioral/BehavioralTestCaseServiceLocalCILocalVCTest.java @@ -8,13 +8,11 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.assessment.domain.Visibility; import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.core.user.util.UserUtilService; -import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTestBase; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; @@ -25,57 +23,14 @@ import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseSolutionEntry; import de.tum.cit.aet.artemis.programming.domain.hestia.ProgrammingExerciseTestCaseType; import de.tum.cit.aet.artemis.programming.domain.hestia.TestwiseCoverageReportEntry; -import de.tum.cit.aet.artemis.programming.hestia.util.HestiaUtilTestService; -import de.tum.cit.aet.artemis.programming.icl.AbstractLocalCILocalVCIntegrationTest; -import de.tum.cit.aet.artemis.programming.repository.SolutionProgrammingExerciseParticipationRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.CoverageFileReportRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.CoverageReportRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.ProgrammingExerciseGitDiffReportRepository; -import de.tum.cit.aet.artemis.programming.repository.hestia.TestwiseCoverageReportEntryRepository; -import de.tum.cit.aet.artemis.programming.service.hestia.behavioral.BehavioralTestCaseService; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; import de.tum.cit.aet.artemis.programming.util.LocalRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -class BehavioralTestCaseServiceTest extends AbstractLocalCILocalVCIntegrationTest { +class BehavioralTestCaseServiceLocalCILocalVCTest extends AbstractProgrammingIntegrationLocalCILocalVCTestBase { private static final String TEST_PREFIX = "behavioraltestcastservice"; private final LocalRepository solutionRepo = new LocalRepository("main"); - @Autowired - private BehavioralTestCaseService behavioralTestCaseService; - - @Autowired - private HestiaUtilTestService hestiaUtilTestService; - - @Autowired - private ProgrammingExerciseTestCaseTestRepository testCaseRepository; - - @Autowired - private ProgrammingExerciseGitDiffReportRepository programmingExerciseGitDiffReportRepository; - - @Autowired - private SolutionProgrammingExerciseParticipationRepository solutionProgrammingExerciseRepository; - - @Autowired - private CoverageReportRepository coverageReportRepository; - - @Autowired - private CoverageFileReportRepository coverageFileReportRepository; - - @Autowired - private TestwiseCoverageReportEntryRepository testwiseCoverageReportEntryRepository; - - @Autowired - private UserUtilService userUtilService; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ExerciseUtilService exerciseUtilService; - private ProgrammingExercise exercise; @Override @@ -113,7 +68,7 @@ private ProgrammingExerciseGitDiffReport newGitDiffReport() { gitDiffReport.setProgrammingExercise(exercise); gitDiffReport.setSolutionRepositoryCommitHash("123a"); gitDiffReport.setTemplateRepositoryCommitHash("123b"); - gitDiffReport = programmingExerciseGitDiffReportRepository.save(gitDiffReport); + gitDiffReport = reportRepository.save(gitDiffReport); return gitDiffReport; } @@ -124,7 +79,7 @@ private ProgrammingExerciseGitDiffReport addGitDiffEntry(String filePath, int st gitDiffEntry.setLineCount(lineCount); gitDiffEntry.setGitDiffReport(gitDiffReport); gitDiffReport.getEntries().add(gitDiffEntry); - return programmingExerciseGitDiffReportRepository.save(gitDiffReport); + return reportRepository.save(gitDiffReport); } private CoverageReport newCoverageReport() { diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIIntegrationTest.java index 4563d7c9f95c..0be99f7c9aa1 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIIntegrationTest.java @@ -40,9 +40,6 @@ import org.junit.jupiter.api.parallel.ExecutionMode; import org.mockito.ArgumentMatcher; import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.FileSystemResource; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; @@ -53,7 +50,6 @@ import com.github.dockerjava.api.exception.NotFoundException; import com.github.dockerjava.api.model.Frame; import com.hazelcast.collection.IQueue; -import com.hazelcast.core.HazelcastInstance; import com.hazelcast.map.IMap; import de.tum.cit.aet.artemis.assessment.domain.Result; @@ -62,15 +58,12 @@ import de.tum.cit.aet.artemis.core.exception.VersionControlException; import de.tum.cit.aet.artemis.exercise.domain.ExerciseMode; import de.tum.cit.aet.artemis.exercise.domain.Team; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTestBase; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; import de.tum.cit.aet.artemis.programming.domain.RepositoryType; import de.tum.cit.aet.artemis.programming.domain.build.BuildJob; import de.tum.cit.aet.artemis.programming.domain.build.BuildStatus; -import de.tum.cit.aet.artemis.programming.service.BuildLogEntryService; -import de.tum.cit.aet.artemis.programming.service.ParticipationVcsAccessTokenService; -import de.tum.cit.aet.artemis.programming.service.localvc.LocalVCServletService; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingSubmissionTestRepository; import de.tum.cit.aet.artemis.programming.util.LocalRepository; // TestInstance.Lifecycle.PER_CLASS allows all test methods in this class to share the same instance of the test class. @@ -84,32 +77,10 @@ // concurrently. For example, it prevents overloading the LocalCI's result processing system with too many build job results at the same time, which could lead to flaky tests // or timeouts. By keeping everything in the same thread, we maintain more predictable and stable test behavior, while not increasing the test execution time significantly. @Execution(ExecutionMode.SAME_THREAD) -class LocalCIIntegrationTest extends AbstractLocalCILocalVCIntegrationTest { +class LocalCIIntegrationTest extends AbstractProgrammingIntegrationLocalCILocalVCTestBase { private static final String TEST_PREFIX = "localciint"; - @Autowired - private LocalVCServletService localVCServletService; - - @Autowired - private ProgrammingSubmissionTestRepository programmingSubmissionRepository; - - @Autowired - private ParticipationVcsAccessTokenService participationVcsAccessTokenService; - - @Autowired - private BuildLogEntryService buildLogEntryService; - - @Autowired - @Qualifier("hazelcastInstance") - private HazelcastInstance hazelcastInstance; - - @Value("${artemis.user-management.internal-admin.username}") - private String localVCUsername; - - @Value("${artemis.user-management.internal-admin.password}") - private String localVCPassword; - @Override protected String getTestPrefix() { return TEST_PREFIX; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIResourceIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIResourceIntegrationTest.java index e05b87776154..e5c7f52e1413 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIResourceIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIResourceIntegrationTest.java @@ -14,14 +14,11 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.util.LinkedMultiValueMap; import com.hazelcast.collection.IQueue; -import com.hazelcast.core.HazelcastInstance; import com.hazelcast.map.IMap; import de.tum.cit.aet.artemis.assessment.domain.AssessmentType; @@ -33,33 +30,18 @@ import de.tum.cit.aet.artemis.buildagent.dto.FinishedBuildJobDTO; import de.tum.cit.aet.artemis.buildagent.dto.JobTimingInfo; import de.tum.cit.aet.artemis.buildagent.dto.RepositoryInfo; -import de.tum.cit.aet.artemis.buildagent.service.SharedQueueProcessingService; import de.tum.cit.aet.artemis.core.dto.SortingOrder; import de.tum.cit.aet.artemis.core.dto.pageablesearch.PageableSearchDTO; -import de.tum.cit.aet.artemis.core.util.PageableSearchUtilService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTestBase; import de.tum.cit.aet.artemis.programming.domain.RepositoryType; import de.tum.cit.aet.artemis.programming.domain.build.BuildJob; import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry; import de.tum.cit.aet.artemis.programming.domain.build.BuildStatus; -import de.tum.cit.aet.artemis.programming.service.BuildLogEntryService; -class LocalCIResourceIntegrationTest extends AbstractLocalCILocalVCIntegrationTest { +class LocalCIResourceIntegrationTest extends AbstractProgrammingIntegrationLocalCILocalVCTestBase { private static final String TEST_PREFIX = "localciresourceint"; - @Autowired - @Qualifier("hazelcastInstance") - private HazelcastInstance hazelcastInstance; - - @Autowired - private SharedQueueProcessingService sharedQueueProcessingService; - - @Autowired - private BuildLogEntryService buildLogEntryService; - - @Autowired - private PageableSearchUtilService pageableSearchUtilService; - protected BuildJobQueueItem job1; protected BuildJobQueueItem job2; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIResultServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIResultServiceTest.java index a951d065e0cc..abba77e692d2 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIResultServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIResultServiceTest.java @@ -6,19 +6,15 @@ import java.util.Collections; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import de.tum.cit.aet.artemis.core.exception.LocalCIException; -import de.tum.cit.aet.artemis.programming.service.localci.LocalCIResultService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTestBase; import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseFactory; -class LocalCIResultServiceTest extends AbstractLocalCILocalVCIntegrationTest { +class LocalCIResultServiceTest extends AbstractProgrammingIntegrationLocalCILocalVCTestBase { private static final String TEST_PREFIX = "localciresultservice"; - @Autowired - private LocalCIResultService localCIResultService; - @Override protected String getTestPrefix() { return TEST_PREFIX; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIServiceTest.java index b935eb8d2d67..a3fbfd1b2a7b 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalCIServiceTest.java @@ -11,8 +11,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.test.context.support.WithMockUser; @@ -20,56 +18,27 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.hazelcast.collection.IQueue; -import com.hazelcast.core.HazelcastInstance; import com.hazelcast.map.IMap; import de.tum.cit.aet.artemis.buildagent.dto.BuildConfig; import de.tum.cit.aet.artemis.buildagent.dto.BuildJobQueueItem; import de.tum.cit.aet.artemis.buildagent.dto.JobTimingInfo; import de.tum.cit.aet.artemis.buildagent.dto.RepositoryInfo; -import de.tum.cit.aet.artemis.buildagent.service.SharedQueueProcessingService; import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; -import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTest; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseBuildConfig; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.RepositoryType; import de.tum.cit.aet.artemis.programming.dto.CheckoutDirectoriesDTO; -import de.tum.cit.aet.artemis.programming.service.BuildScriptProviderService; -import de.tum.cit.aet.artemis.programming.service.aeolus.AeolusTemplateService; import de.tum.cit.aet.artemis.programming.service.aeolus.Windfile; import de.tum.cit.aet.artemis.programming.service.ci.ContinuousIntegrationService.BuildStatus; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationLocalCILocalVCTest; -class LocalCIServiceTest extends AbstractSpringIntegrationLocalCILocalVCTest { +class LocalCIServiceTest extends AbstractProgrammingIntegrationLocalCILocalVCTest { private static final String TEST_PREFIX = "localciservice"; - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ExerciseUtilService exerciseUtilService; - - @Autowired - private ParticipationUtilService participationUtilService; - - @Autowired - private BuildScriptProviderService buildScriptProviderService; - - @Autowired - private AeolusTemplateService aeolusTemplateService; - - @Autowired - private SharedQueueProcessingService sharedQueueProcessingService; - - @Autowired - @Qualifier("hazelcastInstance") - private HazelcastInstance hazelcastInstance; - protected IQueue queuedJobs; protected IMap processingJobs; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCIntegrationTest.java index 1531a4e2649a..af998a54a556 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCIntegrationTest.java @@ -29,13 +29,14 @@ import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.core.service.ldap.LdapUserDto; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTestBase; import de.tum.cit.aet.artemis.programming.service.localvc.LocalVCRepositoryUri; import de.tum.cit.aet.artemis.programming.util.LocalRepository; /** * This class contains integration tests for edge cases pertaining to the local VC system. */ -class LocalVCIntegrationTest extends AbstractLocalCILocalVCIntegrationTest { +class LocalVCIntegrationTest extends AbstractProgrammingIntegrationLocalCILocalVCTestBase { private static final String TEST_PREFIX = "localvcint"; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCLocalCIIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCLocalCIIntegrationTest.java index 17ca800422e9..ff5b37990d74 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCLocalCIIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCLocalCIIntegrationTest.java @@ -38,24 +38,19 @@ import org.junit.jupiter.api.parallel.ExecutionMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import com.hazelcast.collection.IQueue; -import com.hazelcast.core.HazelcastInstance; import de.tum.cit.aet.artemis.buildagent.dto.BuildJobQueueItem; -import de.tum.cit.aet.artemis.buildagent.service.SharedQueueProcessingService; import de.tum.cit.aet.artemis.core.service.ldap.LdapUserDto; import de.tum.cit.aet.artemis.exam.domain.Exam; import de.tum.cit.aet.artemis.exam.domain.ExerciseGroup; import de.tum.cit.aet.artemis.exam.domain.StudentExam; -import de.tum.cit.aet.artemis.exam.util.ExamUtilService; import de.tum.cit.aet.artemis.exercise.domain.ExerciseMode; import de.tum.cit.aet.artemis.exercise.domain.Team; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTestBase; import de.tum.cit.aet.artemis.programming.domain.AuxiliaryRepository; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; @@ -63,8 +58,6 @@ import de.tum.cit.aet.artemis.programming.domain.build.BuildJob; import de.tum.cit.aet.artemis.programming.domain.submissionpolicy.LockRepositoryPolicy; import de.tum.cit.aet.artemis.programming.domain.submissionpolicy.SubmissionPolicy; -import de.tum.cit.aet.artemis.programming.service.localci.LocalCITriggerService; -import de.tum.cit.aet.artemis.programming.test_repository.BuildJobTestRepository; import de.tum.cit.aet.artemis.programming.util.LocalRepository; /** @@ -83,37 +76,12 @@ // concurrently. For example, it prevents overloading the LocalCI's result processing system with too many build job results at the same time, which could lead to flaky tests // or timeouts. By keeping everything in the same thread, we maintain more predictable and stable test behavior, while not increasing the test execution time significantly. @Execution(ExecutionMode.SAME_THREAD) -class LocalVCLocalCIIntegrationTest extends AbstractLocalCILocalVCIntegrationTest { +class LocalVCLocalCIIntegrationTest extends AbstractProgrammingIntegrationLocalCILocalVCTestBase { private static final Logger log = LoggerFactory.getLogger(LocalVCLocalCIIntegrationTest.class); private static final String TEST_PREFIX = "localvcciint"; - @Autowired - private ExamUtilService examUtilService; - - @Autowired - private BuildJobTestRepository buildJobRepository; - - @Autowired - protected LocalCITriggerService localCITriggerService; - - @Autowired - private SharedQueueProcessingService sharedQueueProcessingService; - - @Autowired - @Qualifier("hazelcastInstance") - private HazelcastInstance hazelcastInstance; - - @Value("${artemis.user-management.internal-admin.username}") - private String localVCUsername; - - @Value("${artemis.user-management.internal-admin.password}") - private String localVCPassword; - - @Value("${artemis.version-control.url}") - protected String artemisVersionControlUrl; - // ---- Repository handles ---- private LocalRepository templateRepository; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCLocalCIParticipationIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCLocalCIParticipationIntegrationTest.java index 7552efe686e0..a7389b52273f 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCLocalCIParticipationIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCLocalCIParticipationIntegrationTest.java @@ -6,39 +6,26 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.domain.User; import de.tum.cit.aet.artemis.exercise.domain.participation.StudentParticipation; -import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTest; import de.tum.cit.aet.artemis.programming.domain.AuthenticationMechanism; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.TemplateProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.domain.VcsAccessLog; import de.tum.cit.aet.artemis.programming.dto.VcsAccessLogDTO; -import de.tum.cit.aet.artemis.programming.repository.VcsAccessLogRepository; import de.tum.cit.aet.artemis.programming.service.localvc.LocalVCRepositoryUri; import de.tum.cit.aet.artemis.programming.util.LocalRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; import de.tum.cit.aet.artemis.programming.web.repository.RepositoryActionType; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationLocalCILocalVCTest; -class LocalVCLocalCIParticipationIntegrationTest extends AbstractSpringIntegrationLocalCILocalVCTest { +class LocalVCLocalCIParticipationIntegrationTest extends AbstractProgrammingIntegrationLocalCILocalVCTest { private static final String TEST_PREFIX = "participationlocalvclocalci"; - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private VcsAccessLogRepository vcsAccessLogRepository; - - @Autowired - private ParticipationUtilService participationUtilService; - private ProgrammingExercise programmingExercise; @BeforeEach diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCServiceTest.java index 9c6c71b4d8af..dea6e48fc3bc 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCServiceTest.java @@ -4,32 +4,19 @@ import static org.mockito.Mockito.verifyNoInteractions; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.service.connectors.ConnectorHealth; import de.tum.cit.aet.artemis.exam.domain.Exam; -import de.tum.cit.aet.artemis.exam.util.ExamUtilService; -import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTest; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationLocalCILocalVCTest; -class LocalVCServiceTest extends AbstractSpringIntegrationLocalCILocalVCTest { +class LocalVCServiceTest extends AbstractProgrammingIntegrationLocalCILocalVCTest { private static final String TEST_PREFIX = "localvcservice"; - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ExerciseUtilService exerciseUtilService; - - @Autowired - private ExamUtilService examUtilService; - @Test void testHealth() { ConnectorHealth health = versionControlService.health(); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCSshIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCSshIntegrationTest.java index bca0ed60beb9..fd4abf09a0d1 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCSshIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCSshIntegrationTest.java @@ -22,10 +22,8 @@ import org.apache.sshd.common.config.keys.AuthorizedKeyEntry; import org.apache.sshd.common.config.keys.writer.openssh.OpenSSHKeyPairResourceWriter; import org.apache.sshd.common.session.helpers.AbstractSession; -import org.apache.sshd.server.SshServer; import org.apache.sshd.server.session.ServerSession; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Profile; import org.springframework.security.test.context.support.WithMockUser; @@ -39,9 +37,6 @@ class LocalVCSshIntegrationTest extends LocalVCIntegrationTest { private static final String TEST_PREFIX = "localvcsshint"; - @Autowired - private SshServer sshServer; - @Override protected String getTestPrefix() { return TEST_PREFIX; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/MultipleHostKeyProviderTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/icl/MultipleHostKeyProviderTest.java index a386ae6ccc2d..db3c01ca029e 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/MultipleHostKeyProviderTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/icl/MultipleHostKeyProviderTest.java @@ -8,10 +8,11 @@ import org.junit.jupiter.api.Test; import org.springframework.context.annotation.Profile; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTestBase; import de.tum.cit.aet.artemis.programming.service.localvc.ssh.MultipleHostKeyProvider; @Profile(PROFILE_LOCALVC) -class MultipleHostKeyProviderTest extends AbstractLocalCILocalVCIntegrationTest { +class MultipleHostKeyProviderTest extends AbstractProgrammingIntegrationLocalCILocalVCTestBase { private static final String TEST_PREFIX = "multiplehostkeyprovider"; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/ProgrammingExerciseLocalVCLocalCIIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/icl/ProgrammingExerciseLocalVCLocalCIIntegrationTest.java index f5ec144c6bf0..9d88101f8fb2 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/ProgrammingExerciseLocalVCLocalCIIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/icl/ProgrammingExerciseLocalVCLocalCIIntegrationTest.java @@ -26,17 +26,13 @@ import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.util.LinkedMultiValueMap; -import de.tum.cit.aet.artemis.atlas.competency.util.CompetencyUtilService; import de.tum.cit.aet.artemis.atlas.domain.competency.Competency; -import de.tum.cit.aet.artemis.core.connector.AeolusRequestMockProvider; import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTest; import de.tum.cit.aet.artemis.programming.domain.AeolusTarget; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; @@ -47,8 +43,6 @@ import de.tum.cit.aet.artemis.programming.service.localvc.LocalVCRepositoryUri; import de.tum.cit.aet.artemis.programming.util.LocalRepository; import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseFactory; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationLocalCILocalVCTest; // TestInstance.Lifecycle.PER_CLASS allows all test methods in this class to share the same instance of the test class. // This reduces the overhead of repeatedly creating and tearing down a new Spring application context for each test method. @@ -61,22 +55,10 @@ // concurrently. For example, it prevents overloading the LocalCI's result processing system with too many build job results at the same time, which could lead to flaky tests // or timeouts. By keeping everything in the same thread, we maintain more predictable and stable test behavior, while not increasing the test execution time significantly. @Execution(ExecutionMode.SAME_THREAD) -class ProgrammingExerciseLocalVCLocalCIIntegrationTest extends AbstractSpringIntegrationLocalCILocalVCTest { +class ProgrammingExerciseLocalVCLocalCIIntegrationTest extends AbstractProgrammingIntegrationLocalCILocalVCTest { private static final String TEST_PREFIX = "progexlocalvclocalci"; - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ParticipationUtilService participationUtilService; - - @Autowired - private AeolusRequestMockProvider aeolusRequestMockProvider; - - @Autowired - private CompetencyUtilService competencyUtilService; - private Course course; private ProgrammingExercise programmingExercise; @@ -91,12 +73,6 @@ class ProgrammingExerciseLocalVCLocalCIIntegrationTest extends AbstractSpringInt private Competency competency; - @Value("${artemis.user-management.internal-admin.username}") - private String localVCUsername; - - @Value("${artemis.user-management.internal-admin.password}") - private String localVCPassword; - @BeforeAll void setupAll() { CredentialsProvider.setDefault(new UsernamePasswordCredentialsProvider(localVCUsername, localVCPassword)); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/SharedQueueManagementServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/icl/SharedQueueManagementServiceTest.java index e0a20b1bed46..f61ff0bce53e 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/SharedQueueManagementServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/icl/SharedQueueManagementServiceTest.java @@ -5,24 +5,13 @@ import java.time.ZonedDateTime; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import com.hazelcast.core.HazelcastInstance; import com.hazelcast.map.IMap; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationLocalCILocalVCTest; import de.tum.cit.aet.artemis.programming.domain.build.BuildJob; -import de.tum.cit.aet.artemis.programming.service.localci.SharedQueueManagementService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationLocalCILocalVCTest; -class SharedQueueManagementServiceTest extends AbstractSpringIntegrationLocalCILocalVCTest { - - @Autowired - private SharedQueueManagementService sharedQueueManagementService; - - @Autowired - @Qualifier("hazelcastInstance") - private HazelcastInstance hazelcastInstance; +class SharedQueueManagementServiceTest extends AbstractProgrammingIntegrationLocalCILocalVCTest { @Test void testPushDockerImageCleanupInfo() { diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/service/BuildLogEntryServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/service/BuildLogEntryServiceTest.java index 04f7854a5b2a..e74c88fff158 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/service/BuildLogEntryServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/service/BuildLogEntryServiceTest.java @@ -12,13 +12,12 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.beans.factory.annotation.Autowired; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; -class BuildLogEntryServiceTest extends AbstractSpringIntegrationIndependentTest { +class BuildLogEntryServiceTest extends AbstractProgrammingIntegrationIndependentTest { private static final String GRADLE_SCENARIO = """ ~~~~~~~~~~~~~~~~~~~~ Pull image progress: Downloading ~~~~~~~~~~~~~~~~~~~~ @@ -331,9 +330,6 @@ class BuildLogEntryServiceTest extends AbstractSpringIntegrationIndependentTest Finished building MTCTSTMVN-ARTEMISADMIN-JOB1-7. """; - @Autowired - private BuildLogEntryService buildLogEntryService; - @ValueSource(strings = { GRADLE_SCENARIO, MAVEN_SCENARIO }) @ParameterizedTest void testScenario(String scenario) { diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/service/GitlabCIServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/service/GitlabCIServiceTest.java index e91e0d1fdb83..fda58121a3ac 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/service/GitlabCIServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/service/GitlabCIServiceTest.java @@ -11,7 +11,6 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -import java.net.URL; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.List; @@ -23,69 +22,24 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.exception.GitLabCIException; -import de.tum.cit.aet.artemis.core.user.util.UserUtilService; import de.tum.cit.aet.artemis.exercise.domain.participation.Participation; -import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; -import de.tum.cit.aet.artemis.exercise.test_repository.ParticipationTestRepository; -import de.tum.cit.aet.artemis.exercise.util.ExerciseUtilService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationGitlabCIGitlabSamlTest; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.ProjectType; import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry; -import de.tum.cit.aet.artemis.programming.repository.BuildLogStatisticsEntryRepository; -import de.tum.cit.aet.artemis.programming.repository.BuildPlanRepository; -import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; import de.tum.cit.aet.artemis.programming.service.ci.ContinuousIntegrationService; -import de.tum.cit.aet.artemis.programming.service.gitlabci.GitLabCIResultService; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationGitlabCIGitlabSamlTest; -class GitlabCIServiceTest extends AbstractSpringIntegrationGitlabCIGitlabSamlTest { +class GitlabCIServiceTest extends AbstractProgrammingIntegrationGitlabCIGitlabSamlTest { private static final String TEST_PREFIX = "gitlabciservicetest"; - @Value("${artemis.version-control.url}") - private URL gitlabServerUrl; - - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository; - - @Autowired - private ParticipationTestRepository participationRepository; - - @Autowired - private BuildPlanRepository buildPlanRepository; - - @Autowired - private GitLabCIResultService gitLabCIResultService; - - @Autowired - private BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository; - - @Autowired - private UserUtilService userUtilService; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ExerciseUtilService exerciseUtilService; - - @Autowired - private ParticipationUtilService participationUtilService; - private Long programmingExerciseId; @BeforeEach diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/service/JenkinsAuthorizationInterceptorTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/service/JenkinsAuthorizationInterceptorTest.java index bd42466d4c6c..00cb27b62167 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/service/JenkinsAuthorizationInterceptorTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/service/JenkinsAuthorizationInterceptorTest.java @@ -8,13 +8,10 @@ import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; import java.net.URISyntaxException; -import java.net.URL; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpRequest; @@ -24,28 +21,17 @@ import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.web.client.MockRestServiceServer; -import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; -import de.tum.cit.aet.artemis.programming.service.jenkins.JenkinsAuthorizationInterceptor; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationJenkinsGitlabTest; -class JenkinsAuthorizationInterceptorTest extends AbstractSpringIntegrationJenkinsGitlabTest { +class JenkinsAuthorizationInterceptorTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { private static final String TEST_PREFIX = "jenkinsauthintercept"; - @Value("${artemis.continuous-integration.url}") - private URL jenkinsServerUrl; - - @Autowired - JenkinsAuthorizationInterceptor jenkinsAuthorizationInterceptor; - - @Autowired - private RestTemplate restTemplate; - private MockRestServiceServer mockRestServiceServer; /** diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/service/JenkinsInternalUriServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/service/JenkinsInternalUriServiceTest.java index 404051085bda..7315a73e4b5c 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/service/JenkinsInternalUriServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/service/JenkinsInternalUriServiceTest.java @@ -14,17 +14,12 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.util.ReflectionTestUtils; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationJenkinsGitlabTest; import de.tum.cit.aet.artemis.programming.domain.VcsRepositoryUri; -import de.tum.cit.aet.artemis.programming.service.jenkins.JenkinsInternalUrlService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; -class JenkinsInternalUriServiceTest extends AbstractSpringIntegrationJenkinsGitlabTest { - - @Autowired - private JenkinsInternalUrlService jenkinsInternalUrlService; +class JenkinsInternalUriServiceTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { private VcsRepositoryUri vcsRepositoryUri; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/service/JenkinsJobPermissionServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/service/JenkinsJobPermissionServiceTest.java index c6db962e6c5f..9eb595ca878d 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/service/JenkinsJobPermissionServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/service/JenkinsJobPermissionServiceTest.java @@ -12,23 +12,18 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.test.context.support.WithMockUser; import org.w3c.dom.DOMException; import org.w3c.dom.Document; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationJenkinsGitlabTest; import de.tum.cit.aet.artemis.programming.service.jenkins.jobs.JenkinsJobPermission; -import de.tum.cit.aet.artemis.programming.service.jenkins.jobs.JenkinsJobPermissionsService; import de.tum.cit.aet.artemis.programming.service.jenkins.jobs.JenkinsJobPermissionsUtils; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; -class JenkinsJobPermissionServiceTest extends AbstractSpringIntegrationJenkinsGitlabTest { +class JenkinsJobPermissionServiceTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { private static final String TEST_PREFIX = "jenkinsjobpermservice"; - @Autowired - private JenkinsJobPermissionsService jenkinsJobPermissionsService; - private static MockedStatic mockedJenkinsJobPermissionsUtils; @BeforeEach diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/service/JenkinsJobServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/service/JenkinsJobServiceTest.java index 738312ba6ecd..8c550e21e210 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/service/JenkinsJobServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/service/JenkinsJobServiceTest.java @@ -21,28 +21,19 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.test.context.support.WithMockUser; import org.w3c.dom.Document; import com.offbytwo.jenkins.model.FolderJob; import de.tum.cit.aet.artemis.core.exception.JenkinsException; -import de.tum.cit.aet.artemis.core.user.util.UserUtilService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationJenkinsGitlabTest; import de.tum.cit.aet.artemis.programming.service.jenkins.JenkinsXmlFileUtils; -import de.tum.cit.aet.artemis.programming.service.jenkins.jobs.JenkinsJobService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; -class JenkinsJobServiceTest extends AbstractSpringIntegrationJenkinsGitlabTest { +class JenkinsJobServiceTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { private static final String TEST_PREFIX = "jenkinsjobservicetest"; - @Autowired - private JenkinsJobService jenkinsJobService; - - @Autowired - private UserUtilService userUtilService; - private static MockedStatic mockedXmlFileUtils; private Document invalidDocument; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/service/JenkinsServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/service/JenkinsServiceTest.java index fa7183716ec8..c6e828b48b3a 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/service/JenkinsServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/service/JenkinsServiceTest.java @@ -24,7 +24,6 @@ import org.junit.jupiter.params.provider.EnumSource; import org.mockito.ArgumentCaptor; import org.mockito.MockedStatic; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.util.StreamUtils; @@ -32,48 +31,17 @@ import com.offbytwo.jenkins.model.JobWithDetails; import de.tum.cit.aet.artemis.core.exception.JenkinsException; -import de.tum.cit.aet.artemis.core.util.CourseUtilService; -import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; -import de.tum.cit.aet.artemis.programming.ContinuousIntegrationTestService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationJenkinsGitlabTest; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseBuildConfig; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.build.BuildPlan; -import de.tum.cit.aet.artemis.programming.repository.BuildPlanRepository; -import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; import de.tum.cit.aet.artemis.programming.service.jenkins.build_plan.JenkinsBuildPlanUtils; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; -class JenkinsServiceTest extends AbstractSpringIntegrationJenkinsGitlabTest { +class JenkinsServiceTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { private static final String TEST_PREFIX = "jenkinsservicetest"; - @Autowired - private ContinuousIntegrationTestService continuousIntegrationTestService; - - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository; - - @Autowired - private ProgrammingExerciseImportService programmingExerciseImportService; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ParticipationUtilService participationUtilService; - - @Autowired - private CourseUtilService courseUtilService; - - @Autowired - private BuildPlanRepository buildPlanRepository; - /** * This method initializes the test case by setting up a local repo */ diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseFeedbackCreationServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseFeedbackCreationServiceTest.java index a8e26befb1d3..fa9ff3a32070 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseFeedbackCreationServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseFeedbackCreationServiceTest.java @@ -10,14 +10,13 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import de.tum.cit.aet.artemis.assessment.domain.Feedback; import de.tum.cit.aet.artemis.assessment.domain.Visibility; import de.tum.cit.aet.artemis.core.config.Constants; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.exam.domain.ExerciseGroup; -import de.tum.cit.aet.artemis.exam.util.ExamUtilService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationIndependentTest; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; @@ -27,32 +26,9 @@ import de.tum.cit.aet.artemis.programming.dto.AbstractBuildResultNotificationDTO; import de.tum.cit.aet.artemis.programming.dto.StaticCodeAnalysisIssue; import de.tum.cit.aet.artemis.programming.dto.StaticCodeAnalysisReportDTO; -import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseFactory; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; -class ProgrammingExerciseFeedbackCreationServiceTest extends AbstractSpringIntegrationIndependentTest { - - @Autowired - private ProgrammingExerciseFeedbackCreationService feedbackCreationService; - - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository; - - @Autowired - private ProgrammingExerciseTestCaseTestRepository testCaseRepository; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ExamUtilService examUtilService; +class ProgrammingExerciseFeedbackCreationServiceTest extends AbstractProgrammingIntegrationIndependentTest { private ProgrammingExercise programmingExercise; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/service/RepositoryAccessServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/service/RepositoryAccessServiceTest.java index 8d5106407a89..9c64b9e2845e 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/service/RepositoryAccessServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/service/RepositoryAccessServiceTest.java @@ -14,51 +14,24 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.domain.User; import de.tum.cit.aet.artemis.core.exception.AccessForbiddenException; -import de.tum.cit.aet.artemis.core.test_repository.UserTestRepository; -import de.tum.cit.aet.artemis.core.user.util.UserUtilService; import de.tum.cit.aet.artemis.core.util.TestConstants; -import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationJenkinsGitlabTest; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; import de.tum.cit.aet.artemis.programming.domain.submissionpolicy.LockRepositoryPolicy; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseFactory; -import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; import de.tum.cit.aet.artemis.programming.web.repository.RepositoryActionType; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; -class RepositoryAccessServiceTest extends AbstractSpringIntegrationJenkinsGitlabTest { +class RepositoryAccessServiceTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { private static final String TEST_PREFIX = "rastest"; - @Autowired - private UserTestRepository userRepository; - - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private RepositoryAccessService repositoryAccessService; - - @Autowired - private UserUtilService userUtilService; - - @Autowired - private ProgrammingExerciseUtilService programmingExerciseUtilService; - - @Autowired - private ParticipationUtilService participationUtilService; - - @Autowired - private ProgrammingExerciseGradingService programmingExerciseGradingService; - User student; Course course; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/service/connectors/gitlab/GitLabPersonalAccessTokenManagementServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/service/connectors/gitlab/GitLabPersonalAccessTokenManagementServiceTest.java index e6a8ed1e7eb8..d9f5c658ddb8 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/service/connectors/gitlab/GitLabPersonalAccessTokenManagementServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/service/connectors/gitlab/GitLabPersonalAccessTokenManagementServiceTest.java @@ -21,7 +21,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpMethod; import org.springframework.security.test.context.support.WithMockUser; @@ -30,26 +29,14 @@ import org.springframework.web.client.RestTemplate; import de.tum.cit.aet.artemis.core.domain.User; -import de.tum.cit.aet.artemis.core.test_repository.UserTestRepository; -import de.tum.cit.aet.artemis.core.user.util.UserUtilService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationJenkinsGitlabTest; import de.tum.cit.aet.artemis.programming.service.gitlab.GitLabException; -import de.tum.cit.aet.artemis.programming.service.gitlab.GitLabPersonalAccessTokenManagementService; import de.tum.cit.aet.artemis.programming.service.gitlab.dto.GitLabPersonalAccessTokenListResponseDTO; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; -class GitLabPersonalAccessTokenManagementServiceTest extends AbstractSpringIntegrationJenkinsGitlabTest { +class GitLabPersonalAccessTokenManagementServiceTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { private static final String TEST_PREFIX = "gitlabusermanagementservice"; - @Autowired - private GitLabPersonalAccessTokenManagementService gitLabPersonalAccessTokenManagementService; - - @Autowired - private UserTestRepository userRepository; - - @Autowired - private UserUtilService userUtilService; - @BeforeEach void setUp() { gitlabRequestMockProvider.enableMockingOfRequests(); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/service/connectors/jenkins/build_plan/JenkinsPipelineScriptCreatorTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/service/connectors/jenkins/build_plan/JenkinsPipelineScriptCreatorTest.java index 81110e30059c..34afe9865fd2 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/service/connectors/jenkins/build_plan/JenkinsPipelineScriptCreatorTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/service/connectors/jenkins/build_plan/JenkinsPipelineScriptCreatorTest.java @@ -6,36 +6,15 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import de.tum.cit.aet.artemis.core.util.CourseUtilService; +import de.tum.cit.aet.artemis.programming.AbstractProgrammingIntegrationJenkinsGitlabTest; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseBuildConfig; import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; import de.tum.cit.aet.artemis.programming.domain.ProjectType; import de.tum.cit.aet.artemis.programming.domain.build.BuildPlan; -import de.tum.cit.aet.artemis.programming.repository.BuildPlanRepository; -import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; -import de.tum.cit.aet.artemis.programming.service.jenkins.build_plan.JenkinsPipelineScriptCreator; -import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; -import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationJenkinsGitlabTest; -class JenkinsPipelineScriptCreatorTest extends AbstractSpringIntegrationJenkinsGitlabTest { - - @Autowired - private BuildPlanRepository buildPlanRepository; - - @Autowired - private JenkinsPipelineScriptCreator jenkinsPipelineScriptCreator; - - @Autowired - private ProgrammingExerciseTestRepository programmingExerciseRepository; - - @Autowired - private ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository; - - @Autowired - private CourseUtilService courseUtilService; +class JenkinsPipelineScriptCreatorTest extends AbstractProgrammingIntegrationJenkinsGitlabTest { private ProgrammingExercise programmingExercise; diff --git a/src/test/java/de/tum/cit/aet/artemis/shared/architecture/module/AbstractModuleTestArchitectureTest.java b/src/test/java/de/tum/cit/aet/artemis/shared/architecture/module/AbstractModuleTestArchitectureTest.java index 66da61a07636..b88a82887ac7 100644 --- a/src/test/java/de/tum/cit/aet/artemis/shared/architecture/module/AbstractModuleTestArchitectureTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/shared/architecture/module/AbstractModuleTestArchitectureTest.java @@ -1,26 +1,82 @@ package de.tum.cit.aet.artemis.shared.architecture.module; -import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noMembers; +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; + +import java.util.Set; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import com.tngtech.archunit.base.DescribedPredicate; +import com.tngtech.archunit.core.domain.JavaClass; +import com.tngtech.archunit.core.domain.JavaField; +import com.tngtech.archunit.core.domain.JavaModifier; +import com.tngtech.archunit.lang.ArchCondition; +import com.tngtech.archunit.lang.ConditionEvents; +import com.tngtech.archunit.lang.SimpleConditionEvent; + import de.tum.cit.aet.artemis.shared.architecture.AbstractArchitectureTest; -public abstract class AbstractModuleTestArchitectureTest extends AbstractArchitectureTest implements ModuleArchitectureTest { +public abstract class AbstractModuleTestArchitectureTest extends AbstractArchitectureTest implements ModuleArchitectureTest { - abstract protected Class getAbstractModuleIntegrationTestClass(); + protected abstract Set> getAbstractModuleIntegrationTestClasses(); @Test void integrationTestsShouldExtendAbstractModuleIntegrationTest() { - classesOfThisModuleThat().haveSimpleNameEndingWith("IntegrationTest").should().beAssignableTo(getAbstractModuleIntegrationTestClass()) - .because("All integration tests should extend %s".formatted(getAbstractModuleIntegrationTestClass())).check(testClasses); + classesOfThisModuleThat().haveSimpleNameEndingWith("IntegrationTest").should().beAssignableTo(isAssignableToAnyAllowedClass(getAbstractModuleIntegrationTestClasses())) + .because("All integration tests should extend any of %s".formatted(getAbstractModuleIntegrationTestClasses())).check(testClasses); } @Test void integrationTestsShouldNotAutowireMembers() { - noMembers().that().areAnnotatedWith(Autowired.class).should().beDeclaredInClassesThat().areAssignableTo(getAbstractModuleIntegrationTestClass()).andShould() - .notBeDeclaredIn(getAbstractModuleIntegrationTestClass()) - .because("Integration tests should not autowire members in any class that inherits from %s".formatted(getAbstractModuleIntegrationTestClass())).check(testClasses); + classes().that().doNotHaveModifier(JavaModifier.ABSTRACT).and().areAssignableTo(isAssignableToAnyAllowedClass(getAbstractModuleIntegrationTestClasses())) + .should(notHaveAutowiredFieldsOrMethods()) + .because("Integration tests should not autowire members in any class that inherits from any of %s".formatted(getAbstractModuleIntegrationTestClasses())) + .check(testClasses); + } + + private static DescribedPredicate isAssignableToAnyAllowedClass(Iterable> allowedClasses) { + return new DescribedPredicate<>(stringifyClasses(allowedClasses)) { + + @Override + public boolean test(JavaClass javaClass) { + for (Class allowedClass : allowedClasses) { + if (javaClass.isAssignableTo(allowedClass)) { + return true; + } + } + return false; + } + }; + } + + private static ArchCondition notHaveAutowiredFieldsOrMethods() { + return new ArchCondition<>("not have @Autowired fields or methods") { + + @Override + public void check(JavaClass javaClass, ConditionEvents events) { + // Check fields for @Autowired + for (JavaField field : javaClass.getFields()) { + if (field.isAnnotatedWith(Autowired.class)) { + String message = String.format("%s has a field %s annotated with @Autowired", javaClass.getName(), field.getName()); + events.add(SimpleConditionEvent.violated(field, message)); + } + } + + // Check methods for @Autowired + javaClass.getMethods().stream().filter(method -> method.isAnnotatedWith(Autowired.class)).forEach(method -> { + String message = String.format("%s has a method %s annotated with @Autowired", javaClass.getName(), method.getName()); + events.add(SimpleConditionEvent.violated(method, message)); + }); + } + }; + } + + private static String stringifyClasses(Iterable> classes) { + StringBuilder stringBuilder = new StringBuilder(); + for (Class clazz : classes) { + stringBuilder.append(clazz.getSimpleName()).append(", "); + } + return stringBuilder.substring(0, stringBuilder.length() - 2); } } diff --git a/src/test/java/de/tum/cit/aet/artemis/tutorialgroup/architecture/TutorialGroupTestArchitectureTest.java b/src/test/java/de/tum/cit/aet/artemis/tutorialgroup/architecture/TutorialGroupTestArchitectureTest.java index cd7ad9b0ad12..6aea11fdd6fe 100644 --- a/src/test/java/de/tum/cit/aet/artemis/tutorialgroup/architecture/TutorialGroupTestArchitectureTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/tutorialgroup/architecture/TutorialGroupTestArchitectureTest.java @@ -1,9 +1,11 @@ package de.tum.cit.aet.artemis.tutorialgroup.architecture; +import java.util.Set; + import de.tum.cit.aet.artemis.shared.architecture.module.AbstractModuleTestArchitectureTest; import de.tum.cit.aet.artemis.tutorialgroup.AbstractTutorialGroupIntegrationTest; -class TutorialGroupTestArchitectureTest extends AbstractModuleTestArchitectureTest { +class TutorialGroupTestArchitectureTest extends AbstractModuleTestArchitectureTest { @Override public String getModulePackage() { @@ -11,7 +13,7 @@ public String getModulePackage() { } @Override - protected Class getAbstractModuleIntegrationTestClass() { - return AbstractTutorialGroupIntegrationTest.class; + protected Set> getAbstractModuleIntegrationTestClasses() { + return Set.of(AbstractTutorialGroupIntegrationTest.class); } } diff --git a/src/test/javascript/spec/component/competencies/competency-management/competency-management-table.component.spec.ts b/src/test/javascript/spec/component/competencies/competency-management/competency-management-table.component.spec.ts index d38ba571bcdd..e15cefdbc52d 100644 --- a/src/test/javascript/spec/component/competencies/competency-management/competency-management-table.component.spec.ts +++ b/src/test/javascript/spec/component/competencies/competency-management/competency-management-table.component.spec.ts @@ -53,7 +53,6 @@ describe('CompetencyManagementTableComponent', () => { it('should handle import all data', () => { component.courseCompetencies = []; - component.relations = []; const responseBody: CompetencyWithTailRelationDTO[] = [ { competency: { id: 1 }, tailRelations: [] }, @@ -62,7 +61,6 @@ describe('CompetencyManagementTableComponent', () => { component.updateDataAfterImportAll(responseBody); expect(component.courseCompetencies).toHaveLength(2); - expect(component.relations).toHaveLength(1); }); it('should handle delete competency', () => { @@ -72,7 +70,6 @@ describe('CompetencyManagementTableComponent', () => { const competency2 = { id: 2, type: CourseCompetencyType.COMPETENCY }; component.service = competencyService; component.courseCompetencies = [competency1, competency2]; - component.relations = [{ id: 1, headCompetency: competency1, tailCompetency: competency2, type: CompetencyRelationType.ASSUMES }]; component.deleteCompetency(1); expect(deleteSpy).toHaveBeenCalledOnce(); diff --git a/src/test/javascript/spec/component/competencies/competency-management/competency-management.component.spec.ts b/src/test/javascript/spec/component/competencies/competency-management/competency-management.component.spec.ts index b0305c56b76d..a21a670a38fa 100644 --- a/src/test/javascript/spec/component/competencies/competency-management/competency-management.component.spec.ts +++ b/src/test/javascript/spec/component/competencies/competency-management/competency-management.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { MockComponent, MockDirective, MockPipe, MockProvider } from 'ng-mocks'; import { of } from 'rxjs'; -import { Competency, CompetencyRelation, CompetencyWithTailRelationDTO, CourseCompetencyProgress, CourseCompetencyType } from 'app/entities/competency.model'; +import { Competency, CompetencyWithTailRelationDTO, CourseCompetencyProgress, CourseCompetencyType } from 'app/entities/competency.model'; import { CompetencyManagementComponent } from 'app/course/competencies/competency-management/competency-management.component'; import { ActivatedRoute, provideRouter } from '@angular/router'; import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; @@ -24,7 +24,6 @@ import { IrisSettingsService } from 'app/iris/settings/shared/iris-settings.serv import { ProfileInfo } from 'app/shared/layouts/profiles/profile-info.model'; import { IrisCourseSettings } from 'app/entities/iris/settings/iris-settings.model'; import { PROFILE_IRIS } from 'app/app.constants'; -import { CompetencyRelationGraphStubComponent } from './competency-relation-graph-stub.component'; import { Prerequisite } from 'app/entities/prerequisite.model'; import { CompetencyManagementTableComponent } from 'app/course/competencies/competency-management/competency-management-table.component'; import { CourseCompetencyApiService } from 'app/course/competencies/services/course-competency-api.service'; @@ -42,7 +41,6 @@ describe('CompetencyManagementComponent', () => { let modalService: NgbModal; let getAllForCourseSpy: any; - let getCompetencyRelationsSpy: any; beforeEach(() => { TestBed.configureTestingModule({ @@ -50,7 +48,6 @@ describe('CompetencyManagementComponent', () => { declarations: [ CompetencyManagementComponent, MockHasAnyAuthorityDirective, - CompetencyRelationGraphStubComponent, MockComponent(DocumentationButtonComponent), MockComponent(ImportAllCompetenciesComponent), MockComponent(CompetencyManagementTableComponent), @@ -103,7 +100,6 @@ describe('CompetencyManagementComponent', () => { type: CourseCompetencyType.PREREQUISITE, } as Prerequisite, ]); - getCompetencyRelationsSpy = jest.spyOn(courseCompetencyApiService, 'getCourseCompetencyRelations').mockResolvedValue([{ id: 1 } as CompetencyRelation]); }); }); @@ -139,7 +135,6 @@ describe('CompetencyManagementComponent', () => { await component.loadData(); expect(getAllForCourseSpy).toHaveBeenCalledOnce(); - expect(getCompetencyRelationsSpy).toHaveBeenCalledOnce(); expect(component.competencies).toHaveLength(2); expect(component.prerequisites).toHaveLength(1); @@ -170,7 +165,6 @@ describe('CompetencyManagementComponent', () => { jest.spyOn(courseCompetencyApiService, 'importAllByCourseId').mockResolvedValue(importedCompetencies); await component.loadData(); const existingCompetencies = component.competencies.length; - const existingRelations = component.relations.length; const importButton = fixture.debugElement.query(By.css('#courseCompetencyImportAllButton')); importButton.nativeElement.click(); @@ -184,80 +178,5 @@ describe('CompetencyManagementComponent', () => { await fixture.whenStable(); expect(component.competencies).toHaveLength(existingCompetencies + 2); - expect(component.relations).toHaveLength(existingRelations + 1); - }); - - it('should handle create relation callback', async () => { - const relation: CompetencyRelation = { id: 1 }; - jest.spyOn(courseCompetencyApiService, 'createCourseCompetencyRelation').mockResolvedValue(relation); - - fixture.detectChanges(); - await fixture.whenStable(); - - const existingRelations = component.relations.length; - - const relationGraph: CompetencyRelationGraphStubComponent = fixture.debugElement.query(By.directive(CompetencyRelationGraphStubComponent)).componentInstance; - expect(relationGraph).toBeDefined(); - relationGraph.onCreateRelation.emit(relation); - - fixture.detectChanges(); - await fixture.whenStable(); - - expect(component.relations).toHaveLength(existingRelations + 1); - }); - - it('should handle remove relation callback', () => { - const modalRef = { - result: Promise.resolve(), - componentInstance: {}, - } as NgbModalRef; - jest.spyOn(modalService, 'open').mockReturnValue(modalRef); - - fixture.detectChanges(); - - const relationGraph: CompetencyRelationGraphStubComponent = fixture.debugElement.query(By.directive(CompetencyRelationGraphStubComponent)).componentInstance; - relationGraph.onRemoveRelation.emit(1); - fixture.detectChanges(); - - expect(modalService.open).toHaveBeenCalledOnce(); - }); - - it('should remove relation', async () => { - jest.spyOn(courseCompetencyApiService, 'deleteCourseCompetencyRelation').mockResolvedValue(); - fixture.detectChanges(); - component.relations = [{ id: 1, headCompetency: { id: 5 }, tailCompetency: { id: 3 } }]; - - fixture.detectChanges(); - await fixture.whenStable(); - fixture.detectChanges(); - - expect(component.relations).toHaveLength(1); - - await component['removeRelation'](1); - - expect(component.relations).toHaveLength(0); - }); - - it('should remove competency and its relation', () => { - component.competencies = [ - { id: 1, type: CourseCompetencyType.COMPETENCY }, - { id: 2, type: CourseCompetencyType.COMPETENCY }, - ]; - component.prerequisites = [{ id: 3, type: CourseCompetencyType.PREREQUISITE }]; - component.courseCompetencies = component.competencies.concat(component.prerequisites); - component.relations = [ - { id: 1, tailCompetency: component.competencies.first(), headCompetency: component.competencies.last() }, - { id: 2, tailCompetency: component.competencies.last(), headCompetency: component.prerequisites.first() }, - { id: 3, tailCompetency: component.prerequisites.first(), headCompetency: component.competencies.first() }, - ]; - - component.onRemoveCompetency(2); - - expect(component.relations).toHaveLength(1); - expect(component.relations.first()?.id).toBe(3); - expect(component.competencies).toHaveLength(1); - expect(component.competencies.first()?.id).toBe(1); - expect(component.prerequisites).toHaveLength(1); - expect(component.prerequisites.first()?.id).toBe(3); }); }); diff --git a/src/test/javascript/spec/component/competencies/competency-management/competency-relation-graph-stub.component.ts b/src/test/javascript/spec/component/competencies/competency-management/competency-relation-graph-stub.component.ts deleted file mode 100644 index af5d302a6e1f..000000000000 --- a/src/test/javascript/spec/component/competencies/competency-management/competency-relation-graph-stub.component.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Component, EventEmitter, Output, input } from '@angular/core'; -import { Competency, CompetencyRelation } from 'app/entities/competency.model'; - -@Component({ - selector: 'jhi-competency-relation-graph', - template: '', -}) -export class CompetencyRelationGraphStubComponent { - competencies = input([]); - relations = input([]); - - @Output() onRemoveRelation = new EventEmitter(); - @Output() onCreateRelation = new EventEmitter(); -} diff --git a/src/test/javascript/spec/component/competencies/competency-management/competency-relation-graph.component.spec.ts b/src/test/javascript/spec/component/competencies/competency-management/competency-relation-graph.component.spec.ts deleted file mode 100644 index c60016292749..000000000000 --- a/src/test/javascript/spec/component/competencies/competency-management/competency-relation-graph.component.spec.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { ArtemisTestModule } from '../../../test.module'; -import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { MockDirective, MockPipe } from 'ng-mocks'; -import { Component } from '@angular/core'; -import { Competency, CompetencyRelation, CompetencyRelationError, CompetencyRelationType } from 'app/entities/competency.model'; -import { CompetencyRelationGraphComponent } from 'app/course/competencies/competency-management/competency-relation-graph.component'; -import { NgbAccordionBody, NgbAccordionButton, NgbAccordionCollapse, NgbAccordionDirective, NgbAccordionHeader, NgbAccordionItem } from '@ng-bootstrap/ng-bootstrap'; -import { Edge, Node } from '@swimlane/ngx-graph'; - -// eslint-disable-next-line @angular-eslint/component-selector -@Component({ selector: 'ngx-graph', template: '' }) -class NgxGraphStubComponent {} - -describe('CompetencyRelationGraphComponent', () => { - let componentFixture: ComponentFixture; - let component: CompetencyRelationGraphComponent; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule], - declarations: [ - CompetencyRelationGraphComponent, - MockPipe(ArtemisTranslatePipe), - MockDirective(NgbAccordionDirective), - MockDirective(NgbAccordionItem), - MockDirective(NgbAccordionHeader), - MockDirective(NgbAccordionButton), - MockDirective(NgbAccordionCollapse), - MockDirective(NgbAccordionBody), - NgxGraphStubComponent, - ], - providers: [], - }) - .compileComponents() - .then(() => { - componentFixture = TestBed.createComponent(CompetencyRelationGraphComponent); - component = componentFixture.componentInstance; - }); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should initialize', () => { - componentFixture.detectChanges(); - expect(component).toBeDefined(); - }); - - it('should correctly update nodes and edges from input', () => { - componentFixture.componentRef.setInput('competencies', [createCompetency(1, 'Competency 1'), createCompetency(2, 'Competency 2')]); - componentFixture.componentRef.setInput('relations', [createRelation(1, 1, 2, CompetencyRelationType.EXTENDS)]); - componentFixture.detectChanges(); - - const expectedNodes: Node[] = [ - { id: '1', label: 'Competency 1' }, - { id: '2', label: 'Competency 2' }, - ]; - const expectedEdges: Edge[] = [{ id: 'edge1', source: '1', target: '2', label: CompetencyRelationType.EXTENDS, data: { id: 1 } }]; - - expect(component.nodes()).toEqual(expectedNodes); - expect(component.edges()).toEqual(expectedEdges); - }); - - it('should create competency relation', () => { - componentFixture.componentRef.setInput('competencies', [createCompetency(1, 'Competency 1'), createCompetency(2, 'Competency 2')]); - component.tailCompetencyId = 1; - component.headCompetencyId = 2; - component.relationType = CompetencyRelationType.ASSUMES; - const relation: CompetencyRelation = { - tailCompetency: { id: component.tailCompetencyId }, - headCompetency: { id: component.headCompetencyId }, - type: component.relationType, - }; - const onCreateRelationSpy = jest.spyOn(component.onCreateRelation, 'emit'); - - componentFixture.detectChanges(); - - component.createRelation(); - expect(onCreateRelationSpy).toHaveBeenCalledWith(relation); - }); - - it('should not competency relation on error', () => { - component.tailCompetencyId = 1; - component.headCompetencyId = 1; - component.relationType = CompetencyRelationType.ASSUMES; - const onCreateRelationSpy = jest.spyOn(component.onCreateRelation, 'emit'); - const validateSpy = jest.spyOn(component, 'validate'); - - component.createRelation(); - expect(onCreateRelationSpy).not.toHaveBeenCalled(); - expect(validateSpy).toHaveBeenCalledOnce(); - }); - - it('should remove competency relation', () => { - const onRemoveRelationSpy = jest.spyOn(component.onRemoveRelation, 'emit'); - - component.removeRelation({ source: '123', data: { id: 456 } } as Edge); - expect(onRemoveRelationSpy).toHaveBeenCalledWith(456); - }); - - it('should detect circles on relations', () => { - const competencies = [createCompetency(16, '16'), createCompetency(17, '17'), createCompetency(18, '18')]; - const relations = [createRelation(1, 16, 17, CompetencyRelationType.EXTENDS), createRelation(1, 17, 18, CompetencyRelationType.MATCHES)]; - componentFixture.componentRef.setInput('competencies', competencies); - componentFixture.componentRef.setInput('relations', relations); - componentFixture.detectChanges(); - - component.tailCompetencyId = 18; - component.headCompetencyId = 16; - component.relationType = CompetencyRelationType.ASSUMES; - - component.validate(); - - expect(component.relationError).toBe(CompetencyRelationError.CIRCULAR); - }); - - it('should not detect circles on arbitrary relations', () => { - const competencies = [createCompetency(16, '16'), createCompetency(17, '17')]; - const relations: CompetencyRelation[] = []; - componentFixture.componentRef.setInput('competencies', competencies); - componentFixture.componentRef.setInput('relations', relations); - componentFixture.detectChanges(); - - component.tailCompetencyId = 17; - component.headCompetencyId = 16; - component.relationType = CompetencyRelationType.ASSUMES; - - component.validate(); - - expect(component.relationError).toBeUndefined(); - }); - - it('should prevent creating already existing relations', () => { - const competencies = [createCompetency(16, '16'), createCompetency(17, '17')]; - const relations = [createRelation(1, 16, 17, CompetencyRelationType.EXTENDS)]; - componentFixture.componentRef.setInput('competencies', competencies); - componentFixture.componentRef.setInput('relations', relations); - componentFixture.detectChanges(); - - component.tailCompetencyId = 16; - component.headCompetencyId = 17; - component.relationType = CompetencyRelationType.EXTENDS; - - component.validate(); - - expect(component.relationError).toBe(CompetencyRelationError.EXISTING); - }); - - it('should prevent creating self relations', () => { - const competencies = [createCompetency(16, '16'), createCompetency(17, '17')]; - const relations: CompetencyRelation[] = []; - componentFixture.componentRef.setInput('competencies', competencies); - componentFixture.componentRef.setInput('relations', relations); - componentFixture.detectChanges(); - - component.tailCompetencyId = 16; - component.headCompetencyId = 16; - component.relationType = CompetencyRelationType.EXTENDS; - - component.validate(); - - expect(component.relationError).toBe(CompetencyRelationError.SELF); - }); - - it('should zoom to fit and center on centerView', () => { - const zoomToFitStub = jest.spyOn(component.zoomToFit$, 'next'); - const centerStub = jest.spyOn(component.center$, 'next'); - componentFixture.detectChanges(); - component.centerView(); - expect(zoomToFitStub).toHaveBeenCalledExactlyOnceWith({ autoCenter: true }); - expect(centerStub).toHaveBeenCalledExactlyOnceWith(true); - }); - - function createCompetency(id: number, title: string) { - const competency: Competency = { - id: id, - title: title, - }; - return competency; - } - - function createRelation(id: number, tailCompetencyId: number, headCompetencyId: number, relationType: CompetencyRelationType) { - const relation: CompetencyRelation = { - id: id, - tailCompetency: { id: tailCompetencyId }, - headCompetency: { id: headCompetencyId }, - type: relationType, - }; - return relation; - } -}); diff --git a/src/test/javascript/spec/component/competencies/components/course-competencies-relation-graph.component.spec.ts b/src/test/javascript/spec/component/competencies/components/course-competencies-relation-graph.component.spec.ts new file mode 100644 index 000000000000..561f3ae5bd07 --- /dev/null +++ b/src/test/javascript/spec/component/competencies/components/course-competencies-relation-graph.component.spec.ts @@ -0,0 +1,93 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { CourseCompetenciesRelationGraphComponent } from 'app/course/competencies/components/course-competencies-relation-graph/course-competencies-relation-graph.component'; +import { CompetencyRelationDTO, CompetencyRelationType, CourseCompetency, CourseCompetencyType } from 'app/entities/competency.model'; +import { MockTranslateService } from '../../../helpers/mocks/service/mock-translate.service'; +import { TranslateService } from '@ngx-translate/core'; + +describe('CourseCompetenciesRelationGraphComponent', () => { + let component: CourseCompetenciesRelationGraphComponent; + let fixture: ComponentFixture; + + const courseCompetencies: CourseCompetency[] = [ + { id: 1, type: CourseCompetencyType.COMPETENCY, title: 'Competency' }, + { id: 2, type: CourseCompetencyType.PREREQUISITE, title: 'Prerequisite' }, + ]; + + const relations: CompetencyRelationDTO[] = [ + { + id: 1, + relationType: CompetencyRelationType.EXTENDS, + tailCompetencyId: 1, + headCompetencyId: 2, + }, + ]; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [CourseCompetenciesRelationGraphComponent, NoopAnimationsModule], + providers: [ + { + provide: TranslateService, + useClass: MockTranslateService, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(CourseCompetenciesRelationGraphComponent); + component = fixture.componentInstance; + + fixture.componentRef.setInput('courseCompetencies', courseCompetencies); + fixture.componentRef.setInput('relations', relations); + fixture.componentRef.setInput('selectedRelationId', 1); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should initialize', async () => { + expect(component).toBeDefined(); + expect(component.courseCompetencies()).toEqual(courseCompetencies); + expect(component.relations()).toEqual(relations); + }); + + it('should map edges correctly', () => { + expect(component.edges()).toEqual( + relations.map((relation) => { + return { + id: 'edge-' + relation.id!.toString(), + source: relation.headCompetencyId!.toString(), + target: relation.tailCompetencyId!.toString(), + label: relation.relationType, + data: { + id: relation.id, + }, + }; + }), + ); + }); + + it('should map nodes correctly', () => { + fixture.detectChanges(); + + expect(component.nodes()).toEqual( + courseCompetencies.map((cc) => { + return { + id: cc.id!.toString(), + label: cc.title, + data: { + id: cc.id, + type: cc.type, + }, + }; + }), + ); + }); + + it('should update node dimension', () => { + fixture.detectChanges(); + component['setNodeDimension']({ id: '1', dimension: { width: 0, height: 45.59 } }); + expect(component.nodes().find((node) => node.id === '1')?.dimension).toEqual({ width: 0, height: 45.59 }); + }); +}); diff --git a/src/test/javascript/spec/component/competencies/components/course-competencies-relation-modal.component.spec.ts b/src/test/javascript/spec/component/competencies/components/course-competencies-relation-modal.component.spec.ts new file mode 100644 index 000000000000..a3a99fd7a25e --- /dev/null +++ b/src/test/javascript/spec/component/competencies/components/course-competencies-relation-modal.component.spec.ts @@ -0,0 +1,129 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { CourseCompetenciesRelationModalComponent } from 'app/course/competencies/components/course-competencies-relation-modal/course-competencies-relation-modal.component'; +import { CourseCompetencyApiService } from 'app/course/competencies/services/course-competency-api.service'; +import { AlertService } from 'app/core/util/alert.service'; +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; +import { MockAlertService } from '../../../helpers/mocks/service/mock-alert.service'; +import { CompetencyRelationDTO, CompetencyRelationType, CourseCompetency, CourseCompetencyType } from 'app/entities/competency.model'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { MockNgbActiveModalService } from '../../../helpers/mocks/service/mock-ngb-active-modal.service'; +import { TranslateService } from '@ngx-translate/core'; +import { MockTranslateService } from '../../../helpers/mocks/service/mock-translate.service'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +describe('CourseCompetenciesRelationModalComponent', () => { + let component: CourseCompetenciesRelationModalComponent; + let fixture: ComponentFixture; + let courseCompetencyApiService: CourseCompetencyApiService; + let alertService: AlertService; + let activeModal: NgbActiveModal; + + const courseId = 1; + const courseCompetencies: CourseCompetency[] = [ + { id: 1, type: CourseCompetencyType.COMPETENCY }, + { id: 2, type: CourseCompetencyType.PREREQUISITE }, + ]; + const relations: CompetencyRelationDTO[] = [ + { + id: 1, + relationType: CompetencyRelationType.EXTENDS, + tailCompetencyId: 1, + headCompetencyId: 2, + }, + ]; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [CourseCompetenciesRelationModalComponent, NoopAnimationsModule], + providers: [ + provideHttpClient(), + provideHttpClientTesting(), + { + provide: NgbActiveModal, + useClass: MockNgbActiveModalService, + }, + { + provide: TranslateService, + useClass: MockTranslateService, + }, + { + provide: AlertService, + useClass: MockAlertService, + }, + { + provide: CourseCompetencyApiService, + useValue: { + getCourseCompetencyRelationsByCourseId: jest.fn(), + }, + }, + ], + }).compileComponents(); + + courseCompetencyApiService = TestBed.inject(CourseCompetencyApiService); + alertService = TestBed.inject(AlertService); + activeModal = TestBed.inject(NgbActiveModal); + + fixture = TestBed.createComponent(CourseCompetenciesRelationModalComponent); + component = fixture.componentInstance; + + jest.spyOn(courseCompetencyApiService, 'getCourseCompetencyRelationsByCourseId').mockResolvedValue(relations); + + fixture.componentRef.setInput('courseId', courseId); + fixture.componentRef.setInput('courseCompetencies', courseCompetencies); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should initialize', () => { + expect(component).toBeTruthy(); + }); + + it('should load relations', async () => { + fixture.detectChanges(); + await fixture.whenStable(); + + expect(component.relations()).toEqual(relations); + }); + + it('should show alert on error', async () => { + const errorSpy = jest.spyOn(alertService, 'addAlert'); + jest.spyOn(courseCompetencyApiService, 'getCourseCompetencyRelationsByCourseId').mockReturnValue(Promise.reject(new Error('Error'))); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(errorSpy).toHaveBeenCalledOnce(); + }); + + it('should set isLoading correctly', async () => { + const isLoadingSpy = jest.spyOn(component.isLoading, 'set'); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(isLoadingSpy).toHaveBeenNthCalledWith(1, true); + expect(isLoadingSpy).toHaveBeenNthCalledWith(2, false); + }); + + it('should closeModal', () => { + const closeSpy = jest.spyOn(activeModal, 'close'); + + component['closeModal'](); + + expect(closeSpy).toHaveBeenCalledOnce(); + }); + + it('should call selectCourseCompetency on courseCompetencyRelationFormComponent with valid courseCompetencyId', () => { + fixture.detectChanges(); + + const courseCompetencyId = 1; + const selectSpy = jest.spyOn(component['courseCompetencyRelationFormComponent'](), 'selectCourseCompetency'); + + component['selectCourseCompetency'](courseCompetencyId); + + expect(selectSpy).toHaveBeenCalledExactlyOnceWith(courseCompetencyId); + }); +}); diff --git a/src/test/javascript/spec/component/competencies/components/course-competency-relation-form.component.spec.ts b/src/test/javascript/spec/component/competencies/components/course-competency-relation-form.component.spec.ts new file mode 100644 index 000000000000..aed1e03612f8 --- /dev/null +++ b/src/test/javascript/spec/component/competencies/components/course-competency-relation-form.component.spec.ts @@ -0,0 +1,364 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { CourseCompetencyRelationFormComponent, UnionFind } from 'app/course/competencies/components/course-competency-relation-form/course-competency-relation-form.component'; +import { AlertService } from 'app/core/util/alert.service'; +import { MockAlertService } from '../../../helpers/mocks/service/mock-alert.service'; +import { CourseCompetencyApiService } from 'app/course/competencies/services/course-competency-api.service'; +import { MockTranslateService } from '../../../helpers/mocks/service/mock-translate.service'; +import { TranslateService } from '@ngx-translate/core'; +import { CompetencyRelationDTO, CompetencyRelationType, CourseCompetency, UpdateCourseCompetencyRelationDTO } from 'app/entities/competency.model'; + +describe('CourseCompetencyRelationFormComponent', () => { + let component: CourseCompetencyRelationFormComponent; + let fixture: ComponentFixture; + let courseCompetencyApiService: CourseCompetencyApiService; + let alertService: AlertService; + + let createCourseCompetencyRelationSpy: jest.SpyInstance; + let updateCourseCompetencyRelationSpy: jest.SpyInstance; + let deleteCourseCompetencyRelationSpy: jest.SpyInstance; + + const courseId = 1; + const courseCompetencies: CourseCompetency[] = [ + { id: 1, title: 'Competency 1' }, + { id: 2, title: 'Competency 2' }, + { id: 3, title: 'Competency 3' }, + ]; + const relations: CompetencyRelationDTO[] = [ + { + id: 1, + tailCompetencyId: 1, + headCompetencyId: 2, + relationType: CompetencyRelationType.EXTENDS, + }, + ]; + const selectedRelationId = 1; + + const newRelation = { + id: 2, + headCompetencyId: 2, + tailCompetencyId: 3, + relationType: CompetencyRelationType.EXTENDS, + }; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [CourseCompetencyRelationFormComponent], + providers: [ + { + provide: AlertService, + useClass: MockAlertService, + }, + { + provide: TranslateService, + useClass: MockTranslateService, + }, + { + provide: CourseCompetencyApiService, + useValue: { + createCourseCompetencyRelation: jest.fn(), + updateCourseCompetencyRelation: jest.fn(), + deleteCourseCompetencyRelation: jest.fn(), + }, + }, + ], + }).compileComponents(); + + courseCompetencyApiService = TestBed.inject(CourseCompetencyApiService); + alertService = TestBed.inject(AlertService); + + createCourseCompetencyRelationSpy = jest.spyOn(courseCompetencyApiService, 'createCourseCompetencyRelation').mockResolvedValue(newRelation); + updateCourseCompetencyRelationSpy = jest.spyOn(courseCompetencyApiService, 'updateCourseCompetencyRelation').mockResolvedValue(); + deleteCourseCompetencyRelationSpy = jest.spyOn(courseCompetencyApiService, 'deleteCourseCompetencyRelation').mockResolvedValue(); + + fixture = TestBed.createComponent(CourseCompetencyRelationFormComponent); + component = fixture.componentInstance; + + fixture.componentRef.setInput('courseId', courseId); + fixture.componentRef.setInput('courseCompetencies', courseCompetencies); + fixture.componentRef.setInput('relations', relations); + fixture.componentRef.setInput('selectedRelationId', undefined); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should set relationAlreadyExists correctly', () => { + component.headCompetencyId.set(2); + component.tailCompetencyId.set(1); + component.relationType.set(CompetencyRelationType.ASSUMES); + + fixture.detectChanges(); + + expect(component.relationAlreadyExists()).toBeTrue(); + }); + + it('should set exactRelationAlreadyExists correctly', () => { + component.headCompetencyId.set(2); + component.tailCompetencyId.set(1); + component.relationType.set(CompetencyRelationType.EXTENDS); + + fixture.detectChanges(); + + expect(component.exactRelationAlreadyExists()).toBeTrue(); + }); + + it('should select relation if selectedRelationId is set', () => { + fixture.componentRef.setInput('selectedRelationId', selectedRelationId); + + fixture.detectChanges(); + + expect(component.headCompetencyId()).toBe(2); + expect(component.tailCompetencyId()).toBe(1); + expect(component.relationType()).toBe(CompetencyRelationType.EXTENDS); + }); + + it('should set headCompetencyId if it is undefined', () => { + component.headCompetencyId.set(undefined); + component.tailCompetencyId.set(2); + + component.selectCourseCompetency(1); + + expect(component.headCompetencyId()).toBe(1); + expect(component.tailCompetencyId()).toBeUndefined(); + }); + + it('should set tailCompetencyId if headCompetencyId is defined and tailCompetencyId is undefined', () => { + component.headCompetencyId.set(1); + component.tailCompetencyId.set(undefined); + + component.selectCourseCompetency(2); + + expect(component.tailCompetencyId()).toBe(2); + }); + + it('should reset headCompetencyId if both headCompetencyId and tailCompetencyId are defined', () => { + component.headCompetencyId.set(1); + component.tailCompetencyId.set(2); + + component.selectCourseCompetency(3); + + expect(component.headCompetencyId()).toBe(3); + expect(component.tailCompetencyId()).toBeUndefined(); + }); + + it('should create relation', async () => { + component.headCompetencyId.set(2); + component.tailCompetencyId.set(3); + component.relationType.set(CompetencyRelationType.EXTENDS); + + await component['createRelation'](); + + expect(createCourseCompetencyRelationSpy).toHaveBeenCalledExactlyOnceWith(courseId, { + headCompetencyId: 2, + tailCompetencyId: 3, + relationType: CompetencyRelationType.EXTENDS, + }); + expect(component.headCompetencyId()).toBe(2); + expect(component.tailCompetencyId()).toBe(3); + expect(component.relationType()).toBe(CompetencyRelationType.EXTENDS); + expect(component.selectedRelationId()).toBe(2); + expect(component.relations()).toEqual([...relations, newRelation]); + }); + + it('should set isLoading correctly when creating a relation', async () => { + const isLoadingSpy = jest.spyOn(component.isLoading, 'set'); + + component.headCompetencyId.set(2); + component.tailCompetencyId.set(3); + component.relationType.set(CompetencyRelationType.EXTENDS); + + await component['createRelation'](); + + expect(isLoadingSpy).toHaveBeenNthCalledWith(1, true); + expect(isLoadingSpy).toHaveBeenNthCalledWith(2, false); + }); + + it('should show error when creating relation fails', async () => { + const error = 'Error creating relation'; + createCourseCompetencyRelationSpy.mockRejectedValue(error); + const alertServiceErrorSpy = jest.spyOn(alertService, 'error'); + + component.headCompetencyId.set(2); + component.tailCompetencyId.set(3); + component.relationType.set(CompetencyRelationType.EXTENDS); + + await component['createRelation'](); + + expect(alertServiceErrorSpy).toHaveBeenCalledOnce(); + }); + + it('should update relation', async () => { + fixture.componentRef.setInput('selectedRelationId', selectedRelationId); + + fixture.detectChanges(); + + component.relationType.set(CompetencyRelationType.ASSUMES); + + await component['updateRelation'](); + + expect(updateCourseCompetencyRelationSpy).toHaveBeenCalledExactlyOnceWith(courseId, selectedRelationId, { + newRelationType: CompetencyRelationType.ASSUMES, + }); + const newRelations = [...relations].map((relation) => { + if (relation.id === selectedRelationId) { + return { ...relation, relationType: CompetencyRelationType.ASSUMES }; + } + return relation; + }); + expect(component.relations()).toEqual(newRelations); + }); + + it('should set isLoading correctly when updating a relation', async () => { + const isLoadingSpy = jest.spyOn(component.isLoading, 'set'); + fixture.componentRef.setInput('selectedRelationId', selectedRelationId); + + fixture.detectChanges(); + + component.relationType.set(CompetencyRelationType.ASSUMES); + + await component['updateRelation'](); + + expect(isLoadingSpy).toHaveBeenNthCalledWith(1, true); + expect(isLoadingSpy).toHaveBeenNthCalledWith(2, false); + }); + + it('should show error when updating relation fails', async () => { + updateCourseCompetencyRelationSpy.mockRejectedValue('Error updating relation'); + const alertServiceErrorSpy = jest.spyOn(alertService, 'error'); + fixture.componentRef.setInput('selectedRelationId', selectedRelationId); + + fixture.detectChanges(); + + component.relationType.set(CompetencyRelationType.ASSUMES); + + await component['updateRelation'](); + + expect(alertServiceErrorSpy).toHaveBeenCalledOnce(); + }); + + it('should select head course competency', () => { + component['selectHeadCourseCompetency'](2); + + expect(component.headCompetencyId()).toBe(2); + expect(component.tailCompetencyId()).toBeUndefined(); + expect(component.selectedRelationId()).toBeUndefined(); + }); + + it('should set tailCompetencyId and selectedRelationId when an existing relation is found', async () => { + fixture.detectChanges(); + await fixture.whenStable(); + + const tailId = 1; + component.headCompetencyId.set(2); + component.relationType.set(CompetencyRelationType.EXTENDS); + + component['selectTailCourseCompetency'](tailId); + + expect(component.tailCompetencyId()).toBe(1); + expect(component.selectedRelationId()).toBe(1); + }); + + it('should set tailCompetencyId and clear selectedRelationId when no existing relation is found', async () => { + fixture.detectChanges(); + await fixture.whenStable(); + + const tailId = 2; + component.headCompetencyId.set(3); + component.relationType.set(CompetencyRelationType.EXTENDS); + + component['selectTailCourseCompetency'](tailId); + + expect(component.tailCompetencyId()).toBe(2); + expect(component.selectedRelationId()).toBeUndefined(); + }); + + it('should not allow to create circular dependencies', () => { + component.headCompetencyId.set(1); + component.tailCompetencyId.set(1); + component.relationType.set(CompetencyRelationType.EXTENDS); + + expect(component['selectableTailCourseCompetencyIds']).not.toContain(1); + expect(component.showCircularDependencyError()).toBeTrue(); + }); + + it('should delete relation', async () => { + fixture.componentRef.setInput('selectedRelationId', selectedRelationId); + + fixture.detectChanges(); + + await component['deleteRelation'](); + + expect(deleteCourseCompetencyRelationSpy).toHaveBeenCalledExactlyOnceWith(courseId, selectedRelationId); + expect(component.relations()).toHaveLength(relations.length - 1); + }); + + it('should set isLoading correctly when deleting a relation', async () => { + const isLoadingSpy = jest.spyOn(component.isLoading, 'set'); + fixture.componentRef.setInput('selectedRelationId', selectedRelationId); + + fixture.detectChanges(); + + await component['deleteRelation'](); + + expect(isLoadingSpy).toHaveBeenNthCalledWith(1, true); + expect(isLoadingSpy).toHaveBeenNthCalledWith(2, false); + }); + + it('should show error when deleting relation fails', async () => { + deleteCourseCompetencyRelationSpy.mockRejectedValue('Error deleting relation'); + const alertServiceErrorSpy = jest.spyOn(alertService, 'error'); + fixture.componentRef.setInput('selectedRelationId', selectedRelationId); + + fixture.detectChanges(); + + await component['deleteRelation'](); + + expect(alertServiceErrorSpy).toHaveBeenCalledOnce(); + }); +}); + +describe('UnionFind', () => { + let unionFind: UnionFind; + + beforeEach(() => { + unionFind = new UnionFind(5); + }); + + it('should initialize parent and rank arrays correctly', () => { + expect(unionFind.parent).toEqual([0, 1, 2, 3, 4]); + expect(unionFind.rank).toEqual([1, 1, 1, 1, 1]); + }); + + it('should find the representative of a set', () => { + expect(unionFind.find(0)).toBe(0); + expect(unionFind.find(1)).toBe(1); + }); + + it('should perform union by rank correctly', () => { + unionFind.union(0, 1); + expect(unionFind.find(0)).toBe(unionFind.find(1)); + }); + + it('should perform path compression correctly', () => { + unionFind.union(0, 1); + unionFind.union(1, 2); + expect(unionFind.find(2)).toBe(0); + expect(unionFind.parent[2]).toBe(0); + }); + + it('should handle union of already connected components', () => { + unionFind.union(0, 1); + unionFind.union(1, 2); + unionFind.union(0, 2); + expect(unionFind.find(2)).toBe(0); + }); + + it('should handle union of components with equal rank', () => { + unionFind.union(0, 1); + unionFind.union(2, 3); + unionFind.union(1, 2); + expect(unionFind.find(3)).toBe(0); + expect(unionFind.rank[0]).toBe(3); // Corrected expected rank value + }); +}); diff --git a/src/test/javascript/spec/component/competencies/components/course-competency-relation-node.component.spec.ts b/src/test/javascript/spec/component/competencies/components/course-competency-relation-node.component.spec.ts new file mode 100644 index 000000000000..a44fb57502e2 --- /dev/null +++ b/src/test/javascript/spec/component/competencies/components/course-competency-relation-node.component.spec.ts @@ -0,0 +1,50 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { CourseCompetencyType } from 'app/entities/competency.model'; +import { Node } from '@swimlane/ngx-graph'; +import { MockTranslateService } from '../../../helpers/mocks/service/mock-translate.service'; +import { TranslateService } from '@ngx-translate/core'; +import { CourseCompetencyRelationNodeComponent } from 'app/course/competencies/components/course-competency-relation-node/course-competency-relation-node.component'; + +describe('CourseCompetencyRelationNodeComponent', () => { + let component: CourseCompetencyRelationNodeComponent; + let fixture: ComponentFixture; + + const node: Node = { + id: '1', + label: 'Competency 1', + data: { + type: CourseCompetencyType.COMPETENCY, + }, + }; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [CourseCompetencyRelationNodeComponent], + providers: [ + { + provide: TranslateService, + useClass: MockTranslateService, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(CourseCompetencyRelationNodeComponent); + component = fixture.componentInstance; + + fixture.componentRef.setInput('courseCompetencyNode', node); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should initialize and emit size update', async () => { + const sizeUpdateEmitSpy = jest.spyOn(component.onSizeSet, 'emit'); + + fixture.detectChanges(); + + expect(component).toBeTruthy(); + expect(component.courseCompetencyNode()).toEqual(node); + expect(sizeUpdateEmitSpy).toHaveBeenCalledOnce(); + }); +}); diff --git a/src/test/javascript/spec/component/competencies/components/import-course-competencies-modal/import-all-course-competencies-modal.component.spec.ts b/src/test/javascript/spec/component/competencies/components/import-all-course-competencies-modal.component.spec.ts similarity index 96% rename from src/test/javascript/spec/component/competencies/components/import-course-competencies-modal/import-all-course-competencies-modal.component.spec.ts rename to src/test/javascript/spec/component/competencies/components/import-all-course-competencies-modal.component.spec.ts index eb4ecd81c175..40e81a50cf6f 100644 --- a/src/test/javascript/spec/component/competencies/components/import-course-competencies-modal/import-all-course-competencies-modal.component.spec.ts +++ b/src/test/javascript/spec/component/competencies/components/import-all-course-competencies-modal.component.spec.ts @@ -2,7 +2,7 @@ import '@angular/localize/init'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { ImportAllCourseCompetenciesModalComponent } from 'app/course/competencies/components/import-all-course-competencies-modal/import-all-course-competencies-modal.component'; -import { MockTranslateService } from '../../../../helpers/mocks/service/mock-translate.service'; +import { MockTranslateService } from '../../../helpers/mocks/service/mock-translate.service'; import { TranslateService } from '@ngx-translate/core'; import { provideHttpClient } from '@angular/common/http'; import { provideHttpClientTesting } from '@angular/common/http/testing'; @@ -71,7 +71,7 @@ describe('ImportAllCourseCompetenciesModalComponent', () => { const course = { id: 2, title: 'Course 02', shortName: 'C2' }; component.selectCourse(course); - expect(closeModalSpy).toHaveBeenCalledWith({ + expect(closeModalSpy).toHaveBeenCalledExactlyOnceWith({ course: course, courseCompetencyImportOptions: { sourceCourseId: course.id, diff --git a/src/test/javascript/spec/component/competencies/components/import-course-competencies-settings/import-course-competencies-settings.component.spec.ts b/src/test/javascript/spec/component/competencies/components/import-course-competencies-settings.component.spec.ts similarity index 98% rename from src/test/javascript/spec/component/competencies/components/import-course-competencies-settings/import-course-competencies-settings.component.spec.ts rename to src/test/javascript/spec/component/competencies/components/import-course-competencies-settings.component.spec.ts index 1d9d6356c01a..3777ba7b1a3d 100644 --- a/src/test/javascript/spec/component/competencies/components/import-course-competencies-settings/import-course-competencies-settings.component.spec.ts +++ b/src/test/javascript/spec/component/competencies/components/import-course-competencies-settings.component.spec.ts @@ -3,7 +3,7 @@ import { CourseCompetencyImportSettings, ImportCourseCompetenciesSettingsComponent, } from 'app/course/competencies/components/import-course-competencies-settings/import-course-competencies-settings.component'; -import { MockTranslateService } from '../../../../helpers/mocks/service/mock-translate.service'; +import { MockTranslateService } from '../../../helpers/mocks/service/mock-translate.service'; import { TranslateService } from '@ngx-translate/core'; describe('ImportCourseCompetenciesSettingsComponent', () => { diff --git a/src/test/javascript/spec/service/course-competency/course-competency-api.service.spec.ts b/src/test/javascript/spec/service/course-competency/course-competency-api.service.spec.ts index e641f31f9ae7..fc33eb3fff62 100644 --- a/src/test/javascript/spec/service/course-competency/course-competency-api.service.spec.ts +++ b/src/test/javascript/spec/service/course-competency/course-competency-api.service.spec.ts @@ -2,7 +2,7 @@ import { provideHttpClient } from '@angular/common/http'; import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; import { CourseCompetencyApiService } from 'app/course/competencies/services/course-competency-api.service'; -import { CompetencyRelation, CourseCompetencyImportOptionsDTO } from 'app/entities/competency.model'; +import { CompetencyRelation, CompetencyRelationType, CourseCompetencyImportOptionsDTO, UpdateCourseCompetencyRelationDTO } from 'app/entities/competency.model'; describe('CourseCompetencyApiService', () => { let httpClient: HttpTestingController; @@ -42,6 +42,18 @@ describe('CourseCompetencyApiService', () => { await methodCall; }); + it('should update course competency relation', async () => { + const relationId = 1; + const relationType = CompetencyRelationType.EXTENDS; + const methodCall = courseCompetencyApiService.updateCourseCompetencyRelation(courseId, relationId, { newRelationType: relationType }); + const response = httpClient.expectOne({ + method: 'PATCH', + url: `${baseUrl}/courses/${courseId}/course-competencies/relations/${relationId}`, + }); + response.flush({}); + await methodCall; + }); + it('should create course competency relation', async () => { const relation = { tailCompetencyId: 1, diff --git a/src/test/resources/config/application.yml b/src/test/resources/config/application.yml index 16c56a619dd9..484e5f300ba1 100644 --- a/src/test/resources/config/application.yml +++ b/src/test/resources/config/application.yml @@ -74,6 +74,8 @@ artemis: default: "~~invalid~~" c_plus_plus: default: "~~invalid~~" + typescript: + default: "~~invalid~~" spring: application: