From f56ba273f65912321633c4ba80073bc1b2e6c7ff Mon Sep 17 00:00:00 2001 From: Andrea Lamparelli Date: Mon, 2 Dec 2024 12:32:44 +0100 Subject: [PATCH] Trash run even if it is already trashed Trashing a run that was already trashed should have no effects and it should not throw an exception Signed-off-by: Andrea Lamparelli --- .../tools/horreum/svc/RunServiceImpl.java | 10 +- .../tools/horreum/svc/TestServiceImpl.java | 1 - .../horreum/svc/BaseServiceNoRestTest.java | 24 ++++ .../horreum/svc/RunServiceNoRestTest.java | 116 ++++++++++++++++++ .../horreum/svc/TestServiceNoRestTest.java | 69 +++++++++++ 5 files changed, 215 insertions(+), 5 deletions(-) create mode 100644 horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/RunServiceNoRestTest.java diff --git a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/RunServiceImpl.java b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/RunServiceImpl.java index 176c4a73b..fa8da0f91 100644 --- a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/RunServiceImpl.java +++ b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/RunServiceImpl.java @@ -921,11 +921,13 @@ private void trashInternal(int id, boolean trashed) { if (run == null) { throw ServiceException.notFound("Run not found: " + id); } - if (run.trashed == trashed) - throw ServiceException.badRequest("The run " + id + " has already been trashed, not possible to trash it again."); + if (run.trashed == trashed && trashed) { + log.infof("The run %s has already been trashed, doing nothing.", id); + return; + } if (trashed) { trashConnectedDatasets(run.id, run.testid); - run.trashed = trashed; + run.trashed = true; run.persist(); if (mediator.testMode()) Util.registerTxSynchronization(tm, @@ -935,7 +937,7 @@ private void trashInternal(int id, boolean trashed) { // before we try to recalculate the dataset else { if (TestDAO.findById(run.testid) != null) { - run.trashed = trashed; + run.trashed = false; run.persistAndFlush(); transform(id, true); } else diff --git a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/TestServiceImpl.java b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/TestServiceImpl.java index 431f06773..ad8d71df9 100644 --- a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/TestServiceImpl.java +++ b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/TestServiceImpl.java @@ -119,7 +119,6 @@ public void delete(int id) { if (mediator.testMode()) Util.registerTxSynchronization(tm, txStatus -> mediator.publishEvent(AsyncEventChannels.TEST_DELETED, test.id, TestMapper.from(test))); - ; } @Override diff --git a/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/BaseServiceNoRestTest.java b/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/BaseServiceNoRestTest.java index e17f94fc3..4a75f48c2 100644 --- a/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/BaseServiceNoRestTest.java +++ b/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/BaseServiceNoRestTest.java @@ -1,11 +1,18 @@ package io.hyperfoil.tools.horreum.svc; +import java.time.Instant; import java.util.ArrayList; import java.util.List; +import jakarta.inject.Inject; +import jakarta.persistence.EntityManager; + +import com.fasterxml.jackson.databind.JsonNode; + import io.hyperfoil.tools.horreum.api.data.Access; import io.hyperfoil.tools.horreum.api.data.Extractor; import io.hyperfoil.tools.horreum.api.data.Label; +import io.hyperfoil.tools.horreum.api.data.Run; import io.hyperfoil.tools.horreum.api.data.Schema; import io.hyperfoil.tools.horreum.api.data.Test; import io.hyperfoil.tools.horreum.api.data.Transformer; @@ -19,6 +26,9 @@ public abstract class BaseServiceNoRestTest { protected static final String FOO_UPLOADER = "foo-uploader"; protected static final String BAR_TEAM = "bar-team"; + @Inject + protected EntityManager em; + protected Schema createSampleSchema(String name, String uri, String owner) { Schema schema = new Schema(); schema.owner = owner; @@ -85,4 +95,18 @@ protected Test createSampleTest(String name, String owner, String folder, Intege test.folder = folder == null ? "" : folder; return test; } + + protected Run createSampleRun(int testId, JsonNode runJson, String owner) { + Instant instant = Instant.now(); + + Run run = new Run(); + run.testid = testId; + run.data = runJson; + run.trashed = false; + run.start = instant; + run.stop = instant; + run.owner = owner == null ? FOO_TEAM : owner; + + return run; + } } diff --git a/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/RunServiceNoRestTest.java b/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/RunServiceNoRestTest.java new file mode 100644 index 000000000..48e65c9da --- /dev/null +++ b/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/RunServiceNoRestTest.java @@ -0,0 +1,116 @@ +package io.hyperfoil.tools.horreum.svc; + +import static io.hyperfoil.tools.horreum.svc.BaseServiceNoRestTest.DEFAULT_USER; +import static io.hyperfoil.tools.horreum.svc.BaseServiceNoRestTest.FOO_TEAM; +import static io.hyperfoil.tools.horreum.svc.BaseServiceNoRestTest.FOO_TESTER; +import static io.hyperfoil.tools.horreum.svc.BaseServiceNoRestTest.FOO_UPLOADER; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import jakarta.inject.Inject; +import jakarta.ws.rs.core.Response; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; + +import io.hyperfoil.tools.horreum.api.data.Access; +import io.hyperfoil.tools.horreum.api.data.Run; +import io.hyperfoil.tools.horreum.api.data.Test; +import io.hyperfoil.tools.horreum.api.services.RunService; +import io.hyperfoil.tools.horreum.api.services.TestService; +import io.hyperfoil.tools.horreum.entity.data.RunDAO; +import io.hyperfoil.tools.horreum.entity.data.TestDAO; +import io.hyperfoil.tools.horreum.test.HorreumTestProfile; +import io.hyperfoil.tools.horreum.test.PostgresResource; +import io.quarkus.test.TestTransaction; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.TestProfile; +import io.quarkus.test.security.TestSecurity; + +@QuarkusTest +@QuarkusTestResource(PostgresResource.class) +@TestProfile(HorreumTestProfile.class) +@TestTransaction +@TestSecurity(user = DEFAULT_USER, roles = { Roles.TESTER, Roles.VIEWER, Roles.UPLOADER, FOO_TEAM, FOO_TESTER, FOO_UPLOADER }) +class RunServiceNoRestTest extends BaseServiceNoRestTest { + + @Inject + RunService runService; + + @Inject + TestService testService; + + @Inject + ObjectMapper objectMapper; + + @org.junit.jupiter.api.Test + void testUploadRun() { + Test t1 = createSampleTest("test", null, null, null); + + Test created1 = testService.add(t1); + assertNotNull(created1.id); + assertEquals(1, TestDAO.count()); + + int runId = uploadRun(created1.id, FOO_TEAM, JsonNodeFactory.instance.objectNode()); + assertEquals(1, RunDAO.count()); + assertNotNull(RunDAO.findById(runId)); + } + + @org.junit.jupiter.api.Test + void testTrashRun() { + Test t1 = createSampleTest("test", null, null, null); + + Test created1 = testService.add(t1); + assertNotNull(created1.id); + assertEquals(1, TestDAO.count()); + + int runId = uploadRun(created1.id, FOO_TEAM, JsonNodeFactory.instance.objectNode()); + assertEquals(1, RunDAO.count()); + RunDAO run = RunDAO.findById(runId); + assertFalse(run.trashed); + + runService.trash(runId, true); + assertTrue(run.trashed); + } + + @org.junit.jupiter.api.Test + void testTrashAlreadyTrashedRun() { + Test t1 = createSampleTest("test", null, null, null); + + Test created1 = testService.add(t1); + assertNotNull(created1.id); + assertEquals(1, TestDAO.count()); + + int runId = uploadRun(created1.id, FOO_TEAM, JsonNodeFactory.instance.objectNode()); + assertEquals(1, RunDAO.count()); + RunDAO run = RunDAO.findById(runId); + assertFalse(run.trashed); + + runService.trash(runId, true); + assertTrue(run.trashed); + + // trash again the same trashed run + runService.trash(runId, true); + assertTrue(run.trashed); + } + + // utility to create a sample test and add to Horreum + private Test addTest(String name, String owner, String folder, Integer datastoreId) { + Test test = createSampleTest(name, owner, folder, datastoreId); + return testService.add(test); + } + + // utility to create a sample test and add to Horreum + private int uploadRun(int testId, String owner, JsonNode runData) { + Run run = createSampleRun(testId, runData, owner); + + try (Response resp = runService.add(String.valueOf(testId), owner, Access.PUBLIC, run)) { + assertEquals(Response.Status.OK.getStatusCode(), resp.getStatus()); + return Integer.parseInt(resp.getEntity().toString()); + } + } +} diff --git a/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/TestServiceNoRestTest.java b/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/TestServiceNoRestTest.java index 2cc831b18..ec667083a 100644 --- a/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/TestServiceNoRestTest.java +++ b/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/TestServiceNoRestTest.java @@ -3,6 +3,7 @@ import static io.hyperfoil.tools.horreum.svc.BaseServiceNoRestTest.DEFAULT_USER; import static io.hyperfoil.tools.horreum.svc.BaseServiceNoRestTest.FOO_TEAM; import static io.hyperfoil.tools.horreum.svc.BaseServiceNoRestTest.FOO_TESTER; +import static io.hyperfoil.tools.horreum.svc.BaseServiceNoRestTest.FOO_UPLOADER; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -20,12 +21,16 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; import io.hyperfoil.tools.horreum.api.SortDirection; import io.hyperfoil.tools.horreum.api.data.Access; +import io.hyperfoil.tools.horreum.api.data.Run; import io.hyperfoil.tools.horreum.api.data.Test; +import io.hyperfoil.tools.horreum.api.services.RunService; import io.hyperfoil.tools.horreum.api.services.TestService; +import io.hyperfoil.tools.horreum.entity.data.RunDAO; import io.hyperfoil.tools.horreum.entity.data.TestDAO; import io.hyperfoil.tools.horreum.test.HorreumTestProfile; import io.hyperfoil.tools.horreum.test.PostgresResource; @@ -47,6 +52,9 @@ class TestServiceNoRestTest extends BaseServiceNoRestTest { @Inject TestService testService; + @Inject + RunService runService; + @Inject ObjectMapper objectMapper; @@ -498,6 +506,67 @@ void testDeleteTestNotFound() { assertEquals(Response.Status.NOT_FOUND.getStatusCode(), thrown.getResponse().getStatus()); } + @TestSecurity(user = DEFAULT_USER, roles = { Roles.TESTER, Roles.VIEWER, Roles.UPLOADER, FOO_TEAM, FOO_TESTER, + FOO_UPLOADER }) + @org.junit.jupiter.api.Test + void testDeleteTestWithRun() { + Test created1 = addTest("test", null, null, null); + assertNotNull(created1.id); + assertEquals(1, TestDAO.count()); + + Run run1 = createSampleRun(created1.id, JsonNodeFactory.instance.objectNode(), FOO_TEAM); + int runId; + try (Response resp = runService.add(created1.name, FOO_TEAM, Access.PUBLIC, run1)) { + assertEquals(Response.Status.OK.getStatusCode(), resp.getStatus()); + runId = Integer.parseInt(resp.getEntity().toString()); + } + assertEquals(1, RunDAO.count()); + + // flush data + em.clear(); + + testService.delete(created1.id); + assertEquals(0, TestDAO.count()); + + // atm when a test is deleted, its runs are simply trashed + assertEquals(1, RunDAO.count()); + RunDAO persistedRun = RunDAO.findById(runId); + assertNotNull(persistedRun); + assertTrue(persistedRun.trashed); + } + + @TestSecurity(user = DEFAULT_USER, roles = { Roles.TESTER, Roles.VIEWER, Roles.UPLOADER, FOO_TEAM, FOO_TESTER, + FOO_UPLOADER }) + @org.junit.jupiter.api.Test + void testDeleteTestWithAlreadyTrashedRun() { + Test created1 = addTest("test", null, null, null); + assertNotNull(created1.id); + assertEquals(1, TestDAO.count()); + + Run run1 = createSampleRun(created1.id, JsonNodeFactory.instance.objectNode(), FOO_TEAM); + int runId; + try (Response resp = runService.add(created1.name, FOO_TEAM, Access.PUBLIC, run1)) { + assertEquals(Response.Status.OK.getStatusCode(), resp.getStatus()); + runId = Integer.parseInt(resp.getEntity().toString()); + } + assertEquals(1, RunDAO.count()); + + // flush data + em.clear(); + + // trash the run + runService.trash(runId, true); + + testService.delete(created1.id); + assertEquals(0, TestDAO.count()); + + // atm when a test is deleted, its runs are simply trashed + assertEquals(1, RunDAO.count()); + RunDAO persistedRun = RunDAO.findById(runId); + assertNotNull(persistedRun); + assertTrue(persistedRun.trashed); + } + @TestSecurity(user = DEFAULT_USER, roles = { Roles.TESTER, Roles.VIEWER, FOO_TEAM, FOO_TESTER, FOO_UPLOADER }) @org.junit.jupiter.api.Test void testEnsureTestExists() {