diff --git a/src/main/java/io/flamingock/template/mongodb/model/MongoOperation.java b/src/main/java/io/flamingock/template/mongodb/model/MongoOperation.java index b504141..de72727 100644 --- a/src/main/java/io/flamingock/template/mongodb/model/MongoOperation.java +++ b/src/main/java/io/flamingock/template/mongodb/model/MongoOperation.java @@ -19,6 +19,7 @@ import io.flamingock.api.annotations.NonLockGuarded; import io.flamingock.api.NonLockGuardedType; import io.flamingock.api.template.TemplatePayload; +import io.flamingock.api.template.TemplatePayloadInfo; import io.flamingock.api.template.TemplatePayloadValidationError; import io.flamingock.api.template.TemplateValidationContext; import io.flamingock.template.mongodb.model.operator.MongoOperator; @@ -85,6 +86,14 @@ public String toString() { return sb.toString(); } + @Override + public TemplatePayloadInfo getInfo() { + TemplatePayloadInfo info = new TemplatePayloadInfo(); + MongoOperationType.findByType(type) + .ifPresent(opType -> info.setSupportsTransactions(opType.isTransactional())); + return info; + } + private static final TypeValidator TYPE_VALIDATOR = new TypeValidator(); private static final CollectionValidator COLLECTION_VALIDATOR = new CollectionValidator(); @@ -103,13 +112,6 @@ public List validate(TemplateValidationContext c errors.addAll(COLLECTION_VALIDATOR.validate(this)); errors.addAll(operationType.getOperationValidator().validate(this)); - if (!operationType.isTransactional() && context.isTransactional()) { - errors.add(new TemplatePayloadValidationError("type", - "Operation type '" + type + "' does not support transactions. " - + "Transactional changes require all operations to be transactional " - + "(insert, update, delete).")); - } - return errors; } } \ No newline at end of file diff --git a/src/test/java/io/flamingock/template/mongodb/model/MongoOperationGetInfoTest.java b/src/test/java/io/flamingock/template/mongodb/model/MongoOperationGetInfoTest.java new file mode 100644 index 0000000..8d9263e --- /dev/null +++ b/src/test/java/io/flamingock/template/mongodb/model/MongoOperationGetInfoTest.java @@ -0,0 +1,77 @@ +/* + * Copyright 2025 Flamingock (https://www.flamingock.io) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.flamingock.template.mongodb.model; + +import io.flamingock.api.template.TemplatePayloadInfo; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class MongoOperationGetInfoTest { + + private static MongoOperation operationWithType(String type) { + MongoOperation op = new MongoOperation(); + op.setType(type); + return op; + } + + @ParameterizedTest + @ValueSource(strings = {"insert", "update", "delete"}) + @DisplayName("Transactional types should report supportsTransactions=true") + void transactionalTypes(String type) { + TemplatePayloadInfo info = operationWithType(type).getInfo(); + assertEquals(Optional.of(true), info.getSupportsTransactions()); + } + + @ParameterizedTest + @ValueSource(strings = { + "createCollection", "dropCollection", "renameCollection", "modifyCollection", + "createIndex", "dropIndex", "createView", "dropView" + }) + @DisplayName("Non-transactional types should report supportsTransactions=false") + void nonTransactionalTypes(String type) { + TemplatePayloadInfo info = operationWithType(type).getInfo(); + assertEquals(Optional.of(false), info.getSupportsTransactions()); + } + + @Test + @DisplayName("Null type should leave supportsTransactions empty") + void nullType() { + TemplatePayloadInfo info = operationWithType(null).getInfo(); + assertEquals(Optional.empty(), info.getSupportsTransactions()); + } + + @Test + @DisplayName("Invalid type should leave supportsTransactions empty") + void invalidType() { + TemplatePayloadInfo info = operationWithType("banana").getInfo(); + assertEquals(Optional.empty(), info.getSupportsTransactions()); + } + + @Test + @DisplayName("getInfo() never returns null") + void infoNeverNull() { + assertNotNull(operationWithType(null).getInfo()); + assertNotNull(operationWithType("insert").getInfo()); + assertNotNull(operationWithType("banana").getInfo()); + } +} diff --git a/src/test/java/io/flamingock/template/mongodb/model/MongoOperationValidateTest.java b/src/test/java/io/flamingock/template/mongodb/model/MongoOperationValidateTest.java index 956f50b..c8c064a 100644 --- a/src/test/java/io/flamingock/template/mongodb/model/MongoOperationValidateTest.java +++ b/src/test/java/io/flamingock/template/mongodb/model/MongoOperationValidateTest.java @@ -32,12 +32,6 @@ class MongoOperationValidateTest { private static final TemplateValidationContext NON_TRANSACTIONAL_CONTEXT = new TemplateValidationContext(); - private static TemplateValidationContext transactionalContext() { - TemplateValidationContext ctx = new TemplateValidationContext(); - ctx.setTransactional(true); - return ctx; - } - @Nested @DisplayName("Common Validation Tests") class CommonValidationTests { @@ -1698,117 +1692,4 @@ void insertMultipleUnrecognizedOptionKeysTest() { assertEquals(2, errors.size()); } } - - @Nested - @DisplayName("Transactional Validation Tests") - class TransactionalValidationTests { - - private final TemplateValidationContext TRANSACTIONAL_CONTEXT = transactionalContext(); - - @ParameterizedTest(name = "WHEN transactional context + non-transactional op ''{0}'' THEN error") - @ValueSource(strings = { - "createCollection", "dropCollection", "createIndex", "dropIndex", - "renameCollection", "modifyCollection", "createView", "dropView" - }) - void transactionalContextWithNonTransactionalOpReturnsError(String opType) { - MongoOperation op = buildValidOperation(opType); - - List errors = op.validate(TRANSACTIONAL_CONTEXT); - - assertTrue(errors.stream().anyMatch(e -> - "type".equals(e.getField()) && e.getMessage().contains("does not support transactions")), - "Expected transactional incompatibility error for type '" + opType + "'"); - } - - @ParameterizedTest(name = "WHEN transactional context + transactional op ''{0}'' THEN no transactional error") - @ValueSource(strings = {"insert", "update", "delete"}) - void transactionalContextWithTransactionalOpReturnsNoError(String opType) { - MongoOperation op = buildValidOperation(opType); - - List errors = op.validate(TRANSACTIONAL_CONTEXT); - - assertTrue(errors.stream().noneMatch(e -> - "type".equals(e.getField()) && e.getMessage().contains("does not support transactions")), - "Expected no transactional incompatibility error for type '" + opType + "'"); - } - - @ParameterizedTest(name = "WHEN non-transactional context + transactional op ''{0}'' THEN no error") - @ValueSource(strings = {"insert", "update", "delete"}) - void nonTransactionalContextWithTransactionalOpReturnsNoError(String opType) { - MongoOperation op = buildValidOperation(opType); - - List errors = op.validate(NON_TRANSACTIONAL_CONTEXT); - - assertTrue(errors.isEmpty(), - "Expected no errors for transactional op '" + opType + "' in non-transactional context"); - } - - @ParameterizedTest(name = "WHEN non-transactional context + non-transactional op ''{0}'' THEN no error") - @ValueSource(strings = { - "createCollection", "dropCollection", "createIndex", "dropIndex", - "renameCollection", "modifyCollection", "createView", "dropView" - }) - void nonTransactionalContextWithNonTransactionalOpReturnsNoError(String opType) { - MongoOperation op = buildValidOperation(opType); - - List errors = op.validate(NON_TRANSACTIONAL_CONTEXT); - - assertTrue(errors.isEmpty(), - "Expected no errors for non-transactional op '" + opType + "' in non-transactional context"); - } - - private MongoOperation buildValidOperation(String opType) { - MongoOperation op = new MongoOperation(); - op.setType(opType); - op.setCollection("testCollection"); - op.setParameters(buildRequiredParams(opType)); - return op; - } - - private Map buildRequiredParams(String opType) { - Map params = new HashMap<>(); - switch (opType) { - case "insert": - List> docs = new ArrayList<>(); - Map doc = new HashMap<>(); - doc.put("name", "test"); - docs.add(doc); - params.put("documents", docs); - break; - case "update": - params.put("filter", new HashMap<>()); - Map update = new HashMap<>(); - update.put("$set", Collections.singletonMap("field", "value")); - params.put("update", update); - break; - case "delete": - params.put("filter", new HashMap<>()); - break; - case "createIndex": - params.put("keys", Collections.singletonMap("field", 1)); - break; - case "dropIndex": - params.put("indexName", "test_index"); - break; - case "renameCollection": - params.put("target", "newName"); - break; - case "modifyCollection": - params.put("validationLevel", "strict"); - break; - case "createView": - params.put("viewOn", "sourceCollection"); - params.put("pipeline", Collections.singletonList(new HashMap<>())); - break; - case "createCollection": - case "dropCollection": - case "dropView": - // No parameters needed - return null; - default: - break; - } - return params; - } - } }