From ad583850193f6e55a1148cc537ba30ed9c1500c2 Mon Sep 17 00:00:00 2001 From: aschenzle Date: Sat, 29 Aug 2026 23:40:31 -0700 Subject: [PATCH] DynamoDB DTOs and DSL --- .../execution/DynamoDbExecutionsDto.java | 15 ++++ .../execution/DynamoDbFailedQuery.java | 29 +++++++ .../operations/DynamoDbAttributeValueDto.java | 27 +++++++ .../DynamoDbDatabaseCommandsDto.java | 12 +++ .../operations/DynamoDbInsertionDto.java | 13 +++ .../DynamoDbInsertionResultsDto.java | 28 +++++++ .../operations/DynamoDbScalarTypeDto.java | 10 +++ .../controller/dynamodb/dsl/DynamoDbDsl.java | 81 +++++++++++++++++++ .../dynamodb/dsl/DynamoDbSequenceDsl.java | 15 ++++ .../dynamodb/dsl/DynamoDbStatementDsl.java | 23 ++++++ .../dynamodb/dsl/DynamoDbDslTest.java | 63 +++++++++++++++ 11 files changed, 316 insertions(+) create mode 100644 client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/DynamoDbExecutionsDto.java create mode 100644 client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/DynamoDbFailedQuery.java create mode 100644 client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/DynamoDbAttributeValueDto.java create mode 100644 client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/DynamoDbDatabaseCommandsDto.java create mode 100644 client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/DynamoDbInsertionDto.java create mode 100644 client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/DynamoDbInsertionResultsDto.java create mode 100644 client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/DynamoDbScalarTypeDto.java create mode 100644 client-java/controller/src/main/java/org/evomaster/client/java/controller/dynamodb/dsl/DynamoDbDsl.java create mode 100644 client-java/controller/src/main/java/org/evomaster/client/java/controller/dynamodb/dsl/DynamoDbSequenceDsl.java create mode 100644 client-java/controller/src/main/java/org/evomaster/client/java/controller/dynamodb/dsl/DynamoDbStatementDsl.java create mode 100644 client-java/controller/src/test/java/org/evomaster/client/java/controller/dynamodb/dsl/DynamoDbDslTest.java diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/DynamoDbExecutionsDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/DynamoDbExecutionsDto.java new file mode 100644 index 0000000000..0d57642ac4 --- /dev/null +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/DynamoDbExecutionsDto.java @@ -0,0 +1,15 @@ +package org.evomaster.client.java.controller.api.dto.database.execution; + +import java.util.ArrayList; +import java.util.List; + +/** + * DynamoDB reads that can be satisfied by generated initialization data. + */ +public class DynamoDbExecutionsDto { + + public List failedQueries = new ArrayList<>(); + + public DynamoDbExecutionsDto() { + } +} diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/DynamoDbFailedQuery.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/DynamoDbFailedQuery.java new file mode 100644 index 0000000000..159c73bb86 --- /dev/null +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/DynamoDbFailedQuery.java @@ -0,0 +1,29 @@ +package org.evomaster.client.java.controller.api.dto.database.execution; + +import org.evomaster.client.java.controller.api.dto.database.operations.DynamoDbAttributeValueDto; + +import java.util.ArrayList; +import java.util.List; + +/** + * Equality constraints from a successful DynamoDB read that returned no matching item. + */ +public class DynamoDbFailedQuery { + + public String tableName; + public List attributes = new ArrayList<>(); + + public DynamoDbFailedQuery() { + } + + /** + * Creates a failed-query description. + * + * @param tableName target table + * @param attributes equality-constrained attributes + */ + public DynamoDbFailedQuery(String tableName, List attributes) { + this.tableName = tableName; + this.attributes = new ArrayList<>(attributes); + } +} diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/DynamoDbAttributeValueDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/DynamoDbAttributeValueDto.java new file mode 100644 index 0000000000..d405c92759 --- /dev/null +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/DynamoDbAttributeValueDto.java @@ -0,0 +1,27 @@ +package org.evomaster.client.java.controller.api.dto.database.operations; + +/** + * A named scalar DynamoDB attribute value. + */ +public class DynamoDbAttributeValueDto { + + public String attributeName; + public DynamoDbScalarTypeDto type; + public String value; + + public DynamoDbAttributeValueDto() { + } + + /** + * Creates an attribute value. + * + * @param attributeName attribute name + * @param type DynamoDB scalar type + * @param value string-preserved value + */ + public DynamoDbAttributeValueDto(String attributeName, DynamoDbScalarTypeDto type, String value) { + this.attributeName = attributeName; + this.type = type; + this.value = value; + } +} diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/DynamoDbDatabaseCommandsDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/DynamoDbDatabaseCommandsDto.java new file mode 100644 index 0000000000..17f45b5d4e --- /dev/null +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/DynamoDbDatabaseCommandsDto.java @@ -0,0 +1,12 @@ +package org.evomaster.client.java.controller.api.dto.database.operations; + +import java.util.ArrayList; +import java.util.List; + +/** + * DynamoDB insertion commands sent to the controller. + */ +public class DynamoDbDatabaseCommandsDto { + + public List insertions = new ArrayList<>(); +} diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/DynamoDbInsertionDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/DynamoDbInsertionDto.java new file mode 100644 index 0000000000..a6a29d5671 --- /dev/null +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/DynamoDbInsertionDto.java @@ -0,0 +1,13 @@ +package org.evomaster.client.java.controller.api.dto.database.operations; + +import java.util.ArrayList; +import java.util.List; + +/** + * An item to insert into a DynamoDB table. + */ +public class DynamoDbInsertionDto { + + public String tableName; + public List attributes = new ArrayList<>(); +} diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/DynamoDbInsertionResultsDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/DynamoDbInsertionResultsDto.java new file mode 100644 index 0000000000..86f0f12142 --- /dev/null +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/DynamoDbInsertionResultsDto.java @@ -0,0 +1,28 @@ +package org.evomaster.client.java.controller.api.dto.database.operations; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Results of a sequence of DynamoDB insertions. + */ +public class DynamoDbInsertionResultsDto { + + public List executionResults = new ArrayList<>(); + public Integer failedInsertionIndex; + + /** + * Records the insertion that failed while preserving earlier successes. + * + * @param insertions attempted insertions + * @param failedIndex zero-based index of the failed insertion + */ + public void handleFailedInsertion(List insertions, int failedIndex) { + executionResults = new ArrayList<>(Collections.nCopies(insertions.size(), false)); + for (int i = 0; i < failedIndex; i++) { + executionResults.set(i, true); + } + failedInsertionIndex = failedIndex; + } +} diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/DynamoDbScalarTypeDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/DynamoDbScalarTypeDto.java new file mode 100644 index 0000000000..7438336709 --- /dev/null +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/DynamoDbScalarTypeDto.java @@ -0,0 +1,10 @@ +package org.evomaster.client.java.controller.api.dto.database.operations; + +/** + * Scalar DynamoDB attribute types supported by generated insertions. + */ +public enum DynamoDbScalarTypeDto { + S, + N, + BOOL +} diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/dynamodb/dsl/DynamoDbDsl.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/dynamodb/dsl/DynamoDbDsl.java new file mode 100644 index 0000000000..d84705f813 --- /dev/null +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/dynamodb/dsl/DynamoDbDsl.java @@ -0,0 +1,81 @@ +package org.evomaster.client.java.controller.dynamodb.dsl; + +import org.evomaster.client.java.controller.api.dto.database.operations.DynamoDbAttributeValueDto; +import org.evomaster.client.java.controller.api.dto.database.operations.DynamoDbInsertionDto; +import org.evomaster.client.java.controller.api.dto.database.operations.DynamoDbScalarTypeDto; + +import java.util.ArrayList; +import java.util.List; + +/** + * DSL for DynamoDB insertions in generated tests. + */ +public final class DynamoDbDsl implements DynamoDbSequenceDsl, DynamoDbStatementDsl { + + private List insertions = new ArrayList<>(); + private DynamoDbInsertionDto current; + + private DynamoDbDsl() { + } + + /** + * @return a new DynamoDB insertion sequence + */ + public static DynamoDbSequenceDsl dynamoDb() { + return new DynamoDbDsl(); + } + + @Override + public DynamoDbStatementDsl insertInto(String tableName) { + checkOpen(); + if (tableName == null || tableName.isEmpty()) { + throw new IllegalArgumentException("Unspecified table"); + } + current = new DynamoDbInsertionDto(); + current.tableName = tableName; + insertions.add(current); + return this; + } + + @Override + public DynamoDbStatementDsl s(String name, String value) { + return attribute(name, DynamoDbScalarTypeDto.S, value); + } + + @Override + public DynamoDbStatementDsl n(String name, String value) { + return attribute(name, DynamoDbScalarTypeDto.N, value); + } + + @Override + public DynamoDbStatementDsl bool(String name, boolean value) { + return attribute(name, DynamoDbScalarTypeDto.BOOL, Boolean.toString(value)); + } + + @Override + public List dtos() { + checkOpen(); + List result = insertions; + insertions = null; + current = null; + return result; + } + + private DynamoDbStatementDsl attribute(String name, DynamoDbScalarTypeDto type, String value) { + checkOpen(); + if (current == null) { + throw new IllegalStateException("Call insertInto before adding attributes"); + } + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("Unspecified attribute name"); + } + current.attributes.add(new DynamoDbAttributeValueDto(name, type, value)); + return this; + } + + private void checkOpen() { + if (insertions == null) { + throw new IllegalStateException("DTO was already built for this object"); + } + } +} diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/dynamodb/dsl/DynamoDbSequenceDsl.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/dynamodb/dsl/DynamoDbSequenceDsl.java new file mode 100644 index 0000000000..8b043cb8f2 --- /dev/null +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/dynamodb/dsl/DynamoDbSequenceDsl.java @@ -0,0 +1,15 @@ +package org.evomaster.client.java.controller.dynamodb.dsl; + +/** + * Entry point for a DynamoDB insertion sequence. + */ +public interface DynamoDbSequenceDsl { + + /** + * Starts an item insertion. + * + * @param tableName target table + * @return item statement + */ + DynamoDbStatementDsl insertInto(String tableName); +} diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/dynamodb/dsl/DynamoDbStatementDsl.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/dynamodb/dsl/DynamoDbStatementDsl.java new file mode 100644 index 0000000000..f45f32b082 --- /dev/null +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/dynamodb/dsl/DynamoDbStatementDsl.java @@ -0,0 +1,23 @@ +package org.evomaster.client.java.controller.dynamodb.dsl; + +import org.evomaster.client.java.controller.api.dto.database.operations.DynamoDbInsertionDto; + +import java.util.List; + +/** + * Fluent definition of one DynamoDB item. + */ +public interface DynamoDbStatementDsl extends DynamoDbSequenceDsl { + + /** Adds a string attribute. */ + DynamoDbStatementDsl s(String name, String value); + + /** Adds a number attribute while preserving its exact text. */ + DynamoDbStatementDsl n(String name, String value); + + /** Adds a boolean attribute. */ + DynamoDbStatementDsl bool(String name, boolean value); + + /** @return the completed insertion DTOs */ + List dtos(); +} diff --git a/client-java/controller/src/test/java/org/evomaster/client/java/controller/dynamodb/dsl/DynamoDbDslTest.java b/client-java/controller/src/test/java/org/evomaster/client/java/controller/dynamodb/dsl/DynamoDbDslTest.java new file mode 100644 index 0000000000..d0f28c8c39 --- /dev/null +++ b/client-java/controller/src/test/java/org/evomaster/client/java/controller/dynamodb/dsl/DynamoDbDslTest.java @@ -0,0 +1,63 @@ +package org.evomaster.client.java.controller.dynamodb.dsl; + +import org.evomaster.client.java.controller.api.dto.database.operations.DynamoDbAttributeValueDto; +import org.evomaster.client.java.controller.api.dto.database.operations.DynamoDbInsertionDto; +import org.evomaster.client.java.controller.api.dto.database.operations.DynamoDbScalarTypeDto; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** Tests the DynamoDB insertion DTOs produced by the generated-test DSL. */ +public class DynamoDbDslTest { + + @Test + public void testBuildsWorldCupPlayerInsertions() { + List insertions = DynamoDbDsl.dynamoDb() + .insertInto("WorldCupPlayers") + .s("country", "Argentina") + .n("fifaId", "10") + .bool("captain", true) + .insertInto("WorldCupPlayers") + .s("country", "Brazil") + .n("fifaId", "1") + .dtos(); + + assertEquals(2, insertions.size()); + assertInsertion(insertions.get(0), "WorldCupPlayers", "country", DynamoDbScalarTypeDto.S, "Argentina"); + assertInsertion(insertions.get(0), "WorldCupPlayers", "fifaId", DynamoDbScalarTypeDto.N, "10"); + assertInsertion(insertions.get(0), "WorldCupPlayers", "captain", DynamoDbScalarTypeDto.BOOL, "true"); + assertInsertion(insertions.get(1), "WorldCupPlayers", "country", DynamoDbScalarTypeDto.S, "Brazil"); + assertInsertion(insertions.get(1), "WorldCupPlayers", "fifaId", DynamoDbScalarTypeDto.N, "1"); + } + + @Test + public void testRejectsIncompleteInsertionDefinitions() { + assertThrows(IllegalArgumentException.class, () -> DynamoDbDsl.dynamoDb().insertInto(null)); + assertThrows(IllegalArgumentException.class, () -> DynamoDbDsl.dynamoDb().insertInto("")); + + DynamoDbStatementDsl statement = (DynamoDbStatementDsl) DynamoDbDsl.dynamoDb(); + assertThrows(IllegalStateException.class, () -> statement.s("country", "Argentina")); + + DynamoDbStatementDsl completed = DynamoDbDsl.dynamoDb().insertInto("WorldCupPlayers"); + completed.dtos(); + assertThrows(IllegalStateException.class, () -> completed.insertInto("WorldCupPlayers")); + } + + private void assertInsertion( + DynamoDbInsertionDto insertion, + String tableName, + String attributeName, + DynamoDbScalarTypeDto type, + String value) { + assertEquals(tableName, insertion.tableName); + DynamoDbAttributeValueDto attribute = insertion.attributes.stream() + .filter(candidate -> attributeName.equals(candidate.attributeName)) + .findFirst() + .orElseThrow(() -> new AssertionError("Missing attribute " + attributeName)); + assertEquals(type, attribute.type); + assertEquals(value, attribute.value); + } +}