schireson / pytest-mock-resources

Pytest Fixtures that let you actually test against external resource (Postgres, Mongo, Redshift...) dependent code.
https://pytest-mock-resources.readthedocs.io/en/latest/quickstart.html
MIT License
179 stars 19 forks source link

$round operation for MongoDB #145

Closed ruslankrivoshein closed 2 years ago

ruslankrivoshein commented 2 years ago

Describe the bug Description of what the bug is.

Environment Ubuntu Python 3.10 virtualenv==20.0.17

To Reproduce Steps to reproduce the behavior:

  1. Prepare collection with name and value fields. Add a couple of docs.
  2. Run
    
    db.my_col.aggregate(
    [
        {
            '$match': {
                'name': some_value,
            },
        },
        {
            '$group': {
                '_id': None,
                'lowest': {
                    '$min': '$value',
                },
            },
        },
        {
            '$project': {
                'lowest': {
                    '$round': ['$lowest', 3],
                },
            },
        },
    ]
    )
3. See error
`pymongo.errors.OperationFailure: Unrecognized expression '$round', full error: {'ok': 0.0, 'errmsg': "Unrecognized expression '$round'", 'code': 168, 'codeName': 'InvalidPipelineOperator'}`

**Expected behavior**
It should return 

{ 'lowest': value, }


**Actual Behavior**

venv/lib/python3.10/site-packages/werkzeug/test.py:1131: in get return self.open(*args, kw) venv/lib/python3.10/site-packages/flask/testing.py:235: in open return super().open( venv/lib/python3.10/site-packages/werkzeug/test.py:1076: in open response = self.run_wsgi_app(request.environ, buffered=buffered) venv/lib/python3.10/site-packages/werkzeug/test.py:945: in run_wsgi_app rv = run_wsgi_app(self.application, environ, buffered=buffered) venv/lib/python3.10/site-packages/werkzeug/test.py:1233: in run_wsgi_app app_rv = app(environ, start_response) venv/lib/python3.10/site-packages/flask/app.py:2091: in call return self.wsgi_app(environ, start_response) venv/lib/python3.10/site-packages/flask/app.py:2076: in wsgi_app response = self.handle_exception(e) venv/lib/python3.10/site-packages/flask/app.py:2073: in wsgi_app response = self.full_dispatch_request() venv/lib/python3.10/site-packages/flask/app.py:1518: in full_dispatch_request rv = self.handle_user_exception(e) venv/lib/python3.10/site-packages/flask/app.py:1516: in full_dispatch_request rv = self.dispatch_request() venv/lib/python3.10/site-packages/flask/app.py:1502: in dispatch_request return self.ensure_sync(self.view_functions[rule.endpoint])(req.view_args) src/api/init.py:205: in place_where_i_invoke_it db.my_col.aggregate( venv/lib/python3.10/site-packages/pymongo/collection.py:2104: in aggregate return self._aggregate( venv/lib/python3.10/site-packages/pymongo/collection.py:2026: in _aggregate return self.__database.client._retryable_read( venv/lib/python3.10/site-packages/pymongo/mongo_client.py:1359: in _retryable_read return func(session, server, sock_info, secondary_ok) venv/lib/python3.10/site-packages/pymongo/aggregation.py:134: in get_cursor result = sock_info.command( venv/lib/python3.10/site-packages/pymongo/pool.py:742: in command return command( venv/lib/python3.10/site-packages/pymongo/network.py:174: in command helpers._check_command_response(


response = {'code': 168, 'codeName': 'InvalidPipelineOperator', 'errmsg': "Unrecognized expression '$round'", 'ok': 0.0} max_wire_version = 6, allowable_errors = None, parse_write_concern_error = True

def _check_command_response(
    response, max_wire_version, allowable_errors=None, parse_write_concern_error=False
):
    """Check the response to a command for errors."""
    if "ok" not in response:
        # Server didn't recognize our message as a command.
        raise OperationFailure(
            response.get("$err"), response.get("code"), response, max_wire_version
        )

    if parse_write_concern_error and "writeConcernError" in response:
        _error = response["writeConcernError"]
        _labels = response.get("errorLabels")
        if _labels:
            _error.update({"errorLabels": _labels})
        _raise_write_concern_error(_error)

    if response["ok"]:
        return

    details = response
    # Mongos returns the error details in a 'raw' object
    # for some errors.
    if "raw" in response:
        for shard in response["raw"].values():
            # Grab the first non-empty raw error from a shard.
            if shard.get("errmsg") and not shard.get("ok"):
                details = shard
                break

    errmsg = details["errmsg"]
    code = details.get("code")

    # For allowable errors, only check for error messages when the code is not
    # included.
    if allowable_errors:
        if code is not None:
            if code in allowable_errors:
                return
        elif errmsg in allowable_errors:
            return

    # Server is "not primary" or "recovering"
    if code is not None:
        if code in _NOT_PRIMARY_CODES:
            raise NotPrimaryError(errmsg, response)
    elif HelloCompat.LEGACY_ERROR in errmsg or "node is recovering" in errmsg:
        raise NotPrimaryError(errmsg, response)

    # Other errors
    # findAndModify with upsert can raise duplicate key error
    if code in (11000, 11001, 12582):
        raise DuplicateKeyError(errmsg, code, response, max_wire_version)
    elif code == 50:
        raise ExecutionTimeout(errmsg, code, response, max_wire_version)
    elif code == 43:
        raise CursorNotFound(errmsg, code, response, max_wire_version)
DanCardin commented 2 years ago

I'm reasonably certain we dont do anything extra special for mongo, beyond producing the client. So I would be surprised if this were a bug on our end, necessarily.

If i can assume that you know this works against your actual mongo server, I'll hazard a guess that perhaps this is some difference caused by your mongo version not matching the version we default to (3.6).

If so, you could define a MongoConfig to match your version. i.e.

@pytest.fixture(scope='session')
def pmr_mongo_config():
     return MongoConfig(image="mongo:whatever-version")
DanCardin commented 2 years ago

I'm going to close this issue since you haven't responded and i suspect the above comment indicates what the solution should be. Feel free to comment back if otherwise, and I can reopen.