diff --git a/tests/fields/fields.py b/tests/fields/fields.py index 194d07d7..c772b472 100644 --- a/tests/fields/fields.py +++ b/tests/fields/fields.py @@ -1,18 +1,17 @@ # -*- coding: utf-8 -*- import datetime import unittest -import uuid -import sys from nose.plugins.skip import SkipTest -import six - -from decimal import Decimal from bson import DBRef, ObjectId, SON -from mongoengine import * -from mongoengine.base import (BaseDict, BaseField, EmbeddedDocumentList, +from mongoengine import Document, StringField, IntField, DateTimeField, DateField, ValidationError, \ + ComplexDateTimeField, FloatField, ListField, ReferenceField, DictField, EmbeddedDocument, EmbeddedDocumentField, \ + GenericReferenceField, DoesNotExist, NotRegistered, GenericEmbeddedDocumentField, OperationError, DynamicField, \ + FieldDoesNotExist, EmbeddedDocumentListField, MultipleObjectsReturned, NotUniqueError, BooleanField, ObjectIdField, \ + SortedListField, GenericLazyReferenceField, LazyReferenceField, DynamicDocument +from mongoengine.base import (BaseField, EmbeddedDocumentList, _document_registry) from tests.utils import MongoDBTestCase @@ -276,7 +275,7 @@ class FieldTest(MongoDBTestCase): # attempted. self.assertRaises(ValidationError, ret.validate) - def test_object_id_validation(self): + def test_default_id_validation_as_objectid(self): """Ensure that invalid values cannot be assigned to an ObjectIdField. """ @@ -292,7 +291,7 @@ class FieldTest(MongoDBTestCase): person.id = 'abc' self.assertRaises(ValidationError, person.validate) - person.id = '497ce96f395f2f052a494fd4' + person.id = str(ObjectId()) person.validate() def test_string_validation(self): @@ -319,33 +318,6 @@ class FieldTest(MongoDBTestCase): person.name = 'Shorter name' person.validate() - def test_decimal_validation(self): - """Ensure that invalid values cannot be assigned to decimal fields. - """ - class Person(Document): - height = DecimalField(min_value=Decimal('0.1'), - max_value=Decimal('3.5')) - - Person.drop_collection() - - Person(height=Decimal('1.89')).save() - person = Person.objects.first() - self.assertEqual(person.height, Decimal('1.89')) - - person.height = '2.0' - person.save() - person.height = 0.01 - self.assertRaises(ValidationError, person.validate) - person.height = Decimal('0.01') - self.assertRaises(ValidationError, person.validate) - person.height = Decimal('4.0') - self.assertRaises(ValidationError, person.validate) - person.height = 'something invalid' - self.assertRaises(ValidationError, person.validate) - - person_2 = Person(height='something invalid') - self.assertRaises(ValidationError, person_2.validate) - def test_db_field_validation(self): """Ensure that db_field doesn't accept invalid values.""" @@ -364,128 +336,9 @@ class FieldTest(MongoDBTestCase): class User(Document): name = StringField(db_field='name\0') - def test_decimal_comparison(self): - class Person(Document): - money = DecimalField() - - Person.drop_collection() - - Person(money=6).save() - Person(money=8).save() - Person(money=10).save() - - self.assertEqual(2, Person.objects(money__gt=Decimal("7")).count()) - self.assertEqual(2, Person.objects(money__gt=7).count()) - self.assertEqual(2, Person.objects(money__gt="7").count()) - - def test_decimal_storage(self): - class Person(Document): - float_value = DecimalField(precision=4) - string_value = DecimalField(precision=4, force_string=True) - - Person.drop_collection() - values_to_store = [10, 10.1, 10.11, "10.111", Decimal("10.1111"), Decimal("10.11111")] - for store_at_creation in [True, False]: - for value in values_to_store: - # to_python is called explicitly if values were sent in the kwargs of __init__ - if store_at_creation: - Person(float_value=value, string_value=value).save() - else: - person = Person.objects.create() - person.float_value = value - person.string_value = value - person.save() - - # How its stored - expected = [ - {'float_value': 10.0, 'string_value': '10.0000'}, - {'float_value': 10.1, 'string_value': '10.1000'}, - {'float_value': 10.11, 'string_value': '10.1100'}, - {'float_value': 10.111, 'string_value': '10.1110'}, - {'float_value': 10.1111, 'string_value': '10.1111'}, - {'float_value': 10.1111, 'string_value': '10.1111'}] - expected.extend(expected) - actual = list(Person.objects.exclude('id').as_pymongo()) - self.assertEqual(expected, actual) - - # How it comes out locally - expected = [Decimal('10.0000'), Decimal('10.1000'), Decimal('10.1100'), - Decimal('10.1110'), Decimal('10.1111'), Decimal('10.1111')] - expected.extend(expected) - for field_name in ['float_value', 'string_value']: - actual = list(Person.objects().scalar(field_name)) - self.assertEqual(expected, actual) - - def test_boolean_validation(self): - """Ensure that invalid values cannot be assigned to boolean - fields. - """ - class Person(Document): - admin = BooleanField() - - person = Person() - person.admin = True - person.validate() - - person.admin = 2 - self.assertRaises(ValidationError, person.validate) - person.admin = 'Yes' - self.assertRaises(ValidationError, person.validate) - person.admin = 'False' - self.assertRaises(ValidationError, person.validate) - - def test_uuid_field_string(self): - """Test UUID fields storing as String - """ - class Person(Document): - api_key = UUIDField(binary=False) - - Person.drop_collection() - - uu = uuid.uuid4() - Person(api_key=uu).save() - self.assertEqual(1, Person.objects(api_key=uu).count()) - self.assertEqual(uu, Person.objects.first().api_key) - - person = Person() - valid = (uuid.uuid4(), uuid.uuid1()) - for api_key in valid: - person.api_key = api_key - person.validate() - - invalid = ('9d159858-549b-4975-9f98-dd2f987c113g', - '9d159858-549b-4975-9f98-dd2f987c113') - for api_key in invalid: - person.api_key = api_key - self.assertRaises(ValidationError, person.validate) - - def test_uuid_field_binary(self): - """Test UUID fields storing as Binary object.""" - class Person(Document): - api_key = UUIDField(binary=True) - - Person.drop_collection() - - uu = uuid.uuid4() - Person(api_key=uu).save() - self.assertEqual(1, Person.objects(api_key=uu).count()) - self.assertEqual(uu, Person.objects.first().api_key) - - person = Person() - valid = (uuid.uuid4(), uuid.uuid1()) - for api_key in valid: - person.api_key = api_key - person.validate() - - invalid = ('9d159858-549b-4975-9f98-dd2f987c113g', - '9d159858-549b-4975-9f98-dd2f987c113') - for api_key in invalid: - person.api_key = api_key - self.assertRaises(ValidationError, person.validate) - def test_list_validation(self): """Ensure that a list field only accepts lists with valid elements.""" - AccessLevelChoices = ( + access_level_choices = ( ('a', u'Administration'), ('b', u'Manager'), ('c', u'Staff'), @@ -505,7 +358,7 @@ class FieldTest(MongoDBTestCase): authors_as_lazy = ListField(LazyReferenceField(User)) generic = ListField(GenericReferenceField()) generic_as_lazy = ListField(GenericLazyReferenceField()) - access_list = ListField(choices=AccessLevelChoices, display_sep=', ') + access_list = ListField(choices=access_level_choices, display_sep=', ') User.drop_collection() BlogPost.drop_collection() @@ -1187,374 +1040,6 @@ class FieldTest(MongoDBTestCase): self.assertEqual( Simple.objects.filter(mapping__2__list__1__value='Boo').count(), 1) - def test_dict_field(self): - """Ensure that dict types work as expected.""" - class BlogPost(Document): - info = DictField() - - BlogPost.drop_collection() - - post = BlogPost() - post.info = 'my post' - self.assertRaises(ValidationError, post.validate) - - post.info = ['test', 'test'] - self.assertRaises(ValidationError, post.validate) - - post.info = {'$title': 'test'} - self.assertRaises(ValidationError, post.validate) - - post.info = {'nested': {'$title': 'test'}} - self.assertRaises(ValidationError, post.validate) - - post.info = {'the.title': 'test'} - self.assertRaises(ValidationError, post.validate) - - post.info = {'nested': {'the.title': 'test'}} - self.assertRaises(ValidationError, post.validate) - - post.info = {1: 'test'} - self.assertRaises(ValidationError, post.validate) - - post.info = {'title': 'test'} - post.save() - - post = BlogPost() - post.info = {'title': 'dollar_sign', 'details': {'te$t': 'test'}} - post.save() - - post = BlogPost() - post.info = {'details': {'test': 'test'}} - post.save() - - post = BlogPost() - post.info = {'details': {'test': 3}} - post.save() - - self.assertEqual(BlogPost.objects.count(), 4) - self.assertEqual( - BlogPost.objects.filter(info__title__exact='test').count(), 1) - self.assertEqual( - BlogPost.objects.filter(info__details__test__exact='test').count(), 1) - - post = BlogPost.objects.filter(info__title__exact='dollar_sign').first() - self.assertIn('te$t', post['info']['details']) - - # Confirm handles non strings or non existing keys - self.assertEqual( - BlogPost.objects.filter(info__details__test__exact=5).count(), 0) - self.assertEqual( - BlogPost.objects.filter(info__made_up__test__exact='test').count(), 0) - - post = BlogPost.objects.create(info={'title': 'original'}) - post.info.update({'title': 'updated'}) - post.save() - post.reload() - self.assertEqual('updated', post.info['title']) - - post.info.setdefault('authors', []) - post.save() - post.reload() - self.assertEqual([], post.info['authors']) - - def test_dictfield_dump_document(self): - """Ensure a DictField can handle another document's dump.""" - class Doc(Document): - field = DictField() - - class ToEmbed(Document): - id = IntField(primary_key=True, default=1) - recursive = DictField() - - class ToEmbedParent(Document): - id = IntField(primary_key=True, default=1) - recursive = DictField() - - meta = {'allow_inheritance': True} - - class ToEmbedChild(ToEmbedParent): - pass - - to_embed_recursive = ToEmbed(id=1).save() - to_embed = ToEmbed( - id=2, recursive=to_embed_recursive.to_mongo().to_dict()).save() - doc = Doc(field=to_embed.to_mongo().to_dict()) - doc.save() - assert isinstance(doc.field, dict) - assert doc.field == {'_id': 2, 'recursive': {'_id': 1, 'recursive': {}}} - # Same thing with a Document with a _cls field - to_embed_recursive = ToEmbedChild(id=1).save() - to_embed_child = ToEmbedChild( - id=2, recursive=to_embed_recursive.to_mongo().to_dict()).save() - doc = Doc(field=to_embed_child.to_mongo().to_dict()) - doc.save() - assert isinstance(doc.field, dict) - assert doc.field == { - '_id': 2, '_cls': 'ToEmbedParent.ToEmbedChild', - 'recursive': {'_id': 1, '_cls': 'ToEmbedParent.ToEmbedChild', 'recursive': {}} - } - - def test_dictfield_strict(self): - """Ensure that dict field handles validation if provided a strict field type.""" - class Simple(Document): - mapping = DictField(field=IntField()) - - Simple.drop_collection() - - e = Simple() - e.mapping['someint'] = 1 - e.save() - - # try creating an invalid mapping - with self.assertRaises(ValidationError): - e.mapping['somestring'] = "abc" - e.save() - - def test_dictfield_complex(self): - """Ensure that the dict field can handle the complex types.""" - class SettingBase(EmbeddedDocument): - meta = {'allow_inheritance': True} - - class StringSetting(SettingBase): - value = StringField() - - class IntegerSetting(SettingBase): - value = IntField() - - class Simple(Document): - mapping = DictField() - - Simple.drop_collection() - - e = Simple() - e.mapping['somestring'] = StringSetting(value='foo') - e.mapping['someint'] = IntegerSetting(value=42) - e.mapping['nested_dict'] = {'number': 1, 'string': 'Hi!', - 'float': 1.001, - 'complex': IntegerSetting(value=42), - 'list': [IntegerSetting(value=42), - StringSetting(value='foo')]} - e.save() - - e2 = Simple.objects.get(id=e.id) - self.assertIsInstance(e2.mapping['somestring'], StringSetting) - self.assertIsInstance(e2.mapping['someint'], IntegerSetting) - - # Test querying - self.assertEqual( - Simple.objects.filter(mapping__someint__value=42).count(), 1) - self.assertEqual( - Simple.objects.filter(mapping__nested_dict__number=1).count(), 1) - self.assertEqual( - Simple.objects.filter(mapping__nested_dict__complex__value=42).count(), 1) - self.assertEqual( - Simple.objects.filter(mapping__nested_dict__list__0__value=42).count(), 1) - self.assertEqual( - Simple.objects.filter(mapping__nested_dict__list__1__value='foo').count(), 1) - - # Confirm can update - Simple.objects().update( - set__mapping={"someint": IntegerSetting(value=10)}) - Simple.objects().update( - set__mapping__nested_dict__list__1=StringSetting(value='Boo')) - self.assertEqual( - Simple.objects.filter(mapping__nested_dict__list__1__value='foo').count(), 0) - self.assertEqual( - Simple.objects.filter(mapping__nested_dict__list__1__value='Boo').count(), 1) - - def test_atomic_update_dict_field(self): - """Ensure that the entire DictField can be atomically updated.""" - class Simple(Document): - mapping = DictField(field=ListField(IntField(required=True))) - - Simple.drop_collection() - - e = Simple() - e.mapping['someints'] = [1, 2] - e.save() - e.update(set__mapping={"ints": [3, 4]}) - e.reload() - self.assertEqual(BaseDict, type(e.mapping)) - self.assertEqual({"ints": [3, 4]}, e.mapping) - - # try creating an invalid mapping - with self.assertRaises(ValueError): - e.update(set__mapping={"somestrings": ["foo", "bar", ]}) - - def test_dictfield_with_referencefield_complex_nesting_cases(self): - """Ensure complex nesting inside DictField handles dereferencing of ReferenceField(dbref=True | False)""" - # Relates to Issue #1453 - class Doc(Document): - s = StringField() - - class Simple(Document): - mapping0 = DictField(ReferenceField(Doc, dbref=True)) - mapping1 = DictField(ReferenceField(Doc, dbref=False)) - mapping2 = DictField(ListField(ReferenceField(Doc, dbref=True))) - mapping3 = DictField(ListField(ReferenceField(Doc, dbref=False))) - mapping4 = DictField(DictField(field=ReferenceField(Doc, dbref=True))) - mapping5 = DictField(DictField(field=ReferenceField(Doc, dbref=False))) - mapping6 = DictField(ListField(DictField(ReferenceField(Doc, dbref=True)))) - mapping7 = DictField(ListField(DictField(ReferenceField(Doc, dbref=False)))) - mapping8 = DictField(ListField(DictField(ListField(ReferenceField(Doc, dbref=True))))) - mapping9 = DictField(ListField(DictField(ListField(ReferenceField(Doc, dbref=False))))) - - Doc.drop_collection() - Simple.drop_collection() - - d = Doc(s='aa').save() - e = Simple() - e.mapping0['someint'] = e.mapping1['someint'] = d - e.mapping2['someint'] = e.mapping3['someint'] = [d] - e.mapping4['someint'] = e.mapping5['someint'] = {'d': d} - e.mapping6['someint'] = e.mapping7['someint'] = [{'d': d}] - e.mapping8['someint'] = e.mapping9['someint'] = [{'d': [d]}] - e.save() - - s = Simple.objects.first() - self.assertIsInstance(s.mapping0['someint'], Doc) - self.assertIsInstance(s.mapping1['someint'], Doc) - self.assertIsInstance(s.mapping2['someint'][0], Doc) - self.assertIsInstance(s.mapping3['someint'][0], Doc) - self.assertIsInstance(s.mapping4['someint']['d'], Doc) - self.assertIsInstance(s.mapping5['someint']['d'], Doc) - self.assertIsInstance(s.mapping6['someint'][0]['d'], Doc) - self.assertIsInstance(s.mapping7['someint'][0]['d'], Doc) - self.assertIsInstance(s.mapping8['someint'][0]['d'][0], Doc) - self.assertIsInstance(s.mapping9['someint'][0]['d'][0], Doc) - - def test_mapfield(self): - """Ensure that the MapField handles the declared type.""" - class Simple(Document): - mapping = MapField(IntField()) - - Simple.drop_collection() - - e = Simple() - e.mapping['someint'] = 1 - e.save() - - with self.assertRaises(ValidationError): - e.mapping['somestring'] = "abc" - e.save() - - with self.assertRaises(ValidationError): - class NoDeclaredType(Document): - mapping = MapField() - - def test_complex_mapfield(self): - """Ensure that the MapField can handle complex declared types.""" - class SettingBase(EmbeddedDocument): - meta = {"allow_inheritance": True} - - class StringSetting(SettingBase): - value = StringField() - - class IntegerSetting(SettingBase): - value = IntField() - - class Extensible(Document): - mapping = MapField(EmbeddedDocumentField(SettingBase)) - - Extensible.drop_collection() - - e = Extensible() - e.mapping['somestring'] = StringSetting(value='foo') - e.mapping['someint'] = IntegerSetting(value=42) - e.save() - - e2 = Extensible.objects.get(id=e.id) - self.assertIsInstance(e2.mapping['somestring'], StringSetting) - self.assertIsInstance(e2.mapping['someint'], IntegerSetting) - - with self.assertRaises(ValidationError): - e.mapping['someint'] = 123 - e.save() - - def test_embedded_mapfield_db_field(self): - class Embedded(EmbeddedDocument): - number = IntField(default=0, db_field='i') - - class Test(Document): - my_map = MapField(field=EmbeddedDocumentField(Embedded), - db_field='x') - - Test.drop_collection() - - test = Test() - test.my_map['DICTIONARY_KEY'] = Embedded(number=1) - test.save() - - Test.objects.update_one(inc__my_map__DICTIONARY_KEY__number=1) - - test = Test.objects.get() - self.assertEqual(test.my_map['DICTIONARY_KEY'].number, 2) - doc = self.db.test.find_one() - self.assertEqual(doc['x']['DICTIONARY_KEY']['i'], 2) - - def test_mapfield_numerical_index(self): - """Ensure that MapField accept numeric strings as indexes.""" - class Embedded(EmbeddedDocument): - name = StringField() - - class Test(Document): - my_map = MapField(EmbeddedDocumentField(Embedded)) - - Test.drop_collection() - - test = Test() - test.my_map['1'] = Embedded(name='test') - test.save() - test.my_map['1'].name = 'test updated' - test.save() - - def test_map_field_lookup(self): - """Ensure MapField lookups succeed on Fields without a lookup - method. - """ - class Action(EmbeddedDocument): - operation = StringField() - object = StringField() - - class Log(Document): - name = StringField() - visited = MapField(DateTimeField()) - actions = MapField(EmbeddedDocumentField(Action)) - - Log.drop_collection() - Log(name="wilson", visited={'friends': datetime.datetime.now()}, - actions={'friends': Action(operation='drink', object='beer')}).save() - - self.assertEqual(1, Log.objects( - visited__friends__exists=True).count()) - - self.assertEqual(1, Log.objects( - actions__friends__operation='drink', - actions__friends__object='beer').count()) - - def test_map_field_unicode(self): - class Info(EmbeddedDocument): - description = StringField() - value_list = ListField(field=StringField()) - - class BlogPost(Document): - info_dict = MapField(field=EmbeddedDocumentField(Info)) - - BlogPost.drop_collection() - - tree = BlogPost(info_dict={ - u"éééé": { - 'description': u"VALUE: éééé" - } - }) - - tree.save() - - self.assertEqual( - BlogPost.objects.get(id=tree.id).info_dict[u"éééé"].description, - u"VALUE: éééé" - ) - def test_embedded_db_field(self): class Embedded(EmbeddedDocument): number = IntField(default=0, db_field='i') @@ -1741,121 +1226,6 @@ class FieldTest(MongoDBTestCase): bar._fields['generic_ref']._auto_dereference = False self.assertEqual(bar.generic_ref, {'_ref': expected, '_cls': 'Foo'}) - def test_reference_validation(self): - """Ensure that invalid document objects cannot be assigned to - reference fields. - """ - class User(Document): - name = StringField() - - class BlogPost(Document): - content = StringField() - author = ReferenceField(User) - - User.drop_collection() - BlogPost.drop_collection() - - # Make sure ReferenceField only accepts a document class or a string - # with a document class name. - self.assertRaises(ValidationError, ReferenceField, EmbeddedDocument) - - user = User(name='Test User') - - # Ensure that the referenced object must have been saved - post1 = BlogPost(content='Chips and gravy taste good.') - post1.author = user - self.assertRaises(ValidationError, post1.save) - - # Check that an invalid object type cannot be used - post2 = BlogPost(content='Chips and chilli taste good.') - post1.author = post2 - self.assertRaises(ValidationError, post1.validate) - - # Ensure ObjectID's are accepted as references - user_object_id = user.pk - post3 = BlogPost(content="Chips and curry sauce taste good.") - post3.author = user_object_id - post3.save() - - # Make sure referencing a saved document of the right type works - user.save() - post1.author = user - post1.save() - - # Make sure referencing a saved document of the *wrong* type fails - post2.save() - post1.author = post2 - self.assertRaises(ValidationError, post1.validate) - - def test_objectid_reference_fields(self): - """Make sure storing Object ID references works.""" - class Person(Document): - name = StringField() - parent = ReferenceField('self') - - Person.drop_collection() - - p1 = Person(name="John").save() - Person(name="Ross", parent=p1.pk).save() - - p = Person.objects.get(name="Ross") - self.assertEqual(p.parent, p1) - - def test_dbref_reference_fields(self): - """Make sure storing references as bson.dbref.DBRef works.""" - class Person(Document): - name = StringField() - parent = ReferenceField('self', dbref=True) - - Person.drop_collection() - - p1 = Person(name="John").save() - Person(name="Ross", parent=p1).save() - - self.assertEqual( - Person._get_collection().find_one({'name': 'Ross'})['parent'], - DBRef('person', p1.pk) - ) - - p = Person.objects.get(name="Ross") - self.assertEqual(p.parent, p1) - - def test_dbref_to_mongo(self): - """Make sure that calling to_mongo on a ReferenceField which - has dbref=False, but actually actually contains a DBRef returns - an ID of that DBRef. - """ - class Person(Document): - name = StringField() - parent = ReferenceField('self', dbref=False) - - p = Person( - name='Steve', - parent=DBRef('person', 'abcdefghijklmnop') - ) - self.assertEqual(p.to_mongo(), SON([ - ('name', u'Steve'), - ('parent', 'abcdefghijklmnop') - ])) - - def test_objectid_reference_fields(self): - - class Person(Document): - name = StringField() - parent = ReferenceField('self', dbref=False) - - Person.drop_collection() - - p1 = Person(name="John").save() - Person(name="Ross", parent=p1).save() - - col = Person._get_collection() - data = col.find_one({'name': 'Ross'}) - self.assertEqual(data['parent'], p1.pk) - - p = Person.objects.get(name="Ross") - self.assertEqual(p.parent, p1) - def test_list_item_dereference(self): """Ensure that DBRef items in ListFields are dereferenced. """ @@ -1972,99 +1342,6 @@ class FieldTest(MongoDBTestCase): self.assertEqual(tree.children[0].children[0].name, second_child.name) self.assertEqual(tree.children[0].children[1].name, third_child.name) - def test_undefined_reference(self): - """Ensure that ReferenceFields may reference undefined Documents. - """ - class Product(Document): - name = StringField() - company = ReferenceField('Company') - - class Company(Document): - name = StringField() - - Product.drop_collection() - Company.drop_collection() - - ten_gen = Company(name='10gen') - ten_gen.save() - mongodb = Product(name='MongoDB', company=ten_gen) - mongodb.save() - - me = Product(name='MongoEngine') - me.save() - - obj = Product.objects(company=ten_gen).first() - self.assertEqual(obj, mongodb) - self.assertEqual(obj.company, ten_gen) - - obj = Product.objects(company=None).first() - self.assertEqual(obj, me) - - obj = Product.objects.get(company=None) - self.assertEqual(obj, me) - - def test_reference_query_conversion(self): - """Ensure that ReferenceFields can be queried using objects and values - of the type of the primary key of the referenced object. - """ - class Member(Document): - user_num = IntField(primary_key=True) - - class BlogPost(Document): - title = StringField() - author = ReferenceField(Member, dbref=False) - - Member.drop_collection() - BlogPost.drop_collection() - - m1 = Member(user_num=1) - m1.save() - m2 = Member(user_num=2) - m2.save() - - post1 = BlogPost(title='post 1', author=m1) - post1.save() - - post2 = BlogPost(title='post 2', author=m2) - post2.save() - - post = BlogPost.objects(author=m1).first() - self.assertEqual(post.id, post1.id) - - post = BlogPost.objects(author=m2).first() - self.assertEqual(post.id, post2.id) - - def test_reference_query_conversion_dbref(self): - """Ensure that ReferenceFields can be queried using objects and values - of the type of the primary key of the referenced object. - """ - class Member(Document): - user_num = IntField(primary_key=True) - - class BlogPost(Document): - title = StringField() - author = ReferenceField(Member, dbref=True) - - Member.drop_collection() - BlogPost.drop_collection() - - m1 = Member(user_num=1) - m1.save() - m2 = Member(user_num=2) - m2.save() - - post1 = BlogPost(title='post 1', author=m1) - post1.save() - - post2 = BlogPost(title='post 2', author=m2) - post2.save() - - post = BlogPost.objects(author=m1).first() - self.assertEqual(post.id, post1.id) - - post = BlogPost.objects(author=m2).first() - self.assertEqual(post.id, post2.id) - def test_drop_abstract_document(self): """Ensure that an abstract document cannot be dropped given it has no underlying collection. @@ -2681,283 +1958,6 @@ class FieldTest(MongoDBTestCase): self.assertEqual(error_dict['size'], SIZE_MESSAGE) self.assertEqual(error_dict['color'], COLOR_MESSAGE) - def test_ensure_unique_default_instances(self): - """Ensure that every field has it's own unique default instance.""" - class D(Document): - data = DictField() - data2 = DictField(default=lambda: {}) - - d1 = D() - d1.data['foo'] = 'bar' - d1.data2['foo'] = 'bar' - d2 = D() - self.assertEqual(d2.data, {}) - self.assertEqual(d2.data2, {}) - - def test_sequence_field(self): - class Person(Document): - id = SequenceField(primary_key=True) - name = StringField() - - self.db['mongoengine.counters'].drop() - Person.drop_collection() - - for x in range(10): - Person(name="Person %s" % x).save() - - c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'}) - self.assertEqual(c['next'], 10) - - ids = [i.id for i in Person.objects] - self.assertEqual(ids, range(1, 11)) - - c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'}) - self.assertEqual(c['next'], 10) - - Person.id.set_next_value(1000) - c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'}) - self.assertEqual(c['next'], 1000) - - def test_sequence_field_get_next_value(self): - class Person(Document): - id = SequenceField(primary_key=True) - name = StringField() - - self.db['mongoengine.counters'].drop() - Person.drop_collection() - - for x in range(10): - Person(name="Person %s" % x).save() - - self.assertEqual(Person.id.get_next_value(), 11) - self.db['mongoengine.counters'].drop() - - self.assertEqual(Person.id.get_next_value(), 1) - - class Person(Document): - id = SequenceField(primary_key=True, value_decorator=str) - name = StringField() - - self.db['mongoengine.counters'].drop() - Person.drop_collection() - - for x in range(10): - Person(name="Person %s" % x).save() - - self.assertEqual(Person.id.get_next_value(), '11') - self.db['mongoengine.counters'].drop() - - self.assertEqual(Person.id.get_next_value(), '1') - - def test_sequence_field_sequence_name(self): - class Person(Document): - id = SequenceField(primary_key=True, sequence_name='jelly') - name = StringField() - - self.db['mongoengine.counters'].drop() - Person.drop_collection() - - for x in range(10): - Person(name="Person %s" % x).save() - - c = self.db['mongoengine.counters'].find_one({'_id': 'jelly.id'}) - self.assertEqual(c['next'], 10) - - ids = [i.id for i in Person.objects] - self.assertEqual(ids, range(1, 11)) - - c = self.db['mongoengine.counters'].find_one({'_id': 'jelly.id'}) - self.assertEqual(c['next'], 10) - - Person.id.set_next_value(1000) - c = self.db['mongoengine.counters'].find_one({'_id': 'jelly.id'}) - self.assertEqual(c['next'], 1000) - - def test_multiple_sequence_fields(self): - class Person(Document): - id = SequenceField(primary_key=True) - counter = SequenceField() - name = StringField() - - self.db['mongoengine.counters'].drop() - Person.drop_collection() - - for x in range(10): - Person(name="Person %s" % x).save() - - c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'}) - self.assertEqual(c['next'], 10) - - ids = [i.id for i in Person.objects] - self.assertEqual(ids, range(1, 11)) - - counters = [i.counter for i in Person.objects] - self.assertEqual(counters, range(1, 11)) - - c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'}) - self.assertEqual(c['next'], 10) - - Person.id.set_next_value(1000) - c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'}) - self.assertEqual(c['next'], 1000) - - Person.counter.set_next_value(999) - c = self.db['mongoengine.counters'].find_one({'_id': 'person.counter'}) - self.assertEqual(c['next'], 999) - - def test_sequence_fields_reload(self): - class Animal(Document): - counter = SequenceField() - name = StringField() - - self.db['mongoengine.counters'].drop() - Animal.drop_collection() - - a = Animal(name="Boi").save() - - self.assertEqual(a.counter, 1) - a.reload() - self.assertEqual(a.counter, 1) - - a.counter = None - self.assertEqual(a.counter, 2) - a.save() - - self.assertEqual(a.counter, 2) - - a = Animal.objects.first() - self.assertEqual(a.counter, 2) - a.reload() - self.assertEqual(a.counter, 2) - - def test_multiple_sequence_fields_on_docs(self): - class Animal(Document): - id = SequenceField(primary_key=True) - name = StringField() - - class Person(Document): - id = SequenceField(primary_key=True) - name = StringField() - - self.db['mongoengine.counters'].drop() - Animal.drop_collection() - Person.drop_collection() - - for x in range(10): - Animal(name="Animal %s" % x).save() - Person(name="Person %s" % x).save() - - c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'}) - self.assertEqual(c['next'], 10) - - c = self.db['mongoengine.counters'].find_one({'_id': 'animal.id'}) - self.assertEqual(c['next'], 10) - - ids = [i.id for i in Person.objects] - self.assertEqual(ids, range(1, 11)) - - id = [i.id for i in Animal.objects] - self.assertEqual(id, range(1, 11)) - - c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'}) - self.assertEqual(c['next'], 10) - - c = self.db['mongoengine.counters'].find_one({'_id': 'animal.id'}) - self.assertEqual(c['next'], 10) - - def test_sequence_field_value_decorator(self): - class Person(Document): - id = SequenceField(primary_key=True, value_decorator=str) - name = StringField() - - self.db['mongoengine.counters'].drop() - Person.drop_collection() - - for x in range(10): - p = Person(name="Person %s" % x) - p.save() - - c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'}) - self.assertEqual(c['next'], 10) - - ids = [i.id for i in Person.objects] - self.assertEqual(ids, map(str, range(1, 11))) - - c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'}) - self.assertEqual(c['next'], 10) - - def test_embedded_sequence_field(self): - class Comment(EmbeddedDocument): - id = SequenceField() - content = StringField(required=True) - - class Post(Document): - title = StringField(required=True) - comments = ListField(EmbeddedDocumentField(Comment)) - - self.db['mongoengine.counters'].drop() - Post.drop_collection() - - Post(title="MongoEngine", - comments=[Comment(content="NoSQL Rocks"), - Comment(content="MongoEngine Rocks")]).save() - c = self.db['mongoengine.counters'].find_one({'_id': 'comment.id'}) - self.assertEqual(c['next'], 2) - post = Post.objects.first() - self.assertEqual(1, post.comments[0].id) - self.assertEqual(2, post.comments[1].id) - - def test_inherited_sequencefield(self): - class Base(Document): - name = StringField() - counter = SequenceField() - meta = {'abstract': True} - - class Foo(Base): - pass - - class Bar(Base): - pass - - bar = Bar(name='Bar') - bar.save() - - foo = Foo(name='Foo') - foo.save() - - self.assertTrue('base.counter' in - self.db['mongoengine.counters'].find().distinct('_id')) - self.assertFalse(('foo.counter' or 'bar.counter') in - self.db['mongoengine.counters'].find().distinct('_id')) - self.assertNotEqual(foo.counter, bar.counter) - self.assertEqual(foo._fields['counter'].owner_document, Base) - self.assertEqual(bar._fields['counter'].owner_document, Base) - - def test_no_inherited_sequencefield(self): - class Base(Document): - name = StringField() - meta = {'abstract': True} - - class Foo(Base): - counter = SequenceField() - - class Bar(Base): - counter = SequenceField() - - bar = Bar(name='Bar') - bar.save() - - foo = Foo(name='Foo') - foo.save() - - self.assertFalse('base.counter' in - self.db['mongoengine.counters'].find().distinct('_id')) - self.assertTrue(('foo.counter' and 'bar.counter') in - self.db['mongoengine.counters'].find().distinct('_id')) - self.assertEqual(foo.counter, bar.counter) - self.assertEqual(foo._fields['counter'].owner_document, Foo) - self.assertEqual(bar._fields['counter'].owner_document, Bar) - def test_generic_embedded_document(self): class Car(EmbeddedDocument): name = StringField() @@ -3078,117 +2078,6 @@ class FieldTest(MongoDBTestCase): post.comments[1].content = 'here we go' post.validate() - def test_email_field(self): - class User(Document): - email = EmailField() - - user = User(email='ross@example.com') - user.validate() - - user = User(email='ross@example.co.uk') - user.validate() - - user = User(email=('Kofq@rhom0e4klgauOhpbpNdogawnyIKvQS0wk2mjqrgGQ5S' - 'aJIazqqWkm7.net')) - user.validate() - - user = User(email='new-tld@example.technology') - user.validate() - - user = User(email='ross@example.com.') - self.assertRaises(ValidationError, user.validate) - - # unicode domain - user = User(email=u'user@пример.рф') - user.validate() - - # invalid unicode domain - user = User(email=u'user@пример') - self.assertRaises(ValidationError, user.validate) - - # invalid data type - user = User(email=123) - self.assertRaises(ValidationError, user.validate) - - def test_email_field_unicode_user(self): - # Don't run this test on pypy3, which doesn't support unicode regex: - # https://bitbucket.org/pypy/pypy/issues/1821/regular-expression-doesnt-find-unicode - if sys.version_info[:2] == (3, 2): - raise SkipTest('unicode email addresses are not supported on PyPy 3') - - class User(Document): - email = EmailField() - - # unicode user shouldn't validate by default... - user = User(email=u'Dörte@Sörensen.example.com') - self.assertRaises(ValidationError, user.validate) - - # ...but it should be fine with allow_utf8_user set to True - class User(Document): - email = EmailField(allow_utf8_user=True) - - user = User(email=u'Dörte@Sörensen.example.com') - user.validate() - - def test_email_field_domain_whitelist(self): - class User(Document): - email = EmailField() - - # localhost domain shouldn't validate by default... - user = User(email='me@localhost') - self.assertRaises(ValidationError, user.validate) - - # ...but it should be fine if it's whitelisted - class User(Document): - email = EmailField(domain_whitelist=['localhost']) - - user = User(email='me@localhost') - user.validate() - - def test_email_field_ip_domain(self): - class User(Document): - email = EmailField() - - valid_ipv4 = 'email@[127.0.0.1]' - valid_ipv6 = 'email@[2001:dB8::1]' - invalid_ip = 'email@[324.0.0.1]' - - # IP address as a domain shouldn't validate by default... - user = User(email=valid_ipv4) - self.assertRaises(ValidationError, user.validate) - - user = User(email=valid_ipv6) - self.assertRaises(ValidationError, user.validate) - - user = User(email=invalid_ip) - self.assertRaises(ValidationError, user.validate) - - # ...but it should be fine with allow_ip_domain set to True - class User(Document): - email = EmailField(allow_ip_domain=True) - - user = User(email=valid_ipv4) - user.validate() - - user = User(email=valid_ipv6) - user.validate() - - # invalid IP should still fail validation - user = User(email=invalid_ip) - self.assertRaises(ValidationError, user.validate) - - def test_email_field_honors_regex(self): - class User(Document): - email = EmailField(regex=r'\w+@example.com') - - # Fails regex validation - user = User(email='me@foo.com') - self.assertRaises(ValidationError, user.validate) - - # Passes regex validation - user = User(email='me@example.com') - self.assertIsNone(user.validate()) - def test_tuples_as_tuples(self): """Ensure that tuples remain tuples when they are inside a ComplexBaseField. @@ -3289,36 +2178,6 @@ class FieldTest(MongoDBTestCase): assert isinstance(doc.field, ToEmbedChild) assert doc.field == to_embed_child - def test_dict_field_invalid_dict_value(self): - class DictFieldTest(Document): - dictionary = DictField(required=True) - - DictFieldTest.drop_collection() - - test = DictFieldTest(dictionary=None) - test.dictionary # Just access to test getter - self.assertRaises(ValidationError, test.validate) - - test = DictFieldTest(dictionary=False) - test.dictionary # Just access to test getter - self.assertRaises(ValidationError, test.validate) - - def test_dict_field_raises_validation_error_if_wrongly_assign_embedded_doc(self): - class DictFieldTest(Document): - dictionary = DictField(required=True) - - DictFieldTest.drop_collection() - - class Embedded(EmbeddedDocument): - name = StringField() - - embed = Embedded(name='garbage') - doc = DictFieldTest(dictionary=embed) - with self.assertRaises(ValidationError) as ctx_err: - doc.validate() - self.assertIn("'dictionary'", str(ctx_err.exception)) - self.assertIn('Only dictionaries may be used in a DictField', str(ctx_err.exception)) - def test_cls_field(self): class Animal(Document): meta = {'allow_inheritance': True} @@ -3882,442 +2741,5 @@ class TestEmbeddedDocumentField(MongoDBTestCase): emb = EmbeddedDocumentField('MyDoc') -class CachedReferenceFieldTest(MongoDBTestCase): - - def test_cached_reference_field_get_and_save(self): - """ - Tests #1047: CachedReferenceField creates DBRefs on to_python, - but can't save them on to_mongo. - """ - class Animal(Document): - name = StringField() - tag = StringField() - - class Ocorrence(Document): - person = StringField() - animal = CachedReferenceField(Animal) - - Animal.drop_collection() - Ocorrence.drop_collection() - - Ocorrence(person="testte", - animal=Animal(name="Leopard", tag="heavy").save()).save() - p = Ocorrence.objects.get() - p.person = 'new_testte' - p.save() - - def test_cached_reference_fields(self): - class Animal(Document): - name = StringField() - tag = StringField() - - class Ocorrence(Document): - person = StringField() - animal = CachedReferenceField( - Animal, fields=['tag']) - - Animal.drop_collection() - Ocorrence.drop_collection() - - a = Animal(name="Leopard", tag="heavy") - a.save() - - self.assertEqual(Animal._cached_reference_fields, [Ocorrence.animal]) - o = Ocorrence(person="teste", animal=a) - o.save() - - p = Ocorrence(person="Wilson") - p.save() - - self.assertEqual(Ocorrence.objects(animal=None).count(), 1) - - self.assertEqual( - a.to_mongo(fields=['tag']), {'tag': 'heavy', "_id": a.pk}) - - self.assertEqual(o.to_mongo()['animal']['tag'], 'heavy') - - # counts - Ocorrence(person="teste 2").save() - Ocorrence(person="teste 3").save() - - count = Ocorrence.objects(animal__tag='heavy').count() - self.assertEqual(count, 1) - - ocorrence = Ocorrence.objects(animal__tag='heavy').first() - self.assertEqual(ocorrence.person, "teste") - self.assertIsInstance(ocorrence.animal, Animal) - - def test_cached_reference_field_decimal(self): - class PersonAuto(Document): - name = StringField() - salary = DecimalField() - - class SocialTest(Document): - group = StringField() - person = CachedReferenceField( - PersonAuto, - fields=('salary',)) - - PersonAuto.drop_collection() - SocialTest.drop_collection() - - p = PersonAuto(name="Alberto", salary=Decimal('7000.00')) - p.save() - - s = SocialTest(group="dev", person=p) - s.save() - - self.assertEqual( - SocialTest.objects._collection.find_one({'person.salary': 7000.00}), { - '_id': s.pk, - 'group': s.group, - 'person': { - '_id': p.pk, - 'salary': 7000.00 - } - }) - - def test_cached_reference_field_reference(self): - class Group(Document): - name = StringField() - - class Person(Document): - name = StringField() - group = ReferenceField(Group) - - class SocialData(Document): - obs = StringField() - tags = ListField( - StringField()) - person = CachedReferenceField( - Person, - fields=('group',)) - - Group.drop_collection() - Person.drop_collection() - SocialData.drop_collection() - - g1 = Group(name='dev') - g1.save() - - g2 = Group(name="designers") - g2.save() - - p1 = Person(name="Alberto", group=g1) - p1.save() - - p2 = Person(name="Andre", group=g1) - p2.save() - - p3 = Person(name="Afro design", group=g2) - p3.save() - - s1 = SocialData(obs="testing 123", person=p1, tags=['tag1', 'tag2']) - s1.save() - - s2 = SocialData(obs="testing 321", person=p3, tags=['tag3', 'tag4']) - s2.save() - - self.assertEqual(SocialData.objects._collection.find_one( - {'tags': 'tag2'}), { - '_id': s1.pk, - 'obs': 'testing 123', - 'tags': ['tag1', 'tag2'], - 'person': { - '_id': p1.pk, - 'group': g1.pk - } - }) - - self.assertEqual(SocialData.objects(person__group=g2).count(), 1) - self.assertEqual(SocialData.objects(person__group=g2).first(), s2) - - def test_cached_reference_field_push_with_fields(self): - class Product(Document): - name = StringField() - - Product.drop_collection() - - class Basket(Document): - products = ListField(CachedReferenceField(Product, fields=['name'])) - - Basket.drop_collection() - product1 = Product(name='abc').save() - product2 = Product(name='def').save() - basket = Basket(products=[product1]).save() - self.assertEqual( - Basket.objects._collection.find_one(), - { - '_id': basket.pk, - 'products': [ - { - '_id': product1.pk, - 'name': product1.name - } - ] - } - ) - # push to list - basket.update(push__products=product2) - basket.reload() - self.assertEqual( - Basket.objects._collection.find_one(), - { - '_id': basket.pk, - 'products': [ - { - '_id': product1.pk, - 'name': product1.name - }, - { - '_id': product2.pk, - 'name': product2.name - } - ] - } - ) - - def test_cached_reference_field_update_all(self): - class Person(Document): - TYPES = ( - ('pf', "PF"), - ('pj', "PJ") - ) - name = StringField() - tp = StringField( - choices=TYPES - ) - - father = CachedReferenceField('self', fields=('tp',)) - - Person.drop_collection() - - a1 = Person(name="Wilson Father", tp="pj") - a1.save() - - a2 = Person(name='Wilson Junior', tp='pf', father=a1) - a2.save() - - self.assertEqual(dict(a2.to_mongo()), { - "_id": a2.pk, - "name": u"Wilson Junior", - "tp": u"pf", - "father": { - "_id": a1.pk, - "tp": u"pj" - } - }) - - self.assertEqual(Person.objects(father=a1)._query, { - 'father._id': a1.pk - }) - self.assertEqual(Person.objects(father=a1).count(), 1) - - Person.objects.update(set__tp="pf") - Person.father.sync_all() - - a2.reload() - self.assertEqual(dict(a2.to_mongo()), { - "_id": a2.pk, - "name": u"Wilson Junior", - "tp": u"pf", - "father": { - "_id": a1.pk, - "tp": u"pf" - } - }) - - def test_cached_reference_fields_on_embedded_documents(self): - with self.assertRaises(InvalidDocumentError): - class Test(Document): - name = StringField() - - type('WrongEmbeddedDocument', ( - EmbeddedDocument,), { - 'test': CachedReferenceField(Test) - }) - - def test_cached_reference_auto_sync(self): - class Person(Document): - TYPES = ( - ('pf', "PF"), - ('pj', "PJ") - ) - name = StringField() - tp = StringField( - choices=TYPES - ) - - father = CachedReferenceField('self', fields=('tp',)) - - Person.drop_collection() - - a1 = Person(name="Wilson Father", tp="pj") - a1.save() - - a2 = Person(name='Wilson Junior', tp='pf', father=a1) - a2.save() - - a1.tp = 'pf' - a1.save() - - a2.reload() - self.assertEqual(dict(a2.to_mongo()), { - '_id': a2.pk, - 'name': 'Wilson Junior', - 'tp': 'pf', - 'father': { - '_id': a1.pk, - 'tp': 'pf' - } - }) - - def test_cached_reference_auto_sync_disabled(self): - class Persone(Document): - TYPES = ( - ('pf', "PF"), - ('pj', "PJ") - ) - name = StringField() - tp = StringField( - choices=TYPES - ) - - father = CachedReferenceField( - 'self', fields=('tp',), auto_sync=False) - - Persone.drop_collection() - - a1 = Persone(name="Wilson Father", tp="pj") - a1.save() - - a2 = Persone(name='Wilson Junior', tp='pf', father=a1) - a2.save() - - a1.tp = 'pf' - a1.save() - - self.assertEqual(Persone.objects._collection.find_one({'_id': a2.pk}), { - '_id': a2.pk, - 'name': 'Wilson Junior', - 'tp': 'pf', - 'father': { - '_id': a1.pk, - 'tp': 'pj' - } - }) - - def test_cached_reference_embedded_fields(self): - class Owner(EmbeddedDocument): - TPS = ( - ('n', "Normal"), - ('u', "Urgent") - ) - name = StringField() - tp = StringField( - verbose_name="Type", - db_field="t", - choices=TPS) - - class Animal(Document): - name = StringField() - tag = StringField() - - owner = EmbeddedDocumentField(Owner) - - class Ocorrence(Document): - person = StringField() - animal = CachedReferenceField( - Animal, fields=['tag', 'owner.tp']) - - Animal.drop_collection() - Ocorrence.drop_collection() - - a = Animal(name="Leopard", tag="heavy", - owner=Owner(tp='u', name="Wilson Júnior") - ) - a.save() - - o = Ocorrence(person="teste", animal=a) - o.save() - self.assertEqual(dict(a.to_mongo(fields=['tag', 'owner.tp'])), { - '_id': a.pk, - 'tag': 'heavy', - 'owner': { - 't': 'u' - } - }) - self.assertEqual(o.to_mongo()['animal']['tag'], 'heavy') - self.assertEqual(o.to_mongo()['animal']['owner']['t'], 'u') - - # counts - Ocorrence(person="teste 2").save() - Ocorrence(person="teste 3").save() - - count = Ocorrence.objects( - animal__tag='heavy', animal__owner__tp='u').count() - self.assertEqual(count, 1) - - ocorrence = Ocorrence.objects( - animal__tag='heavy', - animal__owner__tp='u').first() - self.assertEqual(ocorrence.person, "teste") - self.assertIsInstance(ocorrence.animal, Animal) - - def test_cached_reference_embedded_list_fields(self): - class Owner(EmbeddedDocument): - name = StringField() - tags = ListField(StringField()) - - class Animal(Document): - name = StringField() - tag = StringField() - - owner = EmbeddedDocumentField(Owner) - - class Ocorrence(Document): - person = StringField() - animal = CachedReferenceField( - Animal, fields=['tag', 'owner.tags']) - - Animal.drop_collection() - Ocorrence.drop_collection() - - a = Animal(name="Leopard", tag="heavy", - owner=Owner(tags=['cool', 'funny'], - name="Wilson Júnior") - ) - a.save() - - o = Ocorrence(person="teste 2", animal=a) - o.save() - self.assertEqual(dict(a.to_mongo(fields=['tag', 'owner.tags'])), { - '_id': a.pk, - 'tag': 'heavy', - 'owner': { - 'tags': ['cool', 'funny'] - } - }) - - self.assertEqual(o.to_mongo()['animal']['tag'], 'heavy') - self.assertEqual(o.to_mongo()['animal']['owner']['tags'], - ['cool', 'funny']) - - # counts - Ocorrence(person="teste 2").save() - Ocorrence(person="teste 3").save() - - query = Ocorrence.objects( - animal__tag='heavy', animal__owner__tags='cool')._query - self.assertEqual( - query, {'animal.owner.tags': 'cool', 'animal.tag': 'heavy'}) - - ocorrence = Ocorrence.objects( - animal__tag='heavy', - animal__owner__tags='cool').first() - self.assertEqual(ocorrence.person, "teste 2") - self.assertIsInstance(ocorrence.animal, Animal) - - if __name__ == '__main__': unittest.main() diff --git a/tests/fields/test_boolean_field.py b/tests/fields/test_boolean_field.py new file mode 100644 index 00000000..7a2a3db6 --- /dev/null +++ b/tests/fields/test_boolean_field.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +from mongoengine import * + +from tests.utils import MongoDBTestCase, get_as_pymongo + + +class TestBooleanField(MongoDBTestCase): + def test_storage(self): + class Person(Document): + admin = BooleanField() + + person = Person(admin=True) + person.save() + self.assertEqual( + get_as_pymongo(person), + {'_id': person.id, + 'admin': True}) + + def test_validation(self): + """Ensure that invalid values cannot be assigned to boolean + fields. + """ + class Person(Document): + admin = BooleanField() + + person = Person() + person.admin = True + person.validate() + + person.admin = 2 + self.assertRaises(ValidationError, person.validate) + person.admin = 'Yes' + self.assertRaises(ValidationError, person.validate) + person.admin = 'False' + self.assertRaises(ValidationError, person.validate) + + def test_weirdness_constructor(self): + """When attribute is set in contructor, it gets cast into a bool + which causes some weird behavior. We dont necessarily want to maintain this behavior + but its a known issue + """ + class Person(Document): + admin = BooleanField() + + new_person = Person(admin='False') + self.assertTrue(new_person.admin) + + new_person = Person(admin='0') + self.assertTrue(new_person.admin) diff --git a/tests/fields/test_cached_reference_field.py b/tests/fields/test_cached_reference_field.py new file mode 100644 index 00000000..989cea6d --- /dev/null +++ b/tests/fields/test_cached_reference_field.py @@ -0,0 +1,443 @@ +# -*- coding: utf-8 -*- +from decimal import Decimal + +from mongoengine import * + +from tests.utils import MongoDBTestCase + + +class TestCachedReferenceField(MongoDBTestCase): + + def test_get_and_save(self): + """ + Tests #1047: CachedReferenceField creates DBRefs on to_python, + but can't save them on to_mongo. + """ + class Animal(Document): + name = StringField() + tag = StringField() + + class Ocorrence(Document): + person = StringField() + animal = CachedReferenceField(Animal) + + Animal.drop_collection() + Ocorrence.drop_collection() + + Ocorrence(person="testte", + animal=Animal(name="Leopard", tag="heavy").save()).save() + p = Ocorrence.objects.get() + p.person = 'new_testte' + p.save() + + def test_general_things(self): + class Animal(Document): + name = StringField() + tag = StringField() + + class Ocorrence(Document): + person = StringField() + animal = CachedReferenceField( + Animal, fields=['tag']) + + Animal.drop_collection() + Ocorrence.drop_collection() + + a = Animal(name="Leopard", tag="heavy") + a.save() + + self.assertEqual(Animal._cached_reference_fields, [Ocorrence.animal]) + o = Ocorrence(person="teste", animal=a) + o.save() + + p = Ocorrence(person="Wilson") + p.save() + + self.assertEqual(Ocorrence.objects(animal=None).count(), 1) + + self.assertEqual( + a.to_mongo(fields=['tag']), {'tag': 'heavy', "_id": a.pk}) + + self.assertEqual(o.to_mongo()['animal']['tag'], 'heavy') + + # counts + Ocorrence(person="teste 2").save() + Ocorrence(person="teste 3").save() + + count = Ocorrence.objects(animal__tag='heavy').count() + self.assertEqual(count, 1) + + ocorrence = Ocorrence.objects(animal__tag='heavy').first() + self.assertEqual(ocorrence.person, "teste") + self.assertIsInstance(ocorrence.animal, Animal) + + def test_with_decimal(self): + class PersonAuto(Document): + name = StringField() + salary = DecimalField() + + class SocialTest(Document): + group = StringField() + person = CachedReferenceField( + PersonAuto, + fields=('salary',)) + + PersonAuto.drop_collection() + SocialTest.drop_collection() + + p = PersonAuto(name="Alberto", salary=Decimal('7000.00')) + p.save() + + s = SocialTest(group="dev", person=p) + s.save() + + self.assertEqual( + SocialTest.objects._collection.find_one({'person.salary': 7000.00}), { + '_id': s.pk, + 'group': s.group, + 'person': { + '_id': p.pk, + 'salary': 7000.00 + } + }) + + def test_cached_reference_field_reference(self): + class Group(Document): + name = StringField() + + class Person(Document): + name = StringField() + group = ReferenceField(Group) + + class SocialData(Document): + obs = StringField() + tags = ListField( + StringField()) + person = CachedReferenceField( + Person, + fields=('group',)) + + Group.drop_collection() + Person.drop_collection() + SocialData.drop_collection() + + g1 = Group(name='dev') + g1.save() + + g2 = Group(name="designers") + g2.save() + + p1 = Person(name="Alberto", group=g1) + p1.save() + + p2 = Person(name="Andre", group=g1) + p2.save() + + p3 = Person(name="Afro design", group=g2) + p3.save() + + s1 = SocialData(obs="testing 123", person=p1, tags=['tag1', 'tag2']) + s1.save() + + s2 = SocialData(obs="testing 321", person=p3, tags=['tag3', 'tag4']) + s2.save() + + self.assertEqual(SocialData.objects._collection.find_one( + {'tags': 'tag2'}), { + '_id': s1.pk, + 'obs': 'testing 123', + 'tags': ['tag1', 'tag2'], + 'person': { + '_id': p1.pk, + 'group': g1.pk + } + }) + + self.assertEqual(SocialData.objects(person__group=g2).count(), 1) + self.assertEqual(SocialData.objects(person__group=g2).first(), s2) + + def test_cached_reference_field_push_with_fields(self): + class Product(Document): + name = StringField() + + Product.drop_collection() + + class Basket(Document): + products = ListField(CachedReferenceField(Product, fields=['name'])) + + Basket.drop_collection() + product1 = Product(name='abc').save() + product2 = Product(name='def').save() + basket = Basket(products=[product1]).save() + self.assertEqual( + Basket.objects._collection.find_one(), + { + '_id': basket.pk, + 'products': [ + { + '_id': product1.pk, + 'name': product1.name + } + ] + } + ) + # push to list + basket.update(push__products=product2) + basket.reload() + self.assertEqual( + Basket.objects._collection.find_one(), + { + '_id': basket.pk, + 'products': [ + { + '_id': product1.pk, + 'name': product1.name + }, + { + '_id': product2.pk, + 'name': product2.name + } + ] + } + ) + + def test_cached_reference_field_update_all(self): + class Person(Document): + TYPES = ( + ('pf', "PF"), + ('pj', "PJ") + ) + name = StringField() + tp = StringField( + choices=TYPES + ) + + father = CachedReferenceField('self', fields=('tp',)) + + Person.drop_collection() + + a1 = Person(name="Wilson Father", tp="pj") + a1.save() + + a2 = Person(name='Wilson Junior', tp='pf', father=a1) + a2.save() + + self.assertEqual(dict(a2.to_mongo()), { + "_id": a2.pk, + "name": u"Wilson Junior", + "tp": u"pf", + "father": { + "_id": a1.pk, + "tp": u"pj" + } + }) + + self.assertEqual(Person.objects(father=a1)._query, { + 'father._id': a1.pk + }) + self.assertEqual(Person.objects(father=a1).count(), 1) + + Person.objects.update(set__tp="pf") + Person.father.sync_all() + + a2.reload() + self.assertEqual(dict(a2.to_mongo()), { + "_id": a2.pk, + "name": u"Wilson Junior", + "tp": u"pf", + "father": { + "_id": a1.pk, + "tp": u"pf" + } + }) + + def test_cached_reference_fields_on_embedded_documents(self): + with self.assertRaises(InvalidDocumentError): + class Test(Document): + name = StringField() + + type('WrongEmbeddedDocument', ( + EmbeddedDocument,), { + 'test': CachedReferenceField(Test) + }) + + def test_cached_reference_auto_sync(self): + class Person(Document): + TYPES = ( + ('pf', "PF"), + ('pj', "PJ") + ) + name = StringField() + tp = StringField( + choices=TYPES + ) + + father = CachedReferenceField('self', fields=('tp',)) + + Person.drop_collection() + + a1 = Person(name="Wilson Father", tp="pj") + a1.save() + + a2 = Person(name='Wilson Junior', tp='pf', father=a1) + a2.save() + + a1.tp = 'pf' + a1.save() + + a2.reload() + self.assertEqual(dict(a2.to_mongo()), { + '_id': a2.pk, + 'name': 'Wilson Junior', + 'tp': 'pf', + 'father': { + '_id': a1.pk, + 'tp': 'pf' + } + }) + + def test_cached_reference_auto_sync_disabled(self): + class Persone(Document): + TYPES = ( + ('pf', "PF"), + ('pj', "PJ") + ) + name = StringField() + tp = StringField( + choices=TYPES + ) + + father = CachedReferenceField( + 'self', fields=('tp',), auto_sync=False) + + Persone.drop_collection() + + a1 = Persone(name="Wilson Father", tp="pj") + a1.save() + + a2 = Persone(name='Wilson Junior', tp='pf', father=a1) + a2.save() + + a1.tp = 'pf' + a1.save() + + self.assertEqual(Persone.objects._collection.find_one({'_id': a2.pk}), { + '_id': a2.pk, + 'name': 'Wilson Junior', + 'tp': 'pf', + 'father': { + '_id': a1.pk, + 'tp': 'pj' + } + }) + + def test_cached_reference_embedded_fields(self): + class Owner(EmbeddedDocument): + TPS = ( + ('n', "Normal"), + ('u', "Urgent") + ) + name = StringField() + tp = StringField( + verbose_name="Type", + db_field="t", + choices=TPS) + + class Animal(Document): + name = StringField() + tag = StringField() + + owner = EmbeddedDocumentField(Owner) + + class Ocorrence(Document): + person = StringField() + animal = CachedReferenceField( + Animal, fields=['tag', 'owner.tp']) + + Animal.drop_collection() + Ocorrence.drop_collection() + + a = Animal(name="Leopard", tag="heavy", + owner=Owner(tp='u', name="Wilson Júnior") + ) + a.save() + + o = Ocorrence(person="teste", animal=a) + o.save() + self.assertEqual(dict(a.to_mongo(fields=['tag', 'owner.tp'])), { + '_id': a.pk, + 'tag': 'heavy', + 'owner': { + 't': 'u' + } + }) + self.assertEqual(o.to_mongo()['animal']['tag'], 'heavy') + self.assertEqual(o.to_mongo()['animal']['owner']['t'], 'u') + + # counts + Ocorrence(person="teste 2").save() + Ocorrence(person="teste 3").save() + + count = Ocorrence.objects( + animal__tag='heavy', animal__owner__tp='u').count() + self.assertEqual(count, 1) + + ocorrence = Ocorrence.objects( + animal__tag='heavy', + animal__owner__tp='u').first() + self.assertEqual(ocorrence.person, "teste") + self.assertIsInstance(ocorrence.animal, Animal) + + def test_cached_reference_embedded_list_fields(self): + class Owner(EmbeddedDocument): + name = StringField() + tags = ListField(StringField()) + + class Animal(Document): + name = StringField() + tag = StringField() + + owner = EmbeddedDocumentField(Owner) + + class Ocorrence(Document): + person = StringField() + animal = CachedReferenceField( + Animal, fields=['tag', 'owner.tags']) + + Animal.drop_collection() + Ocorrence.drop_collection() + + a = Animal(name="Leopard", tag="heavy", + owner=Owner(tags=['cool', 'funny'], + name="Wilson Júnior") + ) + a.save() + + o = Ocorrence(person="teste 2", animal=a) + o.save() + self.assertEqual(dict(a.to_mongo(fields=['tag', 'owner.tags'])), { + '_id': a.pk, + 'tag': 'heavy', + 'owner': { + 'tags': ['cool', 'funny'] + } + }) + + self.assertEqual(o.to_mongo()['animal']['tag'], 'heavy') + self.assertEqual(o.to_mongo()['animal']['owner']['tags'], + ['cool', 'funny']) + + # counts + Ocorrence(person="teste 2").save() + Ocorrence(person="teste 3").save() + + query = Ocorrence.objects( + animal__tag='heavy', animal__owner__tags='cool')._query + self.assertEqual( + query, {'animal.owner.tags': 'cool', 'animal.tag': 'heavy'}) + + ocorrence = Ocorrence.objects( + animal__tag='heavy', + animal__owner__tags='cool').first() + self.assertEqual(ocorrence.person, "teste 2") + self.assertIsInstance(ocorrence.animal, Animal) diff --git a/tests/fields/test_complex_datetime_field.py b/tests/fields/test_complex_datetime_field.py index bac534c0..58dc4b43 100644 --- a/tests/fields/test_complex_datetime_field.py +++ b/tests/fields/test_complex_datetime_field.py @@ -4,11 +4,6 @@ import math import itertools import re -try: - from bson.int64 import Int64 -except ImportError: - Int64 = long - from mongoengine import * from tests.utils import MongoDBTestCase diff --git a/tests/fields/test_date_field.py b/tests/fields/test_date_field.py index b5aed5c1..82adb514 100644 --- a/tests/fields/test_date_field.py +++ b/tests/fields/test_date_field.py @@ -1,13 +1,5 @@ # -*- coding: utf-8 -*- import datetime -import unittest -import uuid -import math -import itertools -import re -import sys - -from nose.plugins.skip import SkipTest import six try: @@ -15,18 +7,7 @@ try: except ImportError: dateutil = None -from decimal import Decimal - -from bson import Binary, DBRef, ObjectId, SON -try: - from bson.int64 import Int64 -except ImportError: - Int64 = long - from mongoengine import * -from mongoengine.connection import get_db -from mongoengine.base import (BaseDict, BaseField, EmbeddedDocumentList, - _document_registry, LazyReference) from tests.utils import MongoDBTestCase diff --git a/tests/fields/test_datetime_field.py b/tests/fields/test_datetime_field.py index 24d1c777..c6253043 100644 --- a/tests/fields/test_datetime_field.py +++ b/tests/fields/test_datetime_field.py @@ -7,11 +7,6 @@ try: except ImportError: dateutil = None -try: - from bson.int64 import Int64 -except ImportError: - Int64 = long - from mongoengine import * from mongoengine import connection diff --git a/tests/fields/test_decimal_field.py b/tests/fields/test_decimal_field.py new file mode 100644 index 00000000..0213b880 --- /dev/null +++ b/tests/fields/test_decimal_field.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +from decimal import Decimal + +from mongoengine import * + +from tests.utils import MongoDBTestCase + + +class TestDecimalField(MongoDBTestCase): + + def test_validation(self): + """Ensure that invalid values cannot be assigned to decimal fields. + """ + class Person(Document): + height = DecimalField(min_value=Decimal('0.1'), + max_value=Decimal('3.5')) + + Person.drop_collection() + + Person(height=Decimal('1.89')).save() + person = Person.objects.first() + self.assertEqual(person.height, Decimal('1.89')) + + person.height = '2.0' + person.save() + person.height = 0.01 + self.assertRaises(ValidationError, person.validate) + person.height = Decimal('0.01') + self.assertRaises(ValidationError, person.validate) + person.height = Decimal('4.0') + self.assertRaises(ValidationError, person.validate) + person.height = 'something invalid' + self.assertRaises(ValidationError, person.validate) + + person_2 = Person(height='something invalid') + self.assertRaises(ValidationError, person_2.validate) + + def test_comparison(self): + class Person(Document): + money = DecimalField() + + Person.drop_collection() + + Person(money=6).save() + Person(money=7).save() + Person(money=8).save() + Person(money=10).save() + + self.assertEqual(2, Person.objects(money__gt=Decimal("7")).count()) + self.assertEqual(2, Person.objects(money__gt=7).count()) + self.assertEqual(2, Person.objects(money__gt="7").count()) + + self.assertEqual(3, Person.objects(money__gte="7").count()) + + def test_storage(self): + class Person(Document): + float_value = DecimalField(precision=4) + string_value = DecimalField(precision=4, force_string=True) + + Person.drop_collection() + values_to_store = [10, 10.1, 10.11, "10.111", Decimal("10.1111"), Decimal("10.11111")] + for store_at_creation in [True, False]: + for value in values_to_store: + # to_python is called explicitly if values were sent in the kwargs of __init__ + if store_at_creation: + Person(float_value=value, string_value=value).save() + else: + person = Person.objects.create() + person.float_value = value + person.string_value = value + person.save() + + # How its stored + expected = [ + {'float_value': 10.0, 'string_value': '10.0000'}, + {'float_value': 10.1, 'string_value': '10.1000'}, + {'float_value': 10.11, 'string_value': '10.1100'}, + {'float_value': 10.111, 'string_value': '10.1110'}, + {'float_value': 10.1111, 'string_value': '10.1111'}, + {'float_value': 10.1111, 'string_value': '10.1111'}] + expected.extend(expected) + actual = list(Person.objects.exclude('id').as_pymongo()) + self.assertEqual(expected, actual) + + # How it comes out locally + expected = [Decimal('10.0000'), Decimal('10.1000'), Decimal('10.1100'), + Decimal('10.1110'), Decimal('10.1111'), Decimal('10.1111')] + expected.extend(expected) + for field_name in ['float_value', 'string_value']: + actual = list(Person.objects().scalar(field_name)) + self.assertEqual(expected, actual) diff --git a/tests/fields/test_dict_field.py b/tests/fields/test_dict_field.py new file mode 100644 index 00000000..2b9cecb7 --- /dev/null +++ b/tests/fields/test_dict_field.py @@ -0,0 +1,303 @@ +# -*- coding: utf-8 -*- +from mongoengine import * +from mongoengine.base import BaseDict + +from tests.utils import MongoDBTestCase, get_as_pymongo + + +class TestDictField(MongoDBTestCase): + + def test_storage(self): + class BlogPost(Document): + info = DictField() + + BlogPost.drop_collection() + + info = {'testkey': 'testvalue'} + post = BlogPost(info=info).save() + self.assertEqual( + get_as_pymongo(post), + { + '_id': post.id, + 'info': info + } + ) + + def test_general_things(self): + """Ensure that dict types work as expected.""" + class BlogPost(Document): + info = DictField() + + BlogPost.drop_collection() + + post = BlogPost() + post.info = 'my post' + self.assertRaises(ValidationError, post.validate) + + post.info = ['test', 'test'] + self.assertRaises(ValidationError, post.validate) + + post.info = {'$title': 'test'} + self.assertRaises(ValidationError, post.validate) + + post.info = {'nested': {'$title': 'test'}} + self.assertRaises(ValidationError, post.validate) + + post.info = {'the.title': 'test'} + self.assertRaises(ValidationError, post.validate) + + post.info = {'nested': {'the.title': 'test'}} + self.assertRaises(ValidationError, post.validate) + + post.info = {1: 'test'} + self.assertRaises(ValidationError, post.validate) + + post.info = {'title': 'test'} + post.save() + + post = BlogPost() + post.info = {'title': 'dollar_sign', 'details': {'te$t': 'test'}} + post.save() + + post = BlogPost() + post.info = {'details': {'test': 'test'}} + post.save() + + post = BlogPost() + post.info = {'details': {'test': 3}} + post.save() + + self.assertEqual(BlogPost.objects.count(), 4) + self.assertEqual( + BlogPost.objects.filter(info__title__exact='test').count(), 1) + self.assertEqual( + BlogPost.objects.filter(info__details__test__exact='test').count(), 1) + + post = BlogPost.objects.filter(info__title__exact='dollar_sign').first() + self.assertIn('te$t', post['info']['details']) + + # Confirm handles non strings or non existing keys + self.assertEqual( + BlogPost.objects.filter(info__details__test__exact=5).count(), 0) + self.assertEqual( + BlogPost.objects.filter(info__made_up__test__exact='test').count(), 0) + + post = BlogPost.objects.create(info={'title': 'original'}) + post.info.update({'title': 'updated'}) + post.save() + post.reload() + self.assertEqual('updated', post.info['title']) + + post.info.setdefault('authors', []) + post.save() + post.reload() + self.assertEqual([], post.info['authors']) + + def test_dictfield_dump_document(self): + """Ensure a DictField can handle another document's dump.""" + class Doc(Document): + field = DictField() + + class ToEmbed(Document): + id = IntField(primary_key=True, default=1) + recursive = DictField() + + class ToEmbedParent(Document): + id = IntField(primary_key=True, default=1) + recursive = DictField() + + meta = {'allow_inheritance': True} + + class ToEmbedChild(ToEmbedParent): + pass + + to_embed_recursive = ToEmbed(id=1).save() + to_embed = ToEmbed( + id=2, recursive=to_embed_recursive.to_mongo().to_dict()).save() + doc = Doc(field=to_embed.to_mongo().to_dict()) + doc.save() + assert isinstance(doc.field, dict) + assert doc.field == {'_id': 2, 'recursive': {'_id': 1, 'recursive': {}}} + # Same thing with a Document with a _cls field + to_embed_recursive = ToEmbedChild(id=1).save() + to_embed_child = ToEmbedChild( + id=2, recursive=to_embed_recursive.to_mongo().to_dict()).save() + doc = Doc(field=to_embed_child.to_mongo().to_dict()) + doc.save() + assert isinstance(doc.field, dict) + assert doc.field == { + '_id': 2, '_cls': 'ToEmbedParent.ToEmbedChild', + 'recursive': {'_id': 1, '_cls': 'ToEmbedParent.ToEmbedChild', 'recursive': {}} + } + + def test_dictfield_strict(self): + """Ensure that dict field handles validation if provided a strict field type.""" + class Simple(Document): + mapping = DictField(field=IntField()) + + Simple.drop_collection() + + e = Simple() + e.mapping['someint'] = 1 + e.save() + + # try creating an invalid mapping + with self.assertRaises(ValidationError): + e.mapping['somestring'] = "abc" + e.save() + + def test_dictfield_complex(self): + """Ensure that the dict field can handle the complex types.""" + class SettingBase(EmbeddedDocument): + meta = {'allow_inheritance': True} + + class StringSetting(SettingBase): + value = StringField() + + class IntegerSetting(SettingBase): + value = IntField() + + class Simple(Document): + mapping = DictField() + + Simple.drop_collection() + + e = Simple() + e.mapping['somestring'] = StringSetting(value='foo') + e.mapping['someint'] = IntegerSetting(value=42) + e.mapping['nested_dict'] = {'number': 1, 'string': 'Hi!', + 'float': 1.001, + 'complex': IntegerSetting(value=42), + 'list': [IntegerSetting(value=42), + StringSetting(value='foo')]} + e.save() + + e2 = Simple.objects.get(id=e.id) + self.assertIsInstance(e2.mapping['somestring'], StringSetting) + self.assertIsInstance(e2.mapping['someint'], IntegerSetting) + + # Test querying + self.assertEqual( + Simple.objects.filter(mapping__someint__value=42).count(), 1) + self.assertEqual( + Simple.objects.filter(mapping__nested_dict__number=1).count(), 1) + self.assertEqual( + Simple.objects.filter(mapping__nested_dict__complex__value=42).count(), 1) + self.assertEqual( + Simple.objects.filter(mapping__nested_dict__list__0__value=42).count(), 1) + self.assertEqual( + Simple.objects.filter(mapping__nested_dict__list__1__value='foo').count(), 1) + + # Confirm can update + Simple.objects().update( + set__mapping={"someint": IntegerSetting(value=10)}) + Simple.objects().update( + set__mapping__nested_dict__list__1=StringSetting(value='Boo')) + self.assertEqual( + Simple.objects.filter(mapping__nested_dict__list__1__value='foo').count(), 0) + self.assertEqual( + Simple.objects.filter(mapping__nested_dict__list__1__value='Boo').count(), 1) + + def test_ensure_unique_default_instances(self): + """Ensure that every field has it's own unique default instance.""" + class D(Document): + data = DictField() + data2 = DictField(default=lambda: {}) + + d1 = D() + d1.data['foo'] = 'bar' + d1.data2['foo'] = 'bar' + d2 = D() + self.assertEqual(d2.data, {}) + self.assertEqual(d2.data2, {}) + + def test_dict_field_invalid_dict_value(self): + class DictFieldTest(Document): + dictionary = DictField(required=True) + + DictFieldTest.drop_collection() + + test = DictFieldTest(dictionary=None) + test.dictionary # Just access to test getter + self.assertRaises(ValidationError, test.validate) + + test = DictFieldTest(dictionary=False) + test.dictionary # Just access to test getter + self.assertRaises(ValidationError, test.validate) + + def test_dict_field_raises_validation_error_if_wrongly_assign_embedded_doc(self): + class DictFieldTest(Document): + dictionary = DictField(required=True) + + DictFieldTest.drop_collection() + + class Embedded(EmbeddedDocument): + name = StringField() + + embed = Embedded(name='garbage') + doc = DictFieldTest(dictionary=embed) + with self.assertRaises(ValidationError) as ctx_err: + doc.validate() + self.assertIn("'dictionary'", str(ctx_err.exception)) + self.assertIn('Only dictionaries may be used in a DictField', str(ctx_err.exception)) + + def test_atomic_update_dict_field(self): + """Ensure that the entire DictField can be atomically updated.""" + class Simple(Document): + mapping = DictField(field=ListField(IntField(required=True))) + + Simple.drop_collection() + + e = Simple() + e.mapping['someints'] = [1, 2] + e.save() + e.update(set__mapping={"ints": [3, 4]}) + e.reload() + self.assertEqual(BaseDict, type(e.mapping)) + self.assertEqual({"ints": [3, 4]}, e.mapping) + + # try creating an invalid mapping + with self.assertRaises(ValueError): + e.update(set__mapping={"somestrings": ["foo", "bar", ]}) + + def test_dictfield_with_referencefield_complex_nesting_cases(self): + """Ensure complex nesting inside DictField handles dereferencing of ReferenceField(dbref=True | False)""" + # Relates to Issue #1453 + class Doc(Document): + s = StringField() + + class Simple(Document): + mapping0 = DictField(ReferenceField(Doc, dbref=True)) + mapping1 = DictField(ReferenceField(Doc, dbref=False)) + mapping2 = DictField(ListField(ReferenceField(Doc, dbref=True))) + mapping3 = DictField(ListField(ReferenceField(Doc, dbref=False))) + mapping4 = DictField(DictField(field=ReferenceField(Doc, dbref=True))) + mapping5 = DictField(DictField(field=ReferenceField(Doc, dbref=False))) + mapping6 = DictField(ListField(DictField(ReferenceField(Doc, dbref=True)))) + mapping7 = DictField(ListField(DictField(ReferenceField(Doc, dbref=False)))) + mapping8 = DictField(ListField(DictField(ListField(ReferenceField(Doc, dbref=True))))) + mapping9 = DictField(ListField(DictField(ListField(ReferenceField(Doc, dbref=False))))) + + Doc.drop_collection() + Simple.drop_collection() + + d = Doc(s='aa').save() + e = Simple() + e.mapping0['someint'] = e.mapping1['someint'] = d + e.mapping2['someint'] = e.mapping3['someint'] = [d] + e.mapping4['someint'] = e.mapping5['someint'] = {'d': d} + e.mapping6['someint'] = e.mapping7['someint'] = [{'d': d}] + e.mapping8['someint'] = e.mapping9['someint'] = [{'d': [d]}] + e.save() + + s = Simple.objects.first() + self.assertIsInstance(s.mapping0['someint'], Doc) + self.assertIsInstance(s.mapping1['someint'], Doc) + self.assertIsInstance(s.mapping2['someint'][0], Doc) + self.assertIsInstance(s.mapping3['someint'][0], Doc) + self.assertIsInstance(s.mapping4['someint']['d'], Doc) + self.assertIsInstance(s.mapping5['someint']['d'], Doc) + self.assertIsInstance(s.mapping6['someint'][0]['d'], Doc) + self.assertIsInstance(s.mapping7['someint'][0]['d'], Doc) + self.assertIsInstance(s.mapping8['someint'][0]['d'][0], Doc) + self.assertIsInstance(s.mapping9['someint'][0]['d'][0], Doc) diff --git a/tests/fields/test_email_field.py b/tests/fields/test_email_field.py new file mode 100644 index 00000000..d8410354 --- /dev/null +++ b/tests/fields/test_email_field.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- +import sys +from unittest import SkipTest + +from mongoengine import * + +from tests.utils import MongoDBTestCase + + +class TestEmailField(MongoDBTestCase): + def test_generic_behavior(self): + class User(Document): + email = EmailField() + + user = User(email='ross@example.com') + user.validate() + + user = User(email='ross@example.co.uk') + user.validate() + + user = User(email=('Kofq@rhom0e4klgauOhpbpNdogawnyIKvQS0wk2mjqrgGQ5S' + 'aJIazqqWkm7.net')) + user.validate() + + user = User(email='new-tld@example.technology') + user.validate() + + user = User(email='ross@example.com.') + self.assertRaises(ValidationError, user.validate) + + # unicode domain + user = User(email=u'user@пример.рф') + user.validate() + + # invalid unicode domain + user = User(email=u'user@пример') + self.assertRaises(ValidationError, user.validate) + + # invalid data type + user = User(email=123) + self.assertRaises(ValidationError, user.validate) + + def test_email_field_unicode_user(self): + # Don't run this test on pypy3, which doesn't support unicode regex: + # https://bitbucket.org/pypy/pypy/issues/1821/regular-expression-doesnt-find-unicode + if sys.version_info[:2] == (3, 2): + raise SkipTest('unicode email addresses are not supported on PyPy 3') + + class User(Document): + email = EmailField() + + # unicode user shouldn't validate by default... + user = User(email=u'Dörte@Sörensen.example.com') + self.assertRaises(ValidationError, user.validate) + + # ...but it should be fine with allow_utf8_user set to True + class User(Document): + email = EmailField(allow_utf8_user=True) + + user = User(email=u'Dörte@Sörensen.example.com') + user.validate() + + def test_email_field_domain_whitelist(self): + class User(Document): + email = EmailField() + + # localhost domain shouldn't validate by default... + user = User(email='me@localhost') + self.assertRaises(ValidationError, user.validate) + + # ...but it should be fine if it's whitelisted + class User(Document): + email = EmailField(domain_whitelist=['localhost']) + + user = User(email='me@localhost') + user.validate() + + def test_email_field_ip_domain(self): + class User(Document): + email = EmailField() + + valid_ipv4 = 'email@[127.0.0.1]' + valid_ipv6 = 'email@[2001:dB8::1]' + invalid_ip = 'email@[324.0.0.1]' + + # IP address as a domain shouldn't validate by default... + user = User(email=valid_ipv4) + self.assertRaises(ValidationError, user.validate) + + user = User(email=valid_ipv6) + self.assertRaises(ValidationError, user.validate) + + user = User(email=invalid_ip) + self.assertRaises(ValidationError, user.validate) + + # ...but it should be fine with allow_ip_domain set to True + class User(Document): + email = EmailField(allow_ip_domain=True) + + user = User(email=valid_ipv4) + user.validate() + + user = User(email=valid_ipv6) + user.validate() + + # invalid IP should still fail validation + user = User(email=invalid_ip) + self.assertRaises(ValidationError, user.validate) + + def test_email_field_honors_regex(self): + class User(Document): + email = EmailField(regex=r'\w+@example.com') + + # Fails regex validation + user = User(email='me@foo.com') + self.assertRaises(ValidationError, user.validate) + + # Passes regex validation + user = User(email='me@example.com') + self.assertIsNone(user.validate()) diff --git a/tests/fields/test_map_field.py b/tests/fields/test_map_field.py new file mode 100644 index 00000000..cb27cfff --- /dev/null +++ b/tests/fields/test_map_field.py @@ -0,0 +1,144 @@ +# -*- coding: utf-8 -*- +import datetime + +from mongoengine import * + +from tests.utils import MongoDBTestCase + + +class TestMapField(MongoDBTestCase): + + def test_mapfield(self): + """Ensure that the MapField handles the declared type.""" + class Simple(Document): + mapping = MapField(IntField()) + + Simple.drop_collection() + + e = Simple() + e.mapping['someint'] = 1 + e.save() + + with self.assertRaises(ValidationError): + e.mapping['somestring'] = "abc" + e.save() + + with self.assertRaises(ValidationError): + class NoDeclaredType(Document): + mapping = MapField() + + def test_complex_mapfield(self): + """Ensure that the MapField can handle complex declared types.""" + + class SettingBase(EmbeddedDocument): + meta = {"allow_inheritance": True} + + class StringSetting(SettingBase): + value = StringField() + + class IntegerSetting(SettingBase): + value = IntField() + + class Extensible(Document): + mapping = MapField(EmbeddedDocumentField(SettingBase)) + + Extensible.drop_collection() + + e = Extensible() + e.mapping['somestring'] = StringSetting(value='foo') + e.mapping['someint'] = IntegerSetting(value=42) + e.save() + + e2 = Extensible.objects.get(id=e.id) + self.assertIsInstance(e2.mapping['somestring'], StringSetting) + self.assertIsInstance(e2.mapping['someint'], IntegerSetting) + + with self.assertRaises(ValidationError): + e.mapping['someint'] = 123 + e.save() + + def test_embedded_mapfield_db_field(self): + class Embedded(EmbeddedDocument): + number = IntField(default=0, db_field='i') + + class Test(Document): + my_map = MapField(field=EmbeddedDocumentField(Embedded), + db_field='x') + + Test.drop_collection() + + test = Test() + test.my_map['DICTIONARY_KEY'] = Embedded(number=1) + test.save() + + Test.objects.update_one(inc__my_map__DICTIONARY_KEY__number=1) + + test = Test.objects.get() + self.assertEqual(test.my_map['DICTIONARY_KEY'].number, 2) + doc = self.db.test.find_one() + self.assertEqual(doc['x']['DICTIONARY_KEY']['i'], 2) + + def test_mapfield_numerical_index(self): + """Ensure that MapField accept numeric strings as indexes.""" + + class Embedded(EmbeddedDocument): + name = StringField() + + class Test(Document): + my_map = MapField(EmbeddedDocumentField(Embedded)) + + Test.drop_collection() + + test = Test() + test.my_map['1'] = Embedded(name='test') + test.save() + test.my_map['1'].name = 'test updated' + test.save() + + def test_map_field_lookup(self): + """Ensure MapField lookups succeed on Fields without a lookup + method. + """ + + class Action(EmbeddedDocument): + operation = StringField() + object = StringField() + + class Log(Document): + name = StringField() + visited = MapField(DateTimeField()) + actions = MapField(EmbeddedDocumentField(Action)) + + Log.drop_collection() + Log(name="wilson", visited={'friends': datetime.datetime.now()}, + actions={'friends': Action(operation='drink', object='beer')}).save() + + self.assertEqual(1, Log.objects( + visited__friends__exists=True).count()) + + self.assertEqual(1, Log.objects( + actions__friends__operation='drink', + actions__friends__object='beer').count()) + + def test_map_field_unicode(self): + class Info(EmbeddedDocument): + description = StringField() + value_list = ListField(field=StringField()) + + class BlogPost(Document): + info_dict = MapField(field=EmbeddedDocumentField(Info)) + + BlogPost.drop_collection() + + tree = BlogPost(info_dict={ + u"éééé": { + 'description': u"VALUE: éééé" + } + }) + + tree.save() + + self.assertEqual( + BlogPost.objects.get(id=tree.id).info_dict[u"éééé"].description, + u"VALUE: éééé" + ) diff --git a/tests/fields/test_reference_field.py b/tests/fields/test_reference_field.py new file mode 100644 index 00000000..5e1fc605 --- /dev/null +++ b/tests/fields/test_reference_field.py @@ -0,0 +1,219 @@ +# -*- coding: utf-8 -*- +from bson import SON, DBRef + +from mongoengine import * + +from tests.utils import MongoDBTestCase + + +class TestReferenceField(MongoDBTestCase): + def test_reference_validation(self): + """Ensure that invalid document objects cannot be assigned to + reference fields. + """ + + class User(Document): + name = StringField() + + class BlogPost(Document): + content = StringField() + author = ReferenceField(User) + + User.drop_collection() + BlogPost.drop_collection() + + # Make sure ReferenceField only accepts a document class or a string + # with a document class name. + self.assertRaises(ValidationError, ReferenceField, EmbeddedDocument) + + user = User(name='Test User') + + # Ensure that the referenced object must have been saved + post1 = BlogPost(content='Chips and gravy taste good.') + post1.author = user + self.assertRaises(ValidationError, post1.save) + + # Check that an invalid object type cannot be used + post2 = BlogPost(content='Chips and chilli taste good.') + post1.author = post2 + self.assertRaises(ValidationError, post1.validate) + + # Ensure ObjectID's are accepted as references + user_object_id = user.pk + post3 = BlogPost(content="Chips and curry sauce taste good.") + post3.author = user_object_id + post3.save() + + # Make sure referencing a saved document of the right type works + user.save() + post1.author = user + post1.save() + + # Make sure referencing a saved document of the *wrong* type fails + post2.save() + post1.author = post2 + self.assertRaises(ValidationError, post1.validate) + + def test_objectid_reference_fields(self): + """Make sure storing Object ID references works.""" + + class Person(Document): + name = StringField() + parent = ReferenceField('self') + + Person.drop_collection() + + p1 = Person(name="John").save() + Person(name="Ross", parent=p1.pk).save() + + p = Person.objects.get(name="Ross") + self.assertEqual(p.parent, p1) + + def test_dbref_reference_fields(self): + """Make sure storing references as bson.dbref.DBRef works.""" + + class Person(Document): + name = StringField() + parent = ReferenceField('self', dbref=True) + + Person.drop_collection() + + p1 = Person(name="John").save() + Person(name="Ross", parent=p1).save() + + self.assertEqual( + Person._get_collection().find_one({'name': 'Ross'})['parent'], + DBRef('person', p1.pk) + ) + + p = Person.objects.get(name="Ross") + self.assertEqual(p.parent, p1) + + def test_dbref_to_mongo(self): + """Make sure that calling to_mongo on a ReferenceField which + has dbref=False, but actually actually contains a DBRef returns + an ID of that DBRef. + """ + + class Person(Document): + name = StringField() + parent = ReferenceField('self', dbref=False) + + p = Person( + name='Steve', + parent=DBRef('person', 'abcdefghijklmnop') + ) + self.assertEqual(p.to_mongo(), SON([ + ('name', u'Steve'), + ('parent', 'abcdefghijklmnop') + ])) + + def test_objectid_reference_fields(self): + class Person(Document): + name = StringField() + parent = ReferenceField('self', dbref=False) + + Person.drop_collection() + + p1 = Person(name="John").save() + Person(name="Ross", parent=p1).save() + + col = Person._get_collection() + data = col.find_one({'name': 'Ross'}) + self.assertEqual(data['parent'], p1.pk) + + p = Person.objects.get(name="Ross") + self.assertEqual(p.parent, p1) + + def test_undefined_reference(self): + """Ensure that ReferenceFields may reference undefined Documents. + """ + class Product(Document): + name = StringField() + company = ReferenceField('Company') + + class Company(Document): + name = StringField() + + Product.drop_collection() + Company.drop_collection() + + ten_gen = Company(name='10gen') + ten_gen.save() + mongodb = Product(name='MongoDB', company=ten_gen) + mongodb.save() + + me = Product(name='MongoEngine') + me.save() + + obj = Product.objects(company=ten_gen).first() + self.assertEqual(obj, mongodb) + self.assertEqual(obj.company, ten_gen) + + obj = Product.objects(company=None).first() + self.assertEqual(obj, me) + + obj = Product.objects.get(company=None) + self.assertEqual(obj, me) + + def test_reference_query_conversion(self): + """Ensure that ReferenceFields can be queried using objects and values + of the type of the primary key of the referenced object. + """ + class Member(Document): + user_num = IntField(primary_key=True) + + class BlogPost(Document): + title = StringField() + author = ReferenceField(Member, dbref=False) + + Member.drop_collection() + BlogPost.drop_collection() + + m1 = Member(user_num=1) + m1.save() + m2 = Member(user_num=2) + m2.save() + + post1 = BlogPost(title='post 1', author=m1) + post1.save() + + post2 = BlogPost(title='post 2', author=m2) + post2.save() + + post = BlogPost.objects(author=m1).first() + self.assertEqual(post.id, post1.id) + + post = BlogPost.objects(author=m2).first() + self.assertEqual(post.id, post2.id) + + def test_reference_query_conversion_dbref(self): + """Ensure that ReferenceFields can be queried using objects and values + of the type of the primary key of the referenced object. + """ + class Member(Document): + user_num = IntField(primary_key=True) + + class BlogPost(Document): + title = StringField() + author = ReferenceField(Member, dbref=True) + + Member.drop_collection() + BlogPost.drop_collection() + + m1 = Member(user_num=1) + m1.save() + m2 = Member(user_num=2) + m2.save() + + post1 = BlogPost(title='post 1', author=m1) + post1.save() + + post2 = BlogPost(title='post 2', author=m2) + post2.save() + + post = BlogPost.objects(author=m1).first() + self.assertEqual(post.id, post1.id) + + post = BlogPost.objects(author=m2).first() + self.assertEqual(post.id, post2.id) diff --git a/tests/fields/test_sequence_field.py b/tests/fields/test_sequence_field.py new file mode 100644 index 00000000..6124c65e --- /dev/null +++ b/tests/fields/test_sequence_field.py @@ -0,0 +1,271 @@ +# -*- coding: utf-8 -*- + +from mongoengine import * + +from tests.utils import MongoDBTestCase + + +class TestSequenceField(MongoDBTestCase): + def test_sequence_field(self): + class Person(Document): + id = SequenceField(primary_key=True) + name = StringField() + + self.db['mongoengine.counters'].drop() + Person.drop_collection() + + for x in range(10): + Person(name="Person %s" % x).save() + + c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'}) + self.assertEqual(c['next'], 10) + + ids = [i.id for i in Person.objects] + self.assertEqual(ids, range(1, 11)) + + c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'}) + self.assertEqual(c['next'], 10) + + Person.id.set_next_value(1000) + c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'}) + self.assertEqual(c['next'], 1000) + + def test_sequence_field_get_next_value(self): + class Person(Document): + id = SequenceField(primary_key=True) + name = StringField() + + self.db['mongoengine.counters'].drop() + Person.drop_collection() + + for x in range(10): + Person(name="Person %s" % x).save() + + self.assertEqual(Person.id.get_next_value(), 11) + self.db['mongoengine.counters'].drop() + + self.assertEqual(Person.id.get_next_value(), 1) + + class Person(Document): + id = SequenceField(primary_key=True, value_decorator=str) + name = StringField() + + self.db['mongoengine.counters'].drop() + Person.drop_collection() + + for x in range(10): + Person(name="Person %s" % x).save() + + self.assertEqual(Person.id.get_next_value(), '11') + self.db['mongoengine.counters'].drop() + + self.assertEqual(Person.id.get_next_value(), '1') + + def test_sequence_field_sequence_name(self): + class Person(Document): + id = SequenceField(primary_key=True, sequence_name='jelly') + name = StringField() + + self.db['mongoengine.counters'].drop() + Person.drop_collection() + + for x in range(10): + Person(name="Person %s" % x).save() + + c = self.db['mongoengine.counters'].find_one({'_id': 'jelly.id'}) + self.assertEqual(c['next'], 10) + + ids = [i.id for i in Person.objects] + self.assertEqual(ids, range(1, 11)) + + c = self.db['mongoengine.counters'].find_one({'_id': 'jelly.id'}) + self.assertEqual(c['next'], 10) + + Person.id.set_next_value(1000) + c = self.db['mongoengine.counters'].find_one({'_id': 'jelly.id'}) + self.assertEqual(c['next'], 1000) + + def test_multiple_sequence_fields(self): + class Person(Document): + id = SequenceField(primary_key=True) + counter = SequenceField() + name = StringField() + + self.db['mongoengine.counters'].drop() + Person.drop_collection() + + for x in range(10): + Person(name="Person %s" % x).save() + + c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'}) + self.assertEqual(c['next'], 10) + + ids = [i.id for i in Person.objects] + self.assertEqual(ids, range(1, 11)) + + counters = [i.counter for i in Person.objects] + self.assertEqual(counters, range(1, 11)) + + c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'}) + self.assertEqual(c['next'], 10) + + Person.id.set_next_value(1000) + c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'}) + self.assertEqual(c['next'], 1000) + + Person.counter.set_next_value(999) + c = self.db['mongoengine.counters'].find_one({'_id': 'person.counter'}) + self.assertEqual(c['next'], 999) + + def test_sequence_fields_reload(self): + class Animal(Document): + counter = SequenceField() + name = StringField() + + self.db['mongoengine.counters'].drop() + Animal.drop_collection() + + a = Animal(name="Boi").save() + + self.assertEqual(a.counter, 1) + a.reload() + self.assertEqual(a.counter, 1) + + a.counter = None + self.assertEqual(a.counter, 2) + a.save() + + self.assertEqual(a.counter, 2) + + a = Animal.objects.first() + self.assertEqual(a.counter, 2) + a.reload() + self.assertEqual(a.counter, 2) + + def test_multiple_sequence_fields_on_docs(self): + class Animal(Document): + id = SequenceField(primary_key=True) + name = StringField() + + class Person(Document): + id = SequenceField(primary_key=True) + name = StringField() + + self.db['mongoengine.counters'].drop() + Animal.drop_collection() + Person.drop_collection() + + for x in range(10): + Animal(name="Animal %s" % x).save() + Person(name="Person %s" % x).save() + + c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'}) + self.assertEqual(c['next'], 10) + + c = self.db['mongoengine.counters'].find_one({'_id': 'animal.id'}) + self.assertEqual(c['next'], 10) + + ids = [i.id for i in Person.objects] + self.assertEqual(ids, range(1, 11)) + + id = [i.id for i in Animal.objects] + self.assertEqual(id, range(1, 11)) + + c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'}) + self.assertEqual(c['next'], 10) + + c = self.db['mongoengine.counters'].find_one({'_id': 'animal.id'}) + self.assertEqual(c['next'], 10) + + def test_sequence_field_value_decorator(self): + class Person(Document): + id = SequenceField(primary_key=True, value_decorator=str) + name = StringField() + + self.db['mongoengine.counters'].drop() + Person.drop_collection() + + for x in range(10): + p = Person(name="Person %s" % x) + p.save() + + c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'}) + self.assertEqual(c['next'], 10) + + ids = [i.id for i in Person.objects] + self.assertEqual(ids, map(str, range(1, 11))) + + c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'}) + self.assertEqual(c['next'], 10) + + def test_embedded_sequence_field(self): + class Comment(EmbeddedDocument): + id = SequenceField() + content = StringField(required=True) + + class Post(Document): + title = StringField(required=True) + comments = ListField(EmbeddedDocumentField(Comment)) + + self.db['mongoengine.counters'].drop() + Post.drop_collection() + + Post(title="MongoEngine", + comments=[Comment(content="NoSQL Rocks"), + Comment(content="MongoEngine Rocks")]).save() + c = self.db['mongoengine.counters'].find_one({'_id': 'comment.id'}) + self.assertEqual(c['next'], 2) + post = Post.objects.first() + self.assertEqual(1, post.comments[0].id) + self.assertEqual(2, post.comments[1].id) + + def test_inherited_sequencefield(self): + class Base(Document): + name = StringField() + counter = SequenceField() + meta = {'abstract': True} + + class Foo(Base): + pass + + class Bar(Base): + pass + + bar = Bar(name='Bar') + bar.save() + + foo = Foo(name='Foo') + foo.save() + + self.assertTrue('base.counter' in + self.db['mongoengine.counters'].find().distinct('_id')) + self.assertFalse(('foo.counter' or 'bar.counter') in + self.db['mongoengine.counters'].find().distinct('_id')) + self.assertNotEqual(foo.counter, bar.counter) + self.assertEqual(foo._fields['counter'].owner_document, Base) + self.assertEqual(bar._fields['counter'].owner_document, Base) + + def test_no_inherited_sequencefield(self): + class Base(Document): + name = StringField() + meta = {'abstract': True} + + class Foo(Base): + counter = SequenceField() + + class Bar(Base): + counter = SequenceField() + + bar = Bar(name='Bar') + bar.save() + + foo = Foo(name='Foo') + foo.save() + + self.assertFalse('base.counter' in + self.db['mongoengine.counters'].find().distinct('_id')) + self.assertTrue(('foo.counter' and 'bar.counter') in + self.db['mongoengine.counters'].find().distinct('_id')) + self.assertEqual(foo.counter, bar.counter) + self.assertEqual(foo._fields['counter'].owner_document, Foo) + self.assertEqual(bar._fields['counter'].owner_document, Bar) diff --git a/tests/fields/test_url_field.py b/tests/fields/test_url_field.py index 0447799e..ddbf707e 100644 --- a/tests/fields/test_url_field.py +++ b/tests/fields/test_url_field.py @@ -4,7 +4,7 @@ from mongoengine import * from tests.utils import MongoDBTestCase -class TestFloatField(MongoDBTestCase): +class TestURLField(MongoDBTestCase): def test_validation(self): """Ensure that URLFields validate urls properly.""" diff --git a/tests/fields/test_uuid_field.py b/tests/fields/test_uuid_field.py new file mode 100644 index 00000000..7b7faaf2 --- /dev/null +++ b/tests/fields/test_uuid_field.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +import uuid + +from mongoengine import * + +from tests.utils import MongoDBTestCase, get_as_pymongo + + +class Person(Document): + api_key = UUIDField(binary=False) + + +class TestUUIDField(MongoDBTestCase): + def test_storage(self): + uid = uuid.uuid4() + person = Person(api_key=uid).save() + self.assertEqual( + get_as_pymongo(person), + {'_id': person.id, + 'api_key': str(uid) + } + ) + + def test_field_string(self): + """Test UUID fields storing as String + """ + Person.drop_collection() + + uu = uuid.uuid4() + Person(api_key=uu).save() + self.assertEqual(1, Person.objects(api_key=uu).count()) + self.assertEqual(uu, Person.objects.first().api_key) + + person = Person() + valid = (uuid.uuid4(), uuid.uuid1()) + for api_key in valid: + person.api_key = api_key + person.validate() + + invalid = ('9d159858-549b-4975-9f98-dd2f987c113g', + '9d159858-549b-4975-9f98-dd2f987c113') + for api_key in invalid: + person.api_key = api_key + self.assertRaises(ValidationError, person.validate) + + def test_field_binary(self): + """Test UUID fields storing as Binary object.""" + Person.drop_collection() + + uu = uuid.uuid4() + Person(api_key=uu).save() + self.assertEqual(1, Person.objects(api_key=uu).count()) + self.assertEqual(uu, Person.objects.first().api_key) + + person = Person() + valid = (uuid.uuid4(), uuid.uuid1()) + for api_key in valid: + person.api_key = api_key + person.validate() + + invalid = ('9d159858-549b-4975-9f98-dd2f987c113g', + '9d159858-549b-4975-9f98-dd2f987c113') + for api_key in invalid: + person.api_key = api_key + self.assertRaises(ValidationError, person.validate) diff --git a/tests/utils.py b/tests/utils.py index 5345f75e..19936a54 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -13,7 +13,7 @@ MONGO_TEST_DB = 'mongoenginetest' # standard name for the test database # Constant that can be used to compare the version retrieved with # get_mongodb_version() MONGODB_26 = (2, 6) -MONGODB_3 = (3,0) +MONGODB_3 = (3, 0) MONGODB_32 = (3, 2) @@ -33,6 +33,11 @@ class MongoDBTestCase(unittest.TestCase): cls._connection.drop_database(MONGO_TEST_DB) +def get_as_pymongo(doc): + """Fetch the pymongo version of a certain Document""" + return doc.__class__.objects.as_pymongo().get(id=doc.id) + + def get_mongodb_version(): """Return the version of the connected mongoDB (first 2 digits)