diff --git a/src/main/java/io/flamingock/template/mongodb/MongoChangeTemplate.java b/src/main/java/io/flamingock/template/mongodb/MongoChangeTemplate.java
index cda3f84..138c052 100644
--- a/src/main/java/io/flamingock/template/mongodb/MongoChangeTemplate.java
+++ b/src/main/java/io/flamingock/template/mongodb/MongoChangeTemplate.java
@@ -23,6 +23,7 @@
import io.flamingock.api.annotations.Rollback;
import io.flamingock.api.template.AbstractChangeTemplate;
import io.flamingock.api.template.wrappers.TemplateVoid;
+import io.flamingock.api.template.TemplateValidationContext;
import io.flamingock.template.mongodb.model.MongoOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -86,7 +87,7 @@ public MongoChangeTemplate() {
* Executes the apply operation for the current step.
*
* Precondition: {@code applyPayload} has been structurally validated at load time
- * via {@link MongoOperation#validate()}, which delegates to the appropriate
+ * via {@link MongoOperation#validate(TemplateValidationContext)}, which delegates to the appropriate
* {@code ParametersValidator} for each operation type. No validation is performed here.
*/
@Apply
@@ -99,7 +100,7 @@ public void apply(MongoDatabase db, @Nullable ClientSession clientSession) {
* Executes the rollback operation for the current step.
*
* Precondition: {@code rollbackPayload} has been structurally validated at load time
- * via {@link MongoOperation#validate()}, which delegates to the appropriate
+ * via {@link MongoOperation#validate(TemplateValidationContext)}, which delegates to the appropriate
* {@code ParametersValidator} for each operation type. No validation is performed here.
*/
@Rollback
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 65d5d76..b504141 100644
--- a/src/main/java/io/flamingock/template/mongodb/model/MongoOperation.java
+++ b/src/main/java/io/flamingock/template/mongodb/model/MongoOperation.java
@@ -20,6 +20,7 @@
import io.flamingock.api.NonLockGuardedType;
import io.flamingock.api.template.TemplatePayload;
import io.flamingock.api.template.TemplatePayloadValidationError;
+import io.flamingock.api.template.TemplateValidationContext;
import io.flamingock.template.mongodb.model.operator.MongoOperator;
import io.flamingock.template.mongodb.validation.CollectionValidator;
import io.flamingock.template.mongodb.validation.TypeValidator;
@@ -31,6 +32,7 @@
@NonLockGuarded(NonLockGuardedType.NONE)
public class MongoOperation implements TemplatePayload {
+
private String type;
private String collection;
private Map parameters;
@@ -87,7 +89,7 @@ public String toString() {
private static final CollectionValidator COLLECTION_VALIDATOR = new CollectionValidator();
@Override
- public List validate() {
+ public List validate(TemplateValidationContext context) {
List typeErrors = TYPE_VALIDATOR.validate(this);
List errors = new ArrayList<>(typeErrors);
@@ -101,6 +103,13 @@ public List validate() {
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/main/java/io/flamingock/template/mongodb/model/MongoOperationType.java b/src/main/java/io/flamingock/template/mongodb/model/MongoOperationType.java
index 9e006e6..798fee9 100644
--- a/src/main/java/io/flamingock/template/mongodb/model/MongoOperationType.java
+++ b/src/main/java/io/flamingock/template/mongodb/model/MongoOperationType.java
@@ -45,28 +45,31 @@
public enum MongoOperationType {
- CREATE_COLLECTION("createCollection", CreateCollectionOperator::new, new NoParametersValidator("CreateCollection")),
- CREATE_INDEX("createIndex", CreateIndexOperator::new, new CreateIndexParametersValidator()),
- INSERT("insert", InsertOperator::new, new InsertParametersValidator()),
- UPDATE("update", UpdateOperator::new, new UpdateParametersValidator()),
- DELETE("delete", DeleteOperator::new, new DeleteParametersValidator()),
- DROP_COLLECTION("dropCollection", DropCollectionOperator::new, new NoParametersValidator("DropCollection")),
- DROP_INDEX("dropIndex", DropIndexOperator::new, new DropIndexParametersValidator()),
- RENAME_COLLECTION("renameCollection", RenameCollectionOperator::new, new RenameCollectionParametersValidator()),
- MODIFY_COLLECTION("modifyCollection", ModifyCollectionOperator::new, new ModifyCollectionParametersValidator()),
- CREATE_VIEW("createView", CreateViewOperator::new, new CreateViewParametersValidator()),
- DROP_VIEW("dropView", DropViewOperator::new, new NoParametersValidator("DropView"));
+ CREATE_COLLECTION("createCollection", CreateCollectionOperator::new, new NoParametersValidator("CreateCollection"), false),
+ CREATE_INDEX("createIndex", CreateIndexOperator::new, new CreateIndexParametersValidator(), false),
+ INSERT("insert", InsertOperator::new, new InsertParametersValidator(), true),
+ UPDATE("update", UpdateOperator::new, new UpdateParametersValidator(), true),
+ DELETE("delete", DeleteOperator::new, new DeleteParametersValidator(), true),
+ DROP_COLLECTION("dropCollection", DropCollectionOperator::new, new NoParametersValidator("DropCollection"), false),
+ DROP_INDEX("dropIndex", DropIndexOperator::new, new DropIndexParametersValidator(), false),
+ RENAME_COLLECTION("renameCollection", RenameCollectionOperator::new, new RenameCollectionParametersValidator(), false),
+ MODIFY_COLLECTION("modifyCollection", ModifyCollectionOperator::new, new ModifyCollectionParametersValidator(), false),
+ CREATE_VIEW("createView", CreateViewOperator::new, new CreateViewParametersValidator(), false),
+ DROP_VIEW("dropView", DropViewOperator::new, new NoParametersValidator("DropView"), false);
private final String value;
private final BiFunction createOperatorFunction;
private final OperationValidator operationValidator;
+ private final boolean transactional;
MongoOperationType(String value,
BiFunction createOperatorFunction,
- OperationValidator operationValidator) {
+ OperationValidator operationValidator,
+ boolean transactional) {
this.value = value;
this.createOperatorFunction = createOperatorFunction;
this.operationValidator = operationValidator;
+ this.transactional = transactional;
}
public static MongoOperationType findByTypeOrThrow(String typeValue) {
@@ -90,6 +93,10 @@ public OperationValidator getOperationValidator() {
return operationValidator;
}
+ public boolean isTransactional() {
+ return transactional;
+ }
+
private boolean matches(String operationType) {
return this.value.equals(operationType);
}
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 a0f3cc4..956f50b 100644
--- a/src/test/java/io/flamingock/template/mongodb/model/MongoOperationValidateTest.java
+++ b/src/test/java/io/flamingock/template/mongodb/model/MongoOperationValidateTest.java
@@ -16,9 +16,12 @@
package io.flamingock.template.mongodb.model;
import io.flamingock.api.template.TemplatePayloadValidationError;
+import io.flamingock.api.template.TemplateValidationContext;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import java.util.*;
@@ -27,6 +30,14 @@
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 {
@@ -38,7 +49,7 @@ void nullOperationTypeTest() {
op.setType(null);
op.setCollection("test");
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("type", errors.get(0).getField());
@@ -52,7 +63,7 @@ void emptyOperationTypeTest() {
op.setType("");
op.setCollection("test");
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("type", errors.get(0).getField());
@@ -66,7 +77,7 @@ void unknownOperationTypeTest() {
op.setType("unknownType");
op.setCollection("test");
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("type", errors.get(0).getField());
@@ -80,7 +91,7 @@ void nullCollectionTest() {
op.setType("createCollection");
op.setCollection(null);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("collection", errors.get(0).getField());
@@ -94,7 +105,7 @@ void emptyCollectionTest() {
op.setType("createCollection");
op.setCollection("");
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("collection", errors.get(0).getField());
@@ -108,7 +119,7 @@ void collectionWithDollarSignTest() {
op.setType("createCollection");
op.setCollection("test$collection");
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("collection", errors.get(0).getField());
@@ -122,7 +133,7 @@ void collectionWithNullCharTest() {
op.setType("createCollection");
op.setCollection("test\0collection");
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("collection", errors.get(0).getField());
@@ -142,7 +153,7 @@ void insertMissingParametersTest() {
op.setCollection("test");
op.setParameters(null);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters", errors.get(0).getField());
@@ -157,7 +168,7 @@ void insertMissingDocumentsTest() {
op.setCollection("test");
op.setParameters(new HashMap<>());
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.documents", errors.get(0).getField());
@@ -174,7 +185,7 @@ void insertEmptyDocumentsTest() {
params.put("documents", new ArrayList<>());
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.documents", errors.get(0).getField());
@@ -193,7 +204,7 @@ void insertNullDocumentElementTest() {
params.put("documents", docs);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertTrue(errors.get(0).getField().contains("parameters.documents"));
@@ -210,7 +221,7 @@ void insertDocumentNotMapTest() {
params.put("documents", Arrays.asList("hello"));
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.documents[0]", errors.get(0).getField());
@@ -229,7 +240,7 @@ void insertDocumentMixedTypesTest() {
params.put("documents", Arrays.asList(validDoc, 123));
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.documents[1]", errors.get(0).getField());
@@ -246,7 +257,7 @@ void insertDocumentsWrongTypeTest() {
params.put("documents", "not a list");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.documents", errors.get(0).getField());
@@ -268,7 +279,7 @@ void insertOptionsWrongTypeTest() {
params.put("options", "not a document");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.options", errors.get(0).getField());
@@ -289,7 +300,7 @@ void insertValidTest() {
params.put("documents", docs);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertTrue(errors.isEmpty());
}
@@ -307,7 +318,7 @@ void updateMissingParametersTest() {
op.setCollection("test");
op.setParameters(null);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters", errors.get(0).getField());
@@ -326,7 +337,7 @@ void updateMissingFilterTest() {
params.put("update", update);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.filter", errors.get(0).getField());
@@ -343,7 +354,7 @@ void updateMissingUpdateParamTest() {
params.put("filter", new HashMap<>());
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.update", errors.get(0).getField());
@@ -361,7 +372,7 @@ void updateParamWrongTypeTest() {
params.put("update", "not a document");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.update", errors.get(0).getField());
@@ -381,7 +392,7 @@ void updateFilterWrongTypeTest() {
params.put("update", update);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.filter", errors.get(0).getField());
@@ -402,7 +413,7 @@ void updateOptionsWrongTypeTest() {
params.put("options", "not a document");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.options", errors.get(0).getField());
@@ -423,7 +434,7 @@ void updateMultiWrongTypeTest() {
params.put("multi", "yes");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.multi", errors.get(0).getField());
@@ -438,7 +449,7 @@ void updateMissingBothTest() {
op.setCollection("test");
op.setParameters(new HashMap<>());
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(2, errors.size());
}
@@ -458,7 +469,7 @@ void updateValidTest() {
params.put("update", update);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertTrue(errors.isEmpty());
}
@@ -476,7 +487,7 @@ void deleteMissingFilterTest() {
op.setCollection("test");
op.setParameters(new HashMap<>());
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.filter", errors.get(0).getField());
@@ -493,7 +504,7 @@ void deleteFilterWrongTypeTest() {
params.put("filter", "not a document");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.filter", errors.get(0).getField());
@@ -510,7 +521,7 @@ void deleteEmptyFilterTest() {
params.put("filter", new HashMap<>());
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertTrue(errors.isEmpty());
}
@@ -528,7 +539,7 @@ void deleteMultiWrongTypeTest() {
params.put("multi", "true");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.multi", errors.get(0).getField());
@@ -547,7 +558,7 @@ void deleteValidTest() {
params.put("filter", filter);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertTrue(errors.isEmpty());
}
@@ -565,7 +576,7 @@ void createIndexMissingParametersTest() {
op.setCollection("test");
op.setParameters(null);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters", errors.get(0).getField());
@@ -580,7 +591,7 @@ void createIndexMissingKeysTest() {
op.setCollection("test");
op.setParameters(new HashMap<>());
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.keys", errors.get(0).getField());
@@ -597,7 +608,7 @@ void createIndexEmptyKeysTest() {
params.put("keys", new HashMap<>());
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.keys", errors.get(0).getField());
@@ -614,7 +625,7 @@ void createIndexKeysWrongTypeTest() {
params.put("keys", "not a map");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.keys", errors.get(0).getField());
@@ -634,7 +645,7 @@ void createIndexOptionsWrongTypeTest() {
params.put("options", "not a document");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.options", errors.get(0).getField());
@@ -653,7 +664,7 @@ void createIndexValidTest() {
params.put("keys", keys);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertTrue(errors.isEmpty());
}
@@ -671,7 +682,7 @@ void dropIndexMissingBothTest() {
op.setCollection("test");
op.setParameters(new HashMap<>());
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertTrue(errors.get(0).getMessage().contains("'indexName' or 'keys'"));
@@ -687,7 +698,7 @@ void dropIndexKeysWrongTypeTest() {
params.put("keys", "not a map");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.keys", errors.get(0).getField());
@@ -704,7 +715,7 @@ void dropIndexIndexNameWrongTypeTest() {
params.put("indexName", 123);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.indexName", errors.get(0).getField());
@@ -724,7 +735,7 @@ void dropIndexBothIndexNameAndKeysTest() {
params.put("keys", keys);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters", errors.get(0).getField());
@@ -741,7 +752,7 @@ void dropIndexWithIndexNameTest() {
params.put("indexName", "email_index");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertTrue(errors.isEmpty());
}
@@ -758,7 +769,7 @@ void dropIndexWithKeysTest() {
params.put("keys", keys);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertTrue(errors.isEmpty());
}
@@ -776,7 +787,7 @@ void renameCollectionMissingTargetTest() {
op.setCollection("test");
op.setParameters(new HashMap<>());
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.target", errors.get(0).getField());
@@ -793,7 +804,7 @@ void renameCollectionEmptyTargetTest() {
params.put("target", "");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.target", errors.get(0).getField());
@@ -810,7 +821,7 @@ void renameCollectionTargetWithDollarSignTest() {
params.put("target", "new$Name");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.target", errors.get(0).getField());
@@ -827,7 +838,7 @@ void renameCollectionTargetWithNullCharTest() {
params.put("target", "new\0Name");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.target", errors.get(0).getField());
@@ -845,7 +856,7 @@ void renameCollectionOptionsWrongTypeTest() {
params.put("options", "not a document");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.options", errors.get(0).getField());
@@ -862,7 +873,7 @@ void renameCollectionValidTest() {
params.put("target", "newName");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertTrue(errors.isEmpty());
}
@@ -880,7 +891,7 @@ void createViewMissingParametersTest() {
op.setCollection("testView");
op.setParameters(null);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters", errors.get(0).getField());
@@ -897,7 +908,7 @@ void createViewMissingViewOnTest() {
params.put("pipeline", Collections.singletonList(new HashMap<>()));
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.viewOn", errors.get(0).getField());
@@ -914,7 +925,7 @@ void createViewMissingPipelineTest() {
params.put("viewOn", "sourceCollection");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.pipeline", errors.get(0).getField());
@@ -932,7 +943,7 @@ void createViewPipelineWrongTypeTest() {
params.put("pipeline", "not a list");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.pipeline", errors.get(0).getField());
@@ -950,7 +961,7 @@ void createViewPipelineStageNotMapTest() {
params.put("pipeline", Arrays.asList("invalid"));
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.pipeline[0]", errors.get(0).getField());
@@ -968,7 +979,7 @@ void createViewViewOnContainsDollarTest() {
params.put("pipeline", Collections.singletonList(new HashMap<>()));
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.viewOn", errors.get(0).getField());
@@ -986,7 +997,7 @@ void createViewViewOnContainsNullCharTest() {
params.put("pipeline", Collections.singletonList(new HashMap<>()));
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.viewOn", errors.get(0).getField());
@@ -1004,7 +1015,7 @@ void createViewViewOnWrongTypeTest() {
params.put("pipeline", Collections.singletonList(new HashMap<>()));
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.viewOn", errors.get(0).getField());
@@ -1023,7 +1034,7 @@ void createViewOptionsWrongTypeTest() {
params.put("options", "not a document");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.options", errors.get(0).getField());
@@ -1041,7 +1052,7 @@ void createViewValidTest() {
params.put("pipeline", Collections.singletonList(new HashMap<>()));
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertTrue(errors.isEmpty());
}
@@ -1058,7 +1069,7 @@ void createCollectionValidTest() {
op.setType("createCollection");
op.setCollection("test");
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertTrue(errors.isEmpty());
}
@@ -1070,7 +1081,7 @@ void dropCollectionValidTest() {
op.setType("dropCollection");
op.setCollection("test");
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertTrue(errors.isEmpty());
}
@@ -1082,7 +1093,7 @@ void dropViewValidTest() {
op.setType("dropView");
op.setCollection("testView");
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertTrue(errors.isEmpty());
}
@@ -1097,7 +1108,7 @@ void createCollectionWithParametersTest() {
params.put("capped", true);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters", errors.get(0).getField());
@@ -1112,7 +1123,7 @@ void createCollectionEmptyParametersTest() {
op.setCollection("test");
op.setParameters(new HashMap<>());
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertTrue(errors.isEmpty());
}
@@ -1127,7 +1138,7 @@ void dropCollectionWithParametersTest() {
params.put("writeConcern", "majority");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters", errors.get(0).getField());
@@ -1142,7 +1153,7 @@ void dropCollectionEmptyParametersTest() {
op.setCollection("test");
op.setParameters(new HashMap<>());
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertTrue(errors.isEmpty());
}
@@ -1157,7 +1168,7 @@ void dropViewWithParametersTest() {
params.put("force", true);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters", errors.get(0).getField());
@@ -1172,7 +1183,7 @@ void dropViewEmptyParametersTest() {
op.setCollection("testView");
op.setParameters(new HashMap<>());
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertTrue(errors.isEmpty());
}
@@ -1190,7 +1201,7 @@ void modifyCollectionNullParametersTest() {
op.setCollection("test");
op.setParameters(null);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters", errors.get(0).getField());
@@ -1205,7 +1216,7 @@ void modifyCollectionEmptyParametersTest() {
op.setCollection("test");
op.setParameters(new HashMap<>());
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters", errors.get(0).getField());
@@ -1222,7 +1233,7 @@ void modifyCollectionInvalidValidationLevelTest() {
params.put("validationLevel", "banana");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.validationLevel", errors.get(0).getField());
@@ -1239,7 +1250,7 @@ void modifyCollectionInvalidValidationActionTest() {
params.put("validationAction", "ignore");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.validationAction", errors.get(0).getField());
@@ -1256,7 +1267,7 @@ void modifyCollectionValidatorWrongTypeTest() {
params.put("validator", "not a map");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.validator", errors.get(0).getField());
@@ -1274,7 +1285,7 @@ void modifyCollectionMultipleInvalidParamsTest() {
params.put("validationAction", "ignore");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(2, errors.size());
}
@@ -1291,7 +1302,7 @@ void modifyCollectionValidValidatorOnlyTest() {
params.put("validator", validator);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertTrue(errors.isEmpty());
}
@@ -1307,7 +1318,7 @@ void modifyCollectionValidValidationLevelOnlyTest() {
params.put("validationLevel", level);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertTrue(errors.isEmpty(), "Expected no errors for validationLevel='" + level + "'");
}
@@ -1324,7 +1335,7 @@ void modifyCollectionValidValidationActionOnlyTest() {
params.put("validationAction", action);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertTrue(errors.isEmpty(), "Expected no errors for validationAction='" + action + "'");
}
@@ -1344,7 +1355,7 @@ void modifyCollectionAllValidParamsTest() {
params.put("validationAction", "error");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertTrue(errors.isEmpty());
}
@@ -1364,7 +1375,7 @@ void multipleErrorsTest() {
params.put("documents", new ArrayList<>());
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(2, errors.size());
}
@@ -1389,7 +1400,7 @@ void insertUnrecognizedKeyTest() {
params.put("unknownKey", "value");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.unknownKey", errors.get(0).getField());
@@ -1410,7 +1421,7 @@ void updateUnrecognizedKeyTest() {
params.put("typoKey", "value");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.typoKey", errors.get(0).getField());
@@ -1428,7 +1439,7 @@ void deleteUnrecognizedKeyTest() {
params.put("unknownKey", "value");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.unknownKey", errors.get(0).getField());
@@ -1448,7 +1459,7 @@ void createIndexUnrecognizedKeyTest() {
params.put("badParam", "value");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.badParam", errors.get(0).getField());
@@ -1466,7 +1477,7 @@ void dropIndexUnrecognizedKeyTest() {
params.put("extra", "value");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.extra", errors.get(0).getField());
@@ -1484,7 +1495,7 @@ void renameCollectionUnrecognizedKeyTest() {
params.put("badParam", "value");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.badParam", errors.get(0).getField());
@@ -1503,7 +1514,7 @@ void createViewUnrecognizedKeyTest() {
params.put("badParam", "value");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.badParam", errors.get(0).getField());
@@ -1521,7 +1532,7 @@ void modifyCollectionUnrecognizedKeyTest() {
params.put("unknownKey", "value");
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.unknownKey", errors.get(0).getField());
@@ -1550,7 +1561,7 @@ void insertUnrecognizedOptionKeyTest() {
params.put("options", options);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.options.banana", errors.get(0).getField());
@@ -1573,7 +1584,7 @@ void updateUnrecognizedOptionKeyTest() {
params.put("options", options);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.options.unknownOption", errors.get(0).getField());
@@ -1595,7 +1606,7 @@ void createIndexUnrecognizedOptionKeyTest() {
params.put("options", options);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.options.invalidOption", errors.get(0).getField());
@@ -1616,7 +1627,7 @@ void createViewUnrecognizedOptionKeyTest() {
params.put("options", options);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.options.unknownViewOption", errors.get(0).getField());
@@ -1636,7 +1647,7 @@ void renameCollectionUnrecognizedOptionKeyTest() {
params.put("options", options);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertEquals(1, errors.size());
assertEquals("parameters.options.badOption", errors.get(0).getField());
@@ -1659,7 +1670,7 @@ void createIndexRecognizedOptionKeyTest() {
params.put("options", options);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
assertTrue(errors.isEmpty());
}
@@ -1682,9 +1693,122 @@ void insertMultipleUnrecognizedOptionKeysTest() {
params.put("options", options);
op.setParameters(params);
- List errors = op.validate();
+ List errors = op.validate(NON_TRANSACTIONAL_CONTEXT);
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