From 1bff5e4f300f968106c4cbd89e99fca883b3f882 Mon Sep 17 00:00:00 2001 From: Juan Pablo Galeotti Date: Thu, 8 Oct 2026 22:39:16 -0300 Subject: [PATCH] instrumentation and e2e test cases for MongoCollection.findOneAndReplace() methods --- .../MongoCollectionClassReplacement.java | 20 +++ .../MongoCollectionClassReplacementTest.java | 107 ++++++++++++++++ .../MongoFindOneAndReplaceApp.java | 20 +++ .../MongoFindOneAndReplaceController.java | 121 ++++++++++++++++++ .../MongoFindOneAndReplaceData.java | 32 +++++ .../MongoFindOneAndReplaceAppController.java | 15 +++ .../MongoFindOneAndReplaceEMTest.java | 61 +++++++++ 7 files changed, 376 insertions(+) create mode 100644 core-tests/e2e-tests/spring/spring-rest-mongo/src/main/java/com/mongo/findoneandreplace/MongoFindOneAndReplaceApp.java create mode 100644 core-tests/e2e-tests/spring/spring-rest-mongo/src/main/java/com/mongo/findoneandreplace/MongoFindOneAndReplaceController.java create mode 100644 core-tests/e2e-tests/spring/spring-rest-mongo/src/main/java/com/mongo/findoneandreplace/MongoFindOneAndReplaceData.java create mode 100644 core-tests/e2e-tests/spring/spring-rest-mongo/src/test/java/com/foo/spring/rest/mongo/findoneandreplace/MongoFindOneAndReplaceAppController.java create mode 100644 core-tests/e2e-tests/spring/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/findoneandreplace/MongoFindOneAndReplaceEMTest.java diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacement.java index a3125006d3..55e7df7d68 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacement.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacement.java @@ -252,6 +252,26 @@ public static Object findOneAndUpdate(Object mongoCollection, @ThirdPartyCast(ac return handleUpdate("findOneAndUpdateClientSessionBsonPipelineFindOneAndUpdateOptions", mongoCollection, Arrays.asList(clientSession, filter, pipeline, options), filter); } + @Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "findOneAndReplaceBsonTDocument", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO) + public static Object findOneAndReplace(Object mongoCollection, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, Object replacement) { + return handleUpdate("findOneAndReplaceBsonTDocument", mongoCollection, Arrays.asList(filter, replacement), filter); + } + + @Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "findOneAndReplaceBsonTDocumentFindOneAndReplaceOptions", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO) + public static Object findOneAndReplace_EM_0(Object mongoCollection, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, Object replacement, @ThirdPartyCast(actualType = "com.mongodb.client.model.FindOneAndReplaceOptions") Object options) { + return handleUpdate("findOneAndReplaceBsonTDocumentFindOneAndReplaceOptions", mongoCollection, Arrays.asList(filter, replacement, options), filter); + } + + @Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "findOneAndReplaceClientSessionBsonTDocument", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO) + public static Object findOneAndReplace(Object mongoCollection, @ThirdPartyCast(actualType = "com.mongodb.client.ClientSession") Object clientSession, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, Object replacement) { + return handleUpdate("findOneAndReplaceClientSessionBsonTDocument", mongoCollection, Arrays.asList(clientSession, filter, replacement), filter); + } + + @Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "findOneAndReplaceClientSessionBsonTDocumentFindOneAndReplaceOptions", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO) + public static Object findOneAndReplace(Object mongoCollection, @ThirdPartyCast(actualType = "com.mongodb.client.ClientSession") Object clientSession, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, Object replacement, @ThirdPartyCast(actualType = "com.mongodb.client.model.FindOneAndReplaceOptions") Object options) { + return handleUpdate("findOneAndReplaceClientSessionBsonTDocumentFindOneAndReplaceOptions", mongoCollection, Arrays.asList(clientSession, filter, replacement, options), filter); + } + /** * Processes the count handling based on the provided parameters. * diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacementTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacementTest.java index 3314af0933..b42d64ecc7 100644 --- a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacementTest.java +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacementTest.java @@ -5,6 +5,7 @@ import com.mongodb.MongoQueryException; import com.mongodb.client.*; import com.mongodb.client.model.DeleteOptions; +import com.mongodb.client.model.FindOneAndReplaceOptions; import com.mongodb.client.model.FindOneAndUpdateOptions; import com.mongodb.client.model.FindOneAndDeleteOptions; import com.mongodb.client.model.UpdateOptions; @@ -1187,4 +1188,110 @@ public void testFindOneAndUpdateWithInvalidFilter() { BsonDocument retrievedQuery = assertSingleRecordedCommand(false); assertEquals(-1, retrievedQuery.getDocument("tags").getInt32("$size").getValue()); } + + private static Document replacement() { + return new Document("name", "Replaced").append("age", 30); + } + + @Test + public void testFindOneAndReplaceWithFilterAndReplacement() { + final MongoCollection collection = getMongoCollection(); + insertPerson("John Doe", 30); + insertPerson("Jim Roe", 25); + + ExecutionTracer.setExecutingInitMongo(false); + Document found = (Document) MongoCollectionClassReplacement.findOneAndReplace(collection, new Document("age", 30), replacement()); + + // by default, the document before the replacement is returned + assertNotNull(found); + assertEquals("John Doe", found.getString("name")); + assertEquals(1, collection.countDocuments(new Document("name", "Replaced"))); + assertEquals(0, collection.countDocuments(new Document("name", "John Doe"))); + BsonDocument retrievedQuery = assertSingleRecordedCommand(true); + assertEquals(30, retrievedQuery.getInt32("age").getValue()); + } + + @Test + public void testFindOneAndReplaceWithFilterReplacementAndOptions() { + final MongoCollection collection = getMongoCollection(); + insertPerson("John Doe", 30); + insertPerson("Jim Roe", 25); + + ExecutionTracer.setExecutingInitMongo(false); + Document found = (Document) MongoCollectionClassReplacement.findOneAndReplace_EM_0(collection, new Document("age", 30), replacement(), new FindOneAndReplaceOptions()); + + // by default, the document before the replacement is returned + assertNotNull(found); + assertEquals("John Doe", found.getString("name")); + assertEquals(1, collection.countDocuments(new Document("name", "Replaced"))); + assertEquals(0, collection.countDocuments(new Document("name", "John Doe"))); + BsonDocument retrievedQuery = assertSingleRecordedCommand(true); + assertEquals(30, retrievedQuery.getInt32("age").getValue()); + } + + @Test + public void testFindOneAndReplaceWithClientSessionFilterAndReplacement() { + try (ClientSession clientSession = mongoClient.startSession()) { + final MongoCollection collection = getMongoCollection(); + insertPerson("John Doe", 30); + insertPerson("Jim Roe", 25); + + ExecutionTracer.setExecutingInitMongo(false); + Document found = (Document) MongoCollectionClassReplacement.findOneAndReplace(collection, clientSession, new Document("age", 30), replacement()); + + // by default, the document before the replacement is returned + assertNotNull(found); + assertEquals("John Doe", found.getString("name")); + assertEquals(1, collection.countDocuments(new Document("name", "Replaced"))); + assertEquals(0, collection.countDocuments(new Document("name", "John Doe"))); + BsonDocument retrievedQuery = assertSingleRecordedCommand(true); + assertEquals(30, retrievedQuery.getInt32("age").getValue()); + } + } + + @Test + public void testFindOneAndReplaceWithClientSessionFilterReplacementAndOptions() { + try (ClientSession clientSession = mongoClient.startSession()) { + final MongoCollection collection = getMongoCollection(); + insertPerson("John Doe", 30); + insertPerson("Jim Roe", 25); + + ExecutionTracer.setExecutingInitMongo(false); + Document found = (Document) MongoCollectionClassReplacement.findOneAndReplace(collection, clientSession, new Document("age", 30), replacement(), new FindOneAndReplaceOptions()); + + // by default, the document before the replacement is returned + assertNotNull(found); + assertEquals("John Doe", found.getString("name")); + assertEquals(1, collection.countDocuments(new Document("name", "Replaced"))); + assertEquals(0, collection.countDocuments(new Document("name", "John Doe"))); + BsonDocument retrievedQuery = assertSingleRecordedCommand(true); + assertEquals(30, retrievedQuery.getInt32("age").getValue()); + } + } + + @Test + public void testFindOneAndReplaceWithFilterNoMatch() { + final MongoCollection collection = getMongoCollection(); + + ExecutionTracer.setExecutingInitMongo(false); + Document found = (Document) MongoCollectionClassReplacement.findOneAndReplace(collection, new Document("age", 99), replacement()); + + assertNull(found); + BsonDocument retrievedQuery = assertSingleRecordedCommand(true); + assertEquals(99, retrievedQuery.getInt32("age").getValue()); + } + + @Test + public void testFindOneAndReplaceWithInvalidFilter() { + final MongoCollection collection = getMongoCollection(); + insertPerson("John Doe", 30); + + Document invalidFilter = new Document("tags", new Document("$size", -1)); + + ExecutionTracer.setExecutingInitMongo(false); + assertThrows(com.mongodb.MongoException.class, () -> MongoCollectionClassReplacement.findOneAndReplace(collection, invalidFilter, replacement())); + + BsonDocument retrievedQuery = assertSingleRecordedCommand(false); + assertEquals(-1, retrievedQuery.getDocument("tags").getInt32("$size").getValue()); + } } diff --git a/core-tests/e2e-tests/spring/spring-rest-mongo/src/main/java/com/mongo/findoneandreplace/MongoFindOneAndReplaceApp.java b/core-tests/e2e-tests/spring/spring-rest-mongo/src/main/java/com/mongo/findoneandreplace/MongoFindOneAndReplaceApp.java new file mode 100644 index 0000000000..00b557bf81 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-mongo/src/main/java/com/mongo/findoneandreplace/MongoFindOneAndReplaceApp.java @@ -0,0 +1,20 @@ +package com.mongo.findoneandreplace; + +import com.mongo.SwaggerConfiguration; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +@EnableSwagger2 +@SpringBootApplication(exclude = SecurityAutoConfiguration.class) +public class MongoFindOneAndReplaceApp extends SwaggerConfiguration { + public MongoFindOneAndReplaceApp() { + super("mongofindoneandreplace"); + } + + public static void main(String[] args) { + SpringApplication.run(MongoFindOneAndReplaceApp.class, args); + } + +} diff --git a/core-tests/e2e-tests/spring/spring-rest-mongo/src/main/java/com/mongo/findoneandreplace/MongoFindOneAndReplaceController.java b/core-tests/e2e-tests/spring/spring-rest-mongo/src/main/java/com/mongo/findoneandreplace/MongoFindOneAndReplaceController.java new file mode 100644 index 0000000000..f451262f14 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-mongo/src/main/java/com/mongo/findoneandreplace/MongoFindOneAndReplaceController.java @@ -0,0 +1,121 @@ +package com.mongo.findoneandreplace; + +import com.mongodb.MongoClientSettings; +import com.mongodb.client.ClientSession; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.FindOneAndReplaceOptions; +import com.mongodb.client.model.ReturnDocument; +import org.bson.codecs.configuration.CodecRegistries; +import org.bson.codecs.configuration.CodecRegistry; +import org.bson.codecs.pojo.PojoCodecProvider; +import org.bson.conversions.Bson; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * Replaces documents directly through the Mongo driver (MongoCollection.findOneAndReplace), + * not through Spring Data, so the filter is only visible at the driver level. + * There is one endpoint for each overload of findOneAndReplace. + */ +@RestController +@RequestMapping(path = "/mongofindoneandreplace") +public class MongoFindOneAndReplaceController { + + /* + Each endpoint uses its own collection, so that a document that + makes one endpoint return 200 cannot accidentally do it for another one. + */ + private static final String FIND_ONE_AND_REPLACE_BY_CITY_AND_AGE = "findOneAndReplaceByCityAndAgeCollection"; + private static final String FIND_ONE_AND_REPLACE_BY_CITY_AND_AGE_WITH_OPTIONS = "findOneAndReplaceByCityAndAgeWithOptionsCollection"; + private static final String FIND_ONE_AND_REPLACE_BY_CITY_AND_AGE_IN_SESSION = "findOneAndReplaceByCityAndAgeInSessionCollection"; + private static final String FIND_ONE_AND_REPLACE_BY_CITY_AND_AGE_IN_SESSION_WITH_OPTIONS = "findOneAndReplaceByCityAndAgeInSessionWithOptionsCollection"; + + /* + findOneAndReplace returns the document, so (unlike count, delete or update) + the driver needs a codec to decode it into our own class + */ + private static final CodecRegistry POJO_CODEC_REGISTRY = CodecRegistries.fromRegistries( + MongoClientSettings.getDefaultCodecRegistry(), + CodecRegistries.fromProviders(PojoCodecProvider.builder().automatic(true).build())); + + @Autowired + private MongoTemplate mongoTemplate; + + @Autowired + private MongoClient mongoClient; + + private MongoCollection collection(String collectionName) { + return mongoTemplate.getDb() + .getCollection(collectionName, MongoFindOneAndReplaceData.class) + .withCodecRegistry(POJO_CODEC_REGISTRY); + } + + private static Bson filter(String city, int minAge) { + return Filters.and(Filters.eq("city", city), Filters.gte("age", minAge)); + } + + /* + the replacement has no id, so the replaced document keeps its own _id + */ + private static MongoFindOneAndReplaceData replacement(String city, int minAge) { + return new MongoFindOneAndReplaceData(city, minAge, true); + } + + private static FindOneAndReplaceOptions options() { + return new FindOneAndReplaceOptions().returnDocument(ReturnDocument.AFTER); + } + + /* + findOneAndReplace returns the document, or null if no document matched the filter + */ + private static ResponseEntity response(MongoFindOneAndReplaceData replaced) { + return ResponseEntity.status(replaced != null ? 200 : 404).build(); + } + + /** + * findOneAndReplace(Bson, TDocument) + */ + @PutMapping("findOneAndReplaceByCityAndAge") + public ResponseEntity findOneAndReplaceByCityAndAge(@RequestParam(name = "city") String city, + @RequestParam(name = "minAge") int minAge) { + return response(collection(FIND_ONE_AND_REPLACE_BY_CITY_AND_AGE).findOneAndReplace(filter(city, minAge), replacement(city, minAge))); + } + + /** + * findOneAndReplace(Bson, TDocument, FindOneAndReplaceOptions) + */ + @PutMapping("findOneAndReplaceByCityAndAgeWithOptions") + public ResponseEntity findOneAndReplaceByCityAndAgeWithOptions(@RequestParam(name = "city") String city, + @RequestParam(name = "minAge") int minAge) { + return response(collection(FIND_ONE_AND_REPLACE_BY_CITY_AND_AGE_WITH_OPTIONS).findOneAndReplace(filter(city, minAge), replacement(city, minAge), options())); + } + + /** + * findOneAndReplace(ClientSession, Bson, TDocument) + */ + @PutMapping("findOneAndReplaceByCityAndAgeInSession") + public ResponseEntity findOneAndReplaceByCityAndAgeInSession(@RequestParam(name = "city") String city, + @RequestParam(name = "minAge") int minAge) { + try (ClientSession session = mongoClient.startSession()) { + return response(collection(FIND_ONE_AND_REPLACE_BY_CITY_AND_AGE_IN_SESSION).findOneAndReplace(session, filter(city, minAge), replacement(city, minAge))); + } + } + + /** + * findOneAndReplace(ClientSession, Bson, TDocument, FindOneAndReplaceOptions) + */ + @PutMapping("findOneAndReplaceByCityAndAgeInSessionWithOptions") + public ResponseEntity findOneAndReplaceByCityAndAgeInSessionWithOptions(@RequestParam(name = "city") String city, + @RequestParam(name = "minAge") int minAge) { + try (ClientSession session = mongoClient.startSession()) { + return response(collection(FIND_ONE_AND_REPLACE_BY_CITY_AND_AGE_IN_SESSION_WITH_OPTIONS).findOneAndReplace(session, filter(city, minAge), replacement(city, minAge), options())); + } + } +} diff --git a/core-tests/e2e-tests/spring/spring-rest-mongo/src/main/java/com/mongo/findoneandreplace/MongoFindOneAndReplaceData.java b/core-tests/e2e-tests/spring/spring-rest-mongo/src/main/java/com/mongo/findoneandreplace/MongoFindOneAndReplaceData.java new file mode 100644 index 0000000000..7085544c93 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-mongo/src/main/java/com/mongo/findoneandreplace/MongoFindOneAndReplaceData.java @@ -0,0 +1,32 @@ +package com.mongo.findoneandreplace; + +import org.bson.BsonType; +import org.bson.codecs.pojo.annotations.BsonRepresentation; +import org.springframework.data.annotation.Id; + +public class MongoFindOneAndReplaceData { + + /* + The documents in the collection have an ObjectId as _id, while this is a String. + As findOneAndReplace decodes the returned document with the driver's POJO codec, + it needs to be told how to read it. + */ + @Id + @BsonRepresentation(BsonType.OBJECT_ID) + public String id; + + public String city; + + public int age; + + public boolean verified; + + public MongoFindOneAndReplaceData() { + } + + public MongoFindOneAndReplaceData(String city, int age, boolean verified) { + this.city = city; + this.age = age; + this.verified = verified; + } +} diff --git a/core-tests/e2e-tests/spring/spring-rest-mongo/src/test/java/com/foo/spring/rest/mongo/findoneandreplace/MongoFindOneAndReplaceAppController.java b/core-tests/e2e-tests/spring/spring-rest-mongo/src/test/java/com/foo/spring/rest/mongo/findoneandreplace/MongoFindOneAndReplaceAppController.java new file mode 100644 index 0000000000..386dd2a094 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-mongo/src/test/java/com/foo/spring/rest/mongo/findoneandreplace/MongoFindOneAndReplaceAppController.java @@ -0,0 +1,15 @@ +package com.foo.spring.rest.mongo.findoneandreplace; + +import com.foo.spring.rest.mongo.MongoController; +import com.mongo.findoneandreplace.MongoFindOneAndReplaceApp; + +public class MongoFindOneAndReplaceAppController extends MongoController { + public MongoFindOneAndReplaceAppController() { + super("findoneandreplace", MongoFindOneAndReplaceApp.class); + } + + @Override + public String getPackagePrefixesToCover() { + return "com.mongo.findoneandreplace"; + } +} diff --git a/core-tests/e2e-tests/spring/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/findoneandreplace/MongoFindOneAndReplaceEMTest.java b/core-tests/e2e-tests/spring/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/findoneandreplace/MongoFindOneAndReplaceEMTest.java new file mode 100644 index 0000000000..04f1acb964 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/findoneandreplace/MongoFindOneAndReplaceEMTest.java @@ -0,0 +1,61 @@ +package org.evomaster.e2etests.spring.rest.mongo.findoneandreplace; + +import com.foo.spring.rest.mongo.findoneandreplace.MongoFindOneAndReplaceAppController; +import org.evomaster.core.EMConfig; +import org.evomaster.core.problem.rest.data.HttpVerb; +import org.evomaster.core.problem.rest.data.RestIndividual; +import org.evomaster.core.search.Solution; +import org.evomaster.e2etests.utils.RestTestBase; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class MongoFindOneAndReplaceEMTest extends RestTestBase { + + @BeforeAll + public static void initClass() throws Exception { + EMConfig config = new EMConfig(); + config.setInstrumentMR_MONGO(true); + RestTestBase.initClass(new MongoFindOneAndReplaceAppController()); + } + + @Test + public void testRunEM() throws Throwable { + + runTestHandlingFlakyAndCompilation( + "MongoFindOneAndReplaceEMTest", + "org.foo.spring.rest.mongo.MongoFindOneAndReplaceEMTest", + 1000, + (args) -> { + setOption(args, "heuristicsForMongo", "true"); + setOption(args, "instrumentMR_MONGO", "true"); + setOption(args, "generateMongoData", "true"); + setOption(args, "extractMongoExecutionInfo", "true"); + + Solution solution = initAndRun(args); + + assertTrue(solution.getIndividuals().size() >= 1); + + // one endpoint per overload of MongoCollection.findOneAndReplace. + // A 200 needs a document with the requested city and age in the collection, + // which can only be generated if the findOneAndReplace filter is observed + + // findOneAndReplace(Bson, TDocument) + assertHasAtLeastOne(solution, HttpVerb.PUT, 404, "/mongofindoneandreplace/findOneAndReplaceByCityAndAge", null); + assertHasAtLeastOne(solution, HttpVerb.PUT, 200, "/mongofindoneandreplace/findOneAndReplaceByCityAndAge", null); + + // findOneAndReplace(Bson, TDocument, FindOneAndReplaceOptions) + assertHasAtLeastOne(solution, HttpVerb.PUT, 404, "/mongofindoneandreplace/findOneAndReplaceByCityAndAgeWithOptions", null); + assertHasAtLeastOne(solution, HttpVerb.PUT, 200, "/mongofindoneandreplace/findOneAndReplaceByCityAndAgeWithOptions", null); + + // findOneAndReplace(ClientSession, Bson, TDocument) + assertHasAtLeastOne(solution, HttpVerb.PUT, 404, "/mongofindoneandreplace/findOneAndReplaceByCityAndAgeInSession", null); + assertHasAtLeastOne(solution, HttpVerb.PUT, 200, "/mongofindoneandreplace/findOneAndReplaceByCityAndAgeInSession", null); + + // findOneAndReplace(ClientSession, Bson, TDocument, FindOneAndReplaceOptions) + assertHasAtLeastOne(solution, HttpVerb.PUT, 404, "/mongofindoneandreplace/findOneAndReplaceByCityAndAgeInSessionWithOptions", null); + assertHasAtLeastOne(solution, HttpVerb.PUT, 200, "/mongofindoneandreplace/findOneAndReplaceByCityAndAgeInSessionWithOptions", null); + }); + } +}