/* 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 bool responder (request_t *request, void *data) { if (!strcmp (request->command_name, "foo")) { mock_server_replies_simple (request, "{'ok': 1}"); request_destroy (request); return true; } return false; }
void mock_server_replies_to_find (request_t *request, mongoc_query_flags_t flags, int64_t cursor_id, int32_t number_returned, const char *ns, const char *reply_json, bool is_command) { char *find_reply; char db[MONGOC_NAMESPACE_MAX]; _mongoc_get_db_name (ns, db); /* minimal validation, we're not testing query / find cmd here */ if (request->is_command && !is_command) { MONGOC_ERROR ("expected query, got command"); abort (); } if (!request->is_command && is_command) { MONGOC_ERROR ("expected command, got query"); abort (); } if (!request_matches_flags (request, flags)) { abort (); } if (is_command) { find_reply = bson_strdup_printf ( "{'ok': 1," " 'cursor': {" " 'id': {'$numberLong': '%" PRId64 "'}," " 'ns': '%s'," " 'firstBatch': [%s]}}", cursor_id, ns, reply_json); mock_server_replies_simple (request, find_reply); bson_free (find_reply); } else { mock_server_replies (request, MONGOC_REPLY_NONE, cursor_id, 0, number_returned, reply_json); } }
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_collection_op_query_or_find_command ( test_collection_find_with_opts_t *test_data, check_request_fn_t check_request_fn, const char *reply_json, int32_t max_wire_version) { mock_server_t *server; mongoc_client_t *client; mongoc_collection_t *collection; mongoc_cursor_t *cursor; bson_error_t error; future_t *future; request_t *request; const bson_t *doc; server = mock_server_with_autoismaster (max_wire_version); 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, test_data->filter_bson, test_data->read_prefs, test_data->opts_bson); ASSERT_OR_PRINT (!mongoc_cursor_error (cursor, &error), error); future = future_cursor_next (cursor, &doc); request = check_request_fn (server, test_data); ASSERT (request); mock_server_replies_simple (request, reply_json); ASSERT (future_get_bool (future)); request_destroy (request); future_destroy (future); mongoc_cursor_destroy (cursor); mongoc_collection_destroy (collection); mongoc_client_destroy (client); mock_server_destroy (server); }
static void test_find_and_modify_bypass (bool bypass) { mongoc_collection_t *collection; mongoc_client_t *client; mock_server_t *server; request_t *request; future_t *future; bson_error_t error; bson_t *update; bson_t doc = BSON_INITIALIZER; bson_t reply; mongoc_find_and_modify_opts_t *opts; server = mock_server_new (); mock_server_run (server); client = mongoc_client_new_from_uri (mock_server_get_uri (server)); ASSERT (client); collection = mongoc_client_get_collection (client, "test", "test_find_and_modify"); auto_ismaster (server, WIRE_VERSION_FAM_WRITE_CONCERN, /* max_wire_version */ 48000000, /* max_message_size */ 16777216, /* max_bson_size */ 1000); /* max_write_batch_size */ BSON_APPEND_INT32 (&doc, "superduper", 77889); update = BCON_NEW ("$set", "{", "superduper", BCON_INT32 (1234), "}"); opts = mongoc_find_and_modify_opts_new (); mongoc_find_and_modify_opts_set_bypass_document_validation (opts, bypass); mongoc_find_and_modify_opts_set_update (opts, update); mongoc_find_and_modify_opts_set_flags (opts, MONGOC_FIND_AND_MODIFY_RETURN_NEW); future = future_collection_find_and_modify_with_opts ( collection, &doc, opts, &reply, &error); if (bypass) { request = mock_server_receives_command ( server, "test", MONGOC_QUERY_NONE, "{ 'findAndModify' : 'test_find_and_modify', " "'query' : { 'superduper' : 77889 }," "'update' : { '$set' : { 'superduper' : 1234 } }," "'new' : true," "'bypassDocumentValidation' : true }"); } else { request = mock_server_receives_command ( server, "test", MONGOC_QUERY_NONE, "{ 'findAndModify' : 'test_find_and_modify', " "'query' : { 'superduper' : 77889 }," "'update' : { '$set' : { 'superduper' : 1234 } }," "'new' : true," "'bypassDocumentValidation' : false }"); } mock_server_replies_simple (request, "{ 'value' : null, 'ok' : 1 }"); ASSERT_OR_PRINT (future_get_bool (future), error); future_destroy (future); mongoc_find_and_modify_opts_destroy (opts); bson_destroy (&reply); bson_destroy (update); mongoc_collection_destroy (collection); mongoc_client_destroy (client); mock_server_destroy (server); bson_destroy (&doc); }
static void test_getmore_cmd_await (void) { bson_t *opts; mock_server_t *server; mongoc_client_t *client; mongoc_collection_t *collection; mongoc_cursor_t *cursor; future_t *future; request_t *request; const bson_t *doc; opts = tmp_bson ("{'tailable': true," " 'awaitData': true," " 'maxAwaitTimeMS': {'$numberLong': '9999'}}"); /* * "find" command */ 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, opts); future = future_cursor_next (cursor, &doc); request = mock_server_receives_command ( server, "db", MONGOC_QUERY_SLAVE_OK, "{'find': 'collection', 'filter': {}}"); ASSERT (request); mock_server_replies_simple (request, "{'ok': 1," " 'cursor': {" " 'id': {'$numberLong': '123'}," " 'ns': 'db.collection'," " 'firstBatch': [{}]}}"); ASSERT (future_get_bool (future)); request_destroy (request); future_destroy (future); /* * "getMore" command */ future = future_cursor_next (cursor, &doc); request = mock_server_receives_command ( server, "db", MONGOC_QUERY_SLAVE_OK, "{'getMore': {'$numberLong': '123'}," " 'collection': 'collection'," " 'maxTimeMS': {'$numberLong': '9999'}}}"); ASSERT (request); mock_server_replies_simple (request, "{'ok': 1," " 'cursor': {" " 'id': {'$numberLong': '0'}," " 'ns': 'db.collection'," " 'nextBatch': [{}]}}"); ASSERT (future_get_bool (future)); request_destroy (request); future_destroy (future); mongoc_cursor_destroy (cursor); mongoc_collection_destroy (collection); mongoc_client_destroy (client); mock_server_destroy (server); }
static void test_func_inherits_opts (void *ctx) { opt_inheritance_test_t *test = (opt_inheritance_test_t *) ctx; /* for example, test mongoc_collection_find_with_opts with no read pref, * with a read pref set on the collection (OPT_SOURCE_COLL), with an explicit * read pref (OPT_SOURCE_FUNC), or with one read pref on the collection and * a different one passed explicitly */ opt_source_t source_matrix[] = {OPT_SOURCE_NONE, test->opt_source, OPT_SOURCE_FUNC, test->opt_source | OPT_SOURCE_FUNC}; size_t i; func_ctx_t func_ctx; mock_rs_t *rs; mongoc_client_t *client; mongoc_database_t *db; mongoc_collection_t *collection; bson_t opts = BSON_INITIALIZER; mongoc_read_prefs_t *func_prefs = NULL; future_t *future; request_t *request; bson_t cmd = BSON_INITIALIZER; bool expect_secondary; bson_error_t error; /* one primary, one secondary */ rs = mock_rs_with_autoismaster (WIRE_VERSION_OP_MSG, true, 1, 0); /* we use read pref tags like "collection": "yes" to verify where the * pref was inherited from; ensure all secondaries match all tags */ mock_rs_tag_secondary (rs, 0, tmp_bson ("{'client': 'yes'," " 'database': 'yes'," " 'collection': 'yes'," " 'function': 'yes'}")); mock_rs_run (rs); /* iterate over all combinations of options sources: e.g., an option set on * collection and not function, on function not collection, both, neither */ for (i = 0; i < sizeof (source_matrix) / (sizeof (opt_source_t)); i++) { expect_secondary = false; func_prefs = NULL; bson_reinit (&cmd); bson_reinit (&opts); client = mongoc_client_new_from_uri (mock_rs_get_uri (rs)); if (source_matrix[i] & OPT_SOURCE_CLIENT) { set_client_opt (client, test->opt_type); } db = mongoc_client_get_database (client, "database"); if (source_matrix[i] & OPT_SOURCE_DB) { set_database_opt (db, test->opt_type); } collection = mongoc_database_get_collection (db, "collection"); if (source_matrix[i] & OPT_SOURCE_COLL) { set_collection_opt (collection, test->opt_type); } if (source_matrix[i] & OPT_SOURCE_FUNC) { set_func_opt (&opts, &func_prefs, test->opt_type); } func_ctx_init ( &func_ctx, test, client, db, collection, func_prefs, &opts); /* func_with_opts creates expected "cmd", like {insert: 'collection'} */ future = test->func_with_opts (&func_ctx, &cmd); if (source_matrix[i] != OPT_SOURCE_NONE) { add_expected_opt (source_matrix[i], test->opt_type, &cmd); if (test->opt_type == OPT_READ_PREFS) { expect_secondary = true; } } /* write commands send two OP_MSG sections */ if (test->n_sections == 2) { request = mock_rs_receives_msg (rs, 0, &cmd, tmp_bson ("{}")); } else { request = mock_rs_receives_msg (rs, 0, &cmd); } if (expect_secondary) { BSON_ASSERT (mock_rs_request_is_to_secondary (rs, request)); } else { BSON_ASSERT (mock_rs_request_is_to_primary (rs, request)); } if (func_ctx.cursor) { mock_server_replies_simple (request, "{'ok': 1," " 'cursor': {" " 'id': 0," " 'ns': 'db.collection'," " 'firstBatch': []}}"); BSON_ASSERT (!future_get_bool (future)); future_destroy (future); ASSERT_OR_PRINT (!mongoc_cursor_error (func_ctx.cursor, &error), error); } else { mock_server_replies_simple (request, "{'ok': 1}"); cleanup_future (future); } request_destroy (request); mongoc_read_prefs_destroy (func_prefs); func_ctx_cleanup (&func_ctx); mongoc_collection_destroy (collection); mongoc_database_destroy (db); mongoc_client_destroy (client); } bson_destroy (&cmd); bson_destroy (&opts); mock_rs_destroy (rs); }
static void test_mongoc_metadata_append_success (void) { mock_server_t *server; mongoc_uri_t *uri; mongoc_client_t *client; mongoc_client_pool_t *pool; request_t *request; const bson_t *request_doc; bson_iter_t iter; bson_iter_t md_iter; bson_iter_t inner_iter; const char *val; const char *driver_name = "php driver"; const char *driver_version = "version abc"; const char *platform = "./configure -nottoomanyflags"; char big_string [METADATA_MAX_SIZE]; memset (big_string, 'a', METADATA_MAX_SIZE - 1); big_string [METADATA_MAX_SIZE - 1] = '\0'; _reset_metadata (); /* Make sure setting the metadata works */ ASSERT (mongoc_metadata_append (driver_name, driver_version, platform)); server = mock_server_new (); mock_server_run (server); uri = mongoc_uri_copy (mock_server_get_uri (server)); mongoc_uri_set_option_as_int32 (uri, "heartbeatFrequencyMS", 500); pool = mongoc_client_pool_new (uri); /* Force topology scanner to start */ client = mongoc_client_pool_pop (pool); request = mock_server_receives_ismaster (server); ASSERT (request); request_doc = request_get_doc (request, 0); ASSERT (request_doc); ASSERT (bson_has_field (request_doc, "isMaster")); ASSERT (bson_has_field (request_doc, METADATA_FIELD)); ASSERT (bson_iter_init_find (&iter, request_doc, METADATA_FIELD)); ASSERT (bson_iter_recurse (&iter, &md_iter)); /* Make sure driver.name and driver.version and platform are all right */ ASSERT (bson_iter_find (&md_iter, "driver")); ASSERT (BSON_ITER_HOLDS_DOCUMENT (&md_iter)); ASSERT (bson_iter_recurse (&md_iter, &inner_iter)); ASSERT (bson_iter_find (&inner_iter, "name")); ASSERT (BSON_ITER_HOLDS_UTF8 (&inner_iter)); val = bson_iter_utf8 (&inner_iter, NULL); ASSERT (val); ASSERT (strstr (val, driver_name) != NULL); ASSERT (bson_iter_find (&inner_iter, "version")); ASSERT (BSON_ITER_HOLDS_UTF8 (&inner_iter)); val = bson_iter_utf8 (&inner_iter, NULL); ASSERT (val); ASSERT (strstr (val, driver_version)); /* Check os type not empty */ ASSERT (bson_iter_find (&md_iter, "os")); ASSERT (BSON_ITER_HOLDS_DOCUMENT (&md_iter)); ASSERT (bson_iter_recurse (&md_iter, &inner_iter)); ASSERT (bson_iter_find (&inner_iter, "type")); ASSERT (BSON_ITER_HOLDS_UTF8 (&inner_iter)); val = bson_iter_utf8 (&inner_iter, NULL); ASSERT (val); ASSERT (strlen (val) > 0); /* Not checking os_name, as the spec says it can be NULL. */ /* Check platform field ok */ ASSERT (bson_iter_find (&md_iter, "platform")); ASSERT (BSON_ITER_HOLDS_UTF8 (&md_iter)); val = bson_iter_utf8 (&md_iter, NULL); ASSERT (val); ASSERT (strstr (val, platform) != NULL); mock_server_replies_simple (request, "{'ok': 1, 'ismaster': true}"); request_destroy (request); /* Cleanup */ mongoc_client_pool_push (pool, client); mongoc_client_pool_destroy (pool); mongoc_uri_destroy (uri); mock_server_destroy (server); _reset_metadata (); }
/* Test the case where we can't prevent the metadata doc being too big * and so we just don't send it */ static void test_mongoc_metadata_cannot_send (void) { mock_server_t *server; mongoc_uri_t *uri; mongoc_client_t *client; mongoc_client_pool_t *pool; request_t *request; const char *const server_reply = "{'ok': 1, 'ismaster': true}"; const bson_t *request_doc; char big_string[METADATA_MAX_SIZE]; mongoc_metadata_t *md; _reset_metadata (); /* Mess with our global metadata struct so the metadata doc will be * way too big */ memset (big_string, 'a', METADATA_MAX_SIZE - 1); big_string[METADATA_MAX_SIZE - 1] = '\0'; md = _mongoc_metadata_get (); bson_free (md->os_name); md->os_name = bson_strdup (big_string); server = mock_server_new (); mock_server_run (server); uri = mongoc_uri_copy (mock_server_get_uri (server)); mongoc_uri_set_option_as_int32 (uri, "heartbeatFrequencyMS", 500); pool = mongoc_client_pool_new (uri); /* Pop a client to trigger the topology scanner */ client = mongoc_client_pool_pop (pool); request = mock_server_receives_ismaster (server); /* Make sure the isMaster request DOESN'T have a metadata field: */ ASSERT (request); request_doc = request_get_doc (request, 0); ASSERT (request_doc); ASSERT (bson_has_field (request_doc, "isMaster")); ASSERT (!bson_has_field (request_doc, METADATA_FIELD)); mock_server_replies_simple (request, server_reply); request_destroy (request); /* Cause failure on client side */ request = mock_server_receives_ismaster (server); ASSERT (request); mock_server_hangs_up (request); request_destroy (request); /* Make sure the isMaster request still DOESN'T have a metadata field * on subsequent heartbeats. */ request = mock_server_receives_ismaster (server); ASSERT (request); request_doc = request_get_doc (request, 0); ASSERT (request_doc); ASSERT (bson_has_field (request_doc, "isMaster")); ASSERT (!bson_has_field (request_doc, METADATA_FIELD)); mock_server_replies_simple (request, server_reply); request_destroy (request); /* cleanup */ mongoc_client_pool_push (pool, client); mongoc_client_pool_destroy (pool); mongoc_uri_destroy (uri); mock_server_destroy (server); /* Reset again so the next tests don't have a metadata doc which * is too big */ _reset_metadata (); }
/* * Append to the platform field a huge string * Make sure that it gets truncated */ static void test_mongoc_metadata_too_big (void) { mongoc_client_t *client; mock_server_t *server; mongoc_uri_t *uri; future_t *future; request_t *request; const bson_t *ismaster_doc; bson_iter_t iter; enum { BUFFER_SIZE = METADATA_MAX_SIZE }; char big_string[BUFFER_SIZE]; uint32_t len; const uint8_t *dummy; server = mock_server_new (); mock_server_run (server); _reset_metadata (); memset (big_string, 'a', BUFFER_SIZE - 1); big_string[BUFFER_SIZE - 1] = '\0'; ASSERT (mongoc_metadata_append (NULL, NULL, big_string)); uri = mongoc_uri_copy (mock_server_get_uri (server)); client = mongoc_client_new_from_uri (uri); ASSERT (mongoc_client_set_appname (client, "my app")); /* Send a ping, mock server deals with it */ future = future_client_command_simple (client, "admin", tmp_bson ("{'ping': 1}"), NULL, NULL, NULL); request = mock_server_receives_ismaster (server); /* Make sure the isMaster request has a metadata field, and it's not huge */ ASSERT (request); ismaster_doc = request_get_doc (request, 0); ASSERT (ismaster_doc); ASSERT (bson_has_field (ismaster_doc, "isMaster")); ASSERT (bson_has_field (ismaster_doc, METADATA_FIELD)); /* isMaster with metadata isn't too big */ bson_iter_init_find (&iter, ismaster_doc, METADATA_FIELD); ASSERT (BSON_ITER_HOLDS_DOCUMENT (&iter)); bson_iter_document (&iter, &len, &dummy); /* Should have truncated the platform field so it fits exactly */ ASSERT (len == METADATA_MAX_SIZE); mock_server_replies_simple (request, "{'ok': 1}"); request_destroy (request); request = mock_server_receives_command (server, "admin", MONGOC_QUERY_SLAVE_OK, "{'ping': 1}"); mock_server_replies_simple (request, "{'ok': 1}"); ASSERT (future_get_bool (future)); future_destroy (future); request_destroy (request); mongoc_client_destroy (client); mongoc_uri_destroy (uri); mock_server_destroy (server); /* So later tests don't have "aaaaa..." as the md platform string */ _reset_metadata (); }