Update the test matrix to reflect what's supported in 2019 (#2066)
Previously, we were running the test suite for several combinations of MongoDB, Python, and PyMongo: - PyPy, MongoDB v2.6, PyMongo v3.x (which really means v3.6.1 at the moment) - Python v2.7, MongoDB v2.6, PyMongo v3.x - Python v3.5, MongoDB v2.6, PyMongo v3.x - Python v3.6, MongoDB v2.6, PyMongo v3.x - Python v2.7, MongoDB v3.0, PyMongo v3.5.0 - Python v3.6, MongoDB v3.0, PyMongo v3.5.0 - Python v3.5, MongoDB v3.2, PyMongo v3.x - Python v3.6, MongoDB v3.2, PyMongo v3.x - Python v3.6, MongoDB v3.4, PyMongo v3.x - Python v3.6, MongoDB v3.6, PyMongo v3.x There were a couple issues with this setup: 1. MongoDB v2.6 – v3.2 have reached their End of Life already (v2.6 almost 3 years ago!). See the "MongoDB Server" section on https://www.mongodb.com/support-policy. 2. We were only testing two recent-ish PyMongo versions (v3.5.0 & v3.6.1). We were not testing the oldest actively supported MongoDB/PyMongo/Python setup. This PR updates the test matrix so that these problems are solved. For the sake of simplicity, it does not yet attempt to cover MongoDB v4.0: - PyPy, MongoDB v3.4, PyMongo v3.x (aka v3.6.1 at the moment) - Python v2.7, MongoDB v3.4, PyMongo v3.x - Python v3.5, MongoDB v3.4, PyMongo v3.x - Python v3.6, MongoDB v3.4, PyMongo v3.x - Python v2.7, MongoDB v3.4, PyMongo v3.4 - Python v3.6, MongoDB v3.6, PyMongo v3.x
This commit is contained in:
		
							
								
								
									
										41
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								.travis.yml
									
									
									
									
									
								
							@@ -2,11 +2,18 @@
 | 
			
		||||
# PyMongo combinations. However, that would result in an overly long build
 | 
			
		||||
# with a very large number of jobs, hence we only test a subset of all the
 | 
			
		||||
# combinations:
 | 
			
		||||
# * MongoDB v2.6 is currently the "main" version tested against Python v2.7,
 | 
			
		||||
#   v3.5, v3.6, PyPy, and PyMongo v3.x.
 | 
			
		||||
# * MongoDB v3.0 & v3.2 are tested against Python v2.7, v3.5 & v3.6
 | 
			
		||||
#   and Pymongo v3.5 & v3.x
 | 
			
		||||
# * MongoDB v3.4 is tested against v3.6 and Pymongo v3.x
 | 
			
		||||
# * MongoDB v3.4 & the latest PyMongo v3.x is currently the "main" setup,
 | 
			
		||||
#   tested against Python v2.7, v3.5, v3.6, and PyPy.
 | 
			
		||||
# * Besides that, we test the lowest actively supported Python/MongoDB/PyMongo
 | 
			
		||||
#   combination: MongoDB v3.4, PyMongo v3.4, Python v2.7.
 | 
			
		||||
# * MongoDB v3.6 is tested against Python v3.6, and PyMongo v3.6, v3.7, v3.8.
 | 
			
		||||
#
 | 
			
		||||
# We should periodically check MongoDB Server versions supported by MongoDB
 | 
			
		||||
# Inc., add newly released versions to the test matrix, and remove versions
 | 
			
		||||
# which have reached their End of Life. See:
 | 
			
		||||
# 1. https://www.mongodb.com/support-policy.
 | 
			
		||||
# 2. https://docs.mongodb.com/ecosystem/drivers/driver-compatibility-reference/#python-driver-compatibility
 | 
			
		||||
#
 | 
			
		||||
# Reminder: Update README.rst if you change MongoDB versions we test.
 | 
			
		||||
 | 
			
		||||
language: python
 | 
			
		||||
@@ -18,7 +25,7 @@ python:
 | 
			
		||||
- pypy
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
- MONGODB=2.6 PYMONGO=3.x
 | 
			
		||||
- MONGODB=3.4 PYMONGO=3.x
 | 
			
		||||
 | 
			
		||||
matrix:
 | 
			
		||||
  # Finish the build as soon as one job fails
 | 
			
		||||
@@ -26,15 +33,7 @@ matrix:
 | 
			
		||||
 | 
			
		||||
  include:
 | 
			
		||||
  - python: 2.7
 | 
			
		||||
    env: MONGODB=3.0 PYMONGO=3.5
 | 
			
		||||
  - python: 3.5
 | 
			
		||||
    env: MONGODB=3.2 PYMONGO=3.x
 | 
			
		||||
  - python: 3.6
 | 
			
		||||
    env: MONGODB=3.0 PYMONGO=3.5
 | 
			
		||||
  - python: 3.6
 | 
			
		||||
    env: MONGODB=3.2 PYMONGO=3.x
 | 
			
		||||
  - python: 3.6
 | 
			
		||||
    env: MONGODB=3.4 PYMONGO=3.x
 | 
			
		||||
    env: MONGODB=3.4 PYMONGO=3.4.x
 | 
			
		||||
  - python: 3.6
 | 
			
		||||
    env: MONGODB=3.6 PYMONGO=3.x
 | 
			
		||||
 | 
			
		||||
@@ -86,15 +85,15 @@ deploy:
 | 
			
		||||
  password:
 | 
			
		||||
    secure: QMyatmWBnC6ZN3XLW2+fTBDU4LQcp1m/LjR2/0uamyeUzWKdlOoh/Wx5elOgLwt/8N9ppdPeG83ose1jOz69l5G0MUMjv8n/RIcMFSpCT59tGYqn3kh55b0cIZXFT9ar+5cxlif6a5rS72IHm5li7QQyxexJIII6Uxp0kpvUmek=
 | 
			
		||||
 | 
			
		||||
  # create a source distribution and a pure python wheel for faster installs
 | 
			
		||||
  # Create a source distribution and a pure python wheel for faster installs.
 | 
			
		||||
  distributions: "sdist bdist_wheel"
 | 
			
		||||
 | 
			
		||||
  # only deploy on tagged commits (aka GitHub releases) and only for the
 | 
			
		||||
  # parent repo's builds running Python 2.7 along with PyMongo v3.x (we run
 | 
			
		||||
  # Travis against many different Python and PyMongo versions and we don't
 | 
			
		||||
  # want the deploy to occur multiple times).
 | 
			
		||||
  # Only deploy on tagged commits (aka GitHub releases) and only for the parent
 | 
			
		||||
  # repo's builds running Python v2.7 along with PyMongo v3.x and MongoDB v3.4.
 | 
			
		||||
  # We run Travis against many different Python, PyMongo, and MongoDB versions
 | 
			
		||||
  # and we don't want the deploy to occur multiple times).
 | 
			
		||||
  on:
 | 
			
		||||
    tags: true
 | 
			
		||||
    repo: MongoEngine/mongoengine
 | 
			
		||||
    condition: "$PYMONGO = 3.x"
 | 
			
		||||
    condition: ($PYMONGO = 3.x) AND ($MONGODB = 3.4)
 | 
			
		||||
    python: 2.7
 | 
			
		||||
 
 | 
			
		||||
@@ -26,10 +26,10 @@ an `API reference <https://mongoengine-odm.readthedocs.io/apireference.html>`_.
 | 
			
		||||
 | 
			
		||||
Supported MongoDB Versions
 | 
			
		||||
==========================
 | 
			
		||||
MongoEngine is currently tested against MongoDB v2.6, v3.0, v3.2, v3.4 and v3.6. Future
 | 
			
		||||
versions should be supported as well, but aren't actively tested at the moment.
 | 
			
		||||
Make sure to open an issue or submit a pull request if you experience any
 | 
			
		||||
problems with MongoDB v3.6+.
 | 
			
		||||
MongoEngine is currently tested against MongoDB v3.4 and v3.6. Future versions
 | 
			
		||||
should be supported as well, but aren't actively tested at the moment. Make
 | 
			
		||||
sure to open an issue or submit a pull request if you experience any problems
 | 
			
		||||
with MongoDB version > 3.6.
 | 
			
		||||
 | 
			
		||||
Installation
 | 
			
		||||
============
 | 
			
		||||
 
 | 
			
		||||
@@ -117,10 +117,22 @@ def _get_connection_settings(
 | 
			
		||||
                    ReadPreference.PRIMARY,
 | 
			
		||||
                    ReadPreference.PRIMARY_PREFERRED,
 | 
			
		||||
                    ReadPreference.SECONDARY,
 | 
			
		||||
                    ReadPreference.SECONDARY_PREFERRED)
 | 
			
		||||
                read_pf_mode = uri_options['readpreference'].lower()
 | 
			
		||||
                    ReadPreference.SECONDARY_PREFERRED,
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                # Starting with PyMongo v3.5, the "readpreference" option is
 | 
			
		||||
                # returned as a string (e.g. "secondaryPreferred") and not an
 | 
			
		||||
                # int (e.g. 3).
 | 
			
		||||
                # TODO simplify the code below once we drop support for
 | 
			
		||||
                # PyMongo v3.4.
 | 
			
		||||
                read_pf_mode = uri_options['readpreference']
 | 
			
		||||
                if isinstance(read_pf_mode, six.string_types):
 | 
			
		||||
                    read_pf_mode = read_pf_mode.lower()
 | 
			
		||||
                for preference in read_preferences:
 | 
			
		||||
                    if preference.name.lower() == read_pf_mode:
 | 
			
		||||
                    if (
 | 
			
		||||
                        preference.name.lower() == read_pf_mode or
 | 
			
		||||
                        preference.mode == read_pf_mode
 | 
			
		||||
                    ):
 | 
			
		||||
                        conn_settings['read_preference'] = preference
 | 
			
		||||
                        break
 | 
			
		||||
        else:
 | 
			
		||||
 
 | 
			
		||||
@@ -7,10 +7,6 @@ from mongoengine.connection import get_connection
 | 
			
		||||
# Constant that can be used to compare the version retrieved with
 | 
			
		||||
# get_mongodb_version()
 | 
			
		||||
MONGODB_36 = (3, 6)
 | 
			
		||||
MONGODB_34 = (3, 4)
 | 
			
		||||
MONGODB_32 = (3, 2)
 | 
			
		||||
MONGODB_3 = (3, 0)
 | 
			
		||||
MONGODB_26 = (2, 6)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_mongodb_version():
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
nose
 | 
			
		||||
pymongo>=3.5
 | 
			
		||||
pymongo>=3.4
 | 
			
		||||
six==1.10.0
 | 
			
		||||
flake8
 | 
			
		||||
flake8-import-order
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								setup.py
									
									
									
									
									
								
							@@ -80,7 +80,7 @@ setup(
 | 
			
		||||
    long_description=LONG_DESCRIPTION,
 | 
			
		||||
    platforms=['any'],
 | 
			
		||||
    classifiers=CLASSIFIERS,
 | 
			
		||||
    install_requires=['pymongo>=3.5', 'six'],
 | 
			
		||||
    install_requires=['pymongo>=3.4', 'six'],
 | 
			
		||||
    test_suite='nose.collector',
 | 
			
		||||
    **extra_opts
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@ from mongoengine.pymongo_support import list_collection_names
 | 
			
		||||
 | 
			
		||||
from mongoengine.queryset import NULLIFY, PULL
 | 
			
		||||
from mongoengine.connection import get_db
 | 
			
		||||
from tests.utils import requires_mongodb_gte_26
 | 
			
		||||
 | 
			
		||||
__all__ = ("ClassMethodsTest", )
 | 
			
		||||
 | 
			
		||||
@@ -187,7 +186,6 @@ class ClassMethodsTest(unittest.TestCase):
 | 
			
		||||
        self.assertEqual(BlogPostWithTags.compare_indexes(), {'missing': [], 'extra': []})
 | 
			
		||||
        self.assertEqual(BlogPostWithCustomField.compare_indexes(), {'missing': [], 'extra': []})
 | 
			
		||||
 | 
			
		||||
    @requires_mongodb_gte_26
 | 
			
		||||
    def test_compare_indexes_for_text_indexes(self):
 | 
			
		||||
        """ Ensure that compare_indexes behaves correctly for text indexes """
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,8 +9,7 @@ from six import iteritems
 | 
			
		||||
 | 
			
		||||
from mongoengine import *
 | 
			
		||||
from mongoengine.connection import get_db
 | 
			
		||||
from mongoengine.mongodb_support import get_mongodb_version, MONGODB_32, MONGODB_3
 | 
			
		||||
from tests.utils import requires_mongodb_gte_26, requires_mongodb_lte_32, requires_mongodb_gte_34
 | 
			
		||||
from mongoengine.mongodb_support import get_mongodb_version
 | 
			
		||||
 | 
			
		||||
__all__ = ("IndexesTest", )
 | 
			
		||||
 | 
			
		||||
@@ -478,8 +477,6 @@ class IndexesTest(unittest.TestCase):
 | 
			
		||||
    def test_covered_index(self):
 | 
			
		||||
        """Ensure that covered indexes can be used
 | 
			
		||||
        """
 | 
			
		||||
        IS_MONGODB_3 = get_mongodb_version() >= MONGODB_3
 | 
			
		||||
 | 
			
		||||
        class Test(Document):
 | 
			
		||||
            a = IntField()
 | 
			
		||||
            b = IntField()
 | 
			
		||||
@@ -497,33 +494,38 @@ class IndexesTest(unittest.TestCase):
 | 
			
		||||
        # Need to be explicit about covered indexes as mongoDB doesn't know if
 | 
			
		||||
        # the documents returned might have more keys in that here.
 | 
			
		||||
        query_plan = Test.objects(id=obj.id).exclude('a').explain()
 | 
			
		||||
        if not IS_MONGODB_3:
 | 
			
		||||
            self.assertFalse(query_plan['indexOnly'])
 | 
			
		||||
        else:
 | 
			
		||||
            self.assertEqual(query_plan.get('queryPlanner').get('winningPlan').get('inputStage').get('stage'), 'IDHACK')
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            query_plan.get('queryPlanner').get('winningPlan').get('inputStage').get('stage'),
 | 
			
		||||
            'IDHACK'
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        query_plan = Test.objects(id=obj.id).only('id').explain()
 | 
			
		||||
        if not IS_MONGODB_3:
 | 
			
		||||
            self.assertTrue(query_plan['indexOnly'])
 | 
			
		||||
        else:
 | 
			
		||||
            self.assertEqual(query_plan.get('queryPlanner').get('winningPlan').get('inputStage').get('stage'), 'IDHACK')
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            query_plan.get('queryPlanner').get('winningPlan').get('inputStage').get('stage'),
 | 
			
		||||
            'IDHACK'
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        query_plan = Test.objects(a=1).only('a').exclude('id').explain()
 | 
			
		||||
        if not IS_MONGODB_3:
 | 
			
		||||
            self.assertTrue(query_plan['indexOnly'])
 | 
			
		||||
        else:
 | 
			
		||||
            self.assertEqual(query_plan.get('queryPlanner').get('winningPlan').get('inputStage').get('stage'), 'IXSCAN')
 | 
			
		||||
            self.assertEqual(query_plan.get('queryPlanner').get('winningPlan').get('stage'), 'PROJECTION')
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            query_plan.get('queryPlanner').get('winningPlan').get('inputStage').get('stage'),
 | 
			
		||||
            'IXSCAN'
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            query_plan.get('queryPlanner').get('winningPlan').get('stage'),
 | 
			
		||||
            'PROJECTION'
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        query_plan = Test.objects(a=1).explain()
 | 
			
		||||
        if not IS_MONGODB_3:
 | 
			
		||||
            self.assertFalse(query_plan['indexOnly'])
 | 
			
		||||
        else:
 | 
			
		||||
            self.assertEqual(query_plan.get('queryPlanner').get('winningPlan').get('inputStage').get('stage'), 'IXSCAN')
 | 
			
		||||
            self.assertEqual(query_plan.get('queryPlanner').get('winningPlan').get('stage'), 'FETCH')
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            query_plan.get('queryPlanner').get('winningPlan').get('inputStage').get('stage'),
 | 
			
		||||
            'IXSCAN'
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            query_plan.get('queryPlanner').get('winningPlan').get('stage'),
 | 
			
		||||
            'FETCH'
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_index_on_id(self):
 | 
			
		||||
 | 
			
		||||
        class BlogPost(Document):
 | 
			
		||||
            meta = {
 | 
			
		||||
                'indexes': [
 | 
			
		||||
@@ -565,13 +567,10 @@ class IndexesTest(unittest.TestCase):
 | 
			
		||||
        self.assertEqual(BlogPost.objects.count(), 10)
 | 
			
		||||
        self.assertEqual(BlogPost.objects.hint().count(), 10)
 | 
			
		||||
 | 
			
		||||
        if MONGO_VER >= MONGODB_32:
 | 
			
		||||
            # Mongo32 throws an error if an index exists (i.e `tags` in our case)
 | 
			
		||||
            # and you use hint on an index name that does not exist
 | 
			
		||||
            with self.assertRaises(OperationFailure):
 | 
			
		||||
                BlogPost.objects.hint([('ZZ', 1)]).count()
 | 
			
		||||
        else:
 | 
			
		||||
            self.assertEqual(BlogPost.objects.hint([('ZZ', 1)]).count(), 10)
 | 
			
		||||
        # MongoDB v3.2+ throws an error if an index exists (i.e `tags` in our
 | 
			
		||||
        # case) and you use hint on an index name that does not exist.
 | 
			
		||||
        with self.assertRaises(OperationFailure):
 | 
			
		||||
            BlogPost.objects.hint([('ZZ', 1)]).count()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(BlogPost.objects.hint(TAGS_INDEX_NAME).count(), 10)
 | 
			
		||||
 | 
			
		||||
@@ -598,9 +597,8 @@ class IndexesTest(unittest.TestCase):
 | 
			
		||||
        # Ensure backwards compatibility for errors
 | 
			
		||||
        self.assertRaises(OperationError, post2.save)
 | 
			
		||||
 | 
			
		||||
    @requires_mongodb_gte_34
 | 
			
		||||
    def test_primary_key_unique_not_working_under_mongo_34(self):
 | 
			
		||||
        """Relates to  #1445"""
 | 
			
		||||
    def test_primary_key_unique_not_working(self):
 | 
			
		||||
        """Relates to #1445"""
 | 
			
		||||
        class Blog(Document):
 | 
			
		||||
            id = StringField(primary_key=True, unique=True)
 | 
			
		||||
 | 
			
		||||
@@ -608,21 +606,17 @@ class IndexesTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(OperationFailure) as ctx_err:
 | 
			
		||||
            Blog(id='garbage').save()
 | 
			
		||||
        try:
 | 
			
		||||
            self.assertIn("The field 'unique' is not valid for an _id index specification", str(ctx_err.exception))
 | 
			
		||||
        except AssertionError:
 | 
			
		||||
            # error is slightly different on python 3.6
 | 
			
		||||
            self.assertIn("The field 'background' is not valid for an _id index specification", str(ctx_err.exception))
 | 
			
		||||
 | 
			
		||||
    @requires_mongodb_lte_32
 | 
			
		||||
    def test_primary_key_unique_working_under_mongo_32(self):
 | 
			
		||||
        """Relates to  #1445"""
 | 
			
		||||
        class Blog(Document):
 | 
			
		||||
            id = StringField(primary_key=True, unique=True)
 | 
			
		||||
 | 
			
		||||
        Blog.drop_collection()
 | 
			
		||||
 | 
			
		||||
        Blog(id='garbage').save()
 | 
			
		||||
        # One of the errors below should happen. Which one depends on the
 | 
			
		||||
        # PyMongo version and dict order.
 | 
			
		||||
        err_msg = str(ctx_err.exception)
 | 
			
		||||
        self.assertTrue(
 | 
			
		||||
            any([
 | 
			
		||||
                "The field 'unique' is not valid for an _id index specification" in err_msg,
 | 
			
		||||
                "The field 'background' is not valid for an _id index specification" in err_msg,
 | 
			
		||||
                "The field 'sparse' is not valid for an _id index specification" in err_msg,
 | 
			
		||||
            ])
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_unique_with(self):
 | 
			
		||||
        """Ensure that unique_with constraints are applied to fields.
 | 
			
		||||
@@ -984,7 +978,6 @@ class IndexesTest(unittest.TestCase):
 | 
			
		||||
                         info['provider_ids.foo_1_provider_ids.bar_1']['key'])
 | 
			
		||||
        self.assertTrue(info['provider_ids.foo_1_provider_ids.bar_1']['sparse'])
 | 
			
		||||
 | 
			
		||||
    @requires_mongodb_gte_26
 | 
			
		||||
    def test_text_indexes(self):
 | 
			
		||||
        class Book(Document):
 | 
			
		||||
            title = DictField()
 | 
			
		||||
 
 | 
			
		||||
@@ -28,8 +28,6 @@ from mongoengine.queryset import NULLIFY, Q
 | 
			
		||||
from mongoengine.context_managers import switch_db, query_counter
 | 
			
		||||
from mongoengine import signals
 | 
			
		||||
 | 
			
		||||
from tests.utils import requires_mongodb_gte_26
 | 
			
		||||
 | 
			
		||||
TEST_IMAGE_PATH = os.path.join(os.path.dirname(__file__),
 | 
			
		||||
                               '../fields/mongoengine.png')
 | 
			
		||||
 | 
			
		||||
@@ -850,7 +848,6 @@ class InstanceTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        self.assertDbEqual([dict(other_doc.to_mongo()), dict(doc.to_mongo())])
 | 
			
		||||
 | 
			
		||||
    @requires_mongodb_gte_26
 | 
			
		||||
    def test_modify_with_positional_push(self):
 | 
			
		||||
        class Content(EmbeddedDocument):
 | 
			
		||||
            keywords = ListField(StringField())
 | 
			
		||||
@@ -3368,7 +3365,6 @@ class InstanceTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        person.update(set__height=2.0)
 | 
			
		||||
 | 
			
		||||
    @requires_mongodb_gte_26
 | 
			
		||||
    def test_push_with_position(self):
 | 
			
		||||
        """Ensure that push with position works properly for an instance."""
 | 
			
		||||
        class BlogPost(Document):
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ import unittest
 | 
			
		||||
 | 
			
		||||
from mongoengine import *
 | 
			
		||||
 | 
			
		||||
from tests.utils import MongoDBTestCase, requires_mongodb_gte_3
 | 
			
		||||
from tests.utils import MongoDBTestCase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ("GeoQueriesTest",)
 | 
			
		||||
@@ -70,9 +70,6 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
        self.assertEqual(events.count(), 1)
 | 
			
		||||
        self.assertEqual(events[0], event2)
 | 
			
		||||
 | 
			
		||||
    # $minDistance was added in MongoDB v2.6, but continued being buggy
 | 
			
		||||
    # until v3.0; skip for older versions
 | 
			
		||||
    @requires_mongodb_gte_3
 | 
			
		||||
    def test_near_and_min_distance(self):
 | 
			
		||||
        """Ensure the "min_distance" operator works alongside the "near"
 | 
			
		||||
        operator.
 | 
			
		||||
@@ -243,9 +240,6 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
        events = self.Event.objects(location__geo_within_polygon=polygon2)
 | 
			
		||||
        self.assertEqual(events.count(), 0)
 | 
			
		||||
 | 
			
		||||
    # $minDistance was added in MongoDB v2.6, but continued being buggy
 | 
			
		||||
    # until v3.0; skip for older versions
 | 
			
		||||
    @requires_mongodb_gte_3
 | 
			
		||||
    def test_2dsphere_near_and_min_max_distance(self):
 | 
			
		||||
        """Ensure "min_distace" and "max_distance" operators work well
 | 
			
		||||
        together with the "near" operator in a 2dsphere index.
 | 
			
		||||
@@ -328,8 +322,6 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
        """Make sure PointField works properly in an embedded document."""
 | 
			
		||||
        self._test_embedded(point_field_class=PointField)
 | 
			
		||||
 | 
			
		||||
    # Needs MongoDB > 2.6.4 https://jira.mongodb.org/browse/SERVER-14039
 | 
			
		||||
    @requires_mongodb_gte_3
 | 
			
		||||
    def test_spherical_geospatial_operators(self):
 | 
			
		||||
        """Ensure that spherical geospatial queries are working."""
 | 
			
		||||
        class Point(Document):
 | 
			
		||||
 
 | 
			
		||||
@@ -2,8 +2,6 @@ import unittest
 | 
			
		||||
 | 
			
		||||
from mongoengine import connect, Document, IntField, StringField, ListField
 | 
			
		||||
 | 
			
		||||
from tests.utils import requires_mongodb_gte_26
 | 
			
		||||
 | 
			
		||||
__all__ = ("FindAndModifyTest",)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -96,7 +94,6 @@ class FindAndModifyTest(unittest.TestCase):
 | 
			
		||||
        self.assertEqual(old_doc.to_mongo(), {"_id": 1})
 | 
			
		||||
        self.assertDbEqual([{"_id": 0, "value": 0}, {"_id": 1, "value": -1}])
 | 
			
		||||
 | 
			
		||||
    @requires_mongodb_gte_26
 | 
			
		||||
    def test_modify_with_push(self):
 | 
			
		||||
        class BlogPost(Document):
 | 
			
		||||
            tags = ListField(StringField())
 | 
			
		||||
 
 | 
			
		||||
@@ -17,10 +17,9 @@ from mongoengine import *
 | 
			
		||||
from mongoengine.connection import get_connection, get_db
 | 
			
		||||
from mongoengine.context_managers import query_counter, switch_db
 | 
			
		||||
from mongoengine.errors import InvalidQueryError
 | 
			
		||||
from mongoengine.mongodb_support import get_mongodb_version, MONGODB_32, MONGODB_36
 | 
			
		||||
from mongoengine.mongodb_support import get_mongodb_version, MONGODB_36
 | 
			
		||||
from mongoengine.queryset import (DoesNotExist, MultipleObjectsReturned,
 | 
			
		||||
                                  QuerySet, QuerySetManager, queryset_manager)
 | 
			
		||||
from tests.utils import requires_mongodb_gte_26
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class db_ops_tracker(query_counter):
 | 
			
		||||
@@ -32,7 +31,7 @@ class db_ops_tracker(query_counter):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_key_compat(mongo_ver):
 | 
			
		||||
    ORDER_BY_KEY = 'sort' if mongo_ver >= MONGODB_32 else '$orderby'
 | 
			
		||||
    ORDER_BY_KEY = 'sort'
 | 
			
		||||
    CMD_QUERY_KEY = 'command' if mongo_ver >= MONGODB_36 else 'query'
 | 
			
		||||
    return ORDER_BY_KEY, CMD_QUERY_KEY
 | 
			
		||||
 | 
			
		||||
@@ -598,7 +597,6 @@ class QuerySetTest(unittest.TestCase):
 | 
			
		||||
        self.assertEqual(post.comments[0].by, 'joe')
 | 
			
		||||
        self.assertEqual(post.comments[0].votes.score, 4)
 | 
			
		||||
 | 
			
		||||
    @requires_mongodb_gte_26
 | 
			
		||||
    def test_update_min_max(self):
 | 
			
		||||
        class Scores(Document):
 | 
			
		||||
            high_score = IntField()
 | 
			
		||||
@@ -616,7 +614,6 @@ class QuerySetTest(unittest.TestCase):
 | 
			
		||||
        Scores.objects(id=scores.id).update(max__high_score=500)
 | 
			
		||||
        self.assertEqual(Scores.objects.get(id=scores.id).high_score, 1000)
 | 
			
		||||
 | 
			
		||||
    @requires_mongodb_gte_26
 | 
			
		||||
    def test_update_multiple(self):
 | 
			
		||||
        class Product(Document):
 | 
			
		||||
            item = StringField()
 | 
			
		||||
@@ -868,11 +865,7 @@ class QuerySetTest(unittest.TestCase):
 | 
			
		||||
        with query_counter() as q:
 | 
			
		||||
            self.assertEqual(q, 0)
 | 
			
		||||
            Blog.objects.insert(blogs, load_bulk=False)
 | 
			
		||||
 | 
			
		||||
            if MONGO_VER >= MONGODB_32:
 | 
			
		||||
                self.assertEqual(q, 1)      # 1 entry containing the list of inserts
 | 
			
		||||
            else:
 | 
			
		||||
                self.assertEqual(q, len(blogs))     # 1 entry per doc inserted
 | 
			
		||||
            self.assertEqual(q, 1)  # 1 entry containing the list of inserts
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Blog.objects.count(), len(blogs))
 | 
			
		||||
 | 
			
		||||
@@ -885,11 +878,7 @@ class QuerySetTest(unittest.TestCase):
 | 
			
		||||
        with query_counter() as q:
 | 
			
		||||
            self.assertEqual(q, 0)
 | 
			
		||||
            Blog.objects.insert(blogs)
 | 
			
		||||
 | 
			
		||||
            if MONGO_VER >= MONGODB_32:
 | 
			
		||||
                self.assertEqual(q, 2)              # 1 for insert 1 for fetch
 | 
			
		||||
            else:
 | 
			
		||||
                self.assertEqual(q, len(blogs)+1)       # + 1 to fetch all docs
 | 
			
		||||
            self.assertEqual(q, 2)  # 1 for insert 1 for fetch
 | 
			
		||||
 | 
			
		||||
        Blog.drop_collection()
 | 
			
		||||
 | 
			
		||||
@@ -2030,7 +2019,6 @@ class QuerySetTest(unittest.TestCase):
 | 
			
		||||
        pymongo_doc = BlogPost.objects.as_pymongo().first()
 | 
			
		||||
        self.assertNotIn('title', pymongo_doc)
 | 
			
		||||
 | 
			
		||||
    @requires_mongodb_gte_26
 | 
			
		||||
    def test_update_push_with_position(self):
 | 
			
		||||
        """Ensure that the 'push' update with position works properly.
 | 
			
		||||
        """
 | 
			
		||||
@@ -2555,8 +2543,8 @@ class QuerySetTest(unittest.TestCase):
 | 
			
		||||
        """Make sure adding a comment to the query gets added to the query"""
 | 
			
		||||
        MONGO_VER = self.mongodb_version
 | 
			
		||||
        _, CMD_QUERY_KEY = get_key_compat(MONGO_VER)
 | 
			
		||||
        QUERY_KEY = 'filter' if MONGO_VER >= MONGODB_32 else '$query'
 | 
			
		||||
        COMMENT_KEY = 'comment' if MONGO_VER >= MONGODB_32 else '$comment'
 | 
			
		||||
        QUERY_KEY = 'filter'
 | 
			
		||||
        COMMENT_KEY = 'comment'
 | 
			
		||||
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            age = IntField()
 | 
			
		||||
@@ -3370,7 +3358,6 @@ class QuerySetTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Foo.objects.distinct("bar"), [bar])
 | 
			
		||||
 | 
			
		||||
    @requires_mongodb_gte_26
 | 
			
		||||
    def test_text_indexes(self):
 | 
			
		||||
        class News(Document):
 | 
			
		||||
            title = StringField()
 | 
			
		||||
@@ -3454,7 +3441,6 @@ class QuerySetTest(unittest.TestCase):
 | 
			
		||||
            'brasil').order_by('$text_score').first()
 | 
			
		||||
        self.assertEqual(item.get_text_score(), max_text_score)
 | 
			
		||||
 | 
			
		||||
    @requires_mongodb_gte_26
 | 
			
		||||
    def test_distinct_handles_references_to_alias(self):
 | 
			
		||||
        register_connection('testdb', 'mongoenginetest2')
 | 
			
		||||
 | 
			
		||||
@@ -4586,7 +4572,6 @@ class QuerySetTest(unittest.TestCase):
 | 
			
		||||
        self.assertEqual(bars._cursor._Cursor__read_preference,
 | 
			
		||||
                         ReadPreference.SECONDARY_PREFERRED)
 | 
			
		||||
 | 
			
		||||
    @requires_mongodb_gte_26
 | 
			
		||||
    def test_read_preference_aggregation_framework(self):
 | 
			
		||||
        class Bar(Document):
 | 
			
		||||
            txt = StringField()
 | 
			
		||||
@@ -5354,7 +5339,6 @@ class QuerySetTest(unittest.TestCase):
 | 
			
		||||
            self.assertTrue(Person.objects._has_data(),
 | 
			
		||||
                            'Cursor has data and returned False')
 | 
			
		||||
 | 
			
		||||
    @requires_mongodb_gte_26
 | 
			
		||||
    def test_queryset_aggregation_framework(self):
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
@@ -5396,7 +5380,6 @@ class QuerySetTest(unittest.TestCase):
 | 
			
		||||
            {'_id': None, 'avg': 29, 'total': 2}
 | 
			
		||||
        ])
 | 
			
		||||
 | 
			
		||||
    @requires_mongodb_gte_26
 | 
			
		||||
    def test_queryset_aggregation_with_skip(self):
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
@@ -5418,7 +5401,6 @@ class QuerySetTest(unittest.TestCase):
 | 
			
		||||
            {'_id': p3.pk, 'name': "SANDRA MARA"}
 | 
			
		||||
        ])
 | 
			
		||||
 | 
			
		||||
    @requires_mongodb_gte_26
 | 
			
		||||
    def test_queryset_aggregation_with_limit(self):
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
@@ -5439,7 +5421,6 @@ class QuerySetTest(unittest.TestCase):
 | 
			
		||||
            {'_id': p1.pk, 'name': "ISABELLA LUANNA"}
 | 
			
		||||
        ])
 | 
			
		||||
 | 
			
		||||
    @requires_mongodb_gte_26
 | 
			
		||||
    def test_queryset_aggregation_with_sort(self):
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
@@ -5462,7 +5443,6 @@ class QuerySetTest(unittest.TestCase):
 | 
			
		||||
            {'_id': p2.pk, 'name': "WILSON JUNIOR"}
 | 
			
		||||
        ])
 | 
			
		||||
 | 
			
		||||
    @requires_mongodb_gte_26
 | 
			
		||||
    def test_queryset_aggregation_with_skip_with_limit(self):
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
@@ -5492,7 +5472,6 @@ class QuerySetTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(data, list(data2))
 | 
			
		||||
 | 
			
		||||
    @requires_mongodb_gte_26
 | 
			
		||||
    def test_queryset_aggregation_with_sort_with_limit(self):
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
@@ -5534,7 +5513,6 @@ class QuerySetTest(unittest.TestCase):
 | 
			
		||||
            {'_id': p3.pk, 'name': "SANDRA MARA"},
 | 
			
		||||
        ])
 | 
			
		||||
 | 
			
		||||
    @requires_mongodb_gte_26
 | 
			
		||||
    def test_queryset_aggregation_with_sort_with_skip(self):
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
@@ -5555,7 +5533,6 @@ class QuerySetTest(unittest.TestCase):
 | 
			
		||||
            {'_id': p2.pk, 'name': "WILSON JUNIOR"}
 | 
			
		||||
        ])
 | 
			
		||||
 | 
			
		||||
    @requires_mongodb_gte_26
 | 
			
		||||
    def test_queryset_aggregation_with_sort_with_skip_with_limit(self):
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ from nose.plugins.skip import SkipTest
 | 
			
		||||
 | 
			
		||||
from mongoengine import connect
 | 
			
		||||
from mongoengine.connection import get_db, disconnect_all
 | 
			
		||||
from mongoengine.mongodb_support import get_mongodb_version, MONGODB_26, MONGODB_3, MONGODB_32, MONGODB_34
 | 
			
		||||
from mongoengine.mongodb_support import get_mongodb_version
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MONGO_TEST_DB = 'mongoenginetest'   # standard name for the test database
 | 
			
		||||
@@ -35,8 +35,20 @@ def get_as_pymongo(doc):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _decorated_with_ver_requirement(func, mongo_version_req, oper):
 | 
			
		||||
    """Return a given function decorated with the version requirement
 | 
			
		||||
    for a particular MongoDB version tuple.
 | 
			
		||||
    """Return a MongoDB version requirement decorator.
 | 
			
		||||
 | 
			
		||||
    The resulting decorator will raise a SkipTest exception if the current
 | 
			
		||||
    MongoDB version doesn't match the provided version/operator.
 | 
			
		||||
 | 
			
		||||
    For example, if you define a decorator like so:
 | 
			
		||||
 | 
			
		||||
        def requires_mongodb_gte_36(func):
 | 
			
		||||
            return _decorated_with_ver_requirement(
 | 
			
		||||
                func, (3.6), oper=operator.ge
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    Then tests decorated with @requires_mongodb_gte_36 will be skipped if
 | 
			
		||||
    ran against MongoDB < v3.6.
 | 
			
		||||
 | 
			
		||||
    :param mongo_version_req: The mongodb version requirement (tuple(int, int))
 | 
			
		||||
    :param oper: The operator to apply (e.g: operator.ge)
 | 
			
		||||
@@ -51,31 +63,3 @@ def _decorated_with_ver_requirement(func, mongo_version_req, oper):
 | 
			
		||||
    _inner.__name__ = func.__name__
 | 
			
		||||
    _inner.__doc__ = func.__doc__
 | 
			
		||||
    return _inner
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def requires_mongodb_gte_34(func):
 | 
			
		||||
    """Raise a SkipTest exception if we're working with MongoDB version
 | 
			
		||||
    lower than v3.4
 | 
			
		||||
    """
 | 
			
		||||
    return _decorated_with_ver_requirement(func, MONGODB_34, oper=operator.ge)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def requires_mongodb_lte_32(func):
 | 
			
		||||
    """Raise a SkipTest exception if we're working with MongoDB version
 | 
			
		||||
    greater than v3.2.
 | 
			
		||||
    """
 | 
			
		||||
    return _decorated_with_ver_requirement(func, MONGODB_32, oper=operator.le)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def requires_mongodb_gte_26(func):
 | 
			
		||||
    """Raise a SkipTest exception if we're working with MongoDB version
 | 
			
		||||
    lower than v2.6.
 | 
			
		||||
    """
 | 
			
		||||
    return _decorated_with_ver_requirement(func, MONGODB_26, oper=operator.ge)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def requires_mongodb_gte_3(func):
 | 
			
		||||
    """Raise a SkipTest exception if we're working with MongoDB version
 | 
			
		||||
    lower than v3.0.
 | 
			
		||||
    """
 | 
			
		||||
    return _decorated_with_ver_requirement(func, MONGODB_3, oper=operator.ge)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user