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 4e8a80644..69b7b2177 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 @@ -752,7 +752,8 @@ public RunsSummary listAllRuns(String query, boolean matchAll, String roles, boo RunsSummary summary = new RunsSummary(); // TODO: total does not consider the query but evaluating all the expressions would be expensive summary.total = trashed ? RunDAO.count() : RunDAO.count("trashed = false"); - summary.runs = runs.stream().map(this::createSummary).collect(Collectors.toList()); + summary.runs = new ArrayList<>(runs.size()); + runs.forEach(r -> summary.runs.add(createSummary(r))); return summary; } catch (PersistenceException pe) { // In case of an error PostgreSQL won't let us execute another query in the same transaction diff --git a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/Util.java b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/Util.java index 1641816b9..2af55e206 100644 --- a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/Util.java +++ b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/Util.java @@ -144,7 +144,7 @@ static void addPaging(StringBuilder sql, Integer limit, Integer page, String sor static void addOrderBy(StringBuilder sql, String sort, SortDirection direction) { sort = sort == null || sort.trim().isEmpty() ? "start" : sort; - direction = direction == null ? SortDirection.Ascending : direction; + direction = direction == null ? SortDirection.Descending : direction; sql.append(" ORDER BY ").append(sort); addDirection(sql, direction); } diff --git a/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/BaseServiceTest.java b/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/BaseServiceTest.java index e893b3c70..7ee54a1fb 100644 --- a/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/BaseServiceTest.java +++ b/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/BaseServiceTest.java @@ -31,6 +31,7 @@ import io.hyperfoil.tools.horreum.api.internal.services.AlertingService; import io.hyperfoil.tools.horreum.api.report.ReportComponent; import io.hyperfoil.tools.horreum.api.report.TableReportConfig; +import io.hyperfoil.tools.horreum.api.services.DatasetService; import io.hyperfoil.tools.horreum.api.services.ExperimentService; import io.hyperfoil.tools.horreum.api.services.RunService; import io.hyperfoil.tools.horreum.bus.MessageBusChannels; @@ -1019,4 +1020,17 @@ protected ArrayNode arrayOf(String... labels) { } return array; } + + protected DatasetService.DatasetList listTestDatasets(long id, SortDirection direction){ + StringBuilder url = new StringBuilder("/api/dataset/list/" + id); + if (direction != null) { + url.append("?direction=" + direction); + } + return jsonRequest() + .get(url.toString()) + .then() + .statusCode(200) + .extract() + .as(DatasetService.DatasetList.class); + } } diff --git a/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/DatasetServiceTest.java b/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/DatasetServiceTest.java index 50c63acfb..4919024ab 100644 --- a/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/DatasetServiceTest.java +++ b/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/DatasetServiceTest.java @@ -16,6 +16,7 @@ import java.util.stream.Collectors; import java.util.stream.StreamSupport; +import io.hyperfoil.tools.horreum.api.SortDirection; import io.hyperfoil.tools.horreum.api.internal.services.SqlService; import io.hyperfoil.tools.horreum.bus.MessageBusChannels; import jakarta.inject.Inject; @@ -430,4 +431,30 @@ public void testEverythingPrivate() throws InterruptedException { } } + @org.junit.jupiter.api.Test + public void testDatasetListOrdering(){ + String testname = "road-runner"; + Test test = createTest(createExampleTest(testname)); + BlockingQueue updateQueue = eventConsumerQueue(Dataset.LabelsUpdatedEvent.class, MessageBusChannels.DATASET_UPDATED_LABELS, e -> checkTestId(e.datasetId, test.id)); + long timestamp = System.currentTimeMillis(); + int initialRunID = uploadRun(timestamp, timestamp, + JsonNodeFactory.instance.objectNode(), + test.name); + waitForDatasets(initialRunID); + timestamp = System.currentTimeMillis(); + int latterRunID = uploadRun(timestamp, timestamp, + JsonNodeFactory.instance.objectNode(), + test.name); + waitForDatasets(latterRunID); + DatasetService.DatasetList datasetsList = listTestDatasets(test.id, null); + assertEquals(2, datasetsList.datasets.size()); + assertEquals(0, datasetsList.datasets.get(0).ordinal); + assertEquals(initialRunID, datasetsList.datasets.get(1).runId); + assertEquals(latterRunID, datasetsList.datasets.get(0).runId); + datasetsList = null; + datasetsList = listTestDatasets(test.id, SortDirection.Ascending); + assertEquals(2, datasetsList.datasets.size()); + assertEquals(initialRunID, datasetsList.datasets.get(0).runId); + assertEquals(latterRunID, datasetsList.datasets.get(1).runId); + } } diff --git a/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/RunServiceTest.java b/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/RunServiceTest.java index d7b827ad4..1ac24b092 100644 --- a/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/RunServiceTest.java +++ b/horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/RunServiceTest.java @@ -4,6 +4,7 @@ import java.io.IOException; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -949,6 +950,46 @@ public void runExperiment() throws InterruptedException { } } + @org.junit.jupiter.api.Test + public void testAllRunsOrdering() throws IOException { + String name = "with_meta"; + Test test = createTest(createExampleTest(name)); + createSchema("Foo", "urn:foo"); + createSchema("Bar", "urn:bar"); + createSchema("Q", "urn:q"); + Schema gooSchema = createSchema("Goo", "urn:goo"); + Schema postSchema = createSchema("Post", "uri:Goo-post-function"); + + long now = System.currentTimeMillis(); + ObjectNode data = simpleObject("urn:foo", "foo", "xxx"); + ArrayNode metadata = JsonNodeFactory.instance.arrayNode(); + metadata.add(simpleObject("urn:bar", "bar", "yyy")); + + uploadRun(now, data, metadata, test.name); + now = System.currentTimeMillis(); + uploadRun(now, data, metadata, test.name); + + RunService.RunsSummary runs = jsonRequest() + .get("/api/run/list?limit=10&page=1&query=$.*") + .then() + .statusCode(200) + .extract() + .as(RunService.RunsSummary.class); + + assertEquals(2, runs.runs.size()); + assertEquals(name, runs.runs.get(0).testname); + assertTrue(runs.runs.get(0).start.isAfter(runs.runs.get(1).start) ); + + runs = jsonRequest() + .get("/api/run/list?limit=10&page=1&query=$.*&direction=Ascending" ) + .then() + .statusCode(200) + .extract() + .as(RunService.RunsSummary.class); + assertEquals( 2, runs.runs.size()); + assertTrue(runs.runs.get(0).start.isBefore(runs.runs.get(1).start) ); + } + private JsonNode getBySchema(JsonNode data, String schema) { JsonNode foo = StreamSupport.stream(data.spliterator(), false) .filter(item -> schema.equals(item.path("$schema").asText())).findFirst().orElse(null);