### Install MockupDB with pip Source: https://mockupdb.readthedocs.io/_sources/installation.rst.txt Run this command to install the MockupDB package using pip. ```bash $ python -m pip install mockupdb ``` -------------------------------- ### Set up local development environment Source: https://mockupdb.readthedocs.io/contributing.html Create a virtualenv, navigate to the project directory, and install the local copy in development mode. ```bash $ mkvirtualenv mongo-mockup-db $ cd mongo-mockup-db/ $ python setup.py develop ``` -------------------------------- ### mockupdb.go() - Launch an operation on a thread and get a future result Source: https://mockupdb.readthedocs.io/reference.html Demonstrates the `go` function, which runs a callable on a background thread and returns a future. The example shows a function that prints before and after sleeping, and how to retrieve the return value via `future()`. ```python >>> from time import sleep >>> def print_sleep_print(duration): ... sleep(duration) ... print('hello from background thread') ... sleep(duration) ... print('goodbye from background thread') ... return 'return value' ... >>> future = go(print_sleep_print, 0.1) >>> sleep(0.15) hello from background thread >>> print('main thread') main thread >>> result = future() goodbye from background thread >>> result 'return value' ``` -------------------------------- ### Autorespond to ismaster with maxWireVersion Source: https://mockupdb.readthedocs.io/reference.html Shows how to start a MockupDB server, connect with pymongo, and use autoresponds to reply to an 'ismaster' command with a custom maxWireVersion. The example asserts the response equals {'ok': 1, 'maxWireVersion': 6}. ```python >>> s = MockupDB() >>> port = s.run() >>> >>> from pymongo import MongoClient >>> client = MongoClient(s.uri) >>> responder = s.autoresponds('ismaster', maxWireVersion=6) >>> client.admin.command('ismaster') == {'ok': 1, 'maxWireVersion': 6} True ``` -------------------------------- ### Start MockupDB and connect with PyMongo Source: https://mockupdb.readthedocs.io/_sources/tutorial.rst.txt Run a MockupDB server and connect to it with PyMongo's MongoClient. Set a longer connectTimeoutMS to avoid the default 10-second timeout. ```python >>> from mockupdb import * >>> server = MockupDB() >>> port = server.run() # Returns the TCP port number it listens on. >>> from pymongo import MongoClient >>> client = MongoClient(server.uri, connectTimeoutMS=999999) ``` -------------------------------- ### Verify client discovers primary with pending operation Source: https://mockupdb.readthedocs.io/tutorial.html When a pending client operation exists, the client discovers the new primary. The example uses a context manager to start a command, waits until the primary address matches, and then pops the buildinfo reply, printing True twice. ```python >>> with going(client.db.command, 'buildinfo'): ... wait_until(lambda: primary.address == client.primary, ... 'discovery primary') ... primary.pop('buildinfo').ok() True True ``` -------------------------------- ### OpReply.update example Source: https://mockupdb.readthedocs.io/reference.html Shows how to update an OpReply document using the update method, which behaves like dict.update(). The example demonstrates setting a field with a keyword argument and then updating with a dictionary. ```python >>> reply = OpReply({'ismaster': True}) >>> reply.update(maxWireVersion=3) >>> reply.doc['maxWireVersion'] 3 >>> reply.update({'maxWriteBatchSize': 10, 'msg': 'isdbgrid'}) ``` -------------------------------- ### MockupDB server loop with query matching and replies Source: https://mockupdb.readthedocs.io/_sources/tutorial.rst.txt Starts a background loop that matches incoming requests most restrictively first. It replies to 'find' queries with cursor batches, handles a 'break' command, and returns an error for unrecognized requests. The loop runs while server.running and prints a traceback on exception. ```python >>> import traceback >>> def loop(): ... try: ... while server.running: ... # Match queries most restrictive first. ... if server.got(OpMsg('find', 'coll', filter={'a': {'$gt': 1}})): ... server.reply(cursor={'id': 0, 'firstBatch':[{'a': 2}]}) ... elif server.got('break'): ... server.ok() ... break ... elif server.got(OpMsg('find', 'coll')): ... server.reply( ... cursor={'id': 0, 'firstBatch':[{'a': 1}, {'a': 2}]}) ... else: ... server.command_err(errmsg='unrecognized request') ... except: ... traceback.print_exc() ... raise ... ``` -------------------------------- ### Connect to replica set and wait for secondary Source: https://mockupdb.readthedocs.io/tutorial.html Connect to the mock replica set with the replicaSet option and use wait_until to wait until the server appears in the client's secondaries list. The example prints True after discovery. ```python >>> client = MongoClient(server.uri, replicaSet='rs') >>> from mockupdb import wait_until >>> wait_until(lambda: server.address in client.secondaries, ... 'discover secondary') True ``` -------------------------------- ### OpMsg.command_name example Source: https://mockupdb.readthedocs.io/reference.html Shows how to get the command name from an OpMsg instance. The command_name property returns the command name or None, as demonstrated with two different constructor calls. ```python >>> OpMsg({'count': 'collection'}).command_name 'count' >>> OpMsg('aggregate', 'collection', cursor=absent).command_name 'aggregate' ``` -------------------------------- ### Use the going context manager for a concise insert Source: https://mockupdb.readthedocs.io/tutorial.html Use the `going` context manager for a more concise alternative to manually managing the `go` future when you don't need the return value. It automatically starts the operation and blocks until the server replies. ```python >>> with going(collection.insert_one, {'_id': 1}): ... server.receives().ok() True ``` -------------------------------- ### Assert writeConcern absent by default Source: https://mockupdb.readthedocs.io/tutorial.html Verify that the default write concern does not include a writeConcern argument. Creating a client without w=1 and then inserting a document results in server.receives() containing no 'writeConcern' key. Uses the same MockupDB server setup as the previous w=1 example. ```python >>> # Accept the default write concern. >>> client = MongoClient(server.uri) >>> collection = client.db.coll >>> future = go(collection.insert_one, {'_id': 5}) >>> assert 'writeConcern' not in server.receives() >>> client.close() ``` -------------------------------- ### mockupdb.going() - Launch a thread and wait for its result before exiting the code block Source: https://mockupdb.readthedocs.io/reference.html Shows the `going` context manager, which launches a thread and waits for its result before exiting the block. The first example retrieves the result, the second discards it, and the third shows that if an exception is raised inside the block, the result is lost and the exception propagates. ```python >>> with going(lambda: 'return value') as future: ... pass >>> future() # Won't block, the future is ready by now. 'return value' ``` ```python >>> with going(lambda: "don't care"): ... pass ``` ```python >>> with going(lambda: 'return value') as future: ... assert 1 == 0 Traceback (most recent call last): ... AssertionError ``` -------------------------------- ### Match opcode-specific properties with Matcher Source: https://mockupdb.readthedocs.io/_sources/tutorial.rst.txt Use Matcher with an opcode and property values to match requests. The example shows that OpGetMore matches only when num_to_return equals the specified value, and OpQuery matches only when the namespace matches exactly. ```python >>> m = Matcher(OpGetMore, num_to_return=3) >>> m.matches(OpGetMore()) False >>> m.matches(OpGetMore(num_to_return=2)) False >>> m.matches(OpGetMore(num_to_return=3)) True >>> m = Matcher(OpQuery(namespace='db.collection')) >>> m.matches(OpQuery) False >>> m.matches(OpQuery(namespace='db.collection')) True ``` -------------------------------- ### MockupDB.got() - Check if a request matches the given message spec Source: https://mockupdb.readthedocs.io/reference.html Shows how to use `s.got()` to check whether a request matching a message spec has been received. The example demonstrates checking for no request, a command 'foo', an OpMsg with namespace 'db', and an OpMsg with a key-value pair. It also shows using `s.ok()` and `s.stop()`. ```python >>> s = MockupDB(auto_ismaster=True) >>> port = s.run() >>> s.got(timeout=0) # No request enqueued. False >>> from pymongo import MongoClient >>> client = MongoClient(s.uri) >>> future = go(client.db.command, 'foo') >>> s.got('foo') True >>> s.got(OpMsg('foo', namespace='db')) True >>> s.got(OpMsg('foo', key='value')) False >>> s.ok() >>> future() == {'ok': 1} True >>> s.stop() ``` -------------------------------- ### Start a background acknowledged insert with go() Source: https://mockupdb.readthedocs.io/tutorial.html Defer an acknowledged write operation to a background thread using the `go` function. Pass the method and arguments as with `functools.partial`; `go` returns a future that later provides the operation's result. ```python >>> collection = client.db.coll >>> from mockupdb import go >>> # Default write concern is acknowledged. >>> future = go(collection.insert_one, {'_id': 1}) ``` -------------------------------- ### Handler that logs all requests Source: https://mockupdb.readthedocs.io/reference.html Shows a handler that receives all requests, logs non-ismaster requests, and returns None to allow other handlers to run. The example prints the request and then the command returns the previous response. ```python >>> def logger(request): ... if not request.matches('ismaster'): ... print('logging: %r' % request) >>> responder = s.autoresponds(logger) >>> client.db.command('baz') == {'ok': 1, 'a': 2} logging: OpMsg({"baz": 1, "$db": "db", "$readPreference": {"mode": "primaryPreferred"}}, namespace="db") True ``` -------------------------------- ### Autorespond to a queued request Source: https://mockupdb.readthedocs.io/reference.html Demonstrates that if a request is at the head of the queue, autoresponds will pop and reply to it immediately, and future matching requests skip the queue. The example uses a future from go() to send a command. ```python >>> future = go(client.db.command, 'baz') >>> # bad >>> responder = s.autoresponds('baz', {'key': 'value'}) >>> future() == {'ok': 1, 'key': 'value'} True ``` -------------------------------- ### mockupdb.wait_until() - Wait for a predicate to be true Source: https://mockupdb.readthedocs.io/reference.html Shows an example of using `wait_until` to wait for a lambda that checks if the client's primary is ('a', 1). The function waits up to 10 seconds by default and raises AssertionError if the predicate never becomes true. ```python wait_until(lambda: client.primary == ('a', 1), 'connect to the primary') ``` -------------------------------- ### Assert writeConcern when w=1 with server.receives() Source: https://mockupdb.readthedocs.io/tutorial.html Verify that PyMongo sends a writeConcern argument when w=1 is specified. The example sets up a MockupDB server, runs it, creates a client with w=1, and uses server.receives() to match the expected writeConcern. The method returns True when the request matches. ```python >>> server = MockupDB() >>> responder = server.autoresponds('ismaster', maxWireVersion=6) >>> port = server.run() >>> >>> # Specify w=1. This is distinct from the default write concern. >>> client = MongoClient(server.uri, w=1) >>> collection = client.db.coll >>> future = go(collection.insert_one, {'_id': 4}) >>> server.receives({'writeConcern': {'w': 1}}).sends() True >>> client.close() ``` -------------------------------- ### run(*args, **kwargs) Source: https://mockupdb.readthedocs.io/reference.html Begins serving and returns the bound port. ```APIDOC ## run(*args, **kwargs) ### Description Begin serving. Returns the bound port, or 0 for domain socket. ### Method N/A (Python method) ### Parameters - **args** - positional arguments - passed to the underlying implementation - **kwargs** - keyword arguments - passed to the underlying implementation ### Returns - **int** - The bound port, or 0 for domain socket. ``` -------------------------------- ### mockupdb.go(fn, *args, **kwargs) Source: https://mockupdb.readthedocs.io/reference.html Launches an operation on a thread and returns a future handle. ```APIDOC ## mockupdb.go(fn, *args, **kwargs) ### Description Launch an operation on a thread and get a handle to its future result. ### Method N/A (Python function) ### Parameters - **fn** (callable) - Required - The function to run on a background thread. - **args** - positional arguments - passed to `fn` - **kwargs** - keyword arguments - passed to `fn` ### Returns - **future** - A callable that returns the result of `fn` when called. ### Example ```python >>> from time import sleep >>> def print_sleep_print(duration): ... sleep(duration) ... print('hello from background thread') ... sleep(duration) ... print('goodbye from background thread') ... return 'return value' ... >>> future = go(print_sleep_print, 0.1) >>> sleep(0.15) hello from background thread >>> print('main thread') main thread >>> result = future() goodbye from background thread >>> result 'return value' ``` ``` -------------------------------- ### mockupdb.going(*args, **kwds) Source: https://mockupdb.readthedocs.io/reference.html Context manager that launches a thread and waits for its result. ```APIDOC ## mockupdb.going(*args, **kwds) ### Description Launch a thread and wait for its result before exiting the code block. ### Method N/A (Python context manager) ### Parameters - **args** - positional arguments - passed to the thread function - **kwds** - keyword arguments - passed to the thread function ### Returns - **future** - A callable that returns the result of the thread function. ### Example ```python >>> with going(lambda: 'return value') as future: ... pass >>> future() # Won't block, the future is ready by now. 'return value' ``` ``` -------------------------------- ### Use going context manager for terse writes Source: https://mockupdb.readthedocs.io/_sources/tutorial.rst.txt Use mockupdb.going as a context manager to run the write in a background thread and respond with server.receives().ok() in the block. ```python >>> with going(collection.insert_one, {'_id': 1}): ... server.receives().ok() True ``` -------------------------------- ### Get the InsertOneResult from the future Source: https://mockupdb.readthedocs.io/tutorial.html Retrieve the future's value after the server replies. The result is a PyMongo InsertOneResult whose inserted_id is 1. ```python >>> write_result = future() >>> write_result # doctest: +ELLIPSIS >>> write_result.inserted_id 1 ``` -------------------------------- ### mockupdb.interactive_server(port=27017, verbose=True, all_ok=False, name='MockupDB', ssl=False, uds_path=None) Source: https://mockupdb.readthedocs.io/reference.html Creates a MockupDB instance suitable for the mongo shell. ```APIDOC ## mockupdb.interactive_server(port=27017, verbose=True, all_ok=False, name='MockupDB', ssl=False, uds_path=None) ### Description A `MockupDB` that the mongo shell can connect to. Call `run` on the returned server, and clean it up with `stop`. ### Method N/A (Python function) ### Parameters - **port** (int) - Optional - Port to listen on. Default is 27017. - **verbose** (bool) - Optional - Enable verbose logging. Default is True. - **all_ok** (bool) - Optional - If True, replies {ok: 1} to anything unmatched by a specific responder. Default is False. - **name** (str) - Optional - Name for the server. Default is 'MockupDB'. - **ssl** (bool) - Optional - Enable SSL. Default is False. - **uds_path** (str or None) - Optional - Path to a Unix domain socket. Default is None. ### Returns - **MockupDB** - A configured MockupDB instance. ``` -------------------------------- ### Loop to accept requests in any order with server.got() Source: https://mockupdb.readthedocs.io/tutorial.html Accept requests in any order by polling server.got(). Use server.running to keep the loop alivetox, match queries from mostrestrictive to least, and handle unrecognized requests with server.command_err(). Wrap the loop in a background future via go() to process actual client calls. ```python >>> import traceback >>> def loop(): ... try: ... while server.running: ... # Match queries most restrictive first. ... if server.got(OpMsg('find', 'coll', filter={'a': {'$gt': 1}})): ... server.reply(cursor={'id': 0, 'firstBatch':[{'a': 2}]}) ... elif server.got('break'): ... server.ok() ... break ... elif server.got(OpMsg('find', 'coll')): ... server.reply( ... cursor={'id': 0, 'firstBatch':[{'a': 1}, {'a': 2}]}) ... else: ... server.command_err(errmsg='unrecognized request') ... except: ... traceback.print_exc() ... raise ... >>> future = go(loop) >>> >>> list(client.db.coll.find()) [{'a': 1}, {'a': 2}] >>> list(client.db.coll.find({'a': {'$gt': 1}})) [{'a': 2}] >>> client.db.command('break') {'ok': 1} >>> future() ``` -------------------------------- ### Autorespond with error message and OpMsg reply Source: https://mockupdb.readthedocs.io/reference.html Demonstrates autoresponds with a message spec: first returning an error for 'bar', then returning a cursor for a find command. The example shows the resulting OperationFailure and successful find results. ```python >>> # ok >>> responder = s.autoresponds('bar', ok=0, errmsg='err') >>> client.db.command('bar') Traceback (most recent call last): ... OperationFailure: command SON([('bar', 1)]) on namespace db.$cmd failed: err >>> responder = s.autoresponds(OpMsg('find', 'collection'), ... {'cursor': {'id': 0, 'firstBatch': [{'_id': 1}, {'_id': 2}]}}) >>> # ok >>> list(client.db.collection.find()) == [{'_id': 1}, {'_id': 2}] True >>> responder = s.autoresponds(OpMsg('find', 'collection'), ... {'cursor': {'id': 0, 'firstBatch': [{'a': 1}, {'a': 2}]}}) >>> # bad >>> list(client.db.collection.find()) == [{'a': 1}, {'a': 2}] True ``` -------------------------------- ### Test server discovery and monitoring with MockupDB Source: https://mockupdb.readthedocs.io/_sources/tutorial.rst.txt These steps test PyMongo's server monitor by simulating a secondary and then a primary in a replica set. The first block configures the server to respond to 'ismaster' with a secondary reply. The second connects to the replica set and waits until the server is discovered as a secondary. The third adds a primary to the host list and configures its 'ismaster' response. The final block triggers a pending operation and waits for the client to discover the primary, then pops the 'buildinfo' command and returns ok. ```python >>> hosts = [server.address_string] >>> secondary_reply = OpReply({ ... 'ismaster': False, ... 'secondary': True, ... 'setName': 'rs', ... 'hosts': hosts, ... 'maxWireVersion': 6}) >>> responder = server.autoresponds('ismaster', secondary_reply) ``` ```python >>> client = MongoClient(server.uri, replicaSet='rs') >>> from mockupdb import wait_until >>> wait_until(lambda: server.address in client.secondaries, ... 'discover secondary') True ``` ```python >>> primary = MockupDB() >>> port = primary.run() >>> hosts.append(primary.address_string) >>> primary_reply = OpReply({ ... 'ismaster': True, ... 'secondary': False, ... 'setName': 'rs', ... 'hosts': hosts, ... 'maxWireVersion': 6}) >>> responder = primary.autoresponds('ismaster', primary_reply) ``` ```python >>> with going(client.db.command, 'buildinfo'): ... wait_until(lambda: primary.address == client.primary, ... 'discovery primary') ... primary.pop('buildinfo').ok() True True ``` -------------------------------- ### Configure mock server as secondary for discovery test Source: https://mockupdb.readthedocs.io/tutorial.html Configure the mock server to respond to ismaster requests as a secondary using an OpReply that specifies a replica set name, host list, and a maxWireVersion of 6. The server's address is appended to the host list for later use. ```python >>> hosts = [server.address_string] >>> secondary_reply = OpReply({ ... 'ismaster': False, ... 'secondary': True, ... 'setName': 'rs', ... 'hosts': hosts, ... 'maxWireVersion': 6}) >>> responder = server.autoresponds('ismaster', secondary_reply) ``` -------------------------------- ### ok(*args, **kwargs) Source: https://mockupdb.readthedocs.io/reference.html Synonym for `replies`. ```APIDOC ## ok(*args, **kwargs) ### Description Synonym for `replies`. Calls `reply` on the currently enqueued request. ### Method N/A (Python method) ### Parameters - **args** - positional arguments - passed to `reply` - **kwargs** - keyword arguments - passed to `reply` ### Returns None ``` -------------------------------- ### Clone the repository Source: https://mockupdb.readthedocs.io/contributing.html Clone your fork of the mongo-mockup-db repository locally. ```bash $ git clone git@github.com:your_name_here/mongo-mockup-db.git ``` -------------------------------- ### MockupDB Source: https://mockupdb.readthedocs.io/reference.html MockupDB class reference with constructor, properties, and methods for simulating a MongoDB server in unittests. ```APIDOC ## MockupDB ### Description A simulated mongod or mongos for use in unittests. Call `run` to start the server, and always `close` it to avoid exceptions during interpreter shutdown. ### Constructor `MockupDB(port=None, verbose=False, request_timeout=10, auto_ismaster=None, ssl=False, min_wire_version=0, max_wire_version=6, uds_path=None)` #### Parameters - **port** (int) - Optional - Listening port number. If not specified, choose some unused port and return the port number from `run`. - **verbose** (bool) - Optional - If `True`, print requests and replies to stdout. - **request_timeout** (int) - Optional - Seconds to wait for the next client request, or else assert. Default 10 seconds. Pass int(1e6) to disable. - **auto_ismaster** (bool or dict or OpReply) - Optional - Pass `True` to autorespond `{'ok': 1}` to ismaster requests, or pass a dict or `OpReply`. - **ssl** (bool) - Optional - Pass `True` to require SSL. - **min_wire_version** (int) - Optional - The minWireVersion to include in ismaster responses if `auto_ismaster` is True, default 0. - **max_wire_version** (int) - Optional - The maxWireVersion to include in ismaster responses if `auto_ismaster` is True, default 6. - **uds_path** (str) - Optional - A Unix domain socket path. MockupDB will attempt to delete the path if it already exists. ### Properties - **address** - The listening (host, port). - **address_string** - The listening "host:port". ### Methods #### `run()` Starts the server. Returns the port number if `port` was not specified. #### `close()` Closes the server. Always call this to avoid exceptions during interpreter shutdown. #### `autoresponds(*args, **kwargs)` Send a canned reply to all matching client requests. `matcher` is a `Matcher` or a command name, or an instance of `OpInsert`, `OpQuery`, etc. The remaining arguments are a message spec. You can pass a request handler in place of the message spec. Return True if you handled the request. The standard `Request.ok`, `replies`, `fail`, `hangup` and so on all return True to make them suitable as handler functions. If the request is not handled, it is checked against the remaining responders, or enqueued if none match. You can pass the handler as the only argument so it receives _all_ requests. Responders are applied in order, most recently added first, until one matches. Remove an autoresponder with `responder.cancel()`. #### `append_responder(*args, **kwargs)` Add a responder of last resort. Like `autoresponds`, but instead of adding a responder to the top of the stack, add it to the bottom. This responder will be called if no others match. #### `cancel_responder(*args, **kwargs)` Cancel a responder that was registered with `autoresponds`. #### `command_err(*args, **kwargs)` Call `command_err` on the currently enqueued request. #### `fail(*args, **kwargs)` Call `fail` on the currently enqueued request. #### `gets(*args, **kwargs)` Synonym for `receives`. #### `subscribe(*args, **kwargs)` Synonym for `autoresponds` that better expresses your intent if your handler never returns True. ### Example ```python >>> s = MockupDB() >>> port = s.run() >>> >>> from pymongo import MongoClient >>> client = MongoClient(s.uri) >>> responder = s.autoresponds('ismaster', maxWireVersion=6) >>> client.admin.command('ismaster') == {'ok': 1, 'maxWireVersion': 6} True ``` ``` -------------------------------- ### Respond to the insert command with cmd.ok() Source: https://mockupdb.readthedocs.io/tutorial.html Respond to the received command by sending an OK reply. This unblocks the client and makes its future return the expected result. ```python >>> cmd.ok() True ``` -------------------------------- ### receive(*args, **kwargs) Source: https://mockupdb.readthedocs.io/reference.html Pops the next request and asserts it matches. ```APIDOC ## receive(*args, **kwargs) ### Description Pop the next `Request` and assert it matches. Returns None if the server is stopped. ### Method N/A (Python method) ### Parameters - **args** - positional arguments - a `Request` or request pattern to specify what client request to expect - **kwargs** - keyword arguments - `timeout` to override this server's `request_timeout` ### Returns - **Request or None** - The popped request, or None if the server is stopped. ``` -------------------------------- ### receives(*args, **kwargs) Source: https://mockupdb.readthedocs.io/reference.html Pops the next request and asserts it matches. ```APIDOC ## receives(*args, **kwargs) ### Description Pop the next `Request` and assert it matches. Returns None if the server is stopped. ### Method N/A (Python method) ### Parameters - **args** - positional arguments - a `Request` or request pattern to specify what client request to expect - **kwargs** - keyword arguments - `timeout` to override this server's `request_timeout` ### Returns - **Request or None** - The popped request, or None if the server is stopped. ``` -------------------------------- ### Receive a request with server.receives() Source: https://mockupdb.readthedocs.io/tutorial.html Block until MockupDB receives a request from the client. The received request has a command_name attribute, which for the initial connection is 'ismaster'. ```python >>> request = server.receives() >>> request.command_name 'ismaster' ``` -------------------------------- ### running Source: https://mockupdb.readthedocs.io/reference.html Indicates if the server is running. ```APIDOC ## running ### Description If this server is started and not stopped. ### Type - **bool** - True if the server is running. ``` -------------------------------- ### Add primary to replica set host list Source: https://mockupdb.readthedocs.io/tutorial.html Add a primary to the replica set by running a new MockupDB instance)SkipThisLine, appending its address to the existing host list, and configuring it to respond as primary. The response uses an OpReply with ismaster true, secondary false, and the updated host list. ```python >>> primary = MockupDB() >>> port = primary.run() >>> hosts.append(primary.address_string) >>> primary_reply = OpReply({ ... 'ismaster': True, ... 'secondary': False, ... 'setName': 'rs', ... 'hosts': hosts, ... 'maxWireVersion': 6}) >>> responder = primary.autoresponds('ismaster', primary_reply) ``` -------------------------------- ### Create a branch for local development Source: https://mockupdb.readthedocs.io/contributing.html Create a new branch for your bugfix or feature. ```bash $ git checkout -b name-of-your-bugfix-or-feature ``` -------------------------------- ### Simulate normal cursor behavior with getMore replies Source: https://mockupdb.readthedocs.io/tutorial.html Simulate a normal query with multiple batches by replying to `find` and then handling repeated `getMore` commands. The `replies` method sets the first batch, and each `getmore.ok` call returns the next batch; the loop ends when the cursor id becomes 0. The final `future()` call shows all documents retrieved. ```python >>> cursor = collection.find().batch_size(2) >>> future = go(list, cursor) >>> documents = [{'_id': 1}, {'x': 2}, {'foo': 'bar'}, {'beauty': True}] >>> request = server.receives(OpMsg('find', 'coll')) >>> n = request['batchSize'] >>> request.replies(cursor={'id': 123, 'firstBatch': documents[:n]}) True >>> while True: ... getmore = server.receives(OpMsg('getMore', 123)) ... n = getmore['batchSize'] ... if documents: ... cursor_id = 123 ... else: ... cursor_id = 0 ... getmore.ok(cursor={'id': cursor_id, 'nextBatch': documents[:n]}) ... print('returned %d' % len(documents[:n])) ... del documents[:n] ... if cursor_id == 0: ... break True returned 2 True returned 2 True returned 0 ``` ```python >>> future() [{'_id': 1}, {'x': 2}, {'_id': 1}, {'x': 2}, {'foo': 'bar'}, {'beauty': True}] ``` -------------------------------- ### wait(*args, **kwargs) Source: https://mockupdb.readthedocs.io/reference.html Synonym for `got`. ```APIDOC ## wait(*args, **kwargs) ### Description Synonym for `got`. ### Method N/A (Python method) ### Parameters - **args** - positional arguments - passed to `got` - **kwargs** - keyword arguments - passed to `got` ### Returns - **bool** - True if a matching request is enqueued, False otherwise. ``` -------------------------------- ### host Source: https://mockupdb.readthedocs.io/reference.html The listening hostname. ```APIDOC ## host ### Description The listening hostname. ### Type - **str** - The listening hostname. ``` -------------------------------- ### mockupdb.wait_until(predicate, success_description, timeout=10) Source: https://mockupdb.readthedocs.io/reference.html Waits until a predicate becomes true. ```APIDOC ## mockupdb.wait_until(predicate, success_description, timeout=10) ### Description Wait up to 10 seconds (by default) for predicate to be true. ### Method N/A (Python function) ### Parameters - **predicate** (callable) - Required - A function that returns a truthy value when the condition is met. - **success_description** (str) - Required - A description of the condition, used in the error message. - **timeout** (float) - Optional - Maximum time to wait in seconds. Default is 10. ### Returns - **value** - The predicate's first true value. ### Raises - **AssertionError** - If the predicate is not true within the timeout. ### Example ```python wait_until(lambda: client.primary == ('a', 1), 'connect to the primary') ``` ``` -------------------------------- ### Receive insert command and respond with ok Source: https://mockupdb.readthedocs.io/_sources/tutorial.rst.txt Wait for the client's request with server.receives(), then respond with cmd.ok() to unblock the client. The future returns an InsertOneResult. ```python >>> cmd = server.receives() >>> cmd OpMsg({"insert": "coll", "ordered": true, "$db": "db", "$readPreference": {"mode": "primary"}, "documents": [{"_id": 1}]}, namespace="db") ``` ```python >>> cmd.ok() True ``` ```python >>> write_result = future() >>> write_result # doctest: +ELLIPSIS >>> write_result.inserted_id 1 ``` -------------------------------- ### Defer acknowledged write with go Source: https://mockupdb.readthedocs.io/_sources/tutorial.rst.txt Use mockupdb.go to run collection.insert_one in a background thread so you can respond from the main thread. The default write concern is acknowledged. ```python >>> collection = client.db.coll >>> from mockupdb import go >>> # Default write concern is acknowledged. >>> future = go(collection.insert_one, {'_id': 1}) ``` -------------------------------- ### Receive the insert request with server.receives() Source: https://mockupdb.readthedocs.io/tutorial.html Wait for the client's insert request on the main thread. MockupDB renders the request as OpMsg with JSON-style notation: booleans are lowercase and key order is preserved even in Python versions with unordered dicts. ```python >>> cmd = server.receives() >>> cmd OpMsg({"insert": "coll", "ordered": true, "$db": "db", "$readPreference": {"mode": "primary"}, "documents": [{"_id": 1}]}, namespace="db") ``` -------------------------------- ### Run a subset of tests Source: https://mockupdb.readthedocs.io/contributing.html Run a specific test module using the -s option. ```bash $ python setup.py test -s tests.test_mockupdb ``` -------------------------------- ### stop(*args, **kwargs) Source: https://mockupdb.readthedocs.io/reference.html Stops serving. ```APIDOC ## stop(*args, **kwargs) ### Description Stop serving. Always call this to clean up after yourself. ### Method N/A (Python method) ### Parameters - **args** - positional arguments - passed to the underlying implementation - **kwargs** - keyword arguments - passed to the underlying implementation ### Returns None ``` -------------------------------- ### uri Source: https://mockupdb.readthedocs.io/reference.html Connection string to pass to `MongoClient`. ```APIDOC ## uri ### Description Connection string to pass to `MongoClient`. ### Type - **str** - Connection string. ``` -------------------------------- ### Implement shutdown command with server.stop() Source: https://mockupdb.readthedocs.io/tutorial.html Implement a shutdown command by handling 'shutdown' in a server loop. Calling server.stop() hangs up the connection, causing the client to raise AutoReconnect. As the loop exits, server.running becomes False which also stops the background future. ```python >>> def loop(): ... try: ... while server.running: ... if server.got('shutdown'): ... server.stop() # Hangs up. ... else: ... server.command_err('unrecognized request') ... except: ... traceback.print_exc() ... raise ... >>> future = go(loop) >>> client.db.command('shutdown') Traceback (most recent call last): ... AutoReconnect: connection closed >>> future() >>> server.running False >>> client.close() ``` -------------------------------- ### Request membership and indexing assertions Source: https://mockupdb.readthedocs.io/reference.html Shows how to use the 'in' operator and indexing on Request subclasses like OpInsert and OpMsg to assert document membership and field access. Useful for writing tests that verify request contents. ```python >>> {'_id': 0} in OpInsert({'_id': 0}) True >>> {'_id': 1} in OpInsert({'_id': 0}) False >>> {'_id': 1} in OpInsert([{'_id': 0}, {'_id': 1}]) True >>> {'_id': 1} == OpInsert([{'_id': 0}, {'_id': 1}])[1] True >>> 'field' in OpMsg(field=1) True >>> 'field' in OpMsg() False >>> 'field' in OpMsg('ismaster') False >>> OpMsg(ismaster=False)['ismaster'] is False True ``` -------------------------------- ### Run flake8 and tests Source: https://mockupdb.readthedocs.io/contributing.html Check that your changes pass flake8 and the tests, including testing other Python versions with tox. ```bash $ flake8 mockupdb tests $ python setup.py test $ tox ``` -------------------------------- ### reply(*args, **kwargs) Source: https://mockupdb.readthedocs.io/reference.html Calls `reply` on the currently enqueued request. ```APIDOC ## reply(*args, **kwargs) ### Description Call `reply` on the currently enqueued request. ### Method N/A (Python method) ### Parameters - **args** - positional arguments - passed to `reply` - **kwargs** - keyword arguments - passed to `reply` ### Returns None ``` -------------------------------- ### send(*args, **kwargs) Source: https://mockupdb.readthedocs.io/reference.html Calls `reply` on the currently enqueued request. ```APIDOC ## send(*args, **kwargs) ### Description Call `reply` on the currently enqueued request. ### Method N/A (Python method) ### Parameters - **args** - positional arguments - passed to `reply` - **kwargs** - keyword arguments - passed to `reply` ### Returns None ``` -------------------------------- ### Use timeout to wait for a request Source: https://mockupdb.readthedocs.io/tutorial.html By default, MockupDB waits 10 seconds for a request before timing out. Pass a timeout in seconds to `receives` to shorten this wait, so tests finish quickly even when no request is sent. ```python >>> try: ... server.receives(timeout=0.1) ... except AssertionError as err: ... print("Error: %s" % err) Error: expected to receive Request(), got nothing ``` -------------------------------- ### Receive and reply to ismaster command Source: https://mockupdb.readthedocs.io/_sources/tutorial.rst.txt MockupDB receives the ismaster command but does not respond until you call replies. The request.command_name is 'ismaster'. ```python >>> request = server.receives() >>> request.command_name 'ismaster' ``` ```python >>> request.replies({'ok': 1, 'maxWireVersion': 6}) True ``` -------------------------------- ### pop(*args, **kwargs) Source: https://mockupdb.readthedocs.io/reference.html Pops the next request and asserts it matches. ```APIDOC ## pop(*args, **kwargs) ### Description Pop the next `Request` and assert it matches. Returns None if the server is stopped. ### Method N/A (Python method) ### Parameters - **args** - positional arguments - a `Request` or request pattern to specify what client request to expect - **kwargs** - keyword arguments - `timeout` to override this server's `request_timeout` ### Returns - **Request or None** - The popped request, or None if the server is stopped. ``` -------------------------------- ### replies(*args, **kwargs) Source: https://mockupdb.readthedocs.io/reference.html Calls `reply` on the currently enqueued request. ```APIDOC ## replies(*args, **kwargs) ### Description Call `reply` on the currently enqueued request. ### Method N/A (Python method) ### Parameters - **args** - positional arguments - passed to `reply` - **kwargs** - keyword arguments - passed to `reply` ### Returns None ``` -------------------------------- ### label Source: https://mockupdb.readthedocs.io/reference.html Label for logging, or None. ```APIDOC ## label ### Description Label for logging, or None. ### Type - **str or None** - Label for logging. ``` -------------------------------- ### port Source: https://mockupdb.readthedocs.io/reference.html The listening port. ```APIDOC ## port ### Description The listening port. ### Type - **int** - The listening port. ``` -------------------------------- ### Commit and push changes Source: https://mockupdb.readthedocs.io/contributing.html Commit your changes and push your branch to GitHub. ```bash $ git add . $ git commit -m "Your detailed description of your changes." $ git push origin name-of-your-bugfix-or-feature ``` -------------------------------- ### Send default reply with ok() Source: https://mockupdb.readthedocs.io/tutorial.html Sends a default OP_REPLY with `{'ok': 1}`. The `server.receives()` call yields a request object. ```python >>> request = server.receives() >>> request.ok() # Send {'ok': 1}. ``` -------------------------------- ### sends(*args, **kwargs) Source: https://mockupdb.readthedocs.io/reference.html Calls `reply` on the currently enqueued request. ```APIDOC ## sends(*args, **kwargs) ### Description Call `reply` on the currently enqueued request. ### Method N/A (Python method) ### Parameters - **args** - positional arguments - passed to `reply` - **kwargs** - keyword arguments - passed to `reply` ### Returns None ``` -------------------------------- ### Pattern-match requests with receives Source: https://mockupdb.readthedocs.io/_sources/tutorial.rst.txt Pass a pattern to server.receives to verify the next request matches. If it doesn't, an AssertionError is raised and the request is still popped from the queue. ```python >>> future = go(client.db.command, 'commandFoo') >>> request = server.receives('commandBar') # doctest: +NORMALIZE_WHITESPACE Traceback (most recent call last): ... AssertionError: expected to receive Command({"commandBar": 1}), got Command({"commandFoo": 1}) ``` -------------------------------- ### subscribe(*args, **kwargs) Source: https://mockupdb.readthedocs.io/reference.html Synonym for `autoresponds`. ```APIDOC ## subscribe(*args, **kwargs) ### Description Synonym for `autoresponds`. ### Method N/A (Python method) ### Parameters - **args** - positional arguments - passed to `autoresponds` - **kwargs** - keyword arguments - passed to `autoresponds` ### Returns None ``` -------------------------------- ### Responder ordering and cancellation Source: https://mockupdb.readthedocs.io/reference.html Shows that responders are applied in order, most recently added first, until one matches. Cancelling the most recent responder causes the previous one to take over. ```python >>> responder = s.autoresponds('baz') >>> client.db.command('baz') == {'ok': 1} True >>> responder.cancel() >>> # The previous responder takes over again. >>> client.db.command('baz') == {'ok': 1, 'key': 'value'} True ``` -------------------------------- ### Match expression with datetime that failed before MockupDB 1.7.0 Source: https://mockupdb.readthedocs.io/changelog.html Shows a Match expression that always failed before version 1.7.0 because Python datetimes have microsecond precision while BSON only supports milliseconds. The matching logic was rewritten in 1.7.0 to recurse through arrays and subdocuments and compare datetime values with millisecond precision. ```python server.receives(Command('foo', when=datetime(2018, 12, 1, 6, 6, 6, 12345))) ``` -------------------------------- ### request Source: https://mockupdb.readthedocs.io/reference.html The currently enqueued `Request`, or None. ```APIDOC ## request ### Description The currently enqueued `Request`, or None. ### Type - **Request or None** - The currently enqueued request. ```