From 8c91a0d374bed34574272d7ff6ae3b4e8cb5fd22 Mon Sep 17 00:00:00 2001 From: Ivan Senic Date: Wed, 1 Mar 2023 16:34:34 +0100 Subject: [PATCH] realtes to #178: adapt delete commands per spec, improve tests --- .../sgv2/jsonapi/StargateJsonApi.java | 19 +- .../model/command/impl/DeleteManyCommand.java | 14 +- .../model/command/impl/DeleteOneCommand.java | 10 +- .../jsonapi/api/v1/CollectionResource.java | 3 +- ...CollectionResourceBaseIntegrationTest.java | 15 +- .../api/v1/DeleteManyIntegrationTest.java | 222 ++++++++--- .../api/v1/DeleteOneIntegrationTest.java | 250 ++++++++---- .../model/impl/DeleteOperationTest.java | 376 ++++++++++++++---- .../impl/DeleteManyCommandResolverTest.java | 165 ++++---- .../impl/DeleteOneCommandResolverTest.java | 147 ++++--- 10 files changed, 848 insertions(+), 373 deletions(-) diff --git a/src/main/java/io/stargate/sgv2/jsonapi/StargateJsonApi.java b/src/main/java/io/stargate/sgv2/jsonapi/StargateJsonApi.java index f055e2973e..b6208b0082 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/StargateJsonApi.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/StargateJsonApi.java @@ -166,7 +166,7 @@ """ { "deleteMany": { - "filter": {"city": "London"} + "filter": {"location": "London"} } } """), @@ -379,13 +379,24 @@ } """), @ExampleObject( - name = "resultDelete", - summary = "Delete command result", + name = "resultDeleteOne", + summary = "`deleteOne` command result", value = """ { "status": { - "deletedIds": ["1", "2"], + "deletedCount": 1 + } + } + """), + @ExampleObject( + name = "resultDeleteMany", + summary = "`deleteMany` command result", + value = + """ + { + "status": { + "deletedCount": 2, "moreData" : true } } diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/impl/DeleteManyCommand.java b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/impl/DeleteManyCommand.java index 16213dbfef..da20df49bb 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/impl/DeleteManyCommand.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/impl/DeleteManyCommand.java @@ -6,27 +6,23 @@ import io.stargate.sgv2.jsonapi.api.model.command.Filterable; import io.stargate.sgv2.jsonapi.api.model.command.ModifyCommand; import io.stargate.sgv2.jsonapi.api.model.command.clause.filter.FilterClause; -import javax.annotation.Nullable; import javax.validation.Valid; import org.eclipse.microprofile.openapi.annotations.media.Schema; /** * Representation of the deleteMany API {@link Command}. * - * @param filterClause {@link FilterClause} used to identify the document. + * @param filterClause {@link FilterClause} used to identify documents. */ @Schema( description = - "Command that finds documents based on the filter and deletes it from a collection") + "Command that finds documents based on the filter and deletes them from a collection") @JsonTypeName("deleteMany") public record DeleteManyCommand( @Schema( - description = "Filter clause based on which document is identified", + description = "Filter clause based on which documents are identified", implementation = FilterClause.class) @Valid @JsonProperty("filter") - FilterClause filterClause, - @Nullable Options options) - implements ModifyCommand, Filterable { - public record Options() {} -} + FilterClause filterClause) + implements ModifyCommand, Filterable {} diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/impl/DeleteOneCommand.java b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/impl/DeleteOneCommand.java index 537acd179f..4b22a7f38b 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/impl/DeleteOneCommand.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/impl/DeleteOneCommand.java @@ -6,7 +6,6 @@ import io.stargate.sgv2.jsonapi.api.model.command.Filterable; import io.stargate.sgv2.jsonapi.api.model.command.ModifyCommand; import io.stargate.sgv2.jsonapi.api.model.command.clause.filter.FilterClause; -import javax.annotation.Nullable; import javax.validation.Valid; import javax.validation.constraints.NotNull; import org.eclipse.microprofile.openapi.annotations.media.Schema; @@ -14,7 +13,7 @@ /** * Representation of the deleteOne API {@link Command}. * - * @param filterClause {@link FilterClause} used to identify the document. + * @param filterClause {@link FilterClause} used to identify a document. */ @Schema(description = "Command that finds a single document and deletes it from a collection") @JsonTypeName("deleteOne") @@ -25,8 +24,5 @@ public record DeleteOneCommand( implementation = FilterClause.class) @Valid @JsonProperty("filter") - FilterClause filterClause, - @Nullable Options options) - implements ModifyCommand, Filterable { - public record Options() {} -} + FilterClause filterClause) + implements ModifyCommand, Filterable {} diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResource.java b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResource.java index c950ce1728..37500e35cf 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResource.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResource.java @@ -107,7 +107,8 @@ public CollectionResource(CommandProcessor commandProcessor) { @ExampleObject(ref = "resultFindOneAndUpdate"), @ExampleObject(ref = "resultInsert"), @ExampleObject(ref = "resultError"), - @ExampleObject(ref = "resultDelete"), + @ExampleObject(ref = "resultDeleteOne"), + @ExampleObject(ref = "resultDeleteMany"), @ExampleObject(ref = "resultUpdateMany"), @ExampleObject(ref = "resultUpdateOne"), }))) diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResourceBaseIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResourceBaseIntegrationTest.java index 46be60bed3..075f5d8890 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResourceBaseIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResourceBaseIntegrationTest.java @@ -28,18 +28,19 @@ public static void enableLog() { } @Test - @Order(1) + @Order(Integer.MIN_VALUE) public final void createCollection() { String json = String.format( """ - { - "createCollection": { - "name": "%s" - } - } - """, + { + "createCollection": { + "name": "%s" + } + } + """, collectionName); + given() .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) .contentType(ContentType.JSON) diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteManyIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteManyIntegrationTest.java index b2a06a69fc..2388f1bbb6 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteManyIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteManyIntegrationTest.java @@ -2,6 +2,8 @@ import static io.restassured.RestAssured.given; import static io.stargate.sgv2.common.IntegrationTestUtils.getAuthToken; +import static net.javacrumbs.jsonunit.JsonMatchers.jsonEquals; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; @@ -11,7 +13,6 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.TestMethodOrder; @@ -28,16 +29,17 @@ private void insert(int countOfDocument) { for (int i = 1; i <= countOfDocument; i++) { String json = """ - { - "insertOne": { - "document": { - "_id": "doc%s", - "username": "user%s", - "status": "active" - } - } - } - """; + { + "insertOne": { + "document": { + "_id": "doc%s", + "username": "user%s", + "status": "active" + } + } + } + """; + given() .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) .contentType(ContentType.JSON) @@ -45,22 +47,68 @@ private void insert(int countOfDocument) { .when() .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) .then() - .statusCode(200); + .statusCode(200) + .body("errors", is(nullValue())); } } @Test - @Order(2) public void deleteManyById() { - insert(1); + insert(2); String json = """ - { - "deleteMany": { - "filter" : {"_id" : "doc1"} - } - } - """; + { + "deleteMany": { + "filter" : {"_id" : "doc1"} + } + } + """; + + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("status.deletedCount", is(1)) + .body("status.moreData", is(nullValue())) + .body("data", is(nullValue())) + .body("errors", is(nullValue())); + + // ensure find does not find the document + json = + """ + { + "findOne": { + "filter" : {"_id" : "doc1"} + } + } + """; + + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("data.docs", jsonEquals("[]")) + .body("status", is(nullValue())) + .body("errors", is(nullValue())); + + // but can find does the non-deleted document + json = + """ + { + "findOne": { + "filter" : {"_id" : "doc2"} + } + } + """; + given() .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) .contentType(ContentType.JSON) @@ -69,21 +117,23 @@ public void deleteManyById() { .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) .then() .statusCode(200) - .body("status.deletedCount", is(1)); + .body("data.docs[0]._id", is("doc2")) + .body("status", is(nullValue())) + .body("errors", is(nullValue())); } @Test - @Order(3) public void deleteManyByColumn() { insert(5); String json = """ - { - "deleteMany": { - "filter" : {"status": "active"} - } - } - """; + { + "deleteMany": { + "filter" : {"status": "active"} + } + } + """; + given() .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) .contentType(ContentType.JSON) @@ -93,22 +143,44 @@ public void deleteManyByColumn() { .then() .statusCode(200) .body("status.deletedCount", is(5)) - .body("status.moreData", nullValue()); + .body("status.moreData", is(nullValue())) + .body("data", is(nullValue())) + .body("errors", is(nullValue())); + + // ensure find does not find the documents + json = + """ + { + "find": { + "filter" : {"status": "active"} + } + } + """; + + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("data.docs", jsonEquals("[]")) + .body("status", is(nullValue())) + .body("errors", is(nullValue())); } @Test - @Order(4) public void deleteManyNoFilter() { insert(20); String json = """ - { - "deleteMany": { - "filter": { - } - } - } - """; + { + "deleteMany": { + } + } + """; + given() .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) .contentType(ContentType.JSON) @@ -118,22 +190,42 @@ public void deleteManyNoFilter() { .then() .statusCode(200) .body("status.deletedCount", is(20)) - .body("status.moreData", nullValue()); + .body("status.moreData", is(nullValue())) + .body("data", is(nullValue())) + .body("errors", is(nullValue())); + + // ensure find does not find the documents + json = """ + { + "find": { + } + } + """; + + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("data.docs", jsonEquals("[]")) + .body("status", is(nullValue())) + .body("errors", is(nullValue())); } @Test - @Order(5) public void deleteManyNoFilterMoreDataFlag() { insert(25); String json = """ - { - "deleteMany": { - "filter": { - } - } - } - """; + { + "deleteMany": { + } + } + """; + given() .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) .contentType(ContentType.JSON) @@ -143,18 +235,41 @@ public void deleteManyNoFilterMoreDataFlag() { .then() .statusCode(200) .body("status.deletedCount", is(20)) - .body("status.moreData", is(true)); + .body("status.moreData", is(true)) + .body("data", is(nullValue())) + .body("errors", is(nullValue())); + + // ensure only 20 are really deleted + json = """ + { + "find": { + } + } + """; + + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("data.docs", hasSize(5)) + .body("status", is(nullValue())) + .body("errors", is(nullValue())); } @AfterEach public void cleanUpData() { String json = """ - { - "deleteMany": { - } - } - """; + { + "deleteMany": { + } + } + """; + given() .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) .contentType(ContentType.JSON) @@ -162,7 +277,8 @@ public void cleanUpData() { .when() .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) .then() - .statusCode(200); + .statusCode(200) + .body("errors", is(nullValue())); } } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteOneIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteOneIntegrationTest.java index ac2a46db60..4baae2eaa4 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteOneIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/DeleteOneIntegrationTest.java @@ -2,14 +2,15 @@ import static io.restassured.RestAssured.given; import static io.stargate.sgv2.common.IntegrationTestUtils.getAuthToken; +import static net.javacrumbs.jsonunit.JsonMatchers.jsonEquals; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; import io.quarkus.test.junit.QuarkusIntegrationTest; import io.restassured.http.ContentType; import io.stargate.sgv2.api.common.config.constants.HttpConstants; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.TestMethodOrder; @@ -22,19 +23,18 @@ public class DeleteOneIntegrationTest extends CollectionResourceBaseIntegrationT @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class DeleteOne { @Test - @Order(2) public void deleteOneById() { String json = """ - { - "insertOne": { - "document": { - "_id": "doc3", - "username": "user3" - } - } - } - """; + { + "insertOne": { + "document": { + "_id": "doc3", + "username": "user3" + } + } + } + """; given() .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) @@ -43,15 +43,40 @@ public void deleteOneById() { .when() .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) .then() - .statusCode(200); + .statusCode(200) + .body("errors", is(nullValue())); + json = """ - { - "deleteOne": { - "filter" : {"_id" : "doc3"} - } - } - """; + { + "deleteOne": { + "filter" : {"_id" : "doc3"} + } + } + """; + + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("status.deletedCount", is(1)) + .body("data", is(nullValue())) + .body("errors", is(nullValue())); + + // ensure find does not find the document + json = + """ + { + "findOne": { + "filter" : {"_id" : "doc3"} + } + } + """; + given() .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) .contentType(ContentType.JSON) @@ -60,23 +85,24 @@ public void deleteOneById() { .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) .then() .statusCode(200) - .body("status.deletedCount", is(1)); + .body("data.docs", jsonEquals("[]")) + .body("status", is(nullValue())) + .body("errors", is(nullValue())); } @Test - @Order(3) public void deleteOneByColumn() { String json = """ - { - "insertOne": { - "document": { - "_id": "doc4", - "username": "user4" - } - } - } - """; + { + "insertOne": { + "document": { + "_id": "doc4", + "username": "user4" + } + } + } + """; given() .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) @@ -85,16 +111,18 @@ public void deleteOneByColumn() { .when() .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) .then() - .statusCode(200); + .statusCode(200) + .body("errors", is(nullValue())); json = """ - { - "deleteOne": { - "filter" : {"username" : "user4"} - } - } - """; + { + "deleteOne": { + "filter" : {"username" : "user4"} + } + } + """; + given() .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) .contentType(ContentType.JSON) @@ -103,23 +131,46 @@ public void deleteOneByColumn() { .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) .then() .statusCode(200) - .body("status.deletedCount", is(1)); + .body("status.deletedCount", is(1)) + .body("data", is(nullValue())) + .body("errors", is(nullValue())); + + // ensure find does not find the document + json = + """ + { + "findOne": { + "filter" : {"_id" : "doc4"} + } + } + """; + + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("data.docs", jsonEquals("[]")) + .body("status", is(nullValue())) + .body("errors", is(nullValue())); } @Test - @Order(4) public void deleteOneNoFilter() { String json = """ - { - "insertOne": { - "document": { - "_id": "doc3", - "username": "user3" - } - } - } - """; + { + "insertOne": { + "document": { + "_id": "doc3", + "username": "user3" + } + } + } + """; given() .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) @@ -128,16 +179,19 @@ public void deleteOneNoFilter() { .when() .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) .then() - .statusCode(200); + .statusCode(200) + .body("errors", is(nullValue())); + json = """ - { - "deleteOne": { - "filter": { - } + { + "deleteOne": { + "filter": { } - } - """; + } + } + """; + given() .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) .contentType(ContentType.JSON) @@ -146,23 +200,46 @@ public void deleteOneNoFilter() { .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) .then() .statusCode(200) - .body("status.deletedCount", is(1)); + .body("status.deletedCount", is(1)) + .body("data", is(nullValue())) + .body("errors", is(nullValue())); + + // ensure find does not find the document + json = + """ + { + "findOne": { + "filter" : {"_id" : "doc3"} + } + } + """; + + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("data.docs", jsonEquals("[]")) + .body("status", is(nullValue())) + .body("errors", is(nullValue())); } @Test - @Order(5) public void deleteOneNoMatch() { String json = """ - { - "insertOne": { - "document": { - "_id": "doc5", - "username": "user5" - } - } - } - """; + { + "insertOne": { + "document": { + "_id": "doc5", + "username": "user5" + } + } + } + """; given() .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) @@ -171,15 +248,40 @@ public void deleteOneNoMatch() { .when() .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) .then() - .statusCode(200); + .statusCode(200) + .body("errors", is(nullValue())); + + json = + """ + { + "deleteOne": { + "filter" : {"username" : "user12345"} + } + } + """; + + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("status.deletedCount", is(0)) + .body("data", is(nullValue())) + .body("errors", is(nullValue())); + + // ensure find does find the document json = """ - { - "deleteOne": { - "filter" : {"username" : "user12345"} - } - } - """; + { + "findOne": { + "filter" : {"_id" : "doc5"} + } + } + """; + given() .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) .contentType(ContentType.JSON) @@ -188,7 +290,9 @@ public void deleteOneNoMatch() { .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) .then() .statusCode(200) - .body("status.deletedCount", is(0)); + .body("data.docs[0]._id", is("doc5")) + .body("status", is(nullValue())) + .body("errors", is(nullValue())); } } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/service/operation/model/impl/DeleteOperationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/service/operation/model/impl/DeleteOperationTest.java index 89892139db..6db0ad7436 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/service/operation/model/impl/DeleteOperationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/service/operation/model/impl/DeleteOperationTest.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.junit.TestProfile; +import io.smallrye.mutiny.helpers.test.UniAssertSubscriber; import io.stargate.bridge.grpc.TypeSpecs; import io.stargate.bridge.grpc.Values; import io.stargate.bridge.proto.QueryOuterClass; @@ -32,20 +33,22 @@ public class DeleteOperationTest extends AbstractValidatingStargateBridgeTest { private static final String KEYSPACE_NAME = RandomStringUtils.randomAlphanumeric(16); private static final String COLLECTION_NAME = RandomStringUtils.randomAlphanumeric(16); - private CommandContext commandContext = new CommandContext(KEYSPACE_NAME, COLLECTION_NAME); + private static final CommandContext COMMAND_CONTEXT = + new CommandContext(KEYSPACE_NAME, COLLECTION_NAME); @Inject QueryExecutor queryExecutor; @Inject ObjectMapper objectMapper; @Nested - class DeleteOperationsTest { + class Execute { @Test - public void deleteWithId() throws Exception { + public void deleteWithId() { + UUID tx_id = UUID.randomUUID(); + String collectionReadCql = "SELECT key, tx_id FROM \"%s\".\"%s\" WHERE key = ? LIMIT 1" .formatted(KEYSPACE_NAME, COLLECTION_NAME); - UUID tx_id = UUID.randomUUID(); ValidatingStargateBridge.QueryAssert readAssert = withQuery( collectionReadCql, @@ -80,9 +83,10 @@ public void deleteWithId() throws Exception { CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1"))), Values.of(tx_id)) .returning(List.of(List.of(Values.of(true)))); + FindOperation findOperation = new FindOperation( - commandContext, + COMMAND_CONTEXT, List.of( new DBFilterBase.IDFilter( DBFilterBase.IDFilter.Operator.EQ, DocumentId.fromString("doc1"))), @@ -91,25 +95,31 @@ public void deleteWithId() throws Exception { 1, ReadType.KEY, objectMapper); + DeleteOperation operation = new DeleteOperation(COMMAND_CONTEXT, findOperation, 1); + Supplier execute = + operation + .execute(queryExecutor) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .awaitItem() + .getItem(); + + // assert query execution + // TODO due to the https://github.com/stargate/stargate/issues/2471 + // fetches 2 times, limit not respected + readAssert.assertExecuteCount().isEqualTo(2); + deleteAssert.assertExecuteCount().isOne(); - DeleteOperation operation = new DeleteOperation(commandContext, findOperation, 1); - final Supplier execute = - operation.execute(queryExecutor).subscribeAsCompletionStage().get(); + // then result CommandResult result = execute.get(); - assertThat(result) - .satisfies( - commandResult -> { - assertThat(result.status().get(CommandStatus.DELETED_COUNT)).isNotNull(); - assertThat(result.status().get(CommandStatus.DELETED_COUNT)).isEqualTo(1); - }); + assertThat(result.status()).hasSize(1).containsEntry(CommandStatus.DELETED_COUNT, 1); } @Test - public void deleteWithIdNoData() throws Exception { + public void deleteWithIdNoData() { String collectionReadCql = "SELECT key, tx_id FROM \"%s\".\"%s\" WHERE key = ? LIMIT 1" .formatted(KEYSPACE_NAME, COLLECTION_NAME); - UUID tx_id = UUID.randomUUID(); ValidatingStargateBridge.QueryAssert readAssert = withQuery( collectionReadCql, @@ -130,7 +140,7 @@ public void deleteWithIdNoData() throws Exception { FindOperation findOperation = new FindOperation( - commandContext, + COMMAND_CONTEXT, List.of( new DBFilterBase.IDFilter( DBFilterBase.IDFilter.Operator.EQ, DocumentId.fromString("doc1"))), @@ -140,20 +150,25 @@ public void deleteWithIdNoData() throws Exception { ReadType.KEY, objectMapper); - DeleteOperation operation = new DeleteOperation(commandContext, findOperation, 1); - final Supplier execute = - operation.execute(queryExecutor).subscribeAsCompletionStage().get(); + DeleteOperation operation = new DeleteOperation(COMMAND_CONTEXT, findOperation, 1); + Supplier execute = + operation + .execute(queryExecutor) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .awaitItem() + .getItem(); + + // assert query execution + readAssert.assertExecuteCount().isEqualTo(1); + + // then result CommandResult result = execute.get(); - assertThat(result) - .satisfies( - commandResult -> { - assertThat(result.status().get(CommandStatus.DELETED_COUNT)).isNotNull(); - assertThat(result.status().get(CommandStatus.DELETED_COUNT)).isEqualTo(0); - }); + assertThat(result.status()).hasSize(1).containsEntry(CommandStatus.DELETED_COUNT, 0); } @Test - public void deleteWithDynamic() throws Exception { + public void deleteWithDynamic() { UUID tx_id = UUID.randomUUID(); String collectionReadCql = "SELECT key, tx_id FROM \"%s\".\"%s\" WHERE array_contains CONTAINS ? LIMIT 1" @@ -180,6 +195,7 @@ public void deleteWithDynamic() throws Exception { CustomValueSerializers.getDocumentIdValue( DocumentId.fromString("doc1"))), Values.of(tx_id)))); + String collectionDeleteCql = "DELETE FROM \"%s\".\"%s\" WHERE key = ? IF tx_id = ?" .formatted(KEYSPACE_NAME, COLLECTION_NAME); @@ -193,7 +209,7 @@ public void deleteWithDynamic() throws Exception { FindOperation findOperation = new FindOperation( - commandContext, + COMMAND_CONTEXT, List.of( new DBFilterBase.TextFilter( "username", DBFilterBase.MapFilterBase.Operator.EQ, "user1")), @@ -202,21 +218,29 @@ public void deleteWithDynamic() throws Exception { 1, ReadType.KEY, objectMapper); + DeleteOperation operation = new DeleteOperation(COMMAND_CONTEXT, findOperation, 1); + + Supplier execute = + operation + .execute(queryExecutor) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .awaitItem() + .getItem(); + + // assert query execution + // TODO due to the https://github.com/stargate/stargate/issues/2471 + // fetches 2 times, limit not respected + candidatesAssert.assertExecuteCount().isEqualTo(2); + deleteAssert.assertExecuteCount().isOne(); - DeleteOperation operation = new DeleteOperation(commandContext, findOperation, 1); - final Supplier execute = - operation.execute(queryExecutor).subscribeAsCompletionStage().get(); + // then result CommandResult result = execute.get(); - assertThat(result) - .satisfies( - commandResult -> { - assertThat(result.status().get(CommandStatus.DELETED_COUNT)).isNotNull(); - assertThat(result.status().get(CommandStatus.DELETED_COUNT)).isEqualTo(1); - }); + assertThat(result.status()).hasSize(1).containsEntry(CommandStatus.DELETED_COUNT, 1); } @Test - public void deleteManyWithDynamic() throws Exception { + public void deleteManyWithDynamic() { UUID tx_id1 = UUID.randomUUID(); UUID tx_id2 = UUID.randomUUID(); String collectionReadCql = @@ -249,24 +273,28 @@ public void deleteManyWithDynamic() throws Exception { CustomValueSerializers.getDocumentIdValue( DocumentId.fromString("doc2"))), Values.of(tx_id2)))); + String collectionDeleteCql = "DELETE FROM \"%s\".\"%s\" WHERE key = ? IF tx_id = ?" .formatted(KEYSPACE_NAME, COLLECTION_NAME); - withQuery( - collectionDeleteCql, - Values.of(CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1"))), - Values.of(tx_id1)) - .returning(List.of(List.of(Values.of(true)))); - - withQuery( - collectionDeleteCql, - Values.of(CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc2"))), - Values.of(tx_id2)) - .returning(List.of(List.of(Values.of(true)))); + ValidatingStargateBridge.QueryAssert deleteFirstAsser = + withQuery( + collectionDeleteCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1"))), + Values.of(tx_id1)) + .returning(List.of(List.of(Values.of(true)))); + ValidatingStargateBridge.QueryAssert deleteSecondAssert = + withQuery( + collectionDeleteCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc2"))), + Values.of(tx_id2)) + .returning(List.of(List.of(Values.of(true)))); FindOperation findOperation = new FindOperation( - commandContext, + COMMAND_CONTEXT, List.of( new DBFilterBase.TextFilter( "username", DBFilterBase.MapFilterBase.Operator.EQ, "user1")), @@ -275,31 +303,208 @@ public void deleteManyWithDynamic() throws Exception { 2, ReadType.KEY, objectMapper); + DeleteOperation operation = new DeleteOperation(COMMAND_CONTEXT, findOperation, 2); - DeleteOperation operation = new DeleteOperation(commandContext, findOperation, 2); - final Supplier execute = - operation.execute(queryExecutor).subscribeAsCompletionStage().get(); + Supplier execute = + operation + .execute(queryExecutor) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .awaitItem() + .getItem(); + + candidatesAssert.assertExecuteCount().isEqualTo(2); + deleteFirstAsser.assertExecuteCount().isOne(); + deleteSecondAssert.assertExecuteCount().isOne(); + + // then result CommandResult result = execute.get(); - assertThat(result) - .satisfies( - commandResult -> { - assertThat(result.status().get(CommandStatus.DELETED_COUNT)).isNotNull(); - assertThat(result.status().get(CommandStatus.DELETED_COUNT)).isEqualTo(2); - }); + assertThat(result.status()).hasSize(1).containsEntry(CommandStatus.DELETED_COUNT, 2); } @Test - public void deleteWithNoResult() throws Exception { + public void deleteManyWithDynamicPaging() { + UUID tx_id1 = UUID.randomUUID(); + UUID tx_id2 = UUID.randomUUID(); + String collectionReadCql = + "SELECT key, tx_id FROM \"%s\".\"%s\" WHERE array_contains CONTAINS ? LIMIT 3" + .formatted(KEYSPACE_NAME, COLLECTION_NAME); + ValidatingStargateBridge.QueryAssert candidatesAssert = + withQuery( + collectionReadCql, + Values.of("username " + new DocValueHasher().getHash("user1").hash())) + .withPageSize(1) + .withColumnSpec( + List.of( + QueryOuterClass.ColumnSpec.newBuilder() + .setName("key") + .setType(TypeSpecs.VARCHAR) + .build(), + QueryOuterClass.ColumnSpec.newBuilder() + .setName("tx_id") + .setType(TypeSpecs.UUID) + .build())) + .returning( + List.of( + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc1"))), + Values.of(tx_id1)), + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc2"))), + Values.of(tx_id2)))); + + String collectionDeleteCql = + "DELETE FROM \"%s\".\"%s\" WHERE key = ? IF tx_id = ?" + .formatted(KEYSPACE_NAME, COLLECTION_NAME); + ValidatingStargateBridge.QueryAssert deleteFirstAsser = + withQuery( + collectionDeleteCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1"))), + Values.of(tx_id1)) + .returning(List.of(List.of(Values.of(true)))); + ValidatingStargateBridge.QueryAssert deleteSecondAssert = + withQuery( + collectionDeleteCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc2"))), + Values.of(tx_id2)) + .returning(List.of(List.of(Values.of(true)))); + + FindOperation findOperation = + new FindOperation( + COMMAND_CONTEXT, + List.of( + new DBFilterBase.TextFilter( + "username", DBFilterBase.MapFilterBase.Operator.EQ, "user1")), + null, + 3, + 1, + ReadType.KEY, + objectMapper); + DeleteOperation operation = new DeleteOperation(COMMAND_CONTEXT, findOperation, 2); + + Supplier execute = + operation + .execute(queryExecutor) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .awaitItem() + .getItem(); + + candidatesAssert.assertExecuteCount().isEqualTo(3); + deleteFirstAsser.assertExecuteCount().isOne(); + deleteSecondAssert.assertExecuteCount().isOne(); + + // then result + CommandResult result = execute.get(); + assertThat(result.status()).hasSize(1).containsEntry(CommandStatus.DELETED_COUNT, 2); + } + + @Test + public void deleteManyWithDynamicPagingAndMoreData() { + UUID tx_id1 = UUID.randomUUID(); + UUID tx_id2 = UUID.randomUUID(); + UUID tx_id3 = UUID.randomUUID(); + String collectionReadCql = + "SELECT key, tx_id FROM \"%s\".\"%s\" WHERE array_contains CONTAINS ? LIMIT 3" + .formatted(KEYSPACE_NAME, COLLECTION_NAME); + ValidatingStargateBridge.QueryAssert candidatesAssert = + withQuery( + collectionReadCql, + Values.of("username " + new DocValueHasher().getHash("user1").hash())) + .withPageSize(1) + .withColumnSpec( + List.of( + QueryOuterClass.ColumnSpec.newBuilder() + .setName("key") + .setType(TypeSpecs.VARCHAR) + .build(), + QueryOuterClass.ColumnSpec.newBuilder() + .setName("tx_id") + .setType(TypeSpecs.UUID) + .build())) + .returning( + List.of( + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc1"))), + Values.of(tx_id1)), + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc2"))), + Values.of(tx_id2)), + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc3"))), + Values.of(tx_id3)))); + + String collectionDeleteCql = + "DELETE FROM \"%s\".\"%s\" WHERE key = ? IF tx_id = ?" + .formatted(KEYSPACE_NAME, COLLECTION_NAME); + ValidatingStargateBridge.QueryAssert deleteFirstAsser = + withQuery( + collectionDeleteCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1"))), + Values.of(tx_id1)) + .returning(List.of(List.of(Values.of(true)))); + ValidatingStargateBridge.QueryAssert deleteSecondAssert = + withQuery( + collectionDeleteCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc2"))), + Values.of(tx_id2)) + .returning(List.of(List.of(Values.of(true)))); + + FindOperation findOperation = + new FindOperation( + COMMAND_CONTEXT, + List.of( + new DBFilterBase.TextFilter( + "username", DBFilterBase.MapFilterBase.Operator.EQ, "user1")), + null, + 3, + 1, + ReadType.KEY, + objectMapper); + DeleteOperation operation = new DeleteOperation(COMMAND_CONTEXT, findOperation, 2); + + Supplier execute = + operation + .execute(queryExecutor) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .awaitItem() + .getItem(); + + // assert query execution + // TODO due to the https://github.com/stargate/stargate/issues/2471 + // fetches 4 times, limit not respected + candidatesAssert.assertExecuteCount().isEqualTo(4); + deleteFirstAsser.assertExecuteCount().isOne(); + deleteSecondAssert.assertExecuteCount().isOne(); + + // then result + CommandResult result = execute.get(); + assertThat(result.status()) + .hasSize(2) + .containsEntry(CommandStatus.DELETED_COUNT, 2) + .containsEntry(CommandStatus.MORE_DATA, true); + } + + @Test + public void deleteWithNoResult() { String collectionReadCql = "SELECT key, tx_id FROM \"%s\".\"%s\" WHERE array_contains CONTAINS ? LIMIT 1" .formatted(KEYSPACE_NAME, COLLECTION_NAME); - String doc1 = - """ - { - "_id": "doc1", - "username": "user1" - } - """; ValidatingStargateBridge.QueryAssert candidatesAssert = withQuery( collectionReadCql, @@ -320,9 +525,10 @@ public void deleteWithNoResult() throws Exception { .setType(TypeSpecs.VARCHAR) .build())) .returning(List.of()); + FindOperation findOperation = new FindOperation( - commandContext, + COMMAND_CONTEXT, List.of( new DBFilterBase.TextFilter( "username", DBFilterBase.MapFilterBase.Operator.EQ, "user1")), @@ -331,16 +537,32 @@ public void deleteWithNoResult() throws Exception { 1, ReadType.KEY, objectMapper); - DeleteOperation operation = new DeleteOperation(commandContext, findOperation, 1); - final Supplier execute = - operation.execute(queryExecutor).subscribeAsCompletionStage().get(); + DeleteOperation operation = new DeleteOperation(COMMAND_CONTEXT, findOperation, 1); + + Supplier execute = + operation + .execute(queryExecutor) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .awaitItem() + .getItem(); + + // assert query execution + candidatesAssert.assertExecuteCount().isEqualTo(1); + + // then result CommandResult result = execute.get(); - assertThat(result) - .satisfies( - commandResult -> { - assertThat(result.status().get(CommandStatus.DELETED_COUNT)).isNotNull(); - assertThat(result.status().get(CommandStatus.DELETED_COUNT)).isEqualTo(0); - }); + assertThat(result.status()).hasSize(1).containsEntry(CommandStatus.DELETED_COUNT, 0); + } + + @Test + public void errorPartial() { + // TODO with stargate v2.0.9 + } + + @Test + public void errorAll() { + // TODO with stargate v2.0.9 } } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/service/resolver/model/impl/DeleteManyCommandResolverTest.java b/src/test/java/io/stargate/sgv2/jsonapi/service/resolver/model/impl/DeleteManyCommandResolverTest.java index cb16249f0a..569c6eca9a 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/service/resolver/model/impl/DeleteManyCommandResolverTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/service/resolver/model/impl/DeleteManyCommandResolverTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import com.fasterxml.jackson.databind.ObjectMapper; +import io.quarkus.test.Mock; import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.junit.TestProfile; import io.stargate.sgv2.common.testprofiles.NoGlobalResourcesTestProfile; @@ -15,7 +16,6 @@ import io.stargate.sgv2.jsonapi.service.operation.model.impl.DeleteOperation; import io.stargate.sgv2.jsonapi.service.operation.model.impl.FindOperation; import io.stargate.sgv2.jsonapi.service.shredding.model.DocumentId; -import java.util.List; import javax.inject.Inject; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -25,45 +25,50 @@ public class DeleteManyCommandResolverTest { @Inject ObjectMapper objectMapper; @Inject DocumentConfig documentConfig; - @Inject DeleteManyCommandResolver deleteManyCommandResolver; + @Inject DeleteManyCommandResolver resolver; @Nested - class DeleteManyCommandResolveCommand { + class ResolveCommand { + + @Mock CommandContext commandContext; @Test public void idFilterCondition() throws Exception { String json = """ - { - "deleteMany": { - "filter" : {"_id" : "id"} - } - } - """; + { + "deleteMany": { + "filter" : {"_id" : "id"} + } + } + """; DeleteManyCommand deleteManyCommand = objectMapper.readValue(json, DeleteManyCommand.class); - final CommandContext commandContext = new CommandContext("namespace", "collection"); - final Operation operation = - deleteManyCommandResolver.resolveCommand(commandContext, deleteManyCommand); - FindOperation findOperation = - new FindOperation( - commandContext, - List.of( - new DBFilterBase.IDFilter( - DBFilterBase.IDFilter.Operator.EQ, DocumentId.fromString("id"))), - null, - documentConfig.maxDocumentDeleteCount() + 1, - documentConfig.defaultPageSize(), - ReadType.KEY, - objectMapper); - DeleteOperation expected = - new DeleteOperation( - commandContext, findOperation, documentConfig.maxDocumentDeleteCount()); + Operation operation = resolver.resolveCommand(commandContext, deleteManyCommand); + assertThat(operation) - .isInstanceOf(DeleteOperation.class) - .satisfies( + .isInstanceOfSatisfying( + DeleteOperation.class, op -> { - assertThat(op).isEqualTo(expected); + assertThat(op.commandContext()).isEqualTo(commandContext); + assertThat(op.deleteLimit()).isEqualTo(documentConfig.maxDocumentDeleteCount()); + assertThat(op.readOperation()) + .isInstanceOfSatisfying( + FindOperation.class, + find -> { + DBFilterBase.IDFilter filter = + new DBFilterBase.IDFilter( + DBFilterBase.IDFilter.Operator.EQ, DocumentId.fromString("id")); + + assertThat(find.objectMapper()).isEqualTo(objectMapper); + assertThat(find.commandContext()).isEqualTo(commandContext); + assertThat(find.pageSize()).isEqualTo(documentConfig.defaultPageSize()); + assertThat(find.limit()) + .isEqualTo(documentConfig.maxDocumentDeleteCount() + 1); + assertThat(find.pagingState()).isNull(); + assertThat(find.readType()).isEqualTo(ReadType.KEY); + assertThat(find.filters()).singleElement().isEqualTo(filter); + }); }); } @@ -71,33 +76,34 @@ public void idFilterCondition() throws Exception { public void noFilterCondition() throws Exception { String json = """ - { - "deleteMany": { - } - } - """; + { + "deleteMany": { + } + } + """; DeleteManyCommand deleteManyCommand = objectMapper.readValue(json, DeleteManyCommand.class); - final CommandContext commandContext = new CommandContext("namespace", "collection"); - final Operation operation = - deleteManyCommandResolver.resolveCommand(commandContext, deleteManyCommand); - FindOperation findOperation = - new FindOperation( - commandContext, - List.of(), - null, - documentConfig.maxDocumentDeleteCount() + 1, - documentConfig.defaultPageSize(), - ReadType.KEY, - objectMapper); - DeleteOperation expected = - new DeleteOperation( - commandContext, findOperation, documentConfig.maxDocumentDeleteCount()); + Operation operation = resolver.resolveCommand(commandContext, deleteManyCommand); + assertThat(operation) - .isInstanceOf(DeleteOperation.class) - .satisfies( + .isInstanceOfSatisfying( + DeleteOperation.class, op -> { - assertThat(op).isEqualTo(expected); + assertThat(op.commandContext()).isEqualTo(commandContext); + assertThat(op.deleteLimit()).isEqualTo(documentConfig.maxDocumentDeleteCount()); + assertThat(op.readOperation()) + .isInstanceOfSatisfying( + FindOperation.class, + find -> { + assertThat(find.objectMapper()).isEqualTo(objectMapper); + assertThat(find.commandContext()).isEqualTo(commandContext); + assertThat(find.pageSize()).isEqualTo(documentConfig.defaultPageSize()); + assertThat(find.limit()) + .isEqualTo(documentConfig.maxDocumentDeleteCount() + 1); + assertThat(find.pagingState()).isNull(); + assertThat(find.readType()).isEqualTo(ReadType.KEY); + assertThat(find.filters()).isEmpty(); + }); }); } @@ -105,36 +111,39 @@ public void noFilterCondition() throws Exception { public void dynamicFilterCondition() throws Exception { String json = """ - { - "deleteMany": { - "filter" : {"col" : "val"} - } - } - """; + { + "deleteMany": { + "filter" : {"col" : "val"} + } + } + """; DeleteManyCommand deleteManyCommand = objectMapper.readValue(json, DeleteManyCommand.class); - final CommandContext commandContext = new CommandContext("namespace", "collection"); - final Operation operation = - deleteManyCommandResolver.resolveCommand(commandContext, deleteManyCommand); - FindOperation findOperation = - new FindOperation( - commandContext, - List.of( - new DBFilterBase.TextFilter( - "col", DBFilterBase.MapFilterBase.Operator.EQ, "val")), - null, - documentConfig.maxDocumentDeleteCount() + 1, - documentConfig.defaultPageSize(), - ReadType.KEY, - objectMapper); - DeleteOperation expected = - new DeleteOperation( - commandContext, findOperation, documentConfig.maxDocumentDeleteCount()); + Operation operation = resolver.resolveCommand(commandContext, deleteManyCommand); + assertThat(operation) - .isInstanceOf(DeleteOperation.class) - .satisfies( + .isInstanceOfSatisfying( + DeleteOperation.class, op -> { - assertThat(op).isEqualTo(expected); + assertThat(op.commandContext()).isEqualTo(commandContext); + assertThat(op.deleteLimit()).isEqualTo(documentConfig.maxDocumentDeleteCount()); + assertThat(op.readOperation()) + .isInstanceOfSatisfying( + FindOperation.class, + find -> { + DBFilterBase.TextFilter filter = + new DBFilterBase.TextFilter( + "col", DBFilterBase.MapFilterBase.Operator.EQ, "val"); + + assertThat(find.objectMapper()).isEqualTo(objectMapper); + assertThat(find.commandContext()).isEqualTo(commandContext); + assertThat(find.pageSize()).isEqualTo(documentConfig.defaultPageSize()); + assertThat(find.limit()) + .isEqualTo(documentConfig.maxDocumentDeleteCount() + 1); + assertThat(find.pagingState()).isNull(); + assertThat(find.readType()).isEqualTo(ReadType.KEY); + assertThat(find.filters()).singleElement().isEqualTo(filter); + }); }); } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/service/resolver/model/impl/DeleteOneCommandResolverTest.java b/src/test/java/io/stargate/sgv2/jsonapi/service/resolver/model/impl/DeleteOneCommandResolverTest.java index df320c78de..c0c989622c 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/service/resolver/model/impl/DeleteOneCommandResolverTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/service/resolver/model/impl/DeleteOneCommandResolverTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import com.fasterxml.jackson.databind.ObjectMapper; +import io.quarkus.test.Mock; import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.junit.TestProfile; import io.stargate.sgv2.common.testprofiles.NoGlobalResourcesTestProfile; @@ -14,7 +15,6 @@ import io.stargate.sgv2.jsonapi.service.operation.model.impl.DeleteOperation; import io.stargate.sgv2.jsonapi.service.operation.model.impl.FindOperation; import io.stargate.sgv2.jsonapi.service.shredding.model.DocumentId; -import java.util.List; import javax.inject.Inject; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -23,43 +23,49 @@ @TestProfile(NoGlobalResourcesTestProfile.Impl.class) public class DeleteOneCommandResolverTest { @Inject ObjectMapper objectMapper; - @Inject DeleteOneCommandResolver deleteOneCommandResolver; + @Inject DeleteOneCommandResolver resolver; @Nested class DeleteOneCommandResolveCommand { + @Mock CommandContext commandContext; + @Test public void idFilterCondition() throws Exception { String json = """ - { - "deleteOne": { - "filter" : {"_id" : "id"} - } - } - """; + { + "deleteOne": { + "filter" : {"_id" : "id"} + } + } + """; DeleteOneCommand deleteOneCommand = objectMapper.readValue(json, DeleteOneCommand.class); - final CommandContext commandContext = new CommandContext("namespace", "collection"); - final Operation operation = - deleteOneCommandResolver.resolveCommand(commandContext, deleteOneCommand); - FindOperation findOperation = - new FindOperation( - commandContext, - List.of( - new DBFilterBase.IDFilter( - DBFilterBase.IDFilter.Operator.EQ, DocumentId.fromString("id"))), - null, - 1, - 1, - ReadType.KEY, - objectMapper); - DeleteOperation expected = new DeleteOperation(commandContext, findOperation, 1); + Operation operation = resolver.resolveCommand(commandContext, deleteOneCommand); + assertThat(operation) - .isInstanceOf(DeleteOperation.class) - .satisfies( + .isInstanceOfSatisfying( + DeleteOperation.class, op -> { - assertThat(op).isEqualTo(expected); + assertThat(op.commandContext()).isEqualTo(commandContext); + assertThat(op.deleteLimit()).isEqualTo(1); + assertThat(op.readOperation()) + .isInstanceOfSatisfying( + FindOperation.class, + find -> { + DBFilterBase.IDFilter filter = + new DBFilterBase.IDFilter( + DBFilterBase.IDFilter.Operator.EQ, DocumentId.fromString("id")); + + assertThat(find.objectMapper()).isEqualTo(objectMapper); + assertThat(find.commandContext()).isEqualTo(commandContext); + assertThat(find.pageSize()).isEqualTo(1); + assertThat(find.limit()).isEqualTo(1); + assertThat(find.pagingState()).isNull(); + assertThat(find.readType()).isEqualTo(ReadType.KEY); + assertThat(find.filters()).singleElement().isEqualTo(filter); + }); }); } @@ -67,24 +73,33 @@ public void idFilterCondition() throws Exception { public void noFilterCondition() throws Exception { String json = """ - { - "deleteOne": { - } - } - """; + { + "deleteOne": { + } + } + """; DeleteOneCommand deleteOneCommand = objectMapper.readValue(json, DeleteOneCommand.class); - final CommandContext commandContext = new CommandContext("namespace", "collection"); - final Operation operation = - deleteOneCommandResolver.resolveCommand(commandContext, deleteOneCommand); - FindOperation findOperation = - new FindOperation(commandContext, List.of(), null, 1, 1, ReadType.KEY, objectMapper); - DeleteOperation expected = new DeleteOperation(commandContext, findOperation, 1); + Operation operation = resolver.resolveCommand(commandContext, deleteOneCommand); + assertThat(operation) - .isInstanceOf(DeleteOperation.class) - .satisfies( + .isInstanceOfSatisfying( + DeleteOperation.class, op -> { - assertThat(op).isEqualTo(expected); + assertThat(op.commandContext()).isEqualTo(commandContext); + assertThat(op.deleteLimit()).isEqualTo(1); + assertThat(op.readOperation()) + .isInstanceOfSatisfying( + FindOperation.class, + find -> { + assertThat(find.objectMapper()).isEqualTo(objectMapper); + assertThat(find.commandContext()).isEqualTo(commandContext); + assertThat(find.pageSize()).isEqualTo(1); + assertThat(find.limit()).isEqualTo(1); + assertThat(find.pagingState()).isNull(); + assertThat(find.readType()).isEqualTo(ReadType.KEY); + assertThat(find.filters()).isEmpty(); + }); }); } @@ -92,34 +107,38 @@ public void noFilterCondition() throws Exception { public void dynamicFilterCondition() throws Exception { String json = """ - { - "deleteOne": { - "filter" : {"col" : "val"} - } - } - """; + { + "deleteOne": { + "filter" : {"col" : "val"} + } + } + """; DeleteOneCommand deleteOneCommand = objectMapper.readValue(json, DeleteOneCommand.class); - final CommandContext commandContext = new CommandContext("namespace", "collection"); - final Operation operation = - deleteOneCommandResolver.resolveCommand(commandContext, deleteOneCommand); - FindOperation findOperation = - new FindOperation( - commandContext, - List.of( - new DBFilterBase.TextFilter( - "col", DBFilterBase.MapFilterBase.Operator.EQ, "val")), - null, - 1, - 1, - ReadType.KEY, - objectMapper); - DeleteOperation expected = new DeleteOperation(commandContext, findOperation, 1); + Operation operation = resolver.resolveCommand(commandContext, deleteOneCommand); + assertThat(operation) - .isInstanceOf(DeleteOperation.class) - .satisfies( + .isInstanceOfSatisfying( + DeleteOperation.class, op -> { - assertThat(op).isEqualTo(expected); + assertThat(op.commandContext()).isEqualTo(commandContext); + assertThat(op.deleteLimit()).isEqualTo(1); + assertThat(op.readOperation()) + .isInstanceOfSatisfying( + FindOperation.class, + find -> { + DBFilterBase.TextFilter filter = + new DBFilterBase.TextFilter( + "col", DBFilterBase.MapFilterBase.Operator.EQ, "val"); + + assertThat(find.objectMapper()).isEqualTo(objectMapper); + assertThat(find.commandContext()).isEqualTo(commandContext); + assertThat(find.pageSize()).isEqualTo(1); + assertThat(find.limit()).isEqualTo(1); + assertThat(find.pagingState()).isNull(); + assertThat(find.readType()).isEqualTo(ReadType.KEY); + assertThat(find.filters()).singleElement().isEqualTo(filter); + }); }); } }