Compare commits

...

4 Commits

Author SHA1 Message Date
Stefan Wojcik
3534daf37d fix tests structure 2017-02-24 16:15:03 -05:00
Stefan Wojcik
6a59e4658e fix the exception message when validating unicode URLs 2017-02-22 12:57:00 -05:00
Stefan Wojcik
3fe8031cf3 fix EmbeddedDocumentListFieldTestCase 2017-02-22 12:44:05 -05:00
bagerard
b27c7ce11b allow to use sets in field choices (#1482) 2017-02-15 08:51:47 -05:00
7 changed files with 108 additions and 60 deletions

View File

@@ -4,7 +4,8 @@ Changelog
Development Development
=========== ===========
- (Fill this out as you fix issues and develop you features). - (Fill this out as you fix issues and develop your features).
- Fixed using sets in field choices #1481
- POTENTIAL BREAKING CHANGE: Fixed limit/skip/hint/batch_size chaining #1476 - POTENTIAL BREAKING CHANGE: Fixed limit/skip/hint/batch_size chaining #1476
- POTENTIAL BREAKING CHANGE: Changed a public `QuerySet.clone_into` method to a private `QuerySet._clone_into` #1476 - POTENTIAL BREAKING CHANGE: Changed a public `QuerySet.clone_into` method to a private `QuerySet._clone_into` #1476
- Fixed connecting to a replica set with PyMongo 2.x #1436 - Fixed connecting to a replica set with PyMongo 2.x #1436

View File

@@ -150,7 +150,7 @@ arguments can be set on all fields:
.. note:: If set, this field is also accessible through the `pk` field. .. note:: If set, this field is also accessible through the `pk` field.
:attr:`choices` (Default: None) :attr:`choices` (Default: None)
An iterable (e.g. a list or tuple) of choices to which the value of this An iterable (e.g. list, tuple or set) of choices to which the value of this
field should be limited. field should be limited.
Can be either be a nested tuples of value (stored in mongo) and a Can be either be a nested tuples of value (stored in mongo) and a

View File

@@ -193,7 +193,8 @@ class BaseField(object):
EmbeddedDocument = _import_class('EmbeddedDocument') EmbeddedDocument = _import_class('EmbeddedDocument')
choice_list = self.choices choice_list = self.choices
if isinstance(choice_list[0], (list, tuple)): if isinstance(next(iter(choice_list)), (list, tuple)):
# next(iter) is useful for sets
choice_list = [k for k, _ in choice_list] choice_list = [k for k, _ in choice_list]
# Choices which are other types of Documents # Choices which are other types of Documents

View File

@@ -139,12 +139,12 @@ class URLField(StringField):
# Check first if the scheme is valid # Check first if the scheme is valid
scheme = value.split('://')[0].lower() scheme = value.split('://')[0].lower()
if scheme not in self.schemes: if scheme not in self.schemes:
self.error('Invalid scheme {} in URL: {}'.format(scheme, value)) self.error(u'Invalid scheme {} in URL: {}'.format(scheme, value))
return return
# Then check full URL # Then check full URL
if not self.url_regex.match(value): if not self.url_regex.match(value):
self.error('Invalid URL: {}'.format(value)) self.error(u'Invalid URL: {}'.format(value))
return return

View File

@@ -1,13 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import six
from nose.plugins.skip import SkipTest
import datetime import datetime
import unittest import unittest
import uuid import uuid
import math import math
import itertools import itertools
import re import re
from nose.plugins.skip import SkipTest
import six import six
try: try:
@@ -27,21 +26,13 @@ from mongoengine import *
from mongoengine.connection import get_db from mongoengine.connection import get_db
from mongoengine.base import (BaseDict, BaseField, EmbeddedDocumentList, from mongoengine.base import (BaseDict, BaseField, EmbeddedDocumentList,
_document_registry) _document_registry)
from mongoengine.errors import NotRegistered, DoesNotExist
from tests.utils import MongoDBTestCase
__all__ = ("FieldTest", "EmbeddedDocumentListFieldTestCase") __all__ = ("FieldTest", "EmbeddedDocumentListFieldTestCase")
class FieldTest(unittest.TestCase): class FieldTest(MongoDBTestCase):
def setUp(self):
connect(db='mongoenginetest')
self.db = get_db()
def tearDown(self):
self.db.drop_collection('fs.files')
self.db.drop_collection('fs.chunks')
self.db.drop_collection('mongoengine.counters')
def test_default_values_nothing_set(self): def test_default_values_nothing_set(self):
"""Ensure that default field values are used when creating a document. """Ensure that default field values are used when creating a document.
@@ -227,9 +218,9 @@ class FieldTest(unittest.TestCase):
self.assertTrue(isinstance(ret.comp_dt_fld, datetime.datetime)) self.assertTrue(isinstance(ret.comp_dt_fld, datetime.datetime))
def test_not_required_handles_none_from_database(self): def test_not_required_handles_none_from_database(self):
"""Ensure that every fields can handle null values from the database. """Ensure that every field can handle null values from the
database.
""" """
class HandleNoneFields(Document): class HandleNoneFields(Document):
str_fld = StringField(required=True) str_fld = StringField(required=True)
int_fld = IntField(required=True) int_fld = IntField(required=True)
@@ -350,11 +341,12 @@ class FieldTest(unittest.TestCase):
person.validate() person.validate()
def test_url_validation(self): def test_url_validation(self):
"""Ensure that URLFields validate urls properly. """Ensure that URLFields validate urls properly."""
"""
class Link(Document): class Link(Document):
url = URLField() url = URLField()
Link.drop_collection()
link = Link() link = Link()
link.url = 'google' link.url = 'google'
self.assertRaises(ValidationError, link.validate) self.assertRaises(ValidationError, link.validate)
@@ -362,6 +354,27 @@ class FieldTest(unittest.TestCase):
link.url = 'http://www.google.com:8080' link.url = 'http://www.google.com:8080'
link.validate() link.validate()
def test_unicode_url_validation(self):
"""Ensure unicode URLs are validated properly."""
class Link(Document):
url = URLField()
Link.drop_collection()
link = Link()
link.url = u'http://привет.com'
# TODO fix URL validation - this *IS* a valid URL
# For now we just want to make sure that the error message is correct
try:
link.validate()
self.assertTrue(False)
except ValidationError as e:
self.assertEqual(
unicode(e),
u"ValidationError (Link:None) (Invalid URL: http://\u043f\u0440\u0438\u0432\u0435\u0442.com: ['url'])"
)
def test_url_scheme_validation(self): def test_url_scheme_validation(self):
"""Ensure that URLFields validate urls with specific schemes properly. """Ensure that URLFields validate urls with specific schemes properly.
""" """
@@ -3186,26 +3199,42 @@ class FieldTest(unittest.TestCase):
att.delete() att.delete()
self.assertEqual(0, Attachment.objects.count()) self.assertEqual(0, Attachment.objects.count())
def test_choices_validation(self): def test_choices_allow_using_sets_as_choices(self):
"""Ensure that value is in a container of allowed values. """Ensure that sets can be used when setting choices
""" """
class Shirt(Document): class Shirt(Document):
size = StringField(max_length=3, choices=( size = StringField(choices={'M', 'L'})
('S', 'Small'), ('M', 'Medium'), ('L', 'Large'),
('XL', 'Extra Large'), ('XXL', 'Extra Extra Large')))
Shirt.drop_collection() Shirt(size='M').validate()
def test_choices_validation_allow_no_value(self):
"""Ensure that .validate passes and no value was provided
for a field setup with choices
"""
class Shirt(Document):
size = StringField(choices=('S', 'M'))
shirt = Shirt() shirt = Shirt()
shirt.validate() shirt.validate()
shirt.size = "S" def test_choices_validation_accept_possible_value(self):
"""Ensure that value is in a container of allowed values.
"""
class Shirt(Document):
size = StringField(choices=('S', 'M'))
shirt = Shirt(size='S')
shirt.validate() shirt.validate()
shirt.size = "XS" def test_choices_validation_reject_unknown_value(self):
self.assertRaises(ValidationError, shirt.validate) """Ensure that unallowed value are rejected upon validation
"""
class Shirt(Document):
size = StringField(choices=('S', 'M'))
Shirt.drop_collection() shirt = Shirt(size="XS")
with self.assertRaises(ValidationError):
shirt.validate()
def test_choices_validation_documents(self): def test_choices_validation_documents(self):
""" """
@@ -4024,12 +4053,13 @@ class FieldTest(unittest.TestCase):
self.assertTrue(isinstance(doc.some_long, six.integer_types)) self.assertTrue(isinstance(doc.some_long, six.integer_types))
class EmbeddedDocumentListFieldTestCase(unittest.TestCase): class EmbeddedDocumentListFieldTestCase(MongoDBTestCase):
@classmethod
def setUpClass(cls):
cls.db = connect(db='EmbeddedDocumentListFieldTestCase')
def setUp(self):
"""
Create two BlogPost entries in the database, each with
several EmbeddedDocuments.
"""
class Comments(EmbeddedDocument): class Comments(EmbeddedDocument):
author = StringField() author = StringField()
message = StringField() message = StringField()
@@ -4037,14 +4067,11 @@ class EmbeddedDocumentListFieldTestCase(unittest.TestCase):
class BlogPost(Document): class BlogPost(Document):
comments = EmbeddedDocumentListField(Comments) comments = EmbeddedDocumentListField(Comments)
cls.Comments = Comments BlogPost.drop_collection()
cls.BlogPost = BlogPost
self.Comments = Comments
self.BlogPost = BlogPost
def setUp(self):
"""
Create two BlogPost entries in the database, each with
several EmbeddedDocuments.
"""
self.post1 = self.BlogPost(comments=[ self.post1 = self.BlogPost(comments=[
self.Comments(author='user1', message='message1'), self.Comments(author='user1', message='message1'),
self.Comments(author='user2', message='message1') self.Comments(author='user2', message='message1')
@@ -4056,13 +4083,6 @@ class EmbeddedDocumentListFieldTestCase(unittest.TestCase):
self.Comments(author='user3', message='message1') self.Comments(author='user3', message='message1')
]).save() ]).save()
def tearDown(self):
self.BlogPost.drop_collection()
@classmethod
def tearDownClass(cls):
cls.db.drop_database('EmbeddedDocumentListFieldTestCase')
def test_no_keyword_filter(self): def test_no_keyword_filter(self):
""" """
Tests the filter method of a List of Embedded Documents Tests the filter method of a List of Embedded Documents
@@ -4420,7 +4440,8 @@ class EmbeddedDocumentListFieldTestCase(unittest.TestCase):
my_list = ListField(EmbeddedDocumentField(EmbeddedWithUnique)) my_list = ListField(EmbeddedDocumentField(EmbeddedWithUnique))
A(my_list=[]).save() A(my_list=[]).save()
self.assertRaises(NotUniqueError, lambda: A(my_list=[]).save()) with self.assertRaises(NotUniqueError):
A(my_list=[]).save()
class EmbeddedWithSparseUnique(EmbeddedDocument): class EmbeddedWithSparseUnique(EmbeddedDocument):
number = IntField(unique=True, sparse=True) number = IntField(unique=True, sparse=True)
@@ -4428,6 +4449,9 @@ class EmbeddedDocumentListFieldTestCase(unittest.TestCase):
class B(Document): class B(Document):
my_list = ListField(EmbeddedDocumentField(EmbeddedWithSparseUnique)) my_list = ListField(EmbeddedDocumentField(EmbeddedWithSparseUnique))
A.drop_collection()
B.drop_collection()
B(my_list=[]).save() B(my_list=[]).save()
B(my_list=[]).save() B(my_list=[]).save()
@@ -4467,6 +4491,8 @@ class EmbeddedDocumentListFieldTestCase(unittest.TestCase):
a_field = IntField() a_field = IntField()
c_field = IntField(custom_data=custom_data) c_field = IntField(custom_data=custom_data)
CustomData.drop_collection()
a1 = CustomData(a_field=1, c_field=2).save() a1 = CustomData(a_field=1, c_field=2).save()
self.assertEqual(2, a1.c_field) self.assertEqual(2, a1.c_field)
self.assertFalse(hasattr(a1.c_field, 'custom_data')) self.assertFalse(hasattr(a1.c_field, 'custom_data'))

View File

@@ -18,15 +18,13 @@ try:
except ImportError: except ImportError:
HAS_PIL = False HAS_PIL = False
from tests.utils import MongoDBTestCase
TEST_IMAGE_PATH = os.path.join(os.path.dirname(__file__), 'mongoengine.png') TEST_IMAGE_PATH = os.path.join(os.path.dirname(__file__), 'mongoengine.png')
TEST_IMAGE2_PATH = os.path.join(os.path.dirname(__file__), 'mongodb_leaf.png') TEST_IMAGE2_PATH = os.path.join(os.path.dirname(__file__), 'mongodb_leaf.png')
class FileTest(unittest.TestCase): class FileTest(MongoDBTestCase):
def setUp(self):
connect(db='mongoenginetest')
self.db = get_db()
def tearDown(self): def tearDown(self):
self.db.drop_collection('fs.files') self.db.drop_collection('fs.files')

22
tests/utils.py Normal file
View File

@@ -0,0 +1,22 @@
import unittest
from mongoengine import connect
from mongoengine.connection import get_db
MONGO_TEST_DB = 'mongoenginetest'
class MongoDBTestCase(unittest.TestCase):
"""Base class for tests that need a mongodb connection
db is being dropped automatically
"""
@classmethod
def setUpClass(cls):
cls._connection = connect(db=MONGO_TEST_DB)
cls._connection.drop_database(MONGO_TEST_DB)
cls.db = get_db()
@classmethod
def tearDownClass(cls):
cls._connection.drop_database(MONGO_TEST_DB)