From ef946cf3e5dff810005c595305cdb5aea9eecd8e Mon Sep 17 00:00:00 2001 From: Tristan Verbeken Date: Thu, 16 May 2024 12:47:47 +0200 Subject: [PATCH 1/9] Added docker zip file uploading --- .../DockerSubmissionTestModel.java | 29 ++++++++++++++++++ .../docker/DockerSubmissionTestTest.java | 20 ++++++++++++ .../DockerSubmissionTestTest/helloworld.zip | Bin 0 -> 886 bytes 3 files changed, 49 insertions(+) create mode 100644 backend/app/src/test/test-cases/DockerSubmissionTestTest/helloworld.zip diff --git a/backend/app/src/main/java/com/ugent/pidgeon/model/submissionTesting/DockerSubmissionTestModel.java b/backend/app/src/main/java/com/ugent/pidgeon/model/submissionTesting/DockerSubmissionTestModel.java index 7e67c969..7722f13d 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/model/submissionTesting/DockerSubmissionTestModel.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/model/submissionTesting/DockerSubmissionTestModel.java @@ -14,6 +14,7 @@ import java.io.File; import java.io.FileReader; import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; @@ -56,6 +57,7 @@ public DockerSubmissionTestModel(String dockerImage) { new File(localMountFolder + "input/").mkdirs(); new File(localMountFolder + "output/").mkdirs(); new File(localMountFolder + "artifacts/").mkdirs(); + new File(localMountFolder + "utils/").mkdirs(); } @@ -86,6 +88,33 @@ public void addInputFiles(File[] files) { } } + public void addUtilFiles(Path pathToZip){ + // first unzip files to the utils folder + try { + ZipFile zipFile = new ZipFile(pathToZip.toFile()); + Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + File entryDestination = new File(localMountFolder + "utils/", entry.getName()); + if (entry.isDirectory()) { + entryDestination.mkdirs(); + } else { + File parent = entryDestination.getParentFile(); + if (parent != null) { + parent.mkdirs(); + } + try { + FileUtils.copyInputStreamToFile(zipFile.getInputStream(entry), entryDestination); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + public void addZipInputFiles(ZipFile zipFile) { Enumeration entries = zipFile.entries(); while (entries.hasMoreElements()) { diff --git a/backend/app/src/test/java/com/ugent/pidgeon/docker/DockerSubmissionTestTest.java b/backend/app/src/test/java/com/ugent/pidgeon/docker/DockerSubmissionTestTest.java index e0507156..3f8dac56 100644 --- a/backend/app/src/test/java/com/ugent/pidgeon/docker/DockerSubmissionTestTest.java +++ b/backend/app/src/test/java/com/ugent/pidgeon/docker/DockerSubmissionTestTest.java @@ -11,6 +11,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.nio.file.Path; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -215,4 +216,23 @@ void isValidTemplate(){ + "bruh\n")); } + @Test + void testDockerReceivesUtilFiles(){ + DockerSubmissionTestModel stm = new DockerSubmissionTestModel("alpine:latest"); + Path zipLocation = Path.of("src/test/test-cases/DockerSubmissionTestTest/d__test.zip"); // simple zip with one file + Path zipLocation2 = Path.of("src/test/test-cases/DockerSubmissionTestTest/helloworld.zip"); // complicated zip with multiple files and folder structure + stm.addUtilFiles(zipLocation); + stm.addUtilFiles(zipLocation2); + DockerTestOutput to = stm.runSubmission("find /shared/utils/"); + List logs = to.logs.stream().map(log -> log.replaceAll("\n", "")).toList(); + assertEquals("/shared/utils/", logs.get(0)); + assertEquals("/shared/utils/helloworld.txt", logs.get(1)); + assertEquals("/shared/utils/helloworld", logs.get(2)); + assertEquals("/shared/utils/helloworld/helloworld2.txt", logs.get(3)); // I don't understand the order of find :sob: but it is important all files are found. + assertEquals("/shared/utils/helloworld/helloworld3.txt", logs.get(4)); + assertEquals("/shared/utils/helloworld/emptyfolder", logs.get(5)); + assertEquals("/shared/utils/helloworld/helloworld1.txt", logs.get(6)); + stm.cleanUp(); + } + } diff --git a/backend/app/src/test/test-cases/DockerSubmissionTestTest/helloworld.zip b/backend/app/src/test/test-cases/DockerSubmissionTestTest/helloworld.zip new file mode 100644 index 0000000000000000000000000000000000000000..54fb8fc7ae27c2c4ff6513da0bc005d0ca724a22 GIT binary patch literal 886 zcmWIWW@h1H0D&jT8zR6AD8bDj!;q1hlapVbUzC%g9~#2Rz?}WSHLVGVODnh;7+GF0 zGcbUO0JtHm(F~D7H3VUvkzPqf3D`_Vpm`vS)6BKRnrRF-lND$l2;(#p-LvAT?o7=s zD5*@#&q+xwLU?>1J_A=0>qtYmBmdoZO#@*JM=~uQBj~Q<;^Faaf x7S(*zSR@=cm~jX*927Wqj3}Xv2pnjP0?o%7o2+a=S23^x;W?lQlYp3k0RZ@o!;Js{ literal 0 HcmV?d00001 From f7ae28f041b269538e9bedc82e87ab2db62f9caa Mon Sep 17 00:00:00 2001 From: Aqua-sc <108478185+Aqua-sc@users.noreply.github.com> Date: Fri, 17 May 2024 08:15:00 +0200 Subject: [PATCH 2/9] Update populate_database.sql --- backend/database/populate_database.sql | 97 ++++++-------------------- 1 file changed, 22 insertions(+), 75 deletions(-) diff --git a/backend/database/populate_database.sql b/backend/database/populate_database.sql index f0e1d506..c5644482 100644 --- a/backend/database/populate_database.sql +++ b/backend/database/populate_database.sql @@ -20,46 +20,26 @@ INSERT INTO courses (course_id,course_name, description, course_year) VALUES -- Inserting into `course_users` -- Assume course_id and user_id start from 1 and match accordingly INSERT INTO course_users (course_id, user_id, course_relation) VALUES - (1, 1, 'enrolled'), - (2, 1, 'enrolled'), - (3, 2, 'creator'), - (4, 3, 'course_admin'), - (5, 4, 'enrolled'); - --- Inserting into `files` --- Assume files are uploaded by different users -INSERT INTO files (file_path, file_name, uploaded_by) VALUES - ('/path/to/file1', 'file1.txt', 1), - ('/path/to/file2', 'file2.txt', 2), - ('/path/to/file3', 'file3.txt', 3), - ('/path/to/file4', 'file4.txt', 4), - ('/path/to/file5', 'file5.txt', 1), - ('/path/to/file6', 'file6.txt', 2), - ('/path/to/file7', 'file7.txt', 3), - ('/path/to/file8', 'file8.txt', 4), - ('/path/to/file9', 'file9.txt', 1), - ('/path/to/file10', 'file10.txt', 2), - ('/path/to/file11', 'file11.txt', 3), - ('/path/to/file12', 'file12.txt', 4), - ('/path/to/file13', 'file13.txt', 5), - ('/path/to/file14', 'file14.txt', 4), - ('/path/to/file15', 'file15.txt', 5), - ('/path/to/file16', 'file16.txt', 1), - ('/path/to/file17', 'file17.txt', 2), - ('/path/to/file18', 'file18.txt', 3), - ('/path/to/file19', 'file19.txt', 4), - ('/path/to/file20', 'file20.txt', 1), - ('/path/to/file21', 'file21.txt', 2), - ('/path/to/file22', 'file22.txt', 3); + (1, 1, 'creator'), + (2, 1, 'enrolled'), + (2, 2, 'creator'), + (3, 2, 'creator'), + (4, 3, 'creator'), + (5, 4, 'creator'); -- Inserting into `group_clusters` INSERT INTO group_clusters (course_id, cluster_name, max_size, group_amount) VALUES - (1, 'Project: priemgetallen', 4, 20), - (2, 'Analyse van alkanen', 3, 10), - (3, 'Groepswerk industriƫle revolutie', 5, 13), - (4, 'Linux practica', 2, 100), - (5, 'Review: A shaskespeare story', 3, 30); + (1, 'Project: priemgetallen', 4, 0), + (2, 'Analyse van alkanen', 3, 0), + (3, 'Groepswerk industriƫle revolutie', 5, 0), + (4, 'Linux practica', 2, 0), + (5, 'Review: A shaskespeare story', 3, 0), + (1, 'Students', 1, 0), + (2, 'Students', 1, 0), + (3, 'Students', 1, 0), + (4, 'Students', 1, 0), + (5, 'Students', 1, 0); -- Inserting into `groups` INSERT INTO groups (group_name, group_cluster) VALUES @@ -67,16 +47,13 @@ INSERT INTO groups (group_name, group_cluster) VALUES ('Group 2', 2), ('Group 3', 3), ('Group 4', 4), - ('Group 5', 5); + ('Group 5', 5), + ('Naam van degene die het script heeft uitgevoerd', 7); -- Inserting into `group_users` -- Linking users to groups, assuming group_id and user_id start from 1 INSERT INTO group_users (group_id, user_id) VALUES - (1, 1), - (2, 2), - (3, 3), - (4, 4), - (5, 5); + (6, 1); @@ -133,36 +110,6 @@ def global_multiple_alignment(infile: str | Path, output: str | Path | None = No ```', 2, 6, '2024-06-22 12:00:00+02'), (3, null, 'History Essay 1', 'Discuss historical event', 3, NULL, '2024-03-22 12:00:00+02'), (4, null, 'Programming Assignment 1', 'Write code', 4, 4, '2024-03-23 14:45:00+02'), - (5, null, 'Literature Analysis', 'Analyze text', 5, 10, '2024-03-24 10:00:00+02'); - - --- Inserting into `group_grades` --- Assign grades to group solutions -INSERT INTO group_feedback(group_id, project_id, grade, feedback) VALUES - (1, 1, 95.0, ''), - (2, 2, 88.5, ''), - (3, 3, NULL, ''), - (4, 4, 89.0, ''), - (5, 5, 94.5, ''); - - -INSERT INTO submissions ( - project_id, - group_id, - file_id, - structure_accepted, - docker_accepted, - structure_feedback, - docker_feedback, - docker_test_state, - docker_type -) VALUES - (1, 1, 1, true, true, NULL, NULL, 'finished', 'simple'), - (2, 2, 2, false, true, 'ERROR: .....', NULL, 'finished', 'simple'), - (3, 3, 3, true, false, NULL, 'Docker configuration needs improvement', 'finished', 'simple'), - (4, 4, 4, false, false, 'Structure needs improvement', 'Docker configuration needs improvement', 'finished', 'simple'); - - - --- Makes user with id 1 the creator of courses -UPDATE course_users SET course_relation = 'creator' WHERE user_id = 1 + (5, null, 'Literature Analysis', 'Analyze text', 5, 10, '2024-03-24 10:00:00+02'), + (1, null, 'Individueel project', 'Beschrijving voor individueel project', 6, 20, '2024-05-22 12:00:00+02'), + (2, null, 'Individueel project', 'Beschrijving voor individueel project', 7, 20, '2024-05-22 12:00:00+02'); From 9aac6db85df2d3669e0a41faabb36f091732add1 Mon Sep 17 00:00:00 2001 From: Aqua-sc <108478185+Aqua-sc@users.noreply.github.com> Date: Fri, 17 May 2024 08:57:52 +0200 Subject: [PATCH 3/9] Uploading/deleting/getting extra testfiles --- .../controllers/SubmissionController.java | 20 +--- .../pidgeon/controllers/TestController.java | 98 ++++++++++++++++++- .../ugent/pidgeon/model/json/TestJson.java | 16 ++- .../pidgeon/postgre/models/TestEntity.java | 11 +++ .../pidgeon/util/EntityToJsonConverter.java | 3 +- .../com/ugent/pidgeon/util/Filehandler.java | 33 ++++++- .../controllers/SubmissionControllerTest.java | 2 +- .../ugent/pidgeon/util/FileHandlerTest.java | 12 +-- backend/database/start_database.sql | 3 +- 9 files changed, 161 insertions(+), 37 deletions(-) diff --git a/backend/app/src/main/java/com/ugent/pidgeon/controllers/SubmissionController.java b/backend/app/src/main/java/com/ugent/pidgeon/controllers/SubmissionController.java index 39bdca89..22092865 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/controllers/SubmissionController.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/controllers/SubmissionController.java @@ -186,7 +186,7 @@ public ResponseEntity submitFile(@RequestParam("file") MultipartFile file, @P //Save the file on the server String filename = file.getOriginalFilename(); Path path = Filehandler.getSubmissionPath(projectid, groupId, submission.getId()); - File savedFile = Filehandler.saveSubmission(path, file); + File savedFile = Filehandler.saveFile(path, file, Filehandler.SUBMISSION_FILENAME); String pathname = path.resolve(Filehandler.SUBMISSION_FILENAME).toString(); //Update name and path for the file entry @@ -297,23 +297,7 @@ public ResponseEntity getSubmissionFile(@PathVariable("submissionid") long su } // Get the file from the server - try { - Resource zipFile = Filehandler.getFileAsResource(Path.of(file.getPath())); - if (zipFile == null) { - return ResponseEntity.status(HttpStatus.NOT_FOUND).body("File not found."); - } - - // Set headers for the response - HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getName()); - headers.add(HttpHeaders.CONTENT_TYPE, "application/zip"); - - return ResponseEntity.ok() - .headers(headers) - .body(zipFile); - } catch (Exception e) { - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage()); - } + return Filehandler.getZipFileAsResponse(Path.of(file.getPath()), file.getName()); } @GetMapping(ApiRoutes.SUBMISSION_BASE_PATH + "/{submissionid}/artifacts") //Route to get a submission diff --git a/backend/app/src/main/java/com/ugent/pidgeon/controllers/TestController.java b/backend/app/src/main/java/com/ugent/pidgeon/controllers/TestController.java index 3fc8ea2d..f8ea2e66 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/controllers/TestController.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/controllers/TestController.java @@ -10,8 +10,6 @@ import com.ugent.pidgeon.postgre.repository.*; import com.ugent.pidgeon.util.*; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletableFuture; -import java.util.logging.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.http.*; @@ -19,9 +17,6 @@ import org.springframework.web.multipart.MultipartFile; import java.nio.file.Path; -import java.util.Optional; -import java.util.function.Function; - @RestController public class TestController { @@ -238,5 +233,98 @@ public ResponseEntity deleteTestById(@PathVariable("projectid") long projectI } return ResponseEntity.ok().build(); } + + @PutMapping(ApiRoutes.PROJECT_BASE_PATH + "/{projectid}/tests/extrafiles") + @Roles({UserRole.teacher, UserRole.student}) + public ResponseEntity uploadExtraTestFiles( + @PathVariable("projectid") long projectId, + @RequestParam("file") MultipartFile file, + Auth auth + ) { + CheckResult checkResult = testUtil.getTestIfAdmin(projectId, auth.getUserEntity()); + if (!checkResult.getStatus().equals(HttpStatus.OK)) { + return ResponseEntity.status(checkResult.getStatus()).body(checkResult.getMessage()); + } + + TestEntity testEntity = checkResult.getData(); + + try { + Path path = Filehandler.getTestExtraFilesPath(projectId); + Filehandler.saveFile(path, file, Filehandler.EXTRA_TESTFILES_FILENAME); + + FileEntity fileEntity = new FileEntity(); + fileEntity.setName(Filehandler.EXTRA_TESTFILES_FILENAME); + fileEntity.setPath(path.resolve(Filehandler.EXTRA_TESTFILES_FILENAME).toString()); + fileEntity.setUploadedBy(auth.getUserEntity().getId()); + fileEntity = fileRepository.save(fileEntity); + + testEntity.setExtraFilesId(fileEntity.getId()); + testEntity = testRepository.save(testEntity); + + return ResponseEntity.ok(entityToJsonConverter.testEntityToTestJson(testEntity, projectId)); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error while saving files"); + } + } + + @DeleteMapping(ApiRoutes.PROJECT_BASE_PATH + "/{projectid}/tests/extrafiles") + @Roles({UserRole.teacher, UserRole.student}) + public ResponseEntity deleteExtraTestFiles( + @PathVariable("projectid") long projectId, + Auth auth + ) { + CheckResult checkResult = testUtil.getTestIfAdmin(projectId, auth.getUserEntity()); + if (!checkResult.getStatus().equals(HttpStatus.OK)) { + return ResponseEntity.status(checkResult.getStatus()).body(checkResult.getMessage()); + } + + TestEntity testEntity = checkResult.getData(); + + try { + + FileEntity fileEntity = fileRepository.findById(testEntity.getExtraFilesId()).orElse(null); + if (fileEntity == null) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body("No extra files found"); + } + + Path path = Path.of(fileEntity.getPath()); + Filehandler.deleteLocation(path.toFile()); + + testEntity.setExtraFilesId(null); + testEntity = testRepository.save(testEntity); + + fileRepository.delete(fileEntity); + + + + return ResponseEntity.ok(entityToJsonConverter.testEntityToTestJson(testEntity, projectId)); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error while deleting files"); + } + } + + @GetMapping(ApiRoutes.PROJECT_BASE_PATH + "/{projectid}/tests/extrafiles") + @Roles({UserRole.teacher, UserRole.student}) + public ResponseEntity getExtraTestFiles( + @PathVariable("projectid") long projectId, + Auth auth + ) { + CheckResult checkResult = testUtil.getTestIfAdmin(projectId, auth.getUserEntity()); + if (!checkResult.getStatus().equals(HttpStatus.OK)) { + return ResponseEntity.status(checkResult.getStatus()).body(checkResult.getMessage()); + } + + TestEntity testEntity = checkResult.getData(); + if (testEntity.getExtraFilesId() == null) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body("No extra files found"); + } + + FileEntity fileEntity = fileRepository.findById(testEntity.getExtraFilesId()).orElse(null); + if (fileEntity == null) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body("No extra files found"); + } + + return Filehandler.getZipFileAsResponse(Path.of(fileEntity.getPath()), Filehandler.EXTRA_TESTFILES_FILENAME); + } } diff --git a/backend/app/src/main/java/com/ugent/pidgeon/model/json/TestJson.java b/backend/app/src/main/java/com/ugent/pidgeon/model/json/TestJson.java index e2c0d034..80250960 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/model/json/TestJson.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/model/json/TestJson.java @@ -6,17 +6,19 @@ public class TestJson { private String dockerScript; private String dockerTemplate; private String structureTest; + private String extraFilesUrl; public TestJson() { } public TestJson(String projectUrl, String dockerImage, String dockerScript, - String dockerTemplate, String structureTest) { + String dockerTemplate, String structureTest, String extraFilesUrl) { this.projectUrl = projectUrl; this.dockerImage = dockerImage; this.dockerScript = dockerScript; - this.dockerTemplate = dockerTemplate; - this.structureTest = structureTest; + this.dockerTemplate = dockerTemplate; + this.structureTest = structureTest; + this.extraFilesUrl = extraFilesUrl; } public String getProjectUrl() { @@ -58,4 +60,12 @@ public String getDockerTemplate() { public void setDockerTemplate(String dockerTemplate) { this.dockerTemplate = dockerTemplate; } + + public String getExtraFilesUrl() { + return extraFilesUrl; + } + + public void setExtraFilesUrl(String extraFilesUrl) { + this.extraFilesUrl = extraFilesUrl; + } } diff --git a/backend/app/src/main/java/com/ugent/pidgeon/postgre/models/TestEntity.java b/backend/app/src/main/java/com/ugent/pidgeon/postgre/models/TestEntity.java index 885c6a49..efe8afe4 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/postgre/models/TestEntity.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/postgre/models/TestEntity.java @@ -24,6 +24,9 @@ public class TestEntity { @Column(name = "structure_template") private String structureTemplate; + @Column(name = "extra_files") + private Long extraFilesId; + public TestEntity(String dockerImage, String docker_test_script, String dockerTestTemplate, String structureTemplate) { @@ -76,4 +79,12 @@ public String getStructureTemplate() { public void setStructureTemplate(String structureTemplate) { this.structureTemplate = structureTemplate; } + + public Long getExtraFilesId() { + return extraFilesId; + } + + public void setExtraFilesId(Long extraFilesId) { + this.extraFilesId = extraFilesId; + } } diff --git a/backend/app/src/main/java/com/ugent/pidgeon/util/EntityToJsonConverter.java b/backend/app/src/main/java/com/ugent/pidgeon/util/EntityToJsonConverter.java index 716ae1bf..5deb063c 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/util/EntityToJsonConverter.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/util/EntityToJsonConverter.java @@ -264,7 +264,8 @@ public TestJson testEntityToTestJson(TestEntity testEntity, long projectId) { testEntity.getDockerImage(), testEntity.getDockerTestScript(), testEntity.getDockerTestTemplate(), - testEntity.getStructureTemplate() + testEntity.getStructureTemplate(), + testEntity.getExtraFilesId() == null ? null : ApiRoutes.PROJECT_BASE_PATH + "/" + projectId + "/extrafiles" ); } } \ No newline at end of file diff --git a/backend/app/src/main/java/com/ugent/pidgeon/util/Filehandler.java b/backend/app/src/main/java/com/ugent/pidgeon/util/Filehandler.java index 071d1d4f..7f1cc483 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/util/Filehandler.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/util/Filehandler.java @@ -5,6 +5,9 @@ import java.util.zip.ZipOutputStream; import org.apache.tika.Tika; import org.springframework.core.io.FileSystemResource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.multipart.MultipartFile; import org.springframework.core.io.Resource; @@ -18,6 +21,7 @@ public class Filehandler { static String BASEPATH = "data"; public static String SUBMISSION_FILENAME = "files.zip"; + public static String EXTRA_TESTFILES_FILENAME = "testfiles.zip"; /** * Save a submission to the server @@ -26,7 +30,7 @@ public class Filehandler { * @return the saved file * @throws IOException if an error occurs while saving the file */ - public static File saveSubmission(Path directory, MultipartFile file) throws IOException { + public static File saveFile(Path directory, MultipartFile file, String filename) throws IOException { // Check if the file is empty if (file == null || file.isEmpty()) { throw new IOException("File is empty"); @@ -50,7 +54,7 @@ public static File saveSubmission(Path directory, MultipartFile file) throws IOE } // Save the file to the server - Path filePath = directory.resolve(SUBMISSION_FILENAME); + Path filePath = directory.resolve(filename); try(InputStream stream = new FileInputStream(tempFile)) { Files.copy(stream, filePath, StandardCopyOption.REPLACE_EXISTING); @@ -117,6 +121,10 @@ static public Path getSubmissionArtifactPath(long projectid, long groupid, long return getSubmissionPath(projectid, groupid, submissionid).resolve("artifacts.zip"); } + static public Path getTestExtraFilesPath(long projectid) { + return Path.of(BASEPATH,"projects", String.valueOf(projectid)); + } + /** * Get a file as a resource * @param path path of the file @@ -184,4 +192,25 @@ public static void copyFilesAsZip(List files, Path path) throws IOExceptio } } } + + public static ResponseEntity getZipFileAsResponse(Path path, String filename) { + // Get the file from the server + try { + Resource zipFile = Filehandler.getFileAsResource(path); + if (zipFile == null) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body("File not found."); + } + + // Set headers for the response + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename); + headers.add(HttpHeaders.CONTENT_TYPE, "application/zip"); + + return ResponseEntity.ok() + .headers(headers) + .body(zipFile); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage()); + } + } } diff --git a/backend/app/src/test/java/com/ugent/pidgeon/controllers/SubmissionControllerTest.java b/backend/app/src/test/java/com/ugent/pidgeon/controllers/SubmissionControllerTest.java index 21b496ae..51d24553 100644 --- a/backend/app/src/test/java/com/ugent/pidgeon/controllers/SubmissionControllerTest.java +++ b/backend/app/src/test/java/com/ugent/pidgeon/controllers/SubmissionControllerTest.java @@ -276,7 +276,7 @@ public void testSubmitFile() throws Exception { File file = createTestFile(); try (MockedStatic mockedFileHandler = mockStatic(Filehandler.class)) { mockedFileHandler.when(() -> Filehandler.getSubmissionPath(submission.getProjectId(), groupEntity.getId(), submission.getId())).thenReturn(path); - mockedFileHandler.when(() -> Filehandler.saveSubmission(path, mockMultipartFile)).thenReturn(file); + mockedFileHandler.when(() -> Filehandler.saveFile(path, mockMultipartFile)).thenReturn(file); mockedFileHandler.when(() -> Filehandler.getSubmissionArtifactPath(anyLong(), anyLong(), anyLong())).thenReturn(artifactPath); when(testRunner.runStructureTest(any(), eq(testEntity), any())).thenReturn(null); diff --git a/backend/app/src/test/java/com/ugent/pidgeon/util/FileHandlerTest.java b/backend/app/src/test/java/com/ugent/pidgeon/util/FileHandlerTest.java index 89efd857..83392fef 100644 --- a/backend/app/src/test/java/com/ugent/pidgeon/util/FileHandlerTest.java +++ b/backend/app/src/test/java/com/ugent/pidgeon/util/FileHandlerTest.java @@ -66,7 +66,7 @@ public void setUp() throws IOException { @Test public void testSaveSubmission() throws Exception { - File savedFile = Filehandler.saveSubmission(tempDir, file); + File savedFile = Filehandler.saveFile(tempDir, file); assertTrue(savedFile.exists()); assertEquals(Filehandler.SUBMISSION_FILENAME, savedFile.getName()); @@ -77,7 +77,7 @@ public void testSaveSubmission() throws Exception { @Test public void testSaveSubmission_dirDoesntExist() throws Exception { - File savedFile = Filehandler.saveSubmission(tempDir.resolve("nonexistent"), file); + File savedFile = Filehandler.saveFile(tempDir.resolve("nonexistent"), file); assertTrue(savedFile.exists()); assertEquals(Filehandler.SUBMISSION_FILENAME, savedFile.getName()); @@ -88,7 +88,7 @@ public void testSaveSubmission_dirDoesntExist() throws Exception { @Test public void testSaveSubmission_errorWhileCreatingDir() throws Exception { - assertThrows(IOException.class, () -> Filehandler.saveSubmission(Path.of(""), file)); + assertThrows(IOException.class, () -> Filehandler.saveFile(Path.of(""), file)); } @Test @@ -96,7 +96,7 @@ public void testSaveSubmission_notAZipFile() { MockMultipartFile notAZipFile = new MockMultipartFile( "notAZipFile.txt", "This is not a zip file".getBytes() ); - assertThrows(IOException.class, () -> Filehandler.saveSubmission(tempDir, notAZipFile)); + assertThrows(IOException.class, () -> Filehandler.saveFile(tempDir, notAZipFile)); } @Test @@ -104,12 +104,12 @@ public void testSaveSubmission_fileEmpty() { MockMultipartFile emptyFile = new MockMultipartFile( "emptyFile.txt", new byte[0] ); - assertThrows(IOException.class, () -> Filehandler.saveSubmission(tempDir, emptyFile)); + assertThrows(IOException.class, () -> Filehandler.saveFile(tempDir, emptyFile)); } @Test public void testSaveSubmission_fileNull() { - assertThrows(IOException.class, () -> Filehandler.saveSubmission(tempDir, null)); + assertThrows(IOException.class, () -> Filehandler.saveFile(tempDir, null)); } @Test diff --git a/backend/database/start_database.sql b/backend/database/start_database.sql index 94cf30e6..47267479 100644 --- a/backend/database/start_database.sql +++ b/backend/database/start_database.sql @@ -56,7 +56,8 @@ CREATE TABLE tests ( docker_image VARCHAR(256), docker_test_script TEXT, docker_test_template TEXT, - structure_template TEXT + structure_template TEXT, + extra_files INT REFERENCES files(file_id) ); From 35f9b028494c5bd55c6541806693f471f7ed8a13 Mon Sep 17 00:00:00 2001 From: Aqua-sc <108478185+Aqua-sc@users.noreply.github.com> Date: Fri, 17 May 2024 09:16:14 +0200 Subject: [PATCH 4/9] rename util folder to extra + call addUtilFiles --- .../com/ugent/pidgeon/controllers/SubmissionController.java | 2 +- .../model/submissionTesting/DockerSubmissionTestModel.java | 4 ++-- .../app/src/main/java/com/ugent/pidgeon/util/TestRunner.java | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/backend/app/src/main/java/com/ugent/pidgeon/controllers/SubmissionController.java b/backend/app/src/main/java/com/ugent/pidgeon/controllers/SubmissionController.java index 22092865..73406a24 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/controllers/SubmissionController.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/controllers/SubmissionController.java @@ -237,7 +237,7 @@ public ResponseEntity submitFile(@RequestParam("file") MultipartFile file, @P try { // Check if docker tests succeed DockerSubmissionTestModel dockerModel = new DockerSubmissionTestModel(testEntity.getDockerImage()); - DockerOutput dockerOutput = testRunner.runDockerTest(new ZipFile(finalSavedFile), testEntity, artifactPath, dockerModel); + DockerOutput dockerOutput = testRunner.runDockerTest(new ZipFile(finalSavedFile), testEntity, artifactPath, dockerModel, projectid); if (dockerOutput == null) { throw new RuntimeException("Error while running docker tests."); } diff --git a/backend/app/src/main/java/com/ugent/pidgeon/model/submissionTesting/DockerSubmissionTestModel.java b/backend/app/src/main/java/com/ugent/pidgeon/model/submissionTesting/DockerSubmissionTestModel.java index 7722f13d..f55385d5 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/model/submissionTesting/DockerSubmissionTestModel.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/model/submissionTesting/DockerSubmissionTestModel.java @@ -57,7 +57,7 @@ public DockerSubmissionTestModel(String dockerImage) { new File(localMountFolder + "input/").mkdirs(); new File(localMountFolder + "output/").mkdirs(); new File(localMountFolder + "artifacts/").mkdirs(); - new File(localMountFolder + "utils/").mkdirs(); + new File(localMountFolder + "extra/").mkdirs(); } @@ -95,7 +95,7 @@ public void addUtilFiles(Path pathToZip){ Enumeration entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); - File entryDestination = new File(localMountFolder + "utils/", entry.getName()); + File entryDestination = new File(localMountFolder + "extra/", entry.getName()); if (entry.isDirectory()) { entryDestination.mkdirs(); } else { diff --git a/backend/app/src/main/java/com/ugent/pidgeon/util/TestRunner.java b/backend/app/src/main/java/com/ugent/pidgeon/util/TestRunner.java index 0b3cdc8d..25ca7c64 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/util/TestRunner.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/util/TestRunner.java @@ -27,7 +27,7 @@ public SubmissionTemplateModel.SubmissionResult runStructureTest( return model.checkSubmission(file); } - public DockerOutput runDockerTest(ZipFile file, TestEntity testEntity, Path outputPath, DockerSubmissionTestModel model) throws IOException { + public DockerOutput runDockerTest(ZipFile file, TestEntity testEntity, Path outputPath, DockerSubmissionTestModel model, long projectId) throws IOException { // Get the test file from the server String testScript = testEntity.getDockerTestScript(); String testTemplate = testEntity.getDockerTestTemplate(); @@ -41,6 +41,7 @@ public DockerOutput runDockerTest(ZipFile file, TestEntity testEntity, Path outp try { model.addZipInputFiles(file); + model.addUtilFiles(Filehandler.getTestExtraFilesPath(projectId).resolve(Filehandler.EXTRA_TESTFILES_FILENAME)); DockerOutput output; if (testTemplate == null) { From 87fa0a8acc28f3cd847c078100396e148cc1123f Mon Sep 17 00:00:00 2001 From: Aqua-sc <108478185+Aqua-sc@users.noreply.github.com> Date: Fri, 17 May 2024 14:55:52 +0200 Subject: [PATCH 5/9] Keep filename of uploaded extratestfiles --- .../ugent/pidgeon/controllers/TestController.java | 7 ++++--- .../com/ugent/pidgeon/model/json/TestJson.java | 14 +++++++++++++- .../ugent/pidgeon/util/EntityToJsonConverter.java | 6 +++++- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/backend/app/src/main/java/com/ugent/pidgeon/controllers/TestController.java b/backend/app/src/main/java/com/ugent/pidgeon/controllers/TestController.java index f8ea2e66..76ce9c6a 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/controllers/TestController.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/controllers/TestController.java @@ -9,6 +9,7 @@ import com.ugent.pidgeon.postgre.models.types.UserRole; import com.ugent.pidgeon.postgre.repository.*; import com.ugent.pidgeon.util.*; +import java.io.File; import java.util.concurrent.CompletableFuture; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; @@ -250,10 +251,10 @@ public ResponseEntity uploadExtraTestFiles( try { Path path = Filehandler.getTestExtraFilesPath(projectId); - Filehandler.saveFile(path, file, Filehandler.EXTRA_TESTFILES_FILENAME); + Filehandler.saveFile(path, file, file.getOriginalFilename()); FileEntity fileEntity = new FileEntity(); - fileEntity.setName(Filehandler.EXTRA_TESTFILES_FILENAME); + fileEntity.setName(file.getOriginalFilename()); fileEntity.setPath(path.resolve(Filehandler.EXTRA_TESTFILES_FILENAME).toString()); fileEntity.setUploadedBy(auth.getUserEntity().getId()); fileEntity = fileRepository.save(fileEntity); @@ -324,7 +325,7 @@ public ResponseEntity getExtraTestFiles( return ResponseEntity.status(HttpStatus.NOT_FOUND).body("No extra files found"); } - return Filehandler.getZipFileAsResponse(Path.of(fileEntity.getPath()), Filehandler.EXTRA_TESTFILES_FILENAME); + return Filehandler.getZipFileAsResponse(Path.of(fileEntity.getPath()), fileEntity.getName()); } } diff --git a/backend/app/src/main/java/com/ugent/pidgeon/model/json/TestJson.java b/backend/app/src/main/java/com/ugent/pidgeon/model/json/TestJson.java index 80250960..76c9bbb2 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/model/json/TestJson.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/model/json/TestJson.java @@ -1,24 +1,28 @@ package com.ugent.pidgeon.model.json; public class TestJson { + private String projectUrl; private String dockerImage; private String dockerScript; private String dockerTemplate; private String structureTest; private String extraFilesUrl; + private String extraFilesName; + public TestJson() { } public TestJson(String projectUrl, String dockerImage, String dockerScript, - String dockerTemplate, String structureTest, String extraFilesUrl) { + String dockerTemplate, String structureTest, String extraFilesUrl, String extraFilesName) { this.projectUrl = projectUrl; this.dockerImage = dockerImage; this.dockerScript = dockerScript; this.dockerTemplate = dockerTemplate; this.structureTest = structureTest; this.extraFilesUrl = extraFilesUrl; + this.extraFilesName = extraFilesName; } public String getProjectUrl() { @@ -68,4 +72,12 @@ public String getExtraFilesUrl() { public void setExtraFilesUrl(String extraFilesUrl) { this.extraFilesUrl = extraFilesUrl; } + + public String getExtraFilesName() { + return extraFilesName; + } + + public void setExtraFilesName(String extraFilesName) { + this.extraFilesName = extraFilesName; + } } diff --git a/backend/app/src/main/java/com/ugent/pidgeon/util/EntityToJsonConverter.java b/backend/app/src/main/java/com/ugent/pidgeon/util/EntityToJsonConverter.java index 5deb063c..db8923c5 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/util/EntityToJsonConverter.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/util/EntityToJsonConverter.java @@ -39,6 +39,8 @@ public class EntityToJsonConverter { private TestUtil testUtil; @Autowired private TestRepository testRepository; + @Autowired + private FileRepository fileRepository; public GroupJson groupEntityToJson(GroupEntity groupEntity) { @@ -259,13 +261,15 @@ else if (submission.getDockerTestType().equals(DockerTestType.SIMPLE)) { } public TestJson testEntityToTestJson(TestEntity testEntity, long projectId) { + FileEntity extrafiles = testEntity.getExtraFilesId() == null ? null : fileRepository.findById(testEntity.getExtraFilesId()).orElse(null); return new TestJson( ApiRoutes.PROJECT_BASE_PATH + "/" + projectId, testEntity.getDockerImage(), testEntity.getDockerTestScript(), testEntity.getDockerTestTemplate(), testEntity.getStructureTemplate(), - testEntity.getExtraFilesId() == null ? null : ApiRoutes.PROJECT_BASE_PATH + "/" + projectId + "/extrafiles" + testEntity.getExtraFilesId() == null ? null : ApiRoutes.PROJECT_BASE_PATH + "/" + projectId + "/extrafiles", + extrafiles == null ? null : extrafiles.getName() ); } } \ No newline at end of file From fe8daf8ae5a54629e80d03c429988ebcae8b4751 Mon Sep 17 00:00:00 2001 From: Aqua-sc <108478185+Aqua-sc@users.noreply.github.com> Date: Fri, 17 May 2024 15:22:04 +0200 Subject: [PATCH 6/9] Updated submissionControllerTests --- .../controllers/SubmissionControllerTest.java | 23 ++++++------------- .../controllers/TestControllerTest.java | 21 +++++++++++++---- .../ugent/pidgeon/util/FileHandlerTest.java | 12 +++++----- .../ugent/pidgeon/util/TestRunnerTest.java | 13 ++++++----- 4 files changed, 36 insertions(+), 33 deletions(-) diff --git a/backend/app/src/test/java/com/ugent/pidgeon/controllers/SubmissionControllerTest.java b/backend/app/src/test/java/com/ugent/pidgeon/controllers/SubmissionControllerTest.java index 51d24553..4ac934ac 100644 --- a/backend/app/src/test/java/com/ugent/pidgeon/controllers/SubmissionControllerTest.java +++ b/backend/app/src/test/java/com/ugent/pidgeon/controllers/SubmissionControllerTest.java @@ -276,17 +276,18 @@ public void testSubmitFile() throws Exception { File file = createTestFile(); try (MockedStatic mockedFileHandler = mockStatic(Filehandler.class)) { mockedFileHandler.when(() -> Filehandler.getSubmissionPath(submission.getProjectId(), groupEntity.getId(), submission.getId())).thenReturn(path); - mockedFileHandler.when(() -> Filehandler.saveFile(path, mockMultipartFile)).thenReturn(file); + mockedFileHandler.when(() -> Filehandler.saveFile(path, mockMultipartFile, Filehandler.SUBMISSION_FILENAME)).thenReturn(file); mockedFileHandler.when(() -> Filehandler.getSubmissionArtifactPath(anyLong(), anyLong(), anyLong())).thenReturn(artifactPath); when(testRunner.runStructureTest(any(), eq(testEntity), any())).thenReturn(null); - when(testRunner.runDockerTest(any(), eq(testEntity), eq(artifactPath), any())).thenReturn(null); + when(testRunner.runDockerTest(any(), eq(testEntity), eq(artifactPath), any(), eq(submission.getProjectId()))).thenReturn(null); when(entityToJsonConverter.getSubmissionJson(submission)).thenReturn(submissionJson); when(testRepository.findByProjectId(submission.getProjectId())).thenReturn(Optional.of(testEntity)); when(entityToJsonConverter.getSubmissionJson(submission)).thenReturn(submissionJson); + mockMvc.perform(MockMvcRequestBuilders.multipart(url) .file(mockMultipartFile)) .andExpect(status().isOk()) @@ -295,7 +296,7 @@ public void testSubmitFile() throws Exception { /* assertEquals(DockerTestState.running, submission.getDockerTestState()); */ // This executes too quickly so we can't test this - Thread.sleep(1000); + Thread.sleep(2000); // File repository needs to save again after setting path verify(fileRepository, times(1)).save(argThat( @@ -356,7 +357,7 @@ public void testSubmitFile() throws Exception { testEntity.setDockerImage("dockerImage"); testEntity.setDockerTestScript("dockerTestScript"); DockerOutput dockerOutput = new DockerTestOutput( List.of("dockerFeedback-test"), true); - when(testRunner.runDockerTest(any(), eq(testEntity), eq(artifactPath), any())).thenReturn(dockerOutput); + when(testRunner.runDockerTest(any(), eq(testEntity), eq(artifactPath), any(), eq(submission.getProjectId()))).thenReturn(dockerOutput); submission.setDockerAccepted(false); submission.setDockerFeedback("dockerFeedback-test"); mockMvc.perform(MockMvcRequestBuilders.multipart(url) @@ -380,7 +381,7 @@ public void testSubmitFile() throws Exception { .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(content().json(objectMapper.writeValueAsString(submissionJson))); verify(testRunner, times(0)).runStructureTest(any(), eq(testEntity), any()); - verify(testRunner, times(0)).runDockerTest(any(), eq(testEntity), eq(artifactPath), any()); + verify(testRunner, times(0)).runDockerTest(any(), eq(testEntity), eq(artifactPath), any(), eq(submission.getProjectId())); /* Unexpected error */ reset(fileRepository); @@ -416,6 +417,7 @@ public void testGetSubmissionFile() throws Exception { when(submissionUtil.canGetSubmission(submission.getId(), getMockUser())).thenReturn(new CheckResult<>(HttpStatus.OK, "", submission)); when(fileRepository.findById(submission.getFileId())).thenReturn(Optional.of(fileEntity)); mockedFileHandler.when(() -> Filehandler.getFileAsResource(path)).thenReturn(mockedResource); + mockedFileHandler.when(() -> Filehandler.getZipFileAsResponse(path, fileEntity.getName())).thenCallRealMethod(); mockMvc.perform(MockMvcRequestBuilders.get(url)) .andExpect(status().isOk()) @@ -424,23 +426,12 @@ public void testGetSubmissionFile() throws Exception { HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + fileEntity.getName())) .andExpect(content().bytes(mockedResource.getInputStream().readAllBytes())); - /* Resource not found */ - mockedFileHandler.when(() -> Filehandler.getFileAsResource(path)).thenReturn(null); - mockMvc.perform(MockMvcRequestBuilders.get(url)) - .andExpect(status().isNotFound()); /* file not found */ when(fileRepository.findById(submission.getFileId())).thenReturn(Optional.empty()); mockMvc.perform(MockMvcRequestBuilders.get(url)) .andExpect(status().isNotFound()); - /* Unexpected error */ - when(fileRepository.findById(submission.getFileId())).thenReturn(Optional.of(fileEntity)); - mockedFileHandler.reset(); - mockedFileHandler.when(() -> Filehandler.getFileAsResource(path)).thenThrow(new RuntimeException()); - mockMvc.perform(MockMvcRequestBuilders.get(url)) - .andExpect(status().isInternalServerError()); - /* User can't get submission */ when(submissionUtil.canGetSubmission(submission.getId(), getMockUser())).thenReturn(new CheckResult<>(HttpStatus.I_AM_A_TEAPOT, "", null)); mockMvc.perform(MockMvcRequestBuilders.get(url)) diff --git a/backend/app/src/test/java/com/ugent/pidgeon/controllers/TestControllerTest.java b/backend/app/src/test/java/com/ugent/pidgeon/controllers/TestControllerTest.java index a360aaff..a21c3c63 100644 --- a/backend/app/src/test/java/com/ugent/pidgeon/controllers/TestControllerTest.java +++ b/backend/app/src/test/java/com/ugent/pidgeon/controllers/TestControllerTest.java @@ -106,7 +106,10 @@ public void setup() { test.getDockerImage(), test.getDockerTestScript(), test.getDockerTestTemplate(), - test.getStructureTemplate() + test.getStructureTemplate(), + "extraFilesUrl", + "extraFilesName" + ); } @@ -132,7 +135,9 @@ public void testUpdateTest() throws Exception { dockerImage, dockerTestScript, dockerTestTemplate, - structureTemplate + structureTemplate, + "extraFilesUrl", + "extraFilesName" ); /* All checks succeed */ when(testUtil.checkForTestUpdate( @@ -174,7 +179,9 @@ public void testUpdateTest() throws Exception { null, null, null, - null + null, + "extraFilesUrl", + "extraFilesName" ); testUpdateJson = new TestUpdateJson( dockerImageBlank, @@ -285,7 +292,9 @@ public void testPutTest() throws Exception { dockerImage, dockerTestScript, dockerTestTemplate, - structureTemplate + structureTemplate, + "extraFilesUrl", + "extraFilesName" ); /* All checks succeed */ when(testUtil.checkForTestUpdate( @@ -337,7 +346,9 @@ public void testPutTest() throws Exception { null, null, null, - null + null, + "extraFilesUrl", + "extraFilesName" ); reset(testUtil); when(testUtil.checkForTestUpdate( diff --git a/backend/app/src/test/java/com/ugent/pidgeon/util/FileHandlerTest.java b/backend/app/src/test/java/com/ugent/pidgeon/util/FileHandlerTest.java index 83392fef..e7cd6668 100644 --- a/backend/app/src/test/java/com/ugent/pidgeon/util/FileHandlerTest.java +++ b/backend/app/src/test/java/com/ugent/pidgeon/util/FileHandlerTest.java @@ -66,7 +66,7 @@ public void setUp() throws IOException { @Test public void testSaveSubmission() throws Exception { - File savedFile = Filehandler.saveFile(tempDir, file); + File savedFile = Filehandler.saveFile(tempDir, file, Filehandler.SUBMISSION_FILENAME); assertTrue(savedFile.exists()); assertEquals(Filehandler.SUBMISSION_FILENAME, savedFile.getName()); @@ -77,7 +77,7 @@ public void testSaveSubmission() throws Exception { @Test public void testSaveSubmission_dirDoesntExist() throws Exception { - File savedFile = Filehandler.saveFile(tempDir.resolve("nonexistent"), file); + File savedFile = Filehandler.saveFile(tempDir.resolve("nonexistent"), file, Filehandler.SUBMISSION_FILENAME); assertTrue(savedFile.exists()); assertEquals(Filehandler.SUBMISSION_FILENAME, savedFile.getName()); @@ -88,7 +88,7 @@ public void testSaveSubmission_dirDoesntExist() throws Exception { @Test public void testSaveSubmission_errorWhileCreatingDir() throws Exception { - assertThrows(IOException.class, () -> Filehandler.saveFile(Path.of(""), file)); + assertThrows(IOException.class, () -> Filehandler.saveFile(Path.of(""), file, Filehandler.SUBMISSION_FILENAME)); } @Test @@ -96,7 +96,7 @@ public void testSaveSubmission_notAZipFile() { MockMultipartFile notAZipFile = new MockMultipartFile( "notAZipFile.txt", "This is not a zip file".getBytes() ); - assertThrows(IOException.class, () -> Filehandler.saveFile(tempDir, notAZipFile)); + assertThrows(IOException.class, () -> Filehandler.saveFile(tempDir, notAZipFile, Filehandler.SUBMISSION_FILENAME)); } @Test @@ -104,12 +104,12 @@ public void testSaveSubmission_fileEmpty() { MockMultipartFile emptyFile = new MockMultipartFile( "emptyFile.txt", new byte[0] ); - assertThrows(IOException.class, () -> Filehandler.saveFile(tempDir, emptyFile)); + assertThrows(IOException.class, () -> Filehandler.saveFile(tempDir, emptyFile, Filehandler.SUBMISSION_FILENAME)); } @Test public void testSaveSubmission_fileNull() { - assertThrows(IOException.class, () -> Filehandler.saveFile(tempDir, null)); + assertThrows(IOException.class, () -> Filehandler.saveFile(tempDir, null, Filehandler.SUBMISSION_FILENAME)); } @Test diff --git a/backend/app/src/test/java/com/ugent/pidgeon/util/TestRunnerTest.java b/backend/app/src/test/java/com/ugent/pidgeon/util/TestRunnerTest.java index 828a0f7d..d9a7434f 100644 --- a/backend/app/src/test/java/com/ugent/pidgeon/util/TestRunnerTest.java +++ b/backend/app/src/test/java/com/ugent/pidgeon/util/TestRunnerTest.java @@ -49,6 +49,7 @@ public class TestRunnerTest { private SubmissionResult submissionResult; private DockerTestOutput dockerTestOutput; private DockerTemplateTestOutput dockerTemplateTestOutput; + private final long projectId = 876L; @BeforeEach public void setUp() { @@ -108,7 +109,7 @@ public void testRunDockerTest() throws IOException { .thenReturn(dockerTemplateTestOutput); when(dockerModel.getArtifacts()).thenReturn(artifacts); - DockerOutput result = new TestRunner().runDockerTest(file, testEntity, outputPath, dockerModel); + DockerOutput result = new TestRunner().runDockerTest(file, testEntity, outputPath, dockerModel, projectId); assertEquals(dockerTemplateTestOutput, result); verify(dockerModel, times(1)).addZipInputFiles(file); @@ -117,7 +118,7 @@ public void testRunDockerTest() throws IOException { /* artifacts are empty */ when(dockerModel.getArtifacts()).thenReturn(Collections.emptyList()); - result = new TestRunner().runDockerTest(file, testEntity, outputPath, dockerModel); + result = new TestRunner().runDockerTest(file, testEntity, outputPath, dockerModel, projectId); assertEquals(dockerTemplateTestOutput, result); verify(dockerModel, times(2)).addZipInputFiles(file); verify(dockerModel, times(2)).cleanUp(); @@ -125,7 +126,7 @@ public void testRunDockerTest() throws IOException { /* aritifacts are null */ when(dockerModel.getArtifacts()).thenReturn(null); - result = new TestRunner().runDockerTest(file, testEntity, outputPath, dockerModel); + result = new TestRunner().runDockerTest(file, testEntity, outputPath, dockerModel, projectId); assertEquals(dockerTemplateTestOutput, result); verify(dockerModel, times(3)).addZipInputFiles(file); verify(dockerModel, times(3)).cleanUp(); @@ -134,19 +135,19 @@ public void testRunDockerTest() throws IOException { /* No template */ testEntity.setDockerTestTemplate(null); when(dockerModel.runSubmission(testEntity.getDockerTestScript())).thenReturn(dockerTestOutput); - result = new TestRunner().runDockerTest(file, testEntity, outputPath, dockerModel); + result = new TestRunner().runDockerTest(file, testEntity, outputPath, dockerModel, projectId); assertEquals(dockerTestOutput, result); verify(dockerModel, times(4)).addZipInputFiles(file); verify(dockerModel, times(4)).cleanUp(); /* Error gets thrown */ when(dockerModel.runSubmission(testEntity.getDockerTestScript())).thenThrow(new RuntimeException("Error")); - assertThrows(Exception.class, () -> new TestRunner().runDockerTest(file, testEntity, outputPath, dockerModel)); + assertThrows(Exception.class, () -> new TestRunner().runDockerTest(file, testEntity, outputPath, dockerModel, projectId)); verify(dockerModel, times(5)).cleanUp(); /* No script */ testEntity.setDockerTestScript(null); - result = new TestRunner().runDockerTest(file, testEntity, outputPath, dockerModel); + result = new TestRunner().runDockerTest(file, testEntity, outputPath, dockerModel, projectId); assertNull(result); } From f0224e1c42be7fa077a2144be18e530f270bdaed Mon Sep 17 00:00:00 2001 From: Aqua-sc <108478185+Aqua-sc@users.noreply.github.com> Date: Fri, 17 May 2024 15:59:24 +0200 Subject: [PATCH 7/9] TestControllTest updated --- .../pidgeon/controllers/TestController.java | 15 +- .../controllers/TestControllerTest.java | 213 ++++++++++++++++++ 2 files changed, 220 insertions(+), 8 deletions(-) diff --git a/backend/app/src/main/java/com/ugent/pidgeon/controllers/TestController.java b/backend/app/src/main/java/com/ugent/pidgeon/controllers/TestController.java index 76ce9c6a..e614fc5b 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/controllers/TestController.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/controllers/TestController.java @@ -251,7 +251,7 @@ public ResponseEntity uploadExtraTestFiles( try { Path path = Filehandler.getTestExtraFilesPath(projectId); - Filehandler.saveFile(path, file, file.getOriginalFilename()); + Filehandler.saveFile(path, file, Filehandler.EXTRA_TESTFILES_FILENAME); FileEntity fileEntity = new FileEntity(); fileEntity.setName(file.getOriginalFilename()); @@ -283,20 +283,19 @@ public ResponseEntity deleteExtraTestFiles( try { - FileEntity fileEntity = fileRepository.findById(testEntity.getExtraFilesId()).orElse(null); + FileEntity fileEntity = testEntity.getExtraFilesId() == null ? + null : fileRepository.findById(testEntity.getExtraFilesId()).orElse(null); if (fileEntity == null) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body("No extra files found"); } - Path path = Path.of(fileEntity.getPath()); - Filehandler.deleteLocation(path.toFile()); - testEntity.setExtraFilesId(null); testEntity = testRepository.save(testEntity); - fileRepository.delete(fileEntity); - - + CheckResult delResult = fileUtil.deleteFileById(fileEntity.getId()); + if (!delResult.getStatus().equals(HttpStatus.OK)) { + return ResponseEntity.status(delResult.getStatus()).body(delResult.getMessage()); + } return ResponseEntity.ok(entityToJsonConverter.testEntityToTestJson(testEntity, projectId)); } catch (Exception e) { diff --git a/backend/app/src/test/java/com/ugent/pidgeon/controllers/TestControllerTest.java b/backend/app/src/test/java/com/ugent/pidgeon/controllers/TestControllerTest.java index a21c3c63..7a7cfb6a 100644 --- a/backend/app/src/test/java/com/ugent/pidgeon/controllers/TestControllerTest.java +++ b/backend/app/src/test/java/com/ugent/pidgeon/controllers/TestControllerTest.java @@ -4,6 +4,7 @@ import com.ugent.pidgeon.CustomObjectMapper; import com.ugent.pidgeon.model.json.TestJson; import com.ugent.pidgeon.model.json.TestUpdateJson; +import com.ugent.pidgeon.postgre.models.FileEntity; import com.ugent.pidgeon.postgre.models.GroupEntity; import com.ugent.pidgeon.postgre.models.ProjectEntity; import com.ugent.pidgeon.postgre.models.TestEntity; @@ -13,20 +14,28 @@ import com.ugent.pidgeon.util.CheckResult; import com.ugent.pidgeon.util.CommonDatabaseActions; import com.ugent.pidgeon.util.EntityToJsonConverter; +import com.ugent.pidgeon.util.FileUtil; import com.ugent.pidgeon.util.Filehandler; import com.ugent.pidgeon.util.Pair; import com.ugent.pidgeon.util.ProjectUtil; import com.ugent.pidgeon.util.TestUtil; +import java.io.File; +import java.io.FileOutputStream; import java.time.OffsetDateTime; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.mock.web.MockMultipartFile; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @@ -45,6 +54,7 @@ import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -61,12 +71,17 @@ public class TestControllerTest extends ControllerTest{ private TestRepository testRepository; @Mock private ProjectRepository projectRepository; + @Mock + private FileRepository fileRepository; @Mock private EntityToJsonConverter entityToJsonConverter; @Mock private CommonDatabaseActions commonDatabaseActions; + @Mock + private FileUtil fileUtil; + @InjectMocks private TestController testController; @@ -74,6 +89,8 @@ public class TestControllerTest extends ControllerTest{ private ObjectMapper objectMapper = CustomObjectMapper.createObjectMapper(); + private MockMultipartFile mockMultipartFile; + private FileEntity fileEntity; private ProjectEntity project; private TestEntity test; private TestJson testJson; @@ -112,6 +129,12 @@ public void setup() { ); + byte[] fileContent = "Your file content".getBytes(); + mockMultipartFile = new MockMultipartFile("file", "filename.txt", + MediaType.TEXT_PLAIN_VALUE, fileContent); + + fileEntity = new FileEntity("name", "dir/name", 1L); + fileEntity.setId(77L); } @Test @@ -683,4 +706,194 @@ public void testDeleteTest() throws Exception { mockMvc.perform(MockMvcRequestBuilders.delete(url)) .andExpect(status().isIAmATeapot()); } + + public static File createTestFile() throws IOException { + // Create a temporary directory + File tempDir = Files.createTempDirectory("test-dir").toFile(); + + // Create a temporary file within the directory + File tempFile = File.createTempFile("test-file", ".zip", tempDir); + + // Create some content to write into the zip file + String content = "Hello, this is a test file!"; + byte[] bytes = content.getBytes(); + + // Write the content into a file inside the zip file + try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(tempFile))) { + ZipEntry entry = new ZipEntry("test.txt"); + zipOut.putNextEntry(entry); + zipOut.write(bytes); + zipOut.closeEntry(); + } + + // Return the File object representing the zip file + return tempFile; + } + + @Test + public void testUploadExtraTestFiles() throws IOException { + String url = ApiRoutes.PROJECT_BASE_PATH + "/" + project.getId() + "/tests/extrafiles"; + /* All checks succeed */ + when(testUtil.getTestIfAdmin(project.getId(), getMockUser())) + .thenReturn(new CheckResult<>(HttpStatus.OK, "", test)); + + Path savePath = Path.of("savePath"); + File file = createTestFile(); + + try (MockedStatic mockedFilehandler = mockStatic(Filehandler.class)) { + mockedFilehandler.when(() -> Filehandler.getTestExtraFilesPath(project.getId())).thenReturn(savePath); + mockedFilehandler.when(() -> Filehandler.saveFile(savePath, mockMultipartFile, Filehandler.EXTRA_TESTFILES_FILENAME)) + .thenReturn(file); + + when(fileRepository.save(argThat( + fileEntity -> fileEntity.getName().equals(mockMultipartFile.getOriginalFilename()) && + fileEntity.getPath() + .equals(savePath.resolve(Filehandler.EXTRA_TESTFILES_FILENAME).toString()) && + fileEntity.getUploadedBy() == getMockUser().getId() + ))).thenReturn(fileEntity); + + when(testRepository.save(test)).thenReturn(test); + when(entityToJsonConverter.testEntityToTestJson(test, project.getId())).thenReturn(testJson); + + mockMvc.perform(MockMvcRequestBuilders.multipart(url) + .file(mockMultipartFile) + .with(request -> { + request.setMethod("PUT"); + return request; + }) + ).andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(content().json(objectMapper.writeValueAsString(testJson))); + + verify(testRepository, times(1)).save(test); + assertEquals(fileEntity.getId(), test.getExtraFilesId()); + + /* Unexpected error */ + mockedFilehandler.when(() -> Filehandler.saveFile(savePath, mockMultipartFile, Filehandler.EXTRA_TESTFILES_FILENAME)) + .thenThrow(new IOException("Unexpected error")); + + mockMvc.perform(MockMvcRequestBuilders.multipart(url) + .file(mockMultipartFile) + .with(request -> { + request.setMethod("PUT"); + return request; + }) + ).andExpect(status().isInternalServerError()); + + /* Check fails */ + when(testUtil.getTestIfAdmin(project.getId(), getMockUser())) + .thenReturn(new CheckResult<>(HttpStatus.I_AM_A_TEAPOT, "I'm a teapot", null)); + + mockMvc.perform(MockMvcRequestBuilders.multipart(url) + .file(mockMultipartFile) + .with(request -> { + request.setMethod("PUT"); + return request; + }) + ).andExpect(status().isIAmATeapot()); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Test + public void testDeleteExtraFiles() throws Exception { + String url = ApiRoutes.PROJECT_BASE_PATH + "/" + project.getId() + "/tests/extrafiles"; + test.setExtraFilesId(fileEntity.getId()); + + /* All checks succeed */ + when(testUtil.getTestIfAdmin(project.getId(), getMockUser())) + .thenReturn(new CheckResult<>(HttpStatus.OK, "", test)); + + when(fileRepository.findById(test.getExtraFilesId())).thenReturn(Optional.of(fileEntity)); + when(testRepository.save(test)).thenReturn(test); + when(entityToJsonConverter.testEntityToTestJson(test, project.getId())).thenReturn(testJson); + + when(fileUtil.deleteFileById(test.getExtraFilesId())) + .thenReturn(new CheckResult<>(HttpStatus.OK, "", null)); + + mockMvc.perform(MockMvcRequestBuilders.delete(url)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(content().json(objectMapper.writeValueAsString(testJson))); + + verify(testRepository, times(1)).save(test); + verify(fileUtil, times(1)).deleteFileById(fileEntity.getId()); + assertNull(test.getExtraFilesId()); + + /* Unexpected error when deleting file */ + test.setExtraFilesId(fileEntity.getId()); + when(fileUtil.deleteFileById(test.getExtraFilesId())) + .thenReturn(new CheckResult<>(HttpStatus.I_AM_A_TEAPOT, "Unexpected error", null)); + + mockMvc.perform(MockMvcRequestBuilders.delete(url)) + .andExpect(status().isIAmATeapot()); + + /* Error thrown */ + test.setExtraFilesId(fileEntity.getId()); + when(fileUtil.deleteFileById(test.getExtraFilesId())) + .thenThrow(new RuntimeException("Error thrown")); + + mockMvc.perform(MockMvcRequestBuilders.delete(url)) + .andExpect(status().isInternalServerError()); + + /* No extra files */ + test.setExtraFilesId(null); + + mockMvc.perform(MockMvcRequestBuilders.delete(url)) + .andExpect(status().isNotFound()); + + /* Check fails */ + when(testUtil.getTestIfAdmin(project.getId(), getMockUser())) + .thenReturn(new CheckResult<>(HttpStatus.I_AM_A_TEAPOT, "I'm a teapot", null)); + + mockMvc.perform(MockMvcRequestBuilders.delete(url)) + .andExpect(status().isIAmATeapot()); + } + + @Test + public void getExtraTestFiles() { + String url = ApiRoutes.PROJECT_BASE_PATH + "/" + project.getId() + "/tests/extrafiles"; + + ResponseEntity mockResponseEntity = ResponseEntity.ok().build(); + test.setExtraFilesId(fileEntity.getId()); + + /* All checks succeed */ + when(testUtil.getTestIfAdmin(project.getId(), getMockUser())) + .thenReturn(new CheckResult<>(HttpStatus.OK, "", test)); + + when(fileRepository.findById(test.getExtraFilesId())).thenReturn(Optional.of(fileEntity)); + + try (MockedStatic mockedFilehandler = mockStatic(Filehandler.class)) { + mockedFilehandler.when(() -> Filehandler.getZipFileAsResponse(argThat( + path -> path.toString().equals(fileEntity.getPath()) + ), eq(fileEntity.getName()))) + .thenReturn(mockResponseEntity); + + mockMvc.perform(MockMvcRequestBuilders.get(url)) + .andExpect(status().isOk()); + + /* Files not found */ + when(fileRepository.findById(test.getExtraFilesId())).thenReturn(Optional.empty()); + + mockMvc.perform(MockMvcRequestBuilders.get(url)) + .andExpect(status().isNotFound()); + + /* No extra files */ + test.setExtraFilesId(null); + + mockMvc.perform(MockMvcRequestBuilders.get(url)) + .andExpect(status().isNotFound()); + + /* check fails */ + when(testUtil.getTestIfAdmin(project.getId(), getMockUser())) + .thenReturn(new CheckResult<>(HttpStatus.I_AM_A_TEAPOT, "I'm a teapot", null)); + + mockMvc.perform(MockMvcRequestBuilders.get(url)) + .andExpect(status().isIAmATeapot()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } From af3fe07223686d13c3d4bf512f4391a2d42def16 Mon Sep 17 00:00:00 2001 From: Aqua-sc <108478185+Aqua-sc@users.noreply.github.com> Date: Fri, 17 May 2024 16:27:51 +0200 Subject: [PATCH 8/9] All test updated now DockerSubmissionTestTest still fails for some reason --- .../pidgeon/util/EntityToJsonConverter.java | 2 +- .../com/ugent/pidgeon/util/Filehandler.java | 26 ++++----- .../docker/DockerSubmissionTestTest.java | 16 +++--- .../util/EntityToJsonConverterTest.java | 16 ++++++ .../ugent/pidgeon/util/FileHandlerTest.java | 54 ++++++++++++++++-- .../ugent/pidgeon/util/TestRunnerTest.java | 11 +++- .../DockerSubmissionTestTest/d__test.zip | Bin 162 -> 162 bytes 7 files changed, 94 insertions(+), 31 deletions(-) diff --git a/backend/app/src/main/java/com/ugent/pidgeon/util/EntityToJsonConverter.java b/backend/app/src/main/java/com/ugent/pidgeon/util/EntityToJsonConverter.java index db8923c5..4babf139 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/util/EntityToJsonConverter.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/util/EntityToJsonConverter.java @@ -268,7 +268,7 @@ public TestJson testEntityToTestJson(TestEntity testEntity, long projectId) { testEntity.getDockerTestScript(), testEntity.getDockerTestTemplate(), testEntity.getStructureTemplate(), - testEntity.getExtraFilesId() == null ? null : ApiRoutes.PROJECT_BASE_PATH + "/" + projectId + "/extrafiles", + testEntity.getExtraFilesId() == null ? null : ApiRoutes.PROJECT_BASE_PATH + "/" + projectId + "/tests/extrafiles", extrafiles == null ? null : extrafiles.getName() ); } diff --git a/backend/app/src/main/java/com/ugent/pidgeon/util/Filehandler.java b/backend/app/src/main/java/com/ugent/pidgeon/util/Filehandler.java index 7f1cc483..c7b6b332 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/util/Filehandler.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/util/Filehandler.java @@ -195,22 +195,18 @@ public static void copyFilesAsZip(List files, Path path) throws IOExceptio public static ResponseEntity getZipFileAsResponse(Path path, String filename) { // Get the file from the server - try { - Resource zipFile = Filehandler.getFileAsResource(path); - if (zipFile == null) { - return ResponseEntity.status(HttpStatus.NOT_FOUND).body("File not found."); - } + Resource zipFile = Filehandler.getFileAsResource(path); + if (zipFile == null) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body("File not found."); + } - // Set headers for the response - HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename); - headers.add(HttpHeaders.CONTENT_TYPE, "application/zip"); + // Set headers for the response + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename); + headers.add(HttpHeaders.CONTENT_TYPE, "application/zip"); - return ResponseEntity.ok() - .headers(headers) - .body(zipFile); - } catch (Exception e) { - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage()); - } + return ResponseEntity.ok() + .headers(headers) + .body(zipFile); } } diff --git a/backend/app/src/test/java/com/ugent/pidgeon/docker/DockerSubmissionTestTest.java b/backend/app/src/test/java/com/ugent/pidgeon/docker/DockerSubmissionTestTest.java index 3f8dac56..38945741 100644 --- a/backend/app/src/test/java/com/ugent/pidgeon/docker/DockerSubmissionTestTest.java +++ b/backend/app/src/test/java/com/ugent/pidgeon/docker/DockerSubmissionTestTest.java @@ -223,15 +223,15 @@ void testDockerReceivesUtilFiles(){ Path zipLocation2 = Path.of("src/test/test-cases/DockerSubmissionTestTest/helloworld.zip"); // complicated zip with multiple files and folder structure stm.addUtilFiles(zipLocation); stm.addUtilFiles(zipLocation2); - DockerTestOutput to = stm.runSubmission("find /shared/utils/"); + DockerTestOutput to = stm.runSubmission("find /shared/extra/"); List logs = to.logs.stream().map(log -> log.replaceAll("\n", "")).toList(); - assertEquals("/shared/utils/", logs.get(0)); - assertEquals("/shared/utils/helloworld.txt", logs.get(1)); - assertEquals("/shared/utils/helloworld", logs.get(2)); - assertEquals("/shared/utils/helloworld/helloworld2.txt", logs.get(3)); // I don't understand the order of find :sob: but it is important all files are found. - assertEquals("/shared/utils/helloworld/helloworld3.txt", logs.get(4)); - assertEquals("/shared/utils/helloworld/emptyfolder", logs.get(5)); - assertEquals("/shared/utils/helloworld/helloworld1.txt", logs.get(6)); + assertEquals("/shared/extra/", logs.get(0)); + assertEquals("/shared/extra/helloworld.txt", logs.get(1)); + assertEquals("/shared/extra/helloworld", logs.get(2)); + assertEquals("/shared/extra/helloworld/helloworld2.txt", logs.get(3)); // I don't understand the order of find :sob: but it is important all files are found. + assertEquals("/shared/extra/helloworld/helloworld3.txt", logs.get(4)); + assertEquals("/shared/extra/helloworld/emptyfolder", logs.get(5)); + assertEquals("/shared/extra/helloworld/helloworld1.txt", logs.get(6)); stm.cleanUp(); } diff --git a/backend/app/src/test/java/com/ugent/pidgeon/util/EntityToJsonConverterTest.java b/backend/app/src/test/java/com/ugent/pidgeon/util/EntityToJsonConverterTest.java index fa1a2daa..45f5fb0b 100644 --- a/backend/app/src/test/java/com/ugent/pidgeon/util/EntityToJsonConverterTest.java +++ b/backend/app/src/test/java/com/ugent/pidgeon/util/EntityToJsonConverterTest.java @@ -76,6 +76,9 @@ public class EntityToJsonConverterTest { @Mock private SubmissionRepository submissionRepository; + @Mock + private FileRepository fileRepository; + @Spy @InjectMocks private EntityToJsonConverter entityToJsonConverter; @@ -558,12 +561,25 @@ public void testGetSubmissionJson() { @Test public void testTestEntityToTestJson() { + testEntity.setExtraFilesId(5L); + when(fileRepository.findById(testEntity.getExtraFilesId())) + .thenReturn(Optional.of(new FileEntity("nameoffiles", "path", 5L))); + TestJson result = entityToJsonConverter.testEntityToTestJson(testEntity, projectEntity.getId()); + + assertEquals(ApiRoutes.PROJECT_BASE_PATH + "/" + projectEntity.getId(), result.getProjectUrl()); assertEquals(testEntity.getDockerImage(), result.getDockerImage()); assertEquals(testEntity.getDockerTestScript(), result.getDockerScript()); assertEquals(testEntity.getDockerTestTemplate(), result.getDockerTemplate()); assertEquals(testEntity.getStructureTemplate(), result.getStructureTest()); + assertEquals(ApiRoutes.PROJECT_BASE_PATH + "/" + projectEntity.getId() + "/tests/extrafiles", result.getExtraFilesUrl()); + assertEquals("nameoffiles", result.getExtraFilesName()); + + testEntity.setExtraFilesId(null); + result = entityToJsonConverter.testEntityToTestJson(testEntity, projectEntity.getId()); + assertNull(result.getExtraFilesUrl()); + assertNull(result.getExtraFilesName()); } diff --git a/backend/app/src/test/java/com/ugent/pidgeon/util/FileHandlerTest.java b/backend/app/src/test/java/com/ugent/pidgeon/util/FileHandlerTest.java index e7cd6668..67376b28 100644 --- a/backend/app/src/test/java/com/ugent/pidgeon/util/FileHandlerTest.java +++ b/backend/app/src/test/java/com/ugent/pidgeon/util/FileHandlerTest.java @@ -29,6 +29,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; +import org.springframework.http.ResponseEntity; import org.springframework.mock.web.MockMultipartFile; @ExtendWith(MockitoExtension.class) @@ -65,7 +66,7 @@ public void setUp() throws IOException { } @Test - public void testSaveSubmission() throws Exception { + public void testSaveFile() throws Exception { File savedFile = Filehandler.saveFile(tempDir, file, Filehandler.SUBMISSION_FILENAME); assertTrue(savedFile.exists()); @@ -76,7 +77,7 @@ public void testSaveSubmission() throws Exception { } @Test - public void testSaveSubmission_dirDoesntExist() throws Exception { + public void testSaveFile_dirDoesntExist() throws Exception { File savedFile = Filehandler.saveFile(tempDir.resolve("nonexistent"), file, Filehandler.SUBMISSION_FILENAME); assertTrue(savedFile.exists()); @@ -87,12 +88,12 @@ public void testSaveSubmission_dirDoesntExist() throws Exception { } @Test - public void testSaveSubmission_errorWhileCreatingDir() throws Exception { + public void testSaveFile_errorWhileCreatingDir() throws Exception { assertThrows(IOException.class, () -> Filehandler.saveFile(Path.of(""), file, Filehandler.SUBMISSION_FILENAME)); } @Test - public void testSaveSubmission_notAZipFile() { + public void testSaveFile_notAZipFile() { MockMultipartFile notAZipFile = new MockMultipartFile( "notAZipFile.txt", "This is not a zip file".getBytes() ); @@ -100,7 +101,7 @@ public void testSaveSubmission_notAZipFile() { } @Test - public void testSaveSubmission_fileEmpty() { + public void testSaveFile_fileEmpty() { MockMultipartFile emptyFile = new MockMultipartFile( "emptyFile.txt", new byte[0] ); @@ -108,7 +109,7 @@ public void testSaveSubmission_fileEmpty() { } @Test - public void testSaveSubmission_fileNull() { + public void testSaveFile_fileNull() { assertThrows(IOException.class, () -> Filehandler.saveFile(tempDir, null, Filehandler.SUBMISSION_FILENAME)); } @@ -255,6 +256,12 @@ public void testGetSubmissionArtifactPath() { assertEquals(Path.of(Filehandler.BASEPATH, "projects", "1", "2", "3", "artifacts.zip"), submissionArtifactPath); } + @Test + public void testGetTextExtraFilesPath() { + Path textExtraFilesPath = Filehandler.getTestExtraFilesPath(88); + assertEquals(Path.of(Filehandler.BASEPATH, "projects", String.valueOf(88)), textExtraFilesPath); + } + @Test public void testGetFileAsResource_FileExists() { try { @@ -380,4 +387,39 @@ public void testCopyFilesAsZip_zipFileAlreadyExistNonWriteable() throws IOExcept } } + @Test + public void testGetZipFileAsResponse() throws IOException { + List files = new ArrayList<>(); + File tempFile1 = Files.createTempFile("tempFile1", ".txt").toFile(); + File tempFile2 = Files.createTempFile("tempFile2", ".txt").toFile(); + + try { + files.add(tempFile1); + files.add(tempFile2); + + File zipFile = tempDir.resolve("files.zip").toFile(); + Filehandler.copyFilesAsZip(files, zipFile.toPath()); + + assertTrue(zipFile.exists()); + + ResponseEntity response = Filehandler.getZipFileAsResponse(zipFile.toPath(), "customfilename.zip"); + + assertNotNull(response); + assertEquals(200, response.getStatusCodeValue()); + assertEquals("attachment; filename=customfilename.zip", response.getHeaders().get("Content-Disposition").get(0)); + assertEquals("application/zip", response.getHeaders().get("Content-Type").get(0)); + + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Test + public void testGetZipFileAsResponse_fileDoesNotExist() { + ResponseEntity response = Filehandler.getZipFileAsResponse(Path.of("nonexistent"), "customfilename.zip"); + + assertNotNull(response); + assertEquals(404, response.getStatusCodeValue()); + } + } diff --git a/backend/app/src/test/java/com/ugent/pidgeon/util/TestRunnerTest.java b/backend/app/src/test/java/com/ugent/pidgeon/util/TestRunnerTest.java index d9a7434f..04a4a42f 100644 --- a/backend/app/src/test/java/com/ugent/pidgeon/util/TestRunnerTest.java +++ b/backend/app/src/test/java/com/ugent/pidgeon/util/TestRunnerTest.java @@ -96,8 +96,12 @@ public void testRunStructureTest() throws IOException { @Test public void testRunDockerTest() throws IOException { + Path outputPath = Path.of("outputPath"); + Path extraFilesPath = Path.of("extraFilesPath"); + Path extraFilesPathResolved = extraFilesPath.resolve(Filehandler.EXTRA_TESTFILES_FILENAME); + try (MockedStatic filehandler = org.mockito.Mockito.mockStatic(Filehandler.class)) { - Path outputPath = Path.of("outputPath"); + AtomicInteger filehandlerCalled = new AtomicInteger(); filehandlerCalled.set(0); filehandler.when(() -> Filehandler.copyFilesAsZip(artifacts, outputPath)).thenAnswer( @@ -105,6 +109,7 @@ public void testRunDockerTest() throws IOException { filehandlerCalled.getAndIncrement(); return null; }); + filehandler.when(() -> Filehandler.getTestExtraFilesPath(projectId)).thenReturn(extraFilesPath); when(dockerModel.runSubmissionWithTemplate(testEntity.getDockerTestScript(), testEntity.getDockerTestTemplate())) .thenReturn(dockerTemplateTestOutput); when(dockerModel.getArtifacts()).thenReturn(artifacts); @@ -114,6 +119,7 @@ public void testRunDockerTest() throws IOException { verify(dockerModel, times(1)).addZipInputFiles(file); verify(dockerModel, times(1)).cleanUp(); + verify(dockerModel, times(1)).addUtilFiles(extraFilesPathResolved); assertEquals(1, filehandlerCalled.get()); /* artifacts are empty */ @@ -122,6 +128,7 @@ public void testRunDockerTest() throws IOException { assertEquals(dockerTemplateTestOutput, result); verify(dockerModel, times(2)).addZipInputFiles(file); verify(dockerModel, times(2)).cleanUp(); + verify(dockerModel, times(2)).addUtilFiles(extraFilesPathResolved); assertEquals(1, filehandlerCalled.get()); /* aritifacts are null */ @@ -130,6 +137,7 @@ public void testRunDockerTest() throws IOException { assertEquals(dockerTemplateTestOutput, result); verify(dockerModel, times(3)).addZipInputFiles(file); verify(dockerModel, times(3)).cleanUp(); + verify(dockerModel, times(3)).addUtilFiles(extraFilesPathResolved); assertEquals(1, filehandlerCalled.get()); /* No template */ @@ -139,6 +147,7 @@ public void testRunDockerTest() throws IOException { assertEquals(dockerTestOutput, result); verify(dockerModel, times(4)).addZipInputFiles(file); verify(dockerModel, times(4)).cleanUp(); + verify(dockerModel, times(4)).addUtilFiles(extraFilesPathResolved); /* Error gets thrown */ when(dockerModel.runSubmission(testEntity.getDockerTestScript())).thenThrow(new RuntimeException("Error")); diff --git a/backend/app/src/test/test-cases/DockerSubmissionTestTest/d__test.zip b/backend/app/src/test/test-cases/DockerSubmissionTestTest/d__test.zip index 4e389ca3afc1b890a7685fc0d8db9af2ea9be82b..b6b88bf08700b995cf09e5f2a14a83eeb91444a0 100644 GIT binary patch delta 28 hcmZ3)xQLNAz?+#xgn@&DgMq1e<3wJ6W)Kzc3; Date: Fri, 17 May 2024 22:46:41 +0200 Subject: [PATCH 9/9] Fixed test to work on all os, by making the find consistent. --- .../pidgeon/docker/DockerSubmissionTestTest.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/app/src/test/java/com/ugent/pidgeon/docker/DockerSubmissionTestTest.java b/backend/app/src/test/java/com/ugent/pidgeon/docker/DockerSubmissionTestTest.java index 38945741..e99e185f 100644 --- a/backend/app/src/test/java/com/ugent/pidgeon/docker/DockerSubmissionTestTest.java +++ b/backend/app/src/test/java/com/ugent/pidgeon/docker/DockerSubmissionTestTest.java @@ -224,14 +224,14 @@ void testDockerReceivesUtilFiles(){ stm.addUtilFiles(zipLocation); stm.addUtilFiles(zipLocation2); DockerTestOutput to = stm.runSubmission("find /shared/extra/"); - List logs = to.logs.stream().map(log -> log.replaceAll("\n", "")).toList(); + List logs = to.logs.stream().map(log -> log.replaceAll("\n", "")).sorted().toList(); assertEquals("/shared/extra/", logs.get(0)); - assertEquals("/shared/extra/helloworld.txt", logs.get(1)); - assertEquals("/shared/extra/helloworld", logs.get(2)); - assertEquals("/shared/extra/helloworld/helloworld2.txt", logs.get(3)); // I don't understand the order of find :sob: but it is important all files are found. - assertEquals("/shared/extra/helloworld/helloworld3.txt", logs.get(4)); - assertEquals("/shared/extra/helloworld/emptyfolder", logs.get(5)); - assertEquals("/shared/extra/helloworld/helloworld1.txt", logs.get(6)); + assertEquals("/shared/extra/helloworld", logs.get(1)); + assertEquals("/shared/extra/helloworld.txt", logs.get(2)); + assertEquals("/shared/extra/helloworld/emptyfolder", logs.get(3)); + assertEquals("/shared/extra/helloworld/helloworld1.txt", logs.get(4)); + assertEquals("/shared/extra/helloworld/helloworld2.txt", logs.get(5)); // I don't understand the order of find :sob: but it is important all files are found. + assertEquals("/shared/extra/helloworld/helloworld3.txt", logs.get(6)); stm.cleanUp(); }