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 080c92dd..f410d2c5 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 @@ -187,7 +187,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 @@ -240,7 +240,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."); } @@ -301,23 +301,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..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 @@ -9,9 +9,8 @@ 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 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 +18,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 +234,97 @@ 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(file.getOriginalFilename()); + 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 = testEntity.getExtraFilesId() == null ? + null : fileRepository.findById(testEntity.getExtraFilesId()).orElse(null); + if (fileEntity == null) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body("No extra files found"); + } + + testEntity.setExtraFilesId(null); + testEntity = testRepository.save(testEntity); + + 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) { + 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()), 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 e2c0d034..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,22 +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 dockerTemplate, String structureTest, String extraFilesUrl, String extraFilesName) { this.projectUrl = projectUrl; this.dockerImage = dockerImage; this.dockerScript = dockerScript; - this.dockerTemplate = dockerTemplate; - this.structureTest = structureTest; + this.dockerTemplate = dockerTemplate; + this.structureTest = structureTest; + this.extraFilesUrl = extraFilesUrl; + this.extraFilesName = extraFilesName; } public String getProjectUrl() { @@ -58,4 +64,20 @@ public String getDockerTemplate() { public void setDockerTemplate(String dockerTemplate) { this.dockerTemplate = dockerTemplate; } + + public String getExtraFilesUrl() { + return extraFilesUrl; + } + + 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/model/submissionTesting/DockerSubmissionTestModel.java b/backend/app/src/main/java/com/ugent/pidgeon/model/submissionTesting/DockerSubmissionTestModel.java index 7e67c969..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 @@ -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 + "extra/").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 + "extra/", 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/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 3d5314b1..ba304571 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, boolean hideStudentNumber) { @@ -267,12 +269,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.getStructureTemplate(), + testEntity.getExtraFilesId() == null ? null : ApiRoutes.PROJECT_BASE_PATH + "/" + projectId + "/tests/extrafiles", + extrafiles == null ? null : extrafiles.getName() ); } } \ 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 d2f1f3b7..a06e5399 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"; public static String ADMIN_SUBMISSION_FOLDER = "adminsubmissions"; /** @@ -27,7 +31,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"); @@ -51,7 +55,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); @@ -121,6 +125,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 @@ -188,4 +196,21 @@ public static void copyFilesAsZip(List files, Path path) throws IOExceptio } } } + + public static ResponseEntity getZipFileAsResponse(Path path, String filename) { + // Get the file from the server + 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); + } } 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) { 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 94814845..269e7c77 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 @@ -278,17 +278,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.saveSubmission(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()) @@ -358,7 +359,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) @@ -382,7 +383,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); @@ -418,6 +419,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()) @@ -426,23 +428,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..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; @@ -106,9 +123,18 @@ public void setup() { test.getDockerImage(), test.getDockerTestScript(), test.getDockerTestTemplate(), - test.getStructureTemplate() + test.getStructureTemplate(), + "extraFilesUrl", + "extraFilesName" + ); + 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 @@ -132,7 +158,9 @@ public void testUpdateTest() throws Exception { dockerImage, dockerTestScript, dockerTestTemplate, - structureTemplate + structureTemplate, + "extraFilesUrl", + "extraFilesName" ); /* All checks succeed */ when(testUtil.checkForTestUpdate( @@ -174,7 +202,9 @@ public void testUpdateTest() throws Exception { null, null, null, - null + null, + "extraFilesUrl", + "extraFilesName" ); testUpdateJson = new TestUpdateJson( dockerImageBlank, @@ -285,7 +315,9 @@ public void testPutTest() throws Exception { dockerImage, dockerTestScript, dockerTestTemplate, - structureTemplate + structureTemplate, + "extraFilesUrl", + "extraFilesName" ); /* All checks succeed */ when(testUtil.checkForTestUpdate( @@ -337,7 +369,9 @@ public void testPutTest() throws Exception { null, null, null, - null + null, + "extraFilesUrl", + "extraFilesName" ); reset(testUtil); when(testUtil.checkForTestUpdate( @@ -672,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); + } + } } 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..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 @@ -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/extra/"); + List logs = to.logs.stream().map(log -> log.replaceAll("\n", "")).sorted().toList(); + assertEquals("/shared/extra/", logs.get(0)); + 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(); + } + } 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 643c71c8..f02c545a 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; @@ -607,12 +610,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 af29bee8..a7dcc193 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,8 +66,8 @@ public void setUp() throws IOException { } @Test - public void testSaveSubmission() throws Exception { - File savedFile = Filehandler.saveSubmission(tempDir, file); + public void testSaveFile() throws Exception { + File savedFile = Filehandler.saveFile(tempDir, file, Filehandler.SUBMISSION_FILENAME); assertTrue(savedFile.exists()); assertEquals(Filehandler.SUBMISSION_FILENAME, savedFile.getName()); @@ -76,8 +77,8 @@ public void testSaveSubmission() throws Exception { } @Test - public void testSaveSubmission_dirDoesntExist() throws Exception { - File savedFile = Filehandler.saveSubmission(tempDir.resolve("nonexistent"), file); + public void testSaveFile_dirDoesntExist() throws Exception { + File savedFile = Filehandler.saveFile(tempDir.resolve("nonexistent"), file, Filehandler.SUBMISSION_FILENAME); assertTrue(savedFile.exists()); assertEquals(Filehandler.SUBMISSION_FILENAME, savedFile.getName()); @@ -87,29 +88,29 @@ public void testSaveSubmission_dirDoesntExist() throws Exception { } @Test - public void testSaveSubmission_errorWhileCreatingDir() throws Exception { - assertThrows(IOException.class, () -> Filehandler.saveSubmission(Path.of(""), file)); + 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() ); - assertThrows(IOException.class, () -> Filehandler.saveSubmission(tempDir, notAZipFile)); + assertThrows(IOException.class, () -> Filehandler.saveFile(tempDir, notAZipFile, Filehandler.SUBMISSION_FILENAME)); } @Test - public void testSaveSubmission_fileEmpty() { + public void testSaveFile_fileEmpty() { MockMultipartFile emptyFile = new MockMultipartFile( "emptyFile.txt", new byte[0] ); - assertThrows(IOException.class, () -> Filehandler.saveSubmission(tempDir, emptyFile)); + assertThrows(IOException.class, () -> Filehandler.saveFile(tempDir, emptyFile, Filehandler.SUBMISSION_FILENAME)); } @Test - public void testSaveSubmission_fileNull() { - assertThrows(IOException.class, () -> Filehandler.saveSubmission(tempDir, null)); + public void testSaveFile_fileNull() { + assertThrows(IOException.class, () -> Filehandler.saveFile(tempDir, null, Filehandler.SUBMISSION_FILENAME)); } @Test @@ -261,10 +262,17 @@ 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 testGetSubmissionArtifactPath_groupIdIsNull() { Path submissionArtifactPath = Filehandler.getSubmissionArtifactPath(1, null, 3); assertEquals(Path.of(Filehandler.BASEPATH, "projects", "1", Filehandler.ADMIN_SUBMISSION_FOLDER, "3", "artifacts.zip"), submissionArtifactPath); + } @Test @@ -392,4 +400,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 828a0f7d..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 @@ -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() { @@ -95,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( @@ -104,49 +109,54 @@ 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); - 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); verify(dockerModel, times(1)).cleanUp(); + verify(dockerModel, times(1)).addUtilFiles(extraFilesPathResolved); assertEquals(1, filehandlerCalled.get()); /* 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(); + verify(dockerModel, times(2)).addUtilFiles(extraFilesPathResolved); assertEquals(1, filehandlerCalled.get()); /* 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(); + verify(dockerModel, times(3)).addUtilFiles(extraFilesPathResolved); assertEquals(1, filehandlerCalled.get()); /* 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(); + verify(dockerModel, times(4)).addUtilFiles(extraFilesPathResolved); /* 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); } 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 00000000..54fb8fc7 Binary files /dev/null and b/backend/app/src/test/test-cases/DockerSubmissionTestTest/helloworld.zip differ 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'); diff --git a/backend/database/start_database.sql b/backend/database/start_database.sql index 311762e1..9c15a5be 100644 --- a/backend/database/start_database.sql +++ b/backend/database/start_database.sql @@ -58,7 +58,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) );