static void test_find_and_modify_opts (void) { mock_server_t *server; mongoc_client_t *client; mongoc_collection_t *collection; bson_error_t error; mongoc_find_and_modify_opts_t *opts; future_t *future; request_t *request; server = mock_server_with_autoismaster (0); mock_server_run (server); client = mongoc_client_new_from_uri (mock_server_get_uri (server)); collection = mongoc_client_get_collection (client, "db", "collection"); opts = mongoc_find_and_modify_opts_new (); assert (mongoc_find_and_modify_opts_set_max_time_ms (opts, 42)); assert (mongoc_find_and_modify_opts_append (opts, tmp_bson ("{'foo': 1}"))); future = future_collection_find_and_modify_with_opts ( collection, tmp_bson ("{}"), opts, NULL, &error); request = mock_server_receives_command ( server, "db", MONGOC_QUERY_NONE, "{'findAndModify': 'collection', 'maxTimeMS': 42, 'foo': 1}"); mock_server_replies_ok_and_destroys (request); ASSERT_OR_PRINT (future_get_bool (future), error); future_destroy (future); mongoc_find_and_modify_opts_destroy (opts); mongoc_collection_destroy (collection); mongoc_client_destroy (client); mock_server_destroy (server); }
static bool bulk_update_one (mongoc_bulk_operation_t *bulk, bson_error_t *error, bson_t *cmd) { BSON_APPEND_UTF8 (cmd, "update", "collection"); return mongoc_bulk_operation_update_one_with_opts ( bulk, tmp_bson ("{}"), tmp_bson ("{}"), NULL, error); }
static void _test_collection_find_with_opts (test_collection_find_with_opts_t *test_data) { BSON_ASSERT (test_data->expected_find_command); test_data->filter_bson = tmp_bson (test_data->filter); test_data->opts_bson = tmp_bson (test_data->opts); _test_collection_op_query (test_data); _test_collection_find_command (test_data); }
static future_t * update_one (func_ctx_t *ctx, bson_t *cmd) { BSON_APPEND_UTF8 (cmd, "update", "collection"); BSON_ASSERT (!ctx->prefs); return future_collection_update_one (ctx->collection, tmp_bson ("{}"), tmp_bson ("{}"), ctx->opts, NULL, &ctx->error); }
/* test that we add readConcern only inside $query, not outside it too */ static void test_mongos_read_concern (void) { mock_server_t *server; mongoc_client_t *client; mongoc_collection_t *collection; mongoc_read_prefs_t *prefs; mongoc_cursor_t *cursor; const bson_t *doc; future_t *future; request_t *request; server = mock_mongos_new (WIRE_VERSION_READ_CONCERN); mock_server_run (server); client = mongoc_client_new_from_uri (mock_server_get_uri (server)); collection = mongoc_client_get_collection (client, "test", "test"); prefs = mongoc_read_prefs_new (MONGOC_READ_SECONDARY); cursor = mongoc_collection_find_with_opts ( collection, tmp_bson ("{'a': 1}"), tmp_bson ("{'readConcern': {'level': 'foo'}}"), prefs); future = future_cursor_next (cursor, &doc); request = mock_server_receives_command ( server, "test", MONGOC_QUERY_SLAVE_OK, "{" " '$query': {" " 'find': 'test', 'filter': {}, 'readConcern': {'level': 'foo'}" " }," " '$readPreference': {" " 'mode': 'secondary'" " }," " 'readConcern': {'$exists': false}" "}"); mock_server_replies_to_find ( request, MONGOC_QUERY_SLAVE_OK, 0, 1, "db.collection", "{}", true); /* mongoc_cursor_next returned true */ BSON_ASSERT (future_get_bool (future)); request_destroy (request); future_destroy (future); mongoc_cursor_destroy (cursor); mongoc_read_prefs_destroy (prefs); mongoc_collection_destroy (collection); mongoc_client_destroy (client); mock_server_destroy (server); }
static bool bulk_insert (mongoc_bulk_operation_t *bulk, bson_error_t *error, bson_t *cmd) { BSON_APPEND_UTF8 (cmd, "insert", "collection"); return mongoc_bulk_operation_insert_with_opts ( bulk, tmp_bson ("{}"), NULL, error); }
static future_t * create_index (func_ctx_t *ctx, bson_t *cmd) { BSON_APPEND_UTF8 (cmd, "createIndexes", "collection"); return future_collection_create_index_with_opts ( ctx->collection, tmp_bson ("{}"), NULL, ctx->opts, NULL, &ctx->error); }
static future_t * db_write_cmd (func_ctx_t *ctx, bson_t *cmd) { BSON_APPEND_UTF8 (cmd, "foo", "db"); return future_database_write_command_with_opts ( ctx->db, tmp_bson ("{'foo': 'db'}"), ctx->opts, NULL, &ctx->error); }
static void _test_op_msg (const mongoc_uri_t *uri, mock_server_t *server, const char *query_in, mongoc_read_prefs_t *read_prefs, const char *expected_op_msg) { mongoc_client_t *client; mongoc_collection_t *collection; mongoc_cursor_t *cursor; const bson_t *doc; bson_t b = BSON_INITIALIZER; future_t *future; request_t *request; client = mongoc_client_new_from_uri (uri); collection = mongoc_client_get_collection (client, "test", "test"); cursor = mongoc_collection_find (collection, MONGOC_QUERY_NONE, 0, 1, 0, tmp_bson (query_in), NULL, read_prefs); future = future_cursor_next (cursor, &doc); request = mock_server_receives_msg (server, 0, tmp_bson (expected_op_msg)); mock_server_replies_simple (request, "{'ok': 1," " 'cursor': {" " 'id': 0," " 'ns': 'db.collection'," " 'firstBatch': [{'a': 1}]}}"); /* mongoc_cursor_next returned true */ BSON_ASSERT (future_get_bool (future)); request_destroy (request); future_destroy (future); mongoc_cursor_destroy (cursor); mongoc_collection_destroy (collection); mongoc_client_destroy (client); bson_destroy (&b); }
static void _test_find_command (const mongoc_uri_t *uri, mock_server_t *server, const char *query_in, mongoc_read_prefs_t *read_prefs, mongoc_query_flags_t expected_find_cmd_query_flags, const char *expected_find_cmd) { mongoc_client_t *client; mongoc_collection_t *collection; mongoc_cursor_t *cursor; const bson_t *doc; bson_t b = BSON_INITIALIZER; future_t *future; request_t *request; client = mongoc_client_new_from_uri (uri); collection = mongoc_client_get_collection (client, "test", "test"); mongoc_collection_set_read_prefs (collection, read_prefs); cursor = mongoc_collection_find (collection, MONGOC_QUERY_NONE, 0, 1, 0, tmp_bson (query_in), NULL, read_prefs); future = future_cursor_next (cursor, &doc); request = mock_server_receives_command ( server, "test", expected_find_cmd_query_flags, expected_find_cmd); mock_server_replies (request, MONGOC_REPLY_NONE, /* flags */ 0, /* cursorId */ 0, /* startingFrom */ 1, /* numberReturned */ "{'ok': 1," " 'cursor': {" " 'id': 0," " 'ns': 'db.collection'," " 'firstBatch': [{'a': 1}]}}"); /* mongoc_cursor_next returned true */ assert (future_get_bool (future)); request_destroy (request); future_destroy (future); mongoc_cursor_destroy (cursor); mongoc_collection_destroy (collection); mongoc_client_destroy (client); bson_destroy (&b); }
static bool bulk_remove_many (mongoc_bulk_operation_t *bulk, bson_error_t *error, bson_t *cmd) { BSON_APPEND_UTF8 (cmd, "delete", "collection"); return mongoc_bulk_operation_remove_many_with_opts ( bulk, tmp_bson ("{}"), NULL, error); }
/* direct connection to a secondary requires read pref primaryPreferred to * avoid "not master" error from server */ static void _test_op_msg_direct_connection (bool is_mongos, test_op_msg_direct_fn_t fn, const char *expected_cmd) { mock_server_t *server; mongoc_client_t *client; mongoc_collection_t *collection; mongoc_read_prefs_t *prefs = NULL; mongoc_cursor_t *cursor; const bson_t *doc; bson_t *cmd; future_t *future; request_t *request; const char *reply; int i; if (is_mongos) { server = mock_mongos_new (WIRE_VERSION_OP_MSG); } else { server = mock_server_with_autoismaster (WIRE_VERSION_OP_MSG); } mock_server_auto_endsessions (server); mock_server_run (server); client = mongoc_client_new_from_uri (mock_server_get_uri (server)); collection = mongoc_client_get_collection (client, "db", "collection"); for (i = 0; i < 2; i++) { if (i == 1) { /* user-supplied read preference primary makes no difference */ prefs = mongoc_read_prefs_new (MONGOC_READ_PRIMARY); } cursor = fn (collection, prefs); future = future_cursor_next (cursor, &doc); cmd = tmp_bson (expected_cmd); request = mock_server_receives_msg (server, 0, cmd); reply = "{'ok': 1," " 'cursor': {" " 'id': 0," " 'ns': 'db.collection'," " 'firstBatch': [{'a': 1}]}}"; mock_server_replies_simple (request, reply); BSON_ASSERT (future_get_bool (future)); future_destroy (future); request_destroy (request); mongoc_cursor_destroy (cursor); mongoc_read_prefs_destroy (prefs); /* null ok */ } mongoc_collection_destroy (collection); mongoc_client_destroy (client); mock_server_destroy (server); }
static future_t * find (func_ctx_t *ctx, bson_t *cmd) { BSON_APPEND_UTF8 (cmd, "find", "collection"); ctx->cursor = mongoc_collection_find_with_opts ( ctx->collection, tmp_bson ("{}"), ctx->opts, ctx->prefs); /* use ctx->data as the bson_t** out-param to mongoc_cursor_next () */ return future_cursor_next (ctx->cursor, (const bson_t **) &ctx->data); }
static void test_exhaust (void) { mock_server_t *server; mongoc_client_t *client; mongoc_collection_t *collection; mongoc_cursor_t *cursor; request_t *request; future_t *future; const bson_t *doc; bson_error_t error; server = mock_server_with_autoismaster (WIRE_VERSION_FIND_CMD); mock_server_run (server); client = mongoc_client_new_from_uri (mock_server_get_uri (server)); collection = mongoc_client_get_collection (client, "db", "collection"); cursor = mongoc_collection_find_with_opts (collection, tmp_bson (NULL), NULL, tmp_bson ("{'exhaust': true}")); future = future_cursor_next (cursor, &doc); /* Find, getMore and killCursors commands spec: "The find command does not * support the exhaust flag from OP_QUERY. Drivers that support exhaust MUST * fallback to existing OP_QUERY wire protocol messages." */ request = mock_server_receives_request (server); mock_server_replies_to_find ( request, MONGOC_QUERY_SLAVE_OK | MONGOC_QUERY_EXHAUST, 0, 0, "db.collection", "{}", false /* is_command */); ASSERT (future_get_bool (future)); ASSERT_OR_PRINT (!mongoc_cursor_error (cursor, &error), error); request_destroy (request); future_destroy (future); mongoc_cursor_destroy (cursor); mongoc_collection_destroy (collection); mongoc_client_destroy (client); mock_server_destroy (server); }
static future_t * collection_write_cmd (func_ctx_t *ctx, bson_t *cmd) { BSON_APPEND_UTF8 (cmd, "foo", "collection"); return future_collection_write_command_with_opts ( ctx->collection, tmp_bson ("{'foo': 'collection'}"), ctx->opts, NULL, &ctx->error); }
static future_t * count_documents (func_ctx_t *ctx, bson_t *cmd) { BSON_APPEND_UTF8 (cmd, "aggregate", "collection"); return future_collection_count_documents (ctx->collection, tmp_bson ("{}"), ctx->opts, ctx->prefs, NULL, &ctx->error); }
static void _test_topology_events (bool pooled) { mongoc_client_t *client; mongoc_client_pool_t *pool = NULL; context_t context; bool r; bson_error_t error; bson_iter_t events_iter; bson_iter_t event_iter; uint32_t i; context_init (&context); if (pooled) { pool = test_framework_client_pool_new (); pool_set_topology_event_callbacks (pool, &context); client = mongoc_client_pool_pop (pool); } else { client = test_framework_client_new (); client_set_topology_event_callbacks (client, &context); } r = mongoc_client_command_simple (client, "admin", tmp_bson ("{'ping': 1}"), NULL, NULL, &error); ASSERT_OR_PRINT (r, error); if (pooled) { mongoc_client_pool_push (pool, client); mongoc_client_pool_destroy (pool); } else { mongoc_client_destroy (client); } /* first event is topology opening */ bson_iter_init (&events_iter, &context.events); bson_iter_next (&events_iter); ASSERT (bson_iter_recurse (&events_iter, &event_iter)); ASSERT (bson_iter_find (&event_iter, "topology_opening_event")); /* last event is topology closed */ for (i = 1; i < context.n_events; i++) { ASSERT (bson_iter_next (&events_iter)); } ASSERT (bson_iter_recurse (&events_iter, &event_iter)); ASSERT (bson_iter_find (&event_iter, "topology_closed_event")); /* no more events */ ASSERT (!bson_iter_next (&events_iter)); context_destroy (&context); }
static future_t * client_read_cmd (func_ctx_t *ctx, bson_t *cmd) { BSON_APPEND_INT32 (cmd, "foo", 1); return future_client_read_command_with_opts (ctx->client, "db", tmp_bson ("{'foo': 1}"), ctx->prefs, ctx->opts, NULL, &ctx->error); }
static future_t * aggregate_raw_pipeline (func_ctx_t *ctx, bson_t *cmd) { BSON_APPEND_UTF8 (cmd, "aggregate", "collection"); ctx->cursor = mongoc_collection_aggregate (ctx->collection, MONGOC_QUERY_NONE, tmp_bson ("[{'$out': 'foo'}]"), ctx->opts, ctx->prefs); /* use ctx->data as the bson_t** out-param to mongoc_cursor_next () */ return future_cursor_next (ctx->cursor, (const bson_t **) &ctx->data); }
static void _test_op_query (const mongoc_uri_t *uri, mock_server_t *server, const char *query_in, mongoc_read_prefs_t *read_prefs, mongoc_query_flags_t expected_query_flags, const char *expected_query) { mongoc_client_t *client; mongoc_collection_t *collection; mongoc_cursor_t *cursor; const bson_t *doc; bson_t b = BSON_INITIALIZER; future_t *future; request_t *request; client = mongoc_client_new_from_uri (uri); collection = mongoc_client_get_collection (client, "test", "test"); cursor = mongoc_collection_find (collection, MONGOC_QUERY_NONE, 0, 1, 0, tmp_bson (query_in), NULL, read_prefs); future = future_cursor_next (cursor, &doc); request = mock_server_receives_query ( server, "test.test", expected_query_flags, 0, 1, expected_query, NULL); mock_server_replies (request, MONGOC_REPLY_NONE, /* flags */ 0, /* cursorId */ 0, /* startingFrom */ 1, /* numberReturned */ "{'a': 1}"); /* mongoc_cursor_next returned true */ BSON_ASSERT (future_get_bool (future)); request_destroy (request); future_destroy (future); mongoc_cursor_destroy (cursor); mongoc_collection_destroy (collection); mongoc_client_destroy (client); bson_destroy (&b); }
static future_t * insert_many (func_ctx_t *ctx, bson_t *cmd) { BSON_APPEND_UTF8 (cmd, "insert", "collection"); BSON_ASSERT (!ctx->prefs); /* the "array" of input documents must be a valid pointer, stage it here */ ctx->data = tmp_bson ("{}"); return future_collection_insert_many (ctx->collection, (const bson_t **) &ctx->data, 1, ctx->opts, NULL, &ctx->error); }
/* add BSON we expect to be included in a command due to an inherited option. * e.g., when "count" inherits readConcern from the DB, it should include * readConcern: {level: 'database'} in the command body. */ void add_expected_opt (opt_source_t opt_source, opt_type_t opt_type, bson_t *cmd) { const char *source_name; bson_t *opt; if (opt_source & OPT_SOURCE_FUNC) { source_name = "function"; } else if (opt_source & OPT_SOURCE_COLL) { source_name = "collection"; } else if (opt_source & OPT_SOURCE_DB) { source_name = "database"; } else if (opt_source & OPT_SOURCE_CLIENT) { source_name = "client"; } else { MONGOC_ERROR ("opt_json called with OPT_SOURCE_NONE"); abort (); } switch (opt_type) { case OPT_READ_CONCERN: opt = tmp_bson ("{'readConcern': {'level': '%s'}}", source_name); break; case OPT_WRITE_CONCERN: opt = tmp_bson ("{'writeConcern': {'w': '%s'}}", source_name); break; case OPT_READ_PREFS: opt = tmp_bson ( "{'$readPreference': {'mode': 'secondary', 'tags': [{'%s': 'yes'}]}}", source_name); break; default: abort (); } bson_concat (cmd, opt); }
static void test_write_concern_append (void) { mongoc_write_concern_t *wc; bson_t *cmd; cmd = tmp_bson ("{'foo': 1}"); capture_logs (true); /* cannot append invalid writeConcern */ wc = NULL; assert (!mongoc_write_concern_append (wc, cmd)); /* append valid writeConcern */ wc = mongoc_write_concern_new (); mongoc_write_concern_set_w (wc, 1); assert (mongoc_write_concern_append (wc, cmd)); ASSERT (match_bson (cmd, tmp_bson ("{'foo': 1, 'writeConcern': {'w': 1}}"), true)); mongoc_write_concern_destroy (wc); }
static future_t * find_and_modify (func_ctx_t *ctx, bson_t *cmd) { mongoc_find_and_modify_opts_t *fam; BSON_APPEND_UTF8 (cmd, "findAndModify", "collection"); fam = mongoc_find_and_modify_opts_new (); bson_concat (&fam->extra, ctx->opts); /* destroy the mongoc_find_and_modify_opts_t later */ ctx->data = fam; ctx->destructor = find_and_modify_cleanup; return future_collection_find_and_modify_with_opts ( ctx->collection, tmp_bson ("{}"), fam, NULL, &ctx->error); }
static void _test_command_simple (const mongoc_uri_t *uri, mock_server_t *server, const char *command, mongoc_read_prefs_t *read_prefs, mongoc_query_flags_t expected_query_flags, const char *expected_query) { mongoc_client_t *client; mongoc_collection_t *collection; bson_t b = BSON_INITIALIZER; future_t *future; request_t *request; client = mongoc_client_new_from_uri (uri); collection = mongoc_client_get_collection (client, "test", "test"); mongoc_collection_set_read_prefs (collection, read_prefs); future = future_client_command_simple (client, "test", tmp_bson (command), read_prefs, NULL, NULL); request = mock_server_receives_command ( server, "test", expected_query_flags, expected_query); mock_server_replies (request, MONGOC_REPLY_NONE, /* flags */ 0, /* cursorId */ 0, /* startingFrom */ 1, /* numberReturned */ "{'ok': 1}"); /* mongoc_cursor_next returned true */ assert (future_get_bool (future)); request_destroy (request); future_destroy (future); mongoc_collection_destroy (collection); mongoc_client_destroy (client); bson_destroy (&b); }
static void test_topology_events_disabled (void) { mongoc_client_t *client; context_t context; bool r; bson_error_t error; bson_iter_t events_iter; bson_iter_t event_iter; uint32_t i; context_init (&context); client = test_framework_client_new (); client_set_topology_event_callbacks (client, &context); r = mongoc_client_command_simple ( client, "admin", tmp_bson ("{'ping': 1}"), NULL, NULL, &error); ASSERT_OR_PRINT (r, error); /* disable callbacks before destroying so we don't see a topology closed * event */ mongoc_client_set_apm_callbacks (client, NULL, NULL); mongoc_client_destroy (client); /* first event is topology opening */ bson_iter_init (&events_iter, &context.events); bson_iter_next (&events_iter); ASSERT (bson_iter_recurse (&events_iter, &event_iter)); ASSERT (bson_iter_find (&event_iter, "topology_opening_event")); /* move forward to the last event */ for (i = 1; i < context.n_events; i++) { ASSERT (bson_iter_next (&events_iter)); } /* verify we didn't receive a topology closed event */ ASSERT (bson_iter_recurse (&events_iter, &event_iter)); ASSERT (!bson_iter_find (&event_iter, "topology_closed_event")); /* no more events */ ASSERT (!bson_iter_next (&events_iter)); context_destroy (&context); }
static void test_read_concern_append (void) { mongoc_read_concern_t *rc; bson_t *cmd; cmd = tmp_bson ("{'foo': 1}"); /* append default readConcern */ rc = mongoc_read_concern_new (); ASSERT (mongoc_read_concern_is_default (rc)); ASSERT_MATCH (cmd, "{'foo': 1, 'readConcern': {'$exists': false}}"); /* append readConcern with level */ mongoc_read_concern_set_level (rc, MONGOC_READ_CONCERN_LEVEL_LOCAL); ASSERT (mongoc_read_concern_append (rc, cmd)); ASSERT_MATCH (cmd, "{'foo': 1, 'readConcern': {'level': 'local'}}"); mongoc_read_concern_destroy (rc); }
static void test_command_name (void) { bson_t *commands[] = { tmp_bson ("{'foo': 1}"), tmp_bson ("{'query': {'foo': 1}}"), tmp_bson ("{'query': {'foo': 1}, '$readPreference': 1}"), tmp_bson ("{'$query': {'foo': 1}}"), tmp_bson ("{'$query': {'foo': 1}, '$readPreference': 1}"), tmp_bson ("{'$readPreference': 1, '$query': {'foo': 1}}"), }; size_t i; for (i = 0; i < sizeof (commands) / sizeof (bson_t *); i++) { ASSERT_CMPSTR ("foo", _mongoc_get_command_name (commands[i])); } }
static void _test_heartbeat_events (bool pooled, bool succeeded) { context_t context; mock_server_t *server; mongoc_uri_t *uri; mongoc_client_t *client; mongoc_client_pool_t *pool = NULL; future_t *future; request_t *request; char *expected_json; bson_error_t error; context_init (&context); /* auto-respond to "foo" command */ server = mock_server_new (); mock_server_run (server); mock_server_autoresponds (server, responder, NULL, NULL); uri = mongoc_uri_copy (mock_server_get_uri (server)); mongoc_uri_set_option_as_int32 (uri, "serverSelectionTimeoutMS", 400); if (pooled) { pool = mongoc_client_pool_new (uri); pool_set_heartbeat_event_callbacks (pool, &context); client = mongoc_client_pool_pop (pool); } else { client = mongoc_client_new_from_uri (uri); client_set_heartbeat_event_callbacks (client, &context); } /* trigger "ismaster" handshake */ future = future_client_command_simple (client, "admin", tmp_bson ("{'foo': 1}"), NULL, NULL, &error); /* topology scanner calls ismaster once */ request = mock_server_receives_ismaster (server); if (succeeded) { mock_server_replies_ok_and_destroys (request); } else { mock_server_hangs_up (request); request_destroy (request); } /* pooled client opens new socket, handshakes it by calling ismaster again */ if (pooled && succeeded) { request = mock_server_receives_ismaster (server); mock_server_replies_ok_and_destroys (request); } if (succeeded) { /* "foo" command succeeds */ ASSERT_OR_PRINT (future_get_bool (future), error); } else { ASSERT (!future_get_bool (future)); } if (pooled) { mongoc_client_pool_push (pool, client); mongoc_client_pool_destroy (pool); } else { mongoc_client_destroy (client); } /* even if pooled, only topology scanner sends events, so we get one pair */ if (succeeded) { expected_json = bson_strdup_printf ( "{'0': {'heartbeat_started_event': {'host': '%s'}}," " '1': {'heartbeat_succeeded_event': {'host': '%s'}}}", mock_server_get_host_and_port (server), mock_server_get_host_and_port (server)); } else { expected_json = bson_strdup_printf ( "{'0': {'heartbeat_started_event': {'host': '%s'}}," " '1': {'heartbeat_failed_event': {'host': '%s'}}}", mock_server_get_host_and_port (server), mock_server_get_host_and_port (server)); } check_json_apm_events (&context.events, tmp_bson (expected_json)); future_destroy (future); bson_free (expected_json); mongoc_uri_destroy (uri); mock_server_destroy (server); context_destroy (&context); }
static void test_find_and_modify_collation (int wire) { mock_server_t *server; mongoc_client_t *client; mongoc_collection_t *collection; bson_error_t error; mongoc_find_and_modify_opts_t *opts; future_t *future; request_t *request; bson_t *collation; server = mock_server_with_autoismaster (wire); mock_server_run (server); client = mongoc_client_new_from_uri (mock_server_get_uri (server)); collection = mongoc_client_get_collection (client, "db", "collection"); collation = BCON_NEW ("collation", "{", "locale", BCON_UTF8 ("en_US"), "caseFirst", BCON_UTF8 ("lower"), "}"); opts = mongoc_find_and_modify_opts_new (); mongoc_find_and_modify_opts_append (opts, collation); if (wire >= WIRE_VERSION_COLLATION) { future = future_collection_find_and_modify_with_opts ( collection, tmp_bson ("{}"), opts, NULL, &error); request = mock_server_receives_command ( server, "db", MONGOC_QUERY_NONE, "{'findAndModify': 'collection'," " 'collation': { 'locale': 'en_US', 'caseFirst': 'lower'}" "}"); mock_server_replies_ok_and_destroys (request); ASSERT_OR_PRINT (future_get_bool (future), error); future_destroy (future); } else { bool ok = mongoc_collection_find_and_modify_with_opts ( collection, tmp_bson ("{}"), opts, NULL, &error); ASSERT_ERROR_CONTAINS (error, MONGOC_ERROR_COMMAND, MONGOC_ERROR_PROTOCOL_BAD_WIRE_VERSION, "The selected server does not support collation"); ASSERT (!ok); } bson_destroy (collation); mongoc_find_and_modify_opts_destroy (opts); mongoc_collection_destroy (collection); mongoc_client_destroy (client); mock_server_destroy (server); }