From 6fefc53c58ffb429ad5d33ce89cc3a505a7df5b9 Mon Sep 17 00:00:00 2001 From: romgar Date: Wed, 23 Nov 2016 23:49:53 +0000 Subject: [PATCH] Separate tests in small files, making it easier to edit/add new ones/not conflict on different merge requests --- model_utils/tests/helpers.py | 5 + model_utils/tests/test_choices.py | 261 +++ model_utils/tests/test_fields/__init__.py | 5 + .../tests/test_fields/test_field_tracker.py | 593 +++++ .../tests/test_fields/test_monitor_field.py | 120 + .../tests/test_fields/test_split_field.py | 78 + .../tests/test_fields/test_status_field.py | 32 + model_utils/tests/test_managers/__init__.py | 4 + .../test_managers/test_inheritance_manager.py | 500 ++++ .../tests/test_managers/test_query_manager.py | 29 + .../test_managers/test_status_manager.py | 23 + model_utils/tests/test_miscellaneous.py | 60 + model_utils/tests/test_models/__init__.py | 6 + .../tests/test_models/test_model_tracker.py | 150 ++ .../test_models/test_softdeletable_model.py | 38 + .../tests/test_models/test_status_model.py | 46 + .../test_models/test_timeframed_model.py | 47 + .../test_models/test_timestamped_model.py | 25 + model_utils/tests/tests.py | 2053 +---------------- 19 files changed, 2028 insertions(+), 2047 deletions(-) create mode 100644 model_utils/tests/helpers.py create mode 100644 model_utils/tests/test_choices.py create mode 100644 model_utils/tests/test_fields/__init__.py create mode 100644 model_utils/tests/test_fields/test_field_tracker.py create mode 100644 model_utils/tests/test_fields/test_monitor_field.py create mode 100644 model_utils/tests/test_fields/test_split_field.py create mode 100644 model_utils/tests/test_fields/test_status_field.py create mode 100644 model_utils/tests/test_managers/__init__.py create mode 100644 model_utils/tests/test_managers/test_inheritance_manager.py create mode 100644 model_utils/tests/test_managers/test_query_manager.py create mode 100644 model_utils/tests/test_managers/test_status_manager.py create mode 100644 model_utils/tests/test_miscellaneous.py create mode 100644 model_utils/tests/test_models/__init__.py create mode 100644 model_utils/tests/test_models/test_model_tracker.py create mode 100644 model_utils/tests/test_models/test_softdeletable_model.py create mode 100644 model_utils/tests/test_models/test_status_model.py create mode 100644 model_utils/tests/test_models/test_timeframed_model.py create mode 100644 model_utils/tests/test_models/test_timestamped_model.py diff --git a/model_utils/tests/helpers.py b/model_utils/tests/helpers.py new file mode 100644 index 0000000..499dd52 --- /dev/null +++ b/model_utils/tests/helpers.py @@ -0,0 +1,5 @@ + +try: + from unittest import skipUnless +except ImportError: # Python 2.6 + from django.utils.unittest import skipUnless diff --git a/model_utils/tests/test_choices.py b/model_utils/tests/test_choices.py new file mode 100644 index 0000000..a503405 --- /dev/null +++ b/model_utils/tests/test_choices.py @@ -0,0 +1,261 @@ +from __future__ import unicode_literals + +from django.test import TestCase + +from model_utils import Choices + + +class ChoicesTests(TestCase): + def setUp(self): + self.STATUS = Choices('DRAFT', 'PUBLISHED') + + def test_getattr(self): + self.assertEqual(self.STATUS.DRAFT, 'DRAFT') + + def test_indexing(self): + self.assertEqual(self.STATUS['PUBLISHED'], 'PUBLISHED') + + def test_iteration(self): + self.assertEqual(tuple(self.STATUS), (('DRAFT', 'DRAFT'), ('PUBLISHED', 'PUBLISHED'))) + + def test_len(self): + self.assertEqual(len(self.STATUS), 2) + + def test_repr(self): + self.assertEqual(repr(self.STATUS), "Choices" + repr(( + ('DRAFT', 'DRAFT', 'DRAFT'), + ('PUBLISHED', 'PUBLISHED', 'PUBLISHED'), + ))) + + def test_wrong_length_tuple(self): + with self.assertRaises(ValueError): + Choices(('a',)) + + def test_contains_value(self): + self.assertTrue('PUBLISHED' in self.STATUS) + self.assertTrue('DRAFT' in self.STATUS) + + def test_doesnt_contain_value(self): + self.assertFalse('UNPUBLISHED' in self.STATUS) + + def test_deepcopy(self): + import copy + self.assertEqual(list(self.STATUS), + list(copy.deepcopy(self.STATUS))) + + def test_equality(self): + self.assertEqual(self.STATUS, Choices('DRAFT', 'PUBLISHED')) + + def test_inequality(self): + self.assertNotEqual(self.STATUS, ['DRAFT', 'PUBLISHED']) + self.assertNotEqual(self.STATUS, Choices('DRAFT')) + + def test_composability(self): + self.assertEqual(Choices('DRAFT') + Choices('PUBLISHED'), self.STATUS) + self.assertEqual(Choices('DRAFT') + ('PUBLISHED',), self.STATUS) + self.assertEqual(('DRAFT',) + Choices('PUBLISHED'), self.STATUS) + + def test_option_groups(self): + c = Choices(('group a', ['one', 'two']), ['group b', ('three',)]) + self.assertEqual( + list(c), + [ + ('group a', [('one', 'one'), ('two', 'two')]), + ('group b', [('three', 'three')]), + ], + ) + + +class LabelChoicesTests(ChoicesTests): + def setUp(self): + self.STATUS = Choices( + ('DRAFT', 'is draft'), + ('PUBLISHED', 'is published'), + 'DELETED', + ) + + def test_iteration(self): + self.assertEqual(tuple(self.STATUS), ( + ('DRAFT', 'is draft'), + ('PUBLISHED', 'is published'), + ('DELETED', 'DELETED')) + ) + + def test_indexing(self): + self.assertEqual(self.STATUS['PUBLISHED'], 'is published') + + def test_default(self): + self.assertEqual(self.STATUS.DELETED, 'DELETED') + + def test_provided(self): + self.assertEqual(self.STATUS.DRAFT, 'DRAFT') + + def test_len(self): + self.assertEqual(len(self.STATUS), 3) + + def test_equality(self): + self.assertEqual(self.STATUS, Choices( + ('DRAFT', 'is draft'), + ('PUBLISHED', 'is published'), + 'DELETED', + )) + + def test_inequality(self): + self.assertNotEqual(self.STATUS, [ + ('DRAFT', 'is draft'), + ('PUBLISHED', 'is published'), + 'DELETED' + ]) + self.assertNotEqual(self.STATUS, Choices('DRAFT')) + + def test_repr(self): + self.assertEqual(repr(self.STATUS), "Choices" + repr(( + ('DRAFT', 'DRAFT', 'is draft'), + ('PUBLISHED', 'PUBLISHED', 'is published'), + ('DELETED', 'DELETED', 'DELETED'), + ))) + + def test_contains_value(self): + self.assertTrue('PUBLISHED' in self.STATUS) + self.assertTrue('DRAFT' in self.STATUS) + # This should be True, because both the display value + # and the internal representation are both DELETED. + self.assertTrue('DELETED' in self.STATUS) + + def test_doesnt_contain_value(self): + self.assertFalse('UNPUBLISHED' in self.STATUS) + + def test_doesnt_contain_display_value(self): + self.assertFalse('is draft' in self.STATUS) + + def test_composability(self): + self.assertEqual( + Choices(('DRAFT', 'is draft',)) + Choices(('PUBLISHED', 'is published'), 'DELETED'), + self.STATUS + ) + + self.assertEqual( + (('DRAFT', 'is draft',),) + Choices(('PUBLISHED', 'is published'), 'DELETED'), + self.STATUS + ) + + self.assertEqual( + Choices(('DRAFT', 'is draft',)) + (('PUBLISHED', 'is published'), 'DELETED'), + self.STATUS + ) + + def test_option_groups(self): + c = Choices( + ('group a', [(1, 'one'), (2, 'two')]), + ['group b', ((3, 'three'),)] + ) + self.assertEqual( + list(c), + [ + ('group a', [(1, 'one'), (2, 'two')]), + ('group b', [(3, 'three')]), + ], + ) + + +class IdentifierChoicesTests(ChoicesTests): + def setUp(self): + self.STATUS = Choices( + (0, 'DRAFT', 'is draft'), + (1, 'PUBLISHED', 'is published'), + (2, 'DELETED', 'is deleted')) + + def test_iteration(self): + self.assertEqual(tuple(self.STATUS), ( + (0, 'is draft'), + (1, 'is published'), + (2, 'is deleted'))) + + def test_indexing(self): + self.assertEqual(self.STATUS[1], 'is published') + + def test_getattr(self): + self.assertEqual(self.STATUS.DRAFT, 0) + + def test_len(self): + self.assertEqual(len(self.STATUS), 3) + + def test_repr(self): + self.assertEqual(repr(self.STATUS), "Choices" + repr(( + (0, 'DRAFT', 'is draft'), + (1, 'PUBLISHED', 'is published'), + (2, 'DELETED', 'is deleted'), + ))) + + def test_contains_value(self): + self.assertTrue(0 in self.STATUS) + self.assertTrue(1 in self.STATUS) + self.assertTrue(2 in self.STATUS) + + def test_doesnt_contain_value(self): + self.assertFalse(3 in self.STATUS) + + def test_doesnt_contain_display_value(self): + self.assertFalse('is draft' in self.STATUS) + + def test_doesnt_contain_python_attr(self): + self.assertFalse('PUBLISHED' in self.STATUS) + + def test_equality(self): + self.assertEqual(self.STATUS, Choices( + (0, 'DRAFT', 'is draft'), + (1, 'PUBLISHED', 'is published'), + (2, 'DELETED', 'is deleted') + )) + + def test_inequality(self): + self.assertNotEqual(self.STATUS, [ + (0, 'DRAFT', 'is draft'), + (1, 'PUBLISHED', 'is published'), + (2, 'DELETED', 'is deleted') + ]) + self.assertNotEqual(self.STATUS, Choices('DRAFT')) + + def test_composability(self): + self.assertEqual( + Choices( + (0, 'DRAFT', 'is draft'), + (1, 'PUBLISHED', 'is published') + ) + Choices( + (2, 'DELETED', 'is deleted'), + ), + self.STATUS + ) + + self.assertEqual( + Choices( + (0, 'DRAFT', 'is draft'), + (1, 'PUBLISHED', 'is published') + ) + ( + (2, 'DELETED', 'is deleted'), + ), + self.STATUS + ) + + self.assertEqual( + ( + (0, 'DRAFT', 'is draft'), + (1, 'PUBLISHED', 'is published') + ) + Choices( + (2, 'DELETED', 'is deleted'), + ), + self.STATUS + ) + + def test_option_groups(self): + c = Choices( + ('group a', [(1, 'ONE', 'one'), (2, 'TWO', 'two')]), + ['group b', ((3, 'THREE', 'three'),)] + ) + self.assertEqual( + list(c), + [ + ('group a', [(1, 'one'), (2, 'two')]), + ('group b', [(3, 'three')]), + ], + ) diff --git a/model_utils/tests/test_fields/__init__.py b/model_utils/tests/test_fields/__init__.py new file mode 100644 index 0000000..23a349f --- /dev/null +++ b/model_utils/tests/test_fields/__init__.py @@ -0,0 +1,5 @@ +# Needed for Django 1.4/1.5 test runner +from .test_field_tracker import * +from .test_monitor_field import * +from .test_split_field import * +from .test_status_field import * diff --git a/model_utils/tests/test_fields/test_field_tracker.py b/model_utils/tests/test_fields/test_field_tracker.py new file mode 100644 index 0000000..4bd96e1 --- /dev/null +++ b/model_utils/tests/test_fields/test_field_tracker.py @@ -0,0 +1,593 @@ +from __future__ import unicode_literals + +import django +from django.core.exceptions import FieldError +from django.test import TestCase + +from model_utils import FieldTracker +from model_utils.tests.helpers import skipUnless +from model_utils.tests.models import ( + Tracked, TrackedFK, InheritedTrackedFK, TrackedNotDefault, TrackedNonFieldAttr, TrackedMultiple, + InheritedTracked, TrackedFileField) + + +class FieldTrackerTestCase(TestCase): + + tracker = None + + def assertHasChanged(self, **kwargs): + tracker = kwargs.pop('tracker', self.tracker) + for field, value in kwargs.items(): + if value is None: + with self.assertRaises(FieldError): + tracker.has_changed(field) + else: + self.assertEqual(tracker.has_changed(field), value) + + def assertPrevious(self, **kwargs): + tracker = kwargs.pop('tracker', self.tracker) + for field, value in kwargs.items(): + self.assertEqual(tracker.previous(field), value) + + def assertChanged(self, **kwargs): + tracker = kwargs.pop('tracker', self.tracker) + self.assertEqual(tracker.changed(), kwargs) + + def assertCurrent(self, **kwargs): + tracker = kwargs.pop('tracker', self.tracker) + self.assertEqual(tracker.current(), kwargs) + + def update_instance(self, **kwargs): + for field, value in kwargs.items(): + setattr(self.instance, field, value) + self.instance.save() + + +class FieldTrackerCommonTests(object): + + def test_pre_save_previous(self): + self.assertPrevious(name=None, number=None) + self.instance.name = 'new age' + self.instance.number = 8 + self.assertPrevious(name=None, number=None) + + +class FieldTrackerTests(FieldTrackerTestCase, FieldTrackerCommonTests): + + tracked_class = Tracked + + def setUp(self): + self.instance = self.tracked_class() + self.tracker = self.instance.tracker + + def test_descriptor(self): + self.assertTrue(isinstance(self.tracked_class.tracker, FieldTracker)) + + def test_pre_save_changed(self): + self.assertChanged(name=None) + self.instance.name = 'new age' + self.assertChanged(name=None) + self.instance.number = 8 + self.assertChanged(name=None, number=None) + self.instance.name = '' + self.assertChanged(name=None, number=None) + self.instance.mutable = [1,2,3] + self.assertChanged(name=None, number=None, mutable=None) + + def test_pre_save_has_changed(self): + self.assertHasChanged(name=True, number=False, mutable=False) + self.instance.name = 'new age' + self.assertHasChanged(name=True, number=False, mutable=False) + self.instance.number = 7 + self.assertHasChanged(name=True, number=True) + self.instance.mutable = [1,2,3] + self.assertHasChanged(name=True, number=True, mutable=True) + + def test_first_save(self): + self.assertHasChanged(name=True, number=False, mutable=False) + self.assertPrevious(name=None, number=None, mutable=None) + self.assertCurrent(name='', number=None, id=None, mutable=None) + self.assertChanged(name=None) + self.instance.name = 'retro' + self.instance.number = 4 + self.instance.mutable = [1,2,3] + self.assertHasChanged(name=True, number=True, mutable=True) + self.assertPrevious(name=None, number=None, mutable=None) + self.assertCurrent(name='retro', number=4, id=None, mutable=[1,2,3]) + self.assertChanged(name=None, number=None, mutable=None) + # Django 1.4 doesn't have update_fields + if django.VERSION >= (1, 5, 0): + self.instance.save(update_fields=[]) + self.assertHasChanged(name=True, number=True, mutable=True) + self.assertPrevious(name=None, number=None, mutable=None) + self.assertCurrent(name='retro', number=4, id=None, mutable=[1,2,3]) + self.assertChanged(name=None, number=None, mutable=None) + with self.assertRaises(ValueError): + self.instance.save(update_fields=['number']) + + def test_post_save_has_changed(self): + self.update_instance(name='retro', number=4, mutable=[1,2,3]) + self.assertHasChanged(name=False, number=False, mutable=False) + self.instance.name = 'new age' + self.assertHasChanged(name=True, number=False) + self.instance.number = 8 + self.assertHasChanged(name=True, number=True) + self.instance.mutable[1] = 4 + self.assertHasChanged(name=True, number=True, mutable=True) + self.instance.name = 'retro' + self.assertHasChanged(name=False, number=True, mutable=True) + + def test_post_save_previous(self): + self.update_instance(name='retro', number=4, mutable=[1,2,3]) + self.instance.name = 'new age' + self.assertPrevious(name='retro', number=4, mutable=[1,2,3]) + self.instance.mutable[1] = 4 + self.assertPrevious(name='retro', number=4, mutable=[1,2,3]) + + def test_post_save_changed(self): + self.update_instance(name='retro', number=4, mutable=[1,2,3]) + self.assertChanged() + self.instance.name = 'new age' + self.assertChanged(name='retro') + self.instance.number = 8 + self.assertChanged(name='retro', number=4) + self.instance.name = 'retro' + self.assertChanged(number=4) + self.instance.mutable[1] = 4 + self.assertChanged(number=4, mutable=[1,2,3]) + self.instance.mutable = [1,2,3] + self.assertChanged(number=4) + + def test_current(self): + self.assertCurrent(id=None, name='', number=None, mutable=None) + self.instance.name = 'new age' + self.assertCurrent(id=None, name='new age', number=None, mutable=None) + self.instance.number = 8 + self.assertCurrent(id=None, name='new age', number=8, mutable=None) + self.instance.mutable = [1,2,3] + self.assertCurrent(id=None, name='new age', number=8, mutable=[1,2,3]) + self.instance.mutable[1] = 4 + self.assertCurrent(id=None, name='new age', number=8, mutable=[1,4,3]) + self.instance.save() + self.assertCurrent(id=self.instance.id, name='new age', number=8, mutable=[1,4,3]) + + @skipUnless( + django.VERSION >= (1, 5, 0), "Django 1.4 doesn't have update_fields") + def test_update_fields(self): + self.update_instance(name='retro', number=4, mutable=[1,2,3]) + self.assertChanged() + self.instance.name = 'new age' + self.instance.number = 8 + self.instance.mutable = [4,5,6] + self.assertChanged(name='retro', number=4, mutable=[1,2,3]) + self.instance.save(update_fields=[]) + self.assertChanged(name='retro', number=4, mutable=[1,2,3]) + self.instance.save(update_fields=['name']) + in_db = self.tracked_class.objects.get(id=self.instance.id) + self.assertEqual(in_db.name, self.instance.name) + self.assertNotEqual(in_db.number, self.instance.number) + self.assertChanged(number=4, mutable=[1,2,3]) + self.instance.save(update_fields=['number']) + self.assertChanged(mutable=[1,2,3]) + self.instance.save(update_fields=['mutable']) + self.assertChanged() + in_db = self.tracked_class.objects.get(id=self.instance.id) + self.assertEqual(in_db.name, self.instance.name) + self.assertEqual(in_db.number, self.instance.number) + self.assertEqual(in_db.mutable, self.instance.mutable) + + def test_with_deferred(self): + self.instance.name = 'new age' + self.instance.number = 1 + self.instance.save() + item = list(self.tracked_class.objects.only('name').all())[0] + self.assertTrue(item._deferred_fields) + + self.assertEqual(item.tracker.previous('number'), None) + self.assertTrue('number' in item._deferred_fields) + + self.assertEqual(item.number, 1) + self.assertTrue('number' not in item._deferred_fields) + self.assertEqual(item.tracker.previous('number'), 1) + self.assertFalse(item.tracker.has_changed('number')) + + item.number = 2 + self.assertTrue(item.tracker.has_changed('number')) + + +class FieldTrackerMultipleInstancesTests(TestCase): + + def test_with_deferred_fields_access_multiple(self): + Tracked.objects.create(pk=1, name='foo', number=1) + Tracked.objects.create(pk=2, name='bar', number=2) + + queryset = Tracked.objects.only('id') + + for instance in queryset: + instance.name + + +class FieldTrackedModelCustomTests(FieldTrackerTestCase, + FieldTrackerCommonTests): + + tracked_class = TrackedNotDefault + + def setUp(self): + self.instance = self.tracked_class() + self.tracker = self.instance.name_tracker + + def test_pre_save_changed(self): + self.assertChanged(name=None) + self.instance.name = 'new age' + self.assertChanged(name=None) + self.instance.number = 8 + self.assertChanged(name=None) + self.instance.name = '' + self.assertChanged(name=None) + + def test_first_save(self): + self.assertHasChanged(name=True, number=None) + self.assertPrevious(name=None, number=None) + self.assertCurrent(name='') + self.assertChanged(name=None) + self.instance.name = 'retro' + self.instance.number = 4 + self.assertHasChanged(name=True, number=None) + self.assertPrevious(name=None, number=None) + self.assertCurrent(name='retro') + self.assertChanged(name=None) + + def test_pre_save_has_changed(self): + self.assertHasChanged(name=True, number=None) + self.instance.name = 'new age' + self.assertHasChanged(name=True, number=None) + self.instance.number = 7 + self.assertHasChanged(name=True, number=None) + + def test_post_save_has_changed(self): + self.update_instance(name='retro', number=4) + self.assertHasChanged(name=False, number=None) + self.instance.name = 'new age' + self.assertHasChanged(name=True, number=None) + self.instance.number = 8 + self.assertHasChanged(name=True, number=None) + self.instance.name = 'retro' + self.assertHasChanged(name=False, number=None) + + def test_post_save_previous(self): + self.update_instance(name='retro', number=4) + self.instance.name = 'new age' + self.assertPrevious(name='retro', number=None) + + def test_post_save_changed(self): + self.update_instance(name='retro', number=4) + self.assertChanged() + self.instance.name = 'new age' + self.assertChanged(name='retro') + self.instance.number = 8 + self.assertChanged(name='retro') + self.instance.name = 'retro' + self.assertChanged() + + def test_current(self): + self.assertCurrent(name='') + self.instance.name = 'new age' + self.assertCurrent(name='new age') + self.instance.number = 8 + self.assertCurrent(name='new age') + self.instance.save() + self.assertCurrent(name='new age') + + @skipUnless( + django.VERSION >= (1, 5, 0), "Django 1.4 doesn't have update_fields") + def test_update_fields(self): + self.update_instance(name='retro', number=4) + self.assertChanged() + self.instance.name = 'new age' + self.instance.number = 8 + self.instance.save(update_fields=['name', 'number']) + self.assertChanged() + + +class FieldTrackedModelAttributeTests(FieldTrackerTestCase): + + tracked_class = TrackedNonFieldAttr + + def setUp(self): + self.instance = self.tracked_class() + self.tracker = self.instance.tracker + + def test_previous(self): + self.assertPrevious(rounded=None) + self.instance.number = 7.5 + self.assertPrevious(rounded=None) + self.instance.save() + self.assertPrevious(rounded=8) + self.instance.number = 7.2 + self.assertPrevious(rounded=8) + self.instance.save() + self.assertPrevious(rounded=7) + + def test_has_changed(self): + self.assertHasChanged(rounded=False) + self.instance.number = 7.5 + self.assertHasChanged(rounded=True) + self.instance.save() + self.assertHasChanged(rounded=False) + self.instance.number = 7.2 + self.assertHasChanged(rounded=True) + self.instance.number = 7.8 + self.assertHasChanged(rounded=False) + + def test_changed(self): + self.assertChanged() + self.instance.number = 7.5 + self.assertPrevious(rounded=None) + self.instance.save() + self.assertPrevious() + self.instance.number = 7.8 + self.assertPrevious() + self.instance.number = 7.2 + self.assertPrevious(rounded=8) + self.instance.save() + self.assertPrevious() + + def test_current(self): + self.assertCurrent(rounded=None) + self.instance.number = 7.5 + self.assertCurrent(rounded=8) + self.instance.save() + self.assertCurrent(rounded=8) + + +class FieldTrackedModelMultiTests(FieldTrackerTestCase, + FieldTrackerCommonTests): + + tracked_class = TrackedMultiple + + def setUp(self): + self.instance = self.tracked_class() + self.trackers = [self.instance.name_tracker, + self.instance.number_tracker] + + def test_pre_save_changed(self): + self.tracker = self.instance.name_tracker + self.assertChanged(name=None) + self.instance.name = 'new age' + self.assertChanged(name=None) + self.instance.number = 8 + self.assertChanged(name=None) + self.instance.name = '' + self.assertChanged(name=None) + self.tracker = self.instance.number_tracker + self.assertChanged(number=None) + self.instance.name = 'new age' + self.assertChanged(number=None) + self.instance.number = 8 + self.assertChanged(number=None) + + def test_pre_save_has_changed(self): + self.tracker = self.instance.name_tracker + self.assertHasChanged(name=True, number=None) + self.instance.name = 'new age' + self.assertHasChanged(name=True, number=None) + self.tracker = self.instance.number_tracker + self.assertHasChanged(name=None, number=False) + self.instance.name = 'new age' + self.assertHasChanged(name=None, number=False) + + def test_pre_save_previous(self): + for tracker in self.trackers: + self.tracker = tracker + super(FieldTrackedModelMultiTests, self).test_pre_save_previous() + + def test_post_save_has_changed(self): + self.update_instance(name='retro', number=4) + self.assertHasChanged(tracker=self.trackers[0], name=False, number=None) + self.assertHasChanged(tracker=self.trackers[1], name=None, number=False) + self.instance.name = 'new age' + self.assertHasChanged(tracker=self.trackers[0], name=True, number=None) + self.assertHasChanged(tracker=self.trackers[1], name=None, number=False) + self.instance.number = 8 + self.assertHasChanged(tracker=self.trackers[0], name=True, number=None) + self.assertHasChanged(tracker=self.trackers[1], name=None, number=True) + self.instance.name = 'retro' + self.instance.number = 4 + self.assertHasChanged(tracker=self.trackers[0], name=False, number=None) + self.assertHasChanged(tracker=self.trackers[1], name=None, number=False) + + def test_post_save_previous(self): + self.update_instance(name='retro', number=4) + self.instance.name = 'new age' + self.instance.number = 8 + self.assertPrevious(tracker=self.trackers[0], name='retro', number=None) + self.assertPrevious(tracker=self.trackers[1], name=None, number=4) + + def test_post_save_changed(self): + self.update_instance(name='retro', number=4) + self.assertChanged(tracker=self.trackers[0]) + self.assertChanged(tracker=self.trackers[1]) + self.instance.name = 'new age' + self.assertChanged(tracker=self.trackers[0], name='retro') + self.assertChanged(tracker=self.trackers[1]) + self.instance.number = 8 + self.assertChanged(tracker=self.trackers[0], name='retro') + self.assertChanged(tracker=self.trackers[1], number=4) + self.instance.name = 'retro' + self.instance.number = 4 + self.assertChanged(tracker=self.trackers[0]) + self.assertChanged(tracker=self.trackers[1]) + + def test_current(self): + self.assertCurrent(tracker=self.trackers[0], name='') + self.assertCurrent(tracker=self.trackers[1], number=None) + self.instance.name = 'new age' + self.assertCurrent(tracker=self.trackers[0], name='new age') + self.assertCurrent(tracker=self.trackers[1], number=None) + self.instance.number = 8 + self.assertCurrent(tracker=self.trackers[0], name='new age') + self.assertCurrent(tracker=self.trackers[1], number=8) + self.instance.save() + self.assertCurrent(tracker=self.trackers[0], name='new age') + self.assertCurrent(tracker=self.trackers[1], number=8) + + +class FieldTrackerForeignKeyTests(FieldTrackerTestCase): + + fk_class = Tracked + tracked_class = TrackedFK + + def setUp(self): + self.old_fk = self.fk_class.objects.create(number=8) + self.instance = self.tracked_class.objects.create(fk=self.old_fk) + + def test_default(self): + self.tracker = self.instance.tracker + self.assertChanged() + self.assertPrevious() + self.assertCurrent(id=self.instance.id, fk_id=self.old_fk.id) + self.instance.fk = self.fk_class.objects.create(number=8) + self.assertChanged(fk_id=self.old_fk.id) + self.assertPrevious(fk_id=self.old_fk.id) + self.assertCurrent(id=self.instance.id, fk_id=self.instance.fk_id) + + def test_custom(self): + self.tracker = self.instance.custom_tracker + self.assertChanged() + self.assertPrevious() + self.assertCurrent(fk_id=self.old_fk.id) + self.instance.fk = self.fk_class.objects.create(number=8) + self.assertChanged(fk_id=self.old_fk.id) + self.assertPrevious(fk_id=self.old_fk.id) + self.assertCurrent(fk_id=self.instance.fk_id) + + def test_custom_without_id(self): + with self.assertNumQueries(1): + self.tracked_class.objects.get() + self.tracker = self.instance.custom_tracker_without_id + self.assertChanged() + self.assertPrevious() + self.assertCurrent(fk=self.old_fk.id) + self.instance.fk = self.fk_class.objects.create(number=8) + self.assertChanged(fk=self.old_fk.id) + self.assertPrevious(fk=self.old_fk.id) + self.assertCurrent(fk=self.instance.fk_id) + + +class InheritedFieldTrackerTests(FieldTrackerTests): + + tracked_class = InheritedTracked + + def test_child_fields_not_tracked(self): + self.name2 = 'test' + self.assertEqual(self.tracker.previous('name2'), None) + self.assertRaises(FieldError, self.tracker.has_changed, 'name2') + + +class FieldTrackerInheritedForeignKeyTests(FieldTrackerForeignKeyTests): + + tracked_class = InheritedTrackedFK + + +class FieldTrackerFileFieldTests(FieldTrackerTestCase): + + tracked_class = TrackedFileField + + def setUp(self): + self.instance = self.tracked_class() + self.tracker = self.instance.tracker + self.some_file = 'something.txt' + self.another_file = 'another.txt' + + def test_pre_save_changed(self): + self.assertChanged(some_file=None) + self.instance.some_file = self.some_file + self.assertChanged(some_file=None) + + def test_pre_save_has_changed(self): + self.assertHasChanged(some_file=True) + self.instance.some_file = self.some_file + self.assertHasChanged(some_file=True) + + def test_pre_save_previous(self): + self.assertPrevious(some_file=None) + self.instance.some_file = self.some_file + self.assertPrevious(some_file=None) + + def test_post_save_changed(self): + self.update_instance(some_file=self.some_file) + self.assertChanged() + previous_file = self.instance.some_file + self.instance.some_file = self.another_file + self.assertChanged(some_file=previous_file) + # test deferred file field + deferred_instance = self.tracked_class.objects.defer('some_file')[0] + deferred_instance.some_file # access field to fetch from database + self.assertChanged(tracker=deferred_instance.tracker) + + previous_file = deferred_instance.some_file + deferred_instance.some_file = self.another_file + self.assertChanged( + tracker=deferred_instance.tracker, + some_file=previous_file, + ) + + def test_post_save_has_changed(self): + self.update_instance(some_file=self.some_file) + self.assertHasChanged(some_file=False) + self.instance.some_file = self.another_file + self.assertHasChanged(some_file=True) + + # test deferred file field + deferred_instance = self.tracked_class.objects.defer('some_file')[0] + deferred_instance.some_file # access field to fetch from database + self.assertHasChanged( + tracker=deferred_instance.tracker, + some_file=False, + ) + + deferred_instance.some_file = self.another_file + self.assertHasChanged( + tracker=deferred_instance.tracker, + some_file=True, + ) + + def test_post_save_previous(self): + self.update_instance(some_file=self.some_file) + previous_file = self.instance.some_file + self.instance.some_file = self.another_file + self.assertPrevious(some_file=previous_file) + + # test deferred file field + deferred_instance = self.tracked_class.objects.defer('some_file')[0] + deferred_instance.some_file # access field to fetch from database + self.assertPrevious( + tracker=deferred_instance.tracker, + some_file=previous_file, + ) + + deferred_instance.some_file = self.another_file + self.assertPrevious( + tracker=deferred_instance.tracker, + some_file=previous_file, + ) + + def test_current(self): + self.assertCurrent(some_file=self.instance.some_file, id=None) + self.instance.some_file = self.some_file + self.assertCurrent(some_file=self.instance.some_file, id=None) + + # test deferred file field + self.instance.save() + deferred_instance = self.tracked_class.objects.defer('some_file')[0] + deferred_instance.some_file # access field to fetch from database + self.assertCurrent( + some_file=self.instance.some_file, + id=self.instance.id, + ) + + self.instance.some_file = self.another_file + self.assertCurrent( + some_file=self.instance.some_file, + id=self.instance.id, + ) diff --git a/model_utils/tests/test_fields/test_monitor_field.py b/model_utils/tests/test_fields/test_monitor_field.py new file mode 100644 index 0000000..779f502 --- /dev/null +++ b/model_utils/tests/test_fields/test_monitor_field.py @@ -0,0 +1,120 @@ +from __future__ import unicode_literals + +from datetime import datetime + +from freezegun import freeze_time + +from django.test import TestCase + +from model_utils.fields import MonitorField +from model_utils.tests.models import Monitored, MonitorWhen, MonitorWhenEmpty, DoubleMonitored + + +class MonitorFieldTests(TestCase): + def setUp(self): + with freeze_time(datetime(2016, 1, 1, 10, 0, 0)): + self.instance = Monitored(name='Charlie') + self.created = self.instance.name_changed + + def test_save_no_change(self): + self.instance.save() + self.assertEqual(self.instance.name_changed, self.created) + + def test_save_changed(self): + with freeze_time(datetime(2016, 1, 1, 12, 0, 0)): + self.instance.name = 'Maria' + self.instance.save() + self.assertEqual(self.instance.name_changed, datetime(2016, 1, 1, 12, 0, 0)) + + def test_double_save(self): + self.instance.name = 'Jose' + self.instance.save() + changed = self.instance.name_changed + self.instance.save() + self.assertEqual(self.instance.name_changed, changed) + + def test_no_monitor_arg(self): + with self.assertRaises(TypeError): + MonitorField() + + +class MonitorWhenFieldTests(TestCase): + """ + Will record changes only when name is 'Jose' or 'Maria' + """ + def setUp(self): + with freeze_time(datetime(2016, 1, 1, 10, 0, 0)): + self.instance = MonitorWhen(name='Charlie') + self.created = self.instance.name_changed + + def test_save_no_change(self): + self.instance.save() + self.assertEqual(self.instance.name_changed, self.created) + + def test_save_changed_to_Jose(self): + with freeze_time(datetime(2016, 1, 1, 12, 0, 0)): + self.instance.name = 'Jose' + self.instance.save() + self.assertEqual(self.instance.name_changed, datetime(2016, 1, 1, 12, 0, 0)) + + def test_save_changed_to_Maria(self): + with freeze_time(datetime(2016, 1, 1, 12, 0, 0)): + self.instance.name = 'Maria' + self.instance.save() + self.assertEqual(self.instance.name_changed, datetime(2016, 1, 1, 12, 0, 0)) + + def test_save_changed_to_Pedro(self): + self.instance.name = 'Pedro' + self.instance.save() + self.assertEqual(self.instance.name_changed, self.created) + + def test_double_save(self): + self.instance.name = 'Jose' + self.instance.save() + changed = self.instance.name_changed + self.instance.save() + self.assertEqual(self.instance.name_changed, changed) + + +class MonitorWhenEmptyFieldTests(TestCase): + """ + Monitor should never be updated id when is an empty list. + """ + def setUp(self): + self.instance = MonitorWhenEmpty(name='Charlie') + self.created = self.instance.name_changed + + def test_save_no_change(self): + self.instance.save() + self.assertEqual(self.instance.name_changed, self.created) + + def test_save_changed_to_Jose(self): + self.instance.name = 'Jose' + self.instance.save() + self.assertEqual(self.instance.name_changed, self.created) + + def test_save_changed_to_Maria(self): + self.instance.name = 'Maria' + self.instance.save() + self.assertEqual(self.instance.name_changed, self.created) + + +class MonitorDoubleFieldTests(TestCase): + + def setUp(self): + DoubleMonitored.objects.create(name='Charlie', name2='Charlie2') + + def test_recursion_error_with_only(self): + # Any field passed to only() is generating a recursion error + list(DoubleMonitored.objects.only('id')) + + def test_recursion_error_with_defer(self): + # Only monitored fields passed to defer() are failing + list(DoubleMonitored.objects.defer('name')) + + def test_monitor_still_works_with_deferred_fields_filtered_out_of_save_initial(self): + obj = DoubleMonitored.objects.defer('name').get(name='Charlie') + with freeze_time("2016-12-01"): + obj.name = 'Charlie2' + obj.save() + self.assertEqual(obj.name_changed, datetime(2016, 12, 1)) diff --git a/model_utils/tests/test_fields/test_split_field.py b/model_utils/tests/test_fields/test_split_field.py new file mode 100644 index 0000000..57802fd --- /dev/null +++ b/model_utils/tests/test_fields/test_split_field.py @@ -0,0 +1,78 @@ +from __future__ import unicode_literals + +from django.utils.six import text_type +from django.test import TestCase + +from model_utils.tests.models import Article, SplitFieldAbstractParent + + +class SplitFieldTests(TestCase): + full_text = 'summary\n\n\n\nmore' + excerpt = 'summary\n' + + def setUp(self): + self.post = Article.objects.create( + title='example post', body=self.full_text) + + def test_unicode_content(self): + self.assertEqual(text_type(self.post.body), self.full_text) + + def test_excerpt(self): + self.assertEqual(self.post.body.excerpt, self.excerpt) + + def test_content(self): + self.assertEqual(self.post.body.content, self.full_text) + + def test_has_more(self): + self.assertTrue(self.post.body.has_more) + + def test_not_has_more(self): + post = Article.objects.create(title='example 2', + body='some text\n\nsome more\n') + self.assertFalse(post.body.has_more) + + def test_load_back(self): + post = Article.objects.get(pk=self.post.pk) + self.assertEqual(post.body.content, self.post.body.content) + self.assertEqual(post.body.excerpt, self.post.body.excerpt) + + def test_assign_to_body(self): + new_text = 'different\n\n\n\nother' + self.post.body = new_text + self.post.save() + self.assertEqual(text_type(self.post.body), new_text) + + def test_assign_to_content(self): + new_text = 'different\n\n\n\nother' + self.post.body.content = new_text + self.post.save() + self.assertEqual(text_type(self.post.body), new_text) + + def test_assign_to_excerpt(self): + with self.assertRaises(AttributeError): + self.post.body.excerpt = 'this should fail' + + def test_access_via_class(self): + with self.assertRaises(AttributeError): + Article.body + + def test_none(self): + a = Article(title='Some Title', body=None) + self.assertEqual(a.body, None) + + def test_assign_splittext(self): + a = Article(title='Some Title') + a.body = self.post.body + self.assertEqual(a.body.excerpt, 'summary\n') + + def test_value_to_string(self): + f = self.post._meta.get_field('body') + self.assertEqual(f.value_to_string(self.post), self.full_text) + + def test_abstract_inheritance(self): + class Child(SplitFieldAbstractParent): + pass + + self.assertEqual( + [f.name for f in Child._meta.fields], + ["id", "content", "_content_excerpt"]) diff --git a/model_utils/tests/test_fields/test_status_field.py b/model_utils/tests/test_fields/test_status_field.py new file mode 100644 index 0000000..73dabac --- /dev/null +++ b/model_utils/tests/test_fields/test_status_field.py @@ -0,0 +1,32 @@ +from __future__ import unicode_literals + +from django.test import TestCase + +from model_utils.fields import StatusField +from model_utils.tests.models import ( + Article, StatusFieldDefaultFilled, StatusFieldDefaultNotFilled, + StatusFieldChoicesName, + ) + + +class StatusFieldTests(TestCase): + + def test_status_with_default_filled(self): + instance = StatusFieldDefaultFilled() + self.assertEqual(instance.status, instance.STATUS.yes) + + def test_status_with_default_not_filled(self): + instance = StatusFieldDefaultNotFilled() + self.assertEqual(instance.status, instance.STATUS.no) + + def test_no_check_for_status(self): + field = StatusField(no_check_for_status=True) + # this model has no STATUS attribute, so checking for it would error + field.prepare_class(Article) + + def test_get_status_display(self): + instance = StatusFieldDefaultFilled() + self.assertEqual(instance.get_status_display(), "Yes") + + def test_choices_name(self): + StatusFieldChoicesName() diff --git a/model_utils/tests/test_managers/__init__.py b/model_utils/tests/test_managers/__init__.py new file mode 100644 index 0000000..0f8aec6 --- /dev/null +++ b/model_utils/tests/test_managers/__init__.py @@ -0,0 +1,4 @@ +# Needed for Django 1.4/1.5 test runner +from .test_inheritance_manager import * +from .test_query_manager import * +from .test_status_manager import * diff --git a/model_utils/tests/test_managers/test_inheritance_manager.py b/model_utils/tests/test_managers/test_inheritance_manager.py new file mode 100644 index 0000000..65d8b59 --- /dev/null +++ b/model_utils/tests/test_managers/test_inheritance_manager.py @@ -0,0 +1,500 @@ +from __future__ import unicode_literals + +import django +from django.db import models +from django.test import TestCase + +from model_utils.tests.helpers import skipUnless +from model_utils.tests.models import (InheritanceManagerTestRelated, InheritanceManagerTestGrandChild1, + InheritanceManagerTestGrandChild1_2, InheritanceManagerTestParent, + InheritanceManagerTestChild1, + InheritanceManagerTestChild2, TimeFrame, InheritanceManagerTestChild3 + ) + + +class InheritanceManagerTests(TestCase): + def setUp(self): + self.child1 = InheritanceManagerTestChild1.objects.create() + self.child2 = InheritanceManagerTestChild2.objects.create() + self.grandchild1 = InheritanceManagerTestGrandChild1.objects.create() + self.grandchild1_2 = \ + InheritanceManagerTestGrandChild1_2.objects.create() + + def get_manager(self): + return InheritanceManagerTestParent.objects + + def test_normal(self): + children = set([ + InheritanceManagerTestParent(pk=self.child1.pk), + InheritanceManagerTestParent(pk=self.child2.pk), + InheritanceManagerTestParent(pk=self.grandchild1.pk), + InheritanceManagerTestParent(pk=self.grandchild1_2.pk), + ]) + self.assertEqual(set(self.get_manager().all()), children) + + def test_select_all_subclasses(self): + children = set([self.child1, self.child2]) + if django.VERSION >= (1, 6, 0): + children.add(self.grandchild1) + children.add(self.grandchild1_2) + else: + children.add(InheritanceManagerTestChild1(pk=self.grandchild1.pk)) + children.add(InheritanceManagerTestChild1(pk=self.grandchild1_2.pk)) + self.assertEqual( + set(self.get_manager().select_subclasses()), children) + + def test_select_subclasses_invalid_relation(self): + """ + If an invalid relation string is provided, we can provide the user + with a list which is valid, rather than just have the select_related() + raise an AttributeError further in. + """ + regex = '^.+? is not in the discovered subclasses, tried:.+$' + with self.assertRaisesRegexp(ValueError, regex): + self.get_manager().select_subclasses('user') + + def test_select_specific_subclasses(self): + children = set([ + self.child1, + InheritanceManagerTestParent(pk=self.child2.pk), + InheritanceManagerTestChild1(pk=self.grandchild1.pk), + InheritanceManagerTestChild1(pk=self.grandchild1_2.pk), + ]) + self.assertEqual( + set( + self.get_manager().select_subclasses( + "inheritancemanagertestchild1") + ), + children, + ) + + @skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+") + def test_select_specific_grandchildren(self): + children = set([ + InheritanceManagerTestParent(pk=self.child1.pk), + InheritanceManagerTestParent(pk=self.child2.pk), + self.grandchild1, + InheritanceManagerTestParent(pk=self.grandchild1_2.pk), + ]) + self.assertEqual( + set( + self.get_manager().select_subclasses( + "inheritancemanagertestchild1__inheritancemanagertestgrandchild1" + ) + ), + children, + ) + + @skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+") + def test_children_and_grandchildren(self): + children = set([ + self.child1, + InheritanceManagerTestParent(pk=self.child2.pk), + self.grandchild1, + InheritanceManagerTestChild1(pk=self.grandchild1_2.pk), + ]) + self.assertEqual( + set( + self.get_manager().select_subclasses( + "inheritancemanagertestchild1", + "inheritancemanagertestchild1__inheritancemanagertestgrandchild1" + ) + ), + children, + ) + + def test_get_subclass(self): + self.assertEqual( + self.get_manager().get_subclass(pk=self.child1.pk), + self.child1) + + def test_get_subclass_on_queryset(self): + self.assertEqual( + self.get_manager().all().get_subclass(pk=self.child1.pk), + self.child1) + + def test_prior_select_related(self): + with self.assertNumQueries(1): + obj = self.get_manager().select_related( + "inheritancemanagertestchild1").select_subclasses( + "inheritancemanagertestchild2").get(pk=self.child1.pk) + obj.inheritancemanagertestchild1 + + @skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+") + def test_version_determining_any_depth(self): + self.assertIsNone(self.get_manager().all()._get_maximum_depth()) + + @skipUnless(django.VERSION < (1, 6, 0), "test only applies to Django < 1.6") + def test_version_determining_only_child_depth(self): + self.assertEqual(1, self.get_manager().all()._get_maximum_depth()) + + @skipUnless(django.VERSION < (1, 6, 0), "test only applies to Django < 1.6") + def test_manually_specifying_parent_fk_only_children(self): + """ + given a Model which inherits from another Model, but also declares + the OneToOne link manually using `related_name` and `parent_link`, + ensure that the relation names and subclasses are obtained correctly. + """ + child3 = InheritanceManagerTestChild3.objects.create() + results = InheritanceManagerTestParent.objects.all().select_subclasses() + + expected_objs = [self.child1, self.child2, + InheritanceManagerTestChild1(pk=self.grandchild1.pk), + InheritanceManagerTestChild1(pk=self.grandchild1_2.pk), + child3] + self.assertEqual(list(results), expected_objs) + + expected_related_names = [ + 'inheritancemanagertestchild1', + 'inheritancemanagertestchild2', + 'manual_onetoone', # this was set via parent_link & related_name + ] + self.assertEqual(set(results.subclasses), + set(expected_related_names)) + + @skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+") + def test_manually_specifying_parent_fk_including_grandchildren(self): + """ + given a Model which inherits from another Model, but also declares + the OneToOne link manually using `related_name` and `parent_link`, + ensure that the relation names and subclasses are obtained correctly. + """ + child3 = InheritanceManagerTestChild3.objects.create() + results = InheritanceManagerTestParent.objects.all().select_subclasses() + + expected_objs = [self.child1, self.child2, self.grandchild1, + self.grandchild1_2, child3] + self.assertEqual(list(results), expected_objs) + + expected_related_names = [ + 'inheritancemanagertestchild1__inheritancemanagertestgrandchild1', + 'inheritancemanagertestchild1__inheritancemanagertestgrandchild1_2', + 'inheritancemanagertestchild1', + 'inheritancemanagertestchild2', + 'manual_onetoone', # this was set via parent_link & related_name + ] + self.assertEqual(set(results.subclasses), + set(expected_related_names)) + + def test_manually_specifying_parent_fk_single_subclass(self): + """ + Using a string related_name when the relation is manually defined + instead of implicit should still work in the same way. + """ + related_name = 'manual_onetoone' + child3 = InheritanceManagerTestChild3.objects.create() + results = InheritanceManagerTestParent.objects.all().select_subclasses(related_name) + + expected_objs = [InheritanceManagerTestParent(pk=self.child1.pk), + InheritanceManagerTestParent(pk=self.child2.pk), + InheritanceManagerTestParent(pk=self.grandchild1.pk), + InheritanceManagerTestParent(pk=self.grandchild1_2.pk), + child3] + self.assertEqual(list(results), expected_objs) + expected_related_names = [related_name] + self.assertEqual(set(results.subclasses), + set(expected_related_names)) + + def test_filter_on_values_queryset(self): + queryset = InheritanceManagerTestChild1.objects.values('id').filter(pk=self.child1.pk) + self.assertEqual(list(queryset), [{'id': self.child1.pk}]) + + @skipUnless(django.VERSION >= (1, 9, 0), "test only applies to Django 1.9+") + def test_dj19_values_list_on_select_subclasses(self): + """ + Using `select_subclasses` in conjunction with `values_list()` raised an + exception in `_get_sub_obj_recurse()` because the result of `values_list()` + is either a `tuple` or primitive objects if `flat=True` is specified, + because no type checking was done prior to fetching child nodes. + + Django versions below 1.9 are not affected by this bug. + """ + + # Querysets are cast to lists to force immediate evaluation. + # No exceptions must be thrown. + + # No argument to select_subclasses + objs_1 = list( + self.get_manager(). + select_subclasses(). + values_list('id') + ) + + # String argument to select_subclasses + objs_2 = list( + self.get_manager(). + select_subclasses( + "inheritancemanagertestchild2" + ). + values_list('id') + ) + + # String argument to select_subclasses + objs_3 = list( + self.get_manager(). + select_subclasses( + InheritanceManagerTestChild2 + ). + values_list('id') + ) + + assert all(( + isinstance(objs_1, list), + isinstance(objs_2, list), + isinstance(objs_3, list), + )) + + assert objs_1 == objs_2 == objs_3 + + +class InheritanceManagerUsingModelsTests(TestCase): + def setUp(self): + self.parent1 = InheritanceManagerTestParent.objects.create() + self.child1 = InheritanceManagerTestChild1.objects.create() + self.child2 = InheritanceManagerTestChild2.objects.create() + self.grandchild1 = InheritanceManagerTestGrandChild1.objects.create() + self.grandchild1_2 = InheritanceManagerTestGrandChild1_2.objects.create() + + def test_select_subclass_by_child_model(self): + """ + Confirm that passing a child model works the same as passing the + select_related manually + """ + objs = InheritanceManagerTestParent.objects.select_subclasses( + "inheritancemanagertestchild1").order_by('pk') + objsmodels = InheritanceManagerTestParent.objects.select_subclasses( + InheritanceManagerTestChild1).order_by('pk') + self.assertEqual(objs.subclasses, objsmodels.subclasses) + self.assertEqual(list(objs), list(objsmodels)) + + @skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+") + def test_select_subclass_by_grandchild_model(self): + """ + Confirm that passing a grandchild model works the same as passing the + select_related manually + """ + objs = InheritanceManagerTestParent.objects.select_subclasses( + "inheritancemanagertestchild1__inheritancemanagertestgrandchild1") \ + .order_by('pk') + objsmodels = InheritanceManagerTestParent.objects.select_subclasses( + InheritanceManagerTestGrandChild1).order_by('pk') + self.assertEqual(objs.subclasses, objsmodels.subclasses) + self.assertEqual(list(objs), list(objsmodels)) + + @skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+") + def test_selecting_all_subclasses_specifically_grandchildren(self): + """ + A bare select_subclasses() should achieve the same results as doing + select_subclasses and specifying all possible subclasses. + This test checks grandchildren, so only works on 1.6>= + """ + objs = InheritanceManagerTestParent.objects.select_subclasses().order_by('pk') + objsmodels = InheritanceManagerTestParent.objects.select_subclasses( + InheritanceManagerTestChild1, InheritanceManagerTestChild2, + InheritanceManagerTestChild3, + InheritanceManagerTestGrandChild1, + InheritanceManagerTestGrandChild1_2).order_by('pk') + self.assertEqual(set(objs.subclasses), set(objsmodels.subclasses)) + self.assertEqual(list(objs), list(objsmodels)) + + def test_selecting_all_subclasses_specifically_children(self): + """ + A bare select_subclasses() should achieve the same results as doing + select_subclasses and specifying all possible subclasses. + + Note: This is sort of the same test as + `test_selecting_all_subclasses_specifically_grandchildren` but it + specifically switches what models are used because that happens + behind the scenes in a bare select_subclasses(), so we need to + emulate it. + """ + objs = InheritanceManagerTestParent.objects.select_subclasses().order_by('pk') + + if django.VERSION >= (1, 6, 0): + models = (InheritanceManagerTestChild1, + InheritanceManagerTestChild2, + InheritanceManagerTestChild3, + InheritanceManagerTestGrandChild1, + InheritanceManagerTestGrandChild1_2) + else: + models = (InheritanceManagerTestChild1, + InheritanceManagerTestChild2, + InheritanceManagerTestChild3) + + objsmodels = InheritanceManagerTestParent.objects.select_subclasses( + *models).order_by('pk') + # order shouldn't matter, I don't think, as long as the resulting + # queryset (when cast to a list) is the same. + self.assertEqual(set(objs.subclasses), set(objsmodels.subclasses)) + self.assertEqual(list(objs), list(objsmodels)) + + def test_select_subclass_just_self(self): + """ + Passing in the same model as the manager/queryset is bound against + (ie: the root parent) should have no effect on the result set. + """ + objsmodels = InheritanceManagerTestParent.objects.select_subclasses( + InheritanceManagerTestParent).order_by('pk') + self.assertEqual([], objsmodels.subclasses) + self.assertEqual(list(objsmodels), [ + InheritanceManagerTestParent(pk=self.parent1.pk), + InheritanceManagerTestParent(pk=self.child1.pk), + InheritanceManagerTestParent(pk=self.child2.pk), + InheritanceManagerTestParent(pk=self.grandchild1.pk), + InheritanceManagerTestParent(pk=self.grandchild1_2.pk), + ]) + + def test_select_subclass_invalid_related_model(self): + """ + Confirming that giving a stupid model doesn't work. + """ + regex = '^.+? is not a subclass of .+$' + with self.assertRaisesRegexp(ValueError, regex): + InheritanceManagerTestParent.objects.select_subclasses( + TimeFrame).order_by('pk') + + @skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+") + def test_mixing_strings_and_classes_with_grandchildren(self): + """ + Given arguments consisting of both strings and model classes, + ensure the right resolutions take place, accounting for the extra + depth (grandchildren etc) 1.6> allows. + """ + objs = InheritanceManagerTestParent.objects.select_subclasses( + "inheritancemanagertestchild2", + InheritanceManagerTestGrandChild1_2).order_by('pk') + expecting = ['inheritancemanagertestchild1__inheritancemanagertestgrandchild1_2', + 'inheritancemanagertestchild2'] + self.assertEqual(set(objs.subclasses), set(expecting)) + expecting2 = [ + InheritanceManagerTestParent(pk=self.parent1.pk), + InheritanceManagerTestParent(pk=self.child1.pk), + InheritanceManagerTestChild2(pk=self.child2.pk), + InheritanceManagerTestParent(pk=self.grandchild1.pk), + InheritanceManagerTestGrandChild1_2(pk=self.grandchild1_2.pk), + ] + self.assertEqual(list(objs), expecting2) + + def test_mixing_strings_and_classes_with_children(self): + """ + Given arguments consisting of both strings and model classes, + ensure the right resolutions take place, walking down as far as + children. + """ + objs = InheritanceManagerTestParent.objects.select_subclasses( + "inheritancemanagertestchild2", + InheritanceManagerTestChild1).order_by('pk') + expecting = ['inheritancemanagertestchild1', + 'inheritancemanagertestchild2'] + + self.assertEqual(set(objs.subclasses), set(expecting)) + expecting2 = [ + InheritanceManagerTestParent(pk=self.parent1.pk), + InheritanceManagerTestChild1(pk=self.child1.pk), + InheritanceManagerTestChild2(pk=self.child2.pk), + InheritanceManagerTestChild1(pk=self.grandchild1.pk), + InheritanceManagerTestChild1(pk=self.grandchild1_2.pk), + ] + self.assertEqual(list(objs), expecting2) + + def test_duplications(self): + """ + Check that even if the same thing is provided as a string and a model + that the right results are retrieved. + """ + # mixing strings and models which evaluate to the same thing is fine. + objs = InheritanceManagerTestParent.objects.select_subclasses( + "inheritancemanagertestchild2", + InheritanceManagerTestChild2).order_by('pk') + self.assertEqual(list(objs), [ + InheritanceManagerTestParent(pk=self.parent1.pk), + InheritanceManagerTestParent(pk=self.child1.pk), + InheritanceManagerTestChild2(pk=self.child2.pk), + InheritanceManagerTestParent(pk=self.grandchild1.pk), + InheritanceManagerTestParent(pk=self.grandchild1_2.pk), + ]) + + @skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+") + def test_child_doesnt_accidentally_get_parent(self): + """ + Given a Child model which also has an InheritanceManager, + none of the returned objects should be Parent objects. + """ + objs = InheritanceManagerTestChild1.objects.select_subclasses( + InheritanceManagerTestGrandChild1).order_by('pk') + self.assertEqual([ + InheritanceManagerTestChild1(pk=self.child1.pk), + InheritanceManagerTestGrandChild1(pk=self.grandchild1.pk), + InheritanceManagerTestChild1(pk=self.grandchild1_2.pk), + ], list(objs)) + + def test_manually_specifying_parent_fk_only_specific_child(self): + """ + given a Model which inherits from another Model, but also declares + the OneToOne link manually using `related_name` and `parent_link`, + ensure that the relation names and subclasses are obtained correctly. + """ + child3 = InheritanceManagerTestChild3.objects.create() + results = InheritanceManagerTestParent.objects.all().select_subclasses( + InheritanceManagerTestChild3) + + expected_objs = [InheritanceManagerTestParent(pk=self.parent1.pk), + InheritanceManagerTestParent(pk=self.child1.pk), + InheritanceManagerTestParent(pk=self.child2.pk), + InheritanceManagerTestParent(pk=self.grandchild1.pk), + InheritanceManagerTestParent(pk=self.grandchild1_2.pk), + child3] + self.assertEqual(list(results), expected_objs) + + expected_related_names = ['manual_onetoone'] + self.assertEqual(set(results.subclasses), + set(expected_related_names)) + + def test_extras_descend(self): + """ + Ensure that extra(select=) values are copied onto sub-classes. + """ + results = InheritanceManagerTestParent.objects.select_subclasses().extra( + select={'foo': 'id + 1'} + ) + self.assertTrue(all(result.foo == (result.id + 1) for result in results)) + + +class InheritanceManagerRelatedTests(InheritanceManagerTests): + def setUp(self): + self.related = InheritanceManagerTestRelated.objects.create() + self.child1 = InheritanceManagerTestChild1.objects.create( + related=self.related) + self.child2 = InheritanceManagerTestChild2.objects.create( + related=self.related) + self.grandchild1 = InheritanceManagerTestGrandChild1.objects.create(related=self.related) + self.grandchild1_2 = InheritanceManagerTestGrandChild1_2.objects.create(related=self.related) + + def get_manager(self): + return self.related.imtests + + def test_get_method_with_select_subclasses(self): + self.assertEqual( + InheritanceManagerTestParent.objects.select_subclasses().get( + id=self.child1.id), + self.child1) + + def test_annotate_with_select_subclasses(self): + qs = InheritanceManagerTestParent.objects.select_subclasses().annotate( + models.Count('id')) + self.assertEqual(qs.get(id=self.child1.id).id__count, 1) + + def test_annotate_with_named_arguments_with_select_subclasses(self): + qs = InheritanceManagerTestParent.objects.select_subclasses().annotate( + test_count=models.Count('id')) + self.assertEqual(qs.get(id=self.child1.id).test_count, 1) + + def test_annotate_before_select_subclasses(self): + qs = InheritanceManagerTestParent.objects.annotate( + models.Count('id')).select_subclasses() + self.assertEqual(qs.get(id=self.child1.id).id__count, 1) + + def test_annotate_with_named_arguments_before_select_subclasses(self): + qs = InheritanceManagerTestParent.objects.annotate( + test_count=models.Count('id')).select_subclasses() + self.assertEqual(qs.get(id=self.child1.id).test_count, 1) diff --git a/model_utils/tests/test_managers/test_query_manager.py b/model_utils/tests/test_managers/test_query_manager.py new file mode 100644 index 0000000..70f2f46 --- /dev/null +++ b/model_utils/tests/test_managers/test_query_manager.py @@ -0,0 +1,29 @@ +from __future__ import unicode_literals + +from django.test import TestCase + +from model_utils.tests.models import Post + + +class QueryManagerTests(TestCase): + def setUp(self): + data = ((True, True, 0), + (True, False, 4), + (False, False, 2), + (False, True, 3), + (True, True, 1), + (True, False, 5)) + for p, c, o in data: + Post.objects.create(published=p, confirmed=c, order=o) + + def test_passing_kwargs(self): + qs = Post.public.all() + self.assertEqual([p.order for p in qs], [0, 1, 4, 5]) + + def test_passing_Q(self): + qs = Post.public_confirmed.all() + self.assertEqual([p.order for p in qs], [0, 1]) + + def test_ordering(self): + qs = Post.public_reversed.all() + self.assertEqual([p.order for p in qs], [5, 4, 1, 0]) diff --git a/model_utils/tests/test_managers/test_status_manager.py b/model_utils/tests/test_managers/test_status_manager.py new file mode 100644 index 0000000..af3d7cb --- /dev/null +++ b/model_utils/tests/test_managers/test_status_manager.py @@ -0,0 +1,23 @@ +from __future__ import unicode_literals + +from django.db import models +from django.core.exceptions import ImproperlyConfigured +from django.test import TestCase + +from model_utils.managers import QueryManager +from model_utils.models import StatusModel +from model_utils.tests.models import StatusManagerAdded + + +class StatusManagerAddedTests(TestCase): + def test_manager_available(self): + self.assertTrue(isinstance(StatusManagerAdded.active, QueryManager)) + + def test_conflict_error(self): + with self.assertRaises(ImproperlyConfigured): + class ErrorModel(StatusModel): + STATUS = ( + ('active', 'Is Active'), + ('deleted', 'Is Deleted'), + ) + active = models.BooleanField() diff --git a/model_utils/tests/test_miscellaneous.py b/model_utils/tests/test_miscellaneous.py new file mode 100644 index 0000000..8b1df05 --- /dev/null +++ b/model_utils/tests/test_miscellaneous.py @@ -0,0 +1,60 @@ +from __future__ import unicode_literals + +import django +from django.db.models.fields import FieldDoesNotExist +from django.core.management import call_command +from django.test import TestCase + +from model_utils.fields import get_excerpt +from model_utils.tests.models import ( + Article, + StatusFieldDefaultFilled, +) +from model_utils.tests.helpers import skipUnless + + +class MigrationsTests(TestCase): + @skipUnless(django.VERSION >= (1, 7, 0), "test only applies to Django 1.7+") + def test_makemigrations(self): + call_command('makemigrations', dry_run=True) + + +class GetExcerptTests(TestCase): + def test_split(self): + e = get_excerpt("some content\n\n\n\nsome more") + self.assertEqual(e, 'some content\n') + + def test_auto_split(self): + e = get_excerpt("para one\n\npara two\n\npara three") + self.assertEqual(e, 'para one\n\npara two') + + def test_middle_of_para(self): + e = get_excerpt("some text\n\nmore text") + self.assertEqual(e, 'some text') + + def test_middle_of_line(self): + e = get_excerpt("some text more text") + self.assertEqual(e, "some text more text") + +try: + from south.modelsinspector import introspector +except ImportError: + introspector = None + + +@skipUnless(introspector, 'South is not installed') +class SouthFreezingTests(TestCase): + def test_introspector_adds_no_excerpt_field(self): + mf = Article._meta.get_field('body') + args, kwargs = introspector(mf) + self.assertEqual(kwargs['no_excerpt_field'], 'True') + + def test_no_excerpt_field_works(self): + from .models import NoRendered + with self.assertRaises(FieldDoesNotExist): + NoRendered._meta.get_field('_body_excerpt') + + def test_status_field_no_check_for_status(self): + sf = StatusFieldDefaultFilled._meta.get_field('status') + args, kwargs = introspector(sf) + self.assertEqual(kwargs['no_check_for_status'], 'True') diff --git a/model_utils/tests/test_models/__init__.py b/model_utils/tests/test_models/__init__.py new file mode 100644 index 0000000..5065567 --- /dev/null +++ b/model_utils/tests/test_models/__init__.py @@ -0,0 +1,6 @@ +# Needed for Django 1.4/1.5 test runner +from .test_model_tracker import * +from .test_softdeletable_model import * +from .test_status_model import * +from .test_timeframed_model import * +from .test_timestamped_model import * diff --git a/model_utils/tests/test_models/test_model_tracker.py b/model_utils/tests/test_models/test_model_tracker.py new file mode 100644 index 0000000..c9448d1 --- /dev/null +++ b/model_utils/tests/test_models/test_model_tracker.py @@ -0,0 +1,150 @@ +from __future__ import unicode_literals + +import django + +from model_utils.tests.models import ( + ModelTracked, ModelTrackedFK, ModelTrackedNotDefault, ModelTrackedMultiple, InheritedModelTracked, +) + +from model_utils.tests.test_fields.test_field_tracker import ( + FieldTrackerTests, FieldTrackedModelCustomTests, + FieldTrackedModelMultiTests, FieldTrackerForeignKeyTests +) + + +class ModelTrackerTests(FieldTrackerTests): + + tracked_class = ModelTracked + + def test_pre_save_changed(self): + self.assertChanged() + self.instance.name = 'new age' + self.assertChanged() + self.instance.number = 8 + self.assertChanged() + self.instance.name = '' + self.assertChanged() + self.instance.mutable = [1,2,3] + self.assertChanged() + + def test_first_save(self): + self.assertHasChanged(name=True, number=True, mutable=True) + self.assertPrevious(name=None, number=None, mutable=None) + self.assertCurrent(name='', number=None, id=None, mutable=None) + self.assertChanged() + self.instance.name = 'retro' + self.instance.number = 4 + self.instance.mutable = [1,2,3] + self.assertHasChanged(name=True, number=True, mutable=True) + self.assertPrevious(name=None, number=None, mutable=None) + self.assertCurrent(name='retro', number=4, id=None, mutable=[1,2,3]) + self.assertChanged() + # Django 1.4 doesn't have update_fields + if django.VERSION >= (1, 5, 0): + self.instance.save(update_fields=[]) + self.assertHasChanged(name=True, number=True, mutable=True) + self.assertPrevious(name=None, number=None, mutable=None) + self.assertCurrent(name='retro', number=4, id=None, mutable=[1,2,3]) + self.assertChanged() + with self.assertRaises(ValueError): + self.instance.save(update_fields=['number']) + + def test_pre_save_has_changed(self): + self.assertHasChanged(name=True, number=True) + self.instance.name = 'new age' + self.assertHasChanged(name=True, number=True) + self.instance.number = 7 + self.assertHasChanged(name=True, number=True) + + +class ModelTrackedModelCustomTests(FieldTrackedModelCustomTests): + + tracked_class = ModelTrackedNotDefault + + def test_first_save(self): + self.assertHasChanged(name=True, number=True) + self.assertPrevious(name=None, number=None) + self.assertCurrent(name='') + self.assertChanged() + self.instance.name = 'retro' + self.instance.number = 4 + self.assertHasChanged(name=True, number=True) + self.assertPrevious(name=None, number=None) + self.assertCurrent(name='retro') + self.assertChanged() + + def test_pre_save_has_changed(self): + self.assertHasChanged(name=True, number=True) + self.instance.name = 'new age' + self.assertHasChanged(name=True, number=True) + self.instance.number = 7 + self.assertHasChanged(name=True, number=True) + + def test_pre_save_changed(self): + self.assertChanged() + self.instance.name = 'new age' + self.assertChanged() + self.instance.number = 8 + self.assertChanged() + self.instance.name = '' + self.assertChanged() + + +class ModelTrackedModelMultiTests(FieldTrackedModelMultiTests): + + tracked_class = ModelTrackedMultiple + + def test_pre_save_has_changed(self): + self.tracker = self.instance.name_tracker + self.assertHasChanged(name=True, number=True) + self.instance.name = 'new age' + self.assertHasChanged(name=True, number=True) + self.tracker = self.instance.number_tracker + self.assertHasChanged(name=True, number=True) + self.instance.name = 'new age' + self.assertHasChanged(name=True, number=True) + + def test_pre_save_changed(self): + self.tracker = self.instance.name_tracker + self.assertChanged() + self.instance.name = 'new age' + self.assertChanged() + self.instance.number = 8 + self.assertChanged() + self.instance.name = '' + self.assertChanged() + self.tracker = self.instance.number_tracker + self.assertChanged() + self.instance.name = 'new age' + self.assertChanged() + self.instance.number = 8 + self.assertChanged() + + +class ModelTrackerForeignKeyTests(FieldTrackerForeignKeyTests): + + fk_class = ModelTracked + tracked_class = ModelTrackedFK + + def test_custom_without_id(self): + with self.assertNumQueries(2): + self.tracked_class.objects.get() + self.tracker = self.instance.custom_tracker_without_id + self.assertChanged() + self.assertPrevious() + self.assertCurrent(fk=self.old_fk) + self.instance.fk = self.fk_class.objects.create(number=8) + self.assertNotEqual(self.instance.fk, self.old_fk) + self.assertChanged(fk=self.old_fk) + self.assertPrevious(fk=self.old_fk) + self.assertCurrent(fk=self.instance.fk) + + +class InheritedModelTrackerTests(ModelTrackerTests): + + tracked_class = InheritedModelTracked + + def test_child_fields_not_tracked(self): + self.name2 = 'test' + self.assertEqual(self.tracker.previous('name2'), None) + self.assertTrue(self.tracker.has_changed('name2')) diff --git a/model_utils/tests/test_models/test_softdeletable_model.py b/model_utils/tests/test_models/test_softdeletable_model.py new file mode 100644 index 0000000..dc5e629 --- /dev/null +++ b/model_utils/tests/test_models/test_softdeletable_model.py @@ -0,0 +1,38 @@ +from __future__ import unicode_literals + +from django.db.utils import ConnectionDoesNotExist +from django.test import TestCase + +from model_utils.tests.models import SoftDeletable + + +class SoftDeletableModelTests(TestCase): + def test_can_only_see_not_removed_entries(self): + SoftDeletable.objects.create(name='a', is_removed=True) + SoftDeletable.objects.create(name='b', is_removed=False) + + queryset = SoftDeletable.objects.all() + + self.assertEqual(queryset.count(), 1) + self.assertEqual(queryset[0].name, 'b') + + def test_instance_cannot_be_fully_deleted(self): + instance = SoftDeletable.objects.create(name='a') + + instance.delete() + + self.assertEqual(SoftDeletable.objects.count(), 0) + self.assertEqual(SoftDeletable.all_objects.count(), 1) + + def test_instance_cannot_be_fully_deleted_via_queryset(self): + SoftDeletable.objects.create(name='a') + + SoftDeletable.objects.all().delete() + + self.assertEqual(SoftDeletable.objects.count(), 0) + self.assertEqual(SoftDeletable.all_objects.count(), 1) + + def test_delete_instance_no_connection(self): + obj = SoftDeletable.objects.create(name='a') + + self.assertRaises(ConnectionDoesNotExist, obj.delete, using='other') diff --git a/model_utils/tests/test_models/test_status_model.py b/model_utils/tests/test_models/test_status_model.py new file mode 100644 index 0000000..995b4c6 --- /dev/null +++ b/model_utils/tests/test_models/test_status_model.py @@ -0,0 +1,46 @@ +from datetime import datetime + +from freezegun import freeze_time + +from django.test.testcases import TestCase + +from model_utils.tests.models import Status, StatusPlainTuple + + +class StatusModelTests(TestCase): + def setUp(self): + self.model = Status + self.on_hold = Status.STATUS.on_hold + self.active = Status.STATUS.active + + def test_created(self): + with freeze_time(datetime(2016, 1, 1)): + c1 = self.model.objects.create() + self.assertTrue(c1.status_changed, datetime(2016, 1, 1)) + + c2 = self.model.objects.create() + self.assertEqual(self.model.active.count(), 2) + self.assertEqual(self.model.deleted.count(), 0) + + def test_modification(self): + t1 = self.model.objects.create() + date_created = t1.status_changed + t1.status = self.on_hold + t1.save() + self.assertEqual(self.model.active.count(), 0) + self.assertEqual(self.model.on_hold.count(), 1) + self.assertTrue(t1.status_changed > date_created) + date_changed = t1.status_changed + t1.save() + self.assertEqual(t1.status_changed, date_changed) + date_active_again = t1.status_changed + t1.status = self.active + t1.save() + self.assertTrue(t1.status_changed > date_active_again) + + +class StatusModelPlainTupleTests(StatusModelTests): + def setUp(self): + self.model = StatusPlainTuple + self.on_hold = StatusPlainTuple.STATUS[2][0] + self.active = StatusPlainTuple.STATUS[0][0] diff --git a/model_utils/tests/test_models/test_timeframed_model.py b/model_utils/tests/test_models/test_timeframed_model.py new file mode 100644 index 0000000..993a339 --- /dev/null +++ b/model_utils/tests/test_models/test_timeframed_model.py @@ -0,0 +1,47 @@ +from __future__ import unicode_literals + +from datetime import datetime, timedelta + +from django.db import models +from django.core.exceptions import ImproperlyConfigured +from django.test import TestCase + +from model_utils.managers import QueryManager +from model_utils.models import TimeFramedModel +from model_utils.tests.models import TimeFrame, TimeFrameManagerAdded + + +class TimeFramedModelTests(TestCase): + def setUp(self): + self.now = datetime.now() + + def test_not_yet_begun(self): + TimeFrame.objects.create(start=self.now + timedelta(days=2)) + self.assertEqual(TimeFrame.timeframed.count(), 0) + + def test_finished(self): + TimeFrame.objects.create(end=self.now - timedelta(days=1)) + self.assertEqual(TimeFrame.timeframed.count(), 0) + + def test_no_end(self): + TimeFrame.objects.create(start=self.now - timedelta(days=10)) + self.assertEqual(TimeFrame.timeframed.count(), 1) + + def test_no_start(self): + TimeFrame.objects.create(end=self.now + timedelta(days=2)) + self.assertEqual(TimeFrame.timeframed.count(), 1) + + def test_within_range(self): + TimeFrame.objects.create(start=self.now - timedelta(days=1), + end=self.now + timedelta(days=1)) + self.assertEqual(TimeFrame.timeframed.count(), 1) + + +class TimeFrameManagerAddedTests(TestCase): + def test_manager_available(self): + self.assertTrue(isinstance(TimeFrameManagerAdded.timeframed, QueryManager)) + + def test_conflict_error(self): + with self.assertRaises(ImproperlyConfigured): + class ErrorModel(TimeFramedModel): + timeframed = models.BooleanField() diff --git a/model_utils/tests/test_models/test_timestamped_model.py b/model_utils/tests/test_models/test_timestamped_model.py new file mode 100644 index 0000000..221d682 --- /dev/null +++ b/model_utils/tests/test_models/test_timestamped_model.py @@ -0,0 +1,25 @@ +from __future__ import unicode_literals + +from datetime import datetime + +from freezegun import freeze_time + +from django.test import TestCase + +from model_utils.tests.models import TimeStamp + + +class TimeStampedModelTests(TestCase): + def test_created(self): + with freeze_time(datetime(2016, 1, 1)): + t1 = TimeStamp.objects.create() + self.assertEqual(t1.created, datetime(2016, 1, 1)) + + def test_modified(self): + with freeze_time(datetime(2016, 1, 1)): + t1 = TimeStamp.objects.create() + + with freeze_time(datetime(2016, 1, 2)): + t1.save() + + self.assertEqual(t1.modified, datetime(2016, 1, 2)) diff --git a/model_utils/tests/tests.py b/model_utils/tests/tests.py index fafef32..39eced3 100644 --- a/model_utils/tests/tests.py +++ b/model_utils/tests/tests.py @@ -1,2047 +1,6 @@ -from __future__ import unicode_literals - -from datetime import datetime, timedelta - -from freezegun import freeze_time - -try: - from unittest import skipUnless -except ImportError: # Python 2.6 - from django.utils.unittest import skipUnless - -from freezegun import freeze_time - -import django -from django.db import models -from django.db.models.fields import FieldDoesNotExist -from django.db.utils import ConnectionDoesNotExist -from django.utils.six import text_type -from django.core.exceptions import ImproperlyConfigured, FieldError -from django.core.management import call_command -from django.test import TestCase - -from model_utils import Choices, FieldTracker -from model_utils.fields import get_excerpt, MonitorField, StatusField -from model_utils.managers import QueryManager -from model_utils.models import StatusModel, TimeFramedModel -from model_utils.tests.models import ( - InheritanceManagerTestRelated, InheritanceManagerTestGrandChild1, - InheritanceManagerTestGrandChild1_2, - InheritanceManagerTestParent, InheritanceManagerTestChild1, - InheritanceManagerTestChild2, TimeStamp, Post, Article, Status, - StatusPlainTuple, TimeFrame, Monitored, MonitorWhen, MonitorWhenEmpty, StatusManagerAdded, - TimeFrameManagerAdded, SplitFieldAbstractParent, - ModelTracked, ModelTrackedFK, ModelTrackedNotDefault, ModelTrackedMultiple, InheritedModelTracked, - Tracked, TrackedFK, InheritedTrackedFK, TrackedNotDefault, TrackedNonFieldAttr, TrackedMultiple, - InheritedTracked, TrackedFileField, StatusFieldDefaultFilled, StatusFieldDefaultNotFilled, - InheritanceManagerTestChild3, StatusFieldChoicesName, - SoftDeletable, DoubleMonitored) - - -class MigrationsTests(TestCase): - @skipUnless(django.VERSION >= (1, 7, 0), "test only applies to Django 1.7+") - def test_makemigrations(self): - call_command('makemigrations', dry_run=True) - - -class GetExcerptTests(TestCase): - def test_split(self): - e = get_excerpt("some content\n\n\n\nsome more") - self.assertEqual(e, 'some content\n') - - - def test_auto_split(self): - e = get_excerpt("para one\n\npara two\n\npara three") - self.assertEqual(e, 'para one\n\npara two') - - - def test_middle_of_para(self): - e = get_excerpt("some text\n\nmore text") - self.assertEqual(e, 'some text') - - - def test_middle_of_line(self): - e = get_excerpt("some text more text") - self.assertEqual(e, "some text more text") - - - -class SplitFieldTests(TestCase): - full_text = 'summary\n\n\n\nmore' - excerpt = 'summary\n' - - - def setUp(self): - self.post = Article.objects.create( - title='example post', body=self.full_text) - - - def test_unicode_content(self): - self.assertEqual(text_type(self.post.body), self.full_text) - - - def test_excerpt(self): - self.assertEqual(self.post.body.excerpt, self.excerpt) - - - def test_content(self): - self.assertEqual(self.post.body.content, self.full_text) - - - def test_has_more(self): - self.assertTrue(self.post.body.has_more) - - - def test_not_has_more(self): - post = Article.objects.create(title='example 2', - body='some text\n\nsome more\n') - self.assertFalse(post.body.has_more) - - - def test_load_back(self): - post = Article.objects.get(pk=self.post.pk) - self.assertEqual(post.body.content, self.post.body.content) - self.assertEqual(post.body.excerpt, self.post.body.excerpt) - - - def test_assign_to_body(self): - new_text = 'different\n\n\n\nother' - self.post.body = new_text - self.post.save() - self.assertEqual(text_type(self.post.body), new_text) - - - def test_assign_to_content(self): - new_text = 'different\n\n\n\nother' - self.post.body.content = new_text - self.post.save() - self.assertEqual(text_type(self.post.body), new_text) - - - def test_assign_to_excerpt(self): - with self.assertRaises(AttributeError): - self.post.body.excerpt = 'this should fail' - - - def test_access_via_class(self): - with self.assertRaises(AttributeError): - Article.body - - - def test_none(self): - a = Article(title='Some Title', body=None) - self.assertEqual(a.body, None) - - - def test_assign_splittext(self): - a = Article(title='Some Title') - a.body = self.post.body - self.assertEqual(a.body.excerpt, 'summary\n') - - - def test_value_to_string(self): - f = self.post._meta.get_field('body') - self.assertEqual(f.value_to_string(self.post), self.full_text) - - - def test_abstract_inheritance(self): - class Child(SplitFieldAbstractParent): - pass - - self.assertEqual( - [f.name for f in Child._meta.fields], - ["id", "content", "_content_excerpt"]) - - - -class MonitorFieldTests(TestCase): - def setUp(self): - with freeze_time(datetime(2016, 1, 1, 10, 0, 0)): - self.instance = Monitored(name='Charlie') - self.created = self.instance.name_changed - - - def test_save_no_change(self): - self.instance.save() - self.assertEqual(self.instance.name_changed, self.created) - - - def test_save_changed(self): - with freeze_time(datetime(2016, 1, 1, 12, 0, 0)): - self.instance.name = 'Maria' - self.instance.save() - self.assertEqual(self.instance.name_changed, datetime(2016, 1, 1, 12, 0, 0)) - - - def test_double_save(self): - self.instance.name = 'Jose' - self.instance.save() - changed = self.instance.name_changed - self.instance.save() - self.assertEqual(self.instance.name_changed, changed) - - - def test_no_monitor_arg(self): - with self.assertRaises(TypeError): - MonitorField() - - - -class MonitorWhenFieldTests(TestCase): - """ - Will record changes only when name is 'Jose' or 'Maria' - """ - def setUp(self): - with freeze_time(datetime(2016, 1, 1, 10, 0, 0)): - self.instance = MonitorWhen(name='Charlie') - self.created = self.instance.name_changed - - - def test_save_no_change(self): - self.instance.save() - self.assertEqual(self.instance.name_changed, self.created) - - - def test_save_changed_to_Jose(self): - with freeze_time(datetime(2016, 1, 1, 12, 0, 0)): - self.instance.name = 'Jose' - self.instance.save() - self.assertEqual(self.instance.name_changed, datetime(2016, 1, 1, 12, 0, 0)) - - - def test_save_changed_to_Maria(self): - with freeze_time(datetime(2016, 1, 1, 12, 0, 0)): - self.instance.name = 'Maria' - self.instance.save() - self.assertEqual(self.instance.name_changed, datetime(2016, 1, 1, 12, 0, 0)) - - - def test_save_changed_to_Pedro(self): - self.instance.name = 'Pedro' - self.instance.save() - self.assertEqual(self.instance.name_changed, self.created) - - - def test_double_save(self): - self.instance.name = 'Jose' - self.instance.save() - changed = self.instance.name_changed - self.instance.save() - self.assertEqual(self.instance.name_changed, changed) - - - -class MonitorWhenEmptyFieldTests(TestCase): - """ - Monitor should never be updated id when is an empty list. - """ - def setUp(self): - self.instance = MonitorWhenEmpty(name='Charlie') - self.created = self.instance.name_changed - - - def test_save_no_change(self): - self.instance.save() - self.assertEqual(self.instance.name_changed, self.created) - - - def test_save_changed_to_Jose(self): - self.instance.name = 'Jose' - self.instance.save() - self.assertEqual(self.instance.name_changed, self.created) - - - def test_save_changed_to_Maria(self): - self.instance.name = 'Maria' - self.instance.save() - self.assertEqual(self.instance.name_changed, self.created) - - -class MonitorDoubleFieldTests(TestCase): - - def setUp(self): - DoubleMonitored.objects.create(name='Charlie', name2='Charlie2') - - def test_recursion_error_with_only(self): - # Any field passed to only() is generating a recursion error - list(DoubleMonitored.objects.only('id')) - - def test_recursion_error_with_defer(self): - # Only monitored fields passed to defer() are failing - list(DoubleMonitored.objects.defer('name')) - - def test_monitor_still_works_with_deferred_fields_filtered_out_of_save_initial(self): - obj = DoubleMonitored.objects.defer('name').get(name='Charlie') - with freeze_time("2016-12-01"): - obj.name = 'Charlie2' - obj.save() - self.assertEqual(obj.name_changed, datetime(2016, 12, 1)) - - -class StatusFieldTests(TestCase): - - def test_status_with_default_filled(self): - instance = StatusFieldDefaultFilled() - self.assertEqual(instance.status, instance.STATUS.yes) - - def test_status_with_default_not_filled(self): - instance = StatusFieldDefaultNotFilled() - self.assertEqual(instance.status, instance.STATUS.no) - - def test_no_check_for_status(self): - field = StatusField(no_check_for_status=True) - # this model has no STATUS attribute, so checking for it would error - field.prepare_class(Article) - - def test_get_status_display(self): - instance = StatusFieldDefaultFilled() - self.assertEqual(instance.get_status_display(), "Yes") - - def test_choices_name(self): - StatusFieldChoicesName() - - -class ChoicesTests(TestCase): - def setUp(self): - self.STATUS = Choices('DRAFT', 'PUBLISHED') - - - def test_getattr(self): - self.assertEqual(self.STATUS.DRAFT, 'DRAFT') - - - def test_indexing(self): - self.assertEqual(self.STATUS['PUBLISHED'], 'PUBLISHED') - - - def test_iteration(self): - self.assertEqual(tuple(self.STATUS), (('DRAFT', 'DRAFT'), ('PUBLISHED', 'PUBLISHED'))) - - - def test_len(self): - self.assertEqual(len(self.STATUS), 2) - - - def test_repr(self): - self.assertEqual(repr(self.STATUS), "Choices" + repr(( - ('DRAFT', 'DRAFT', 'DRAFT'), - ('PUBLISHED', 'PUBLISHED', 'PUBLISHED'), - ))) - - - def test_wrong_length_tuple(self): - with self.assertRaises(ValueError): - Choices(('a',)) - - - def test_contains_value(self): - self.assertTrue('PUBLISHED' in self.STATUS) - self.assertTrue('DRAFT' in self.STATUS) - - - def test_doesnt_contain_value(self): - self.assertFalse('UNPUBLISHED' in self.STATUS) - - def test_deepcopy(self): - import copy - self.assertEqual(list(self.STATUS), - list(copy.deepcopy(self.STATUS))) - - - def test_equality(self): - self.assertEqual(self.STATUS, Choices('DRAFT', 'PUBLISHED')) - - - def test_inequality(self): - self.assertNotEqual(self.STATUS, ['DRAFT', 'PUBLISHED']) - self.assertNotEqual(self.STATUS, Choices('DRAFT')) - - - def test_composability(self): - self.assertEqual(Choices('DRAFT') + Choices('PUBLISHED'), self.STATUS) - self.assertEqual(Choices('DRAFT') + ('PUBLISHED',), self.STATUS) - self.assertEqual(('DRAFT',) + Choices('PUBLISHED'), self.STATUS) - - - def test_option_groups(self): - c = Choices(('group a', ['one', 'two']), ['group b', ('three',)]) - self.assertEqual( - list(c), - [ - ('group a', [('one', 'one'), ('two', 'two')]), - ('group b', [('three', 'three')]), - ], - ) - - -class LabelChoicesTests(ChoicesTests): - def setUp(self): - self.STATUS = Choices( - ('DRAFT', 'is draft'), - ('PUBLISHED', 'is published'), - 'DELETED', - ) - - - def test_iteration(self): - self.assertEqual(tuple(self.STATUS), ( - ('DRAFT', 'is draft'), - ('PUBLISHED', 'is published'), - ('DELETED', 'DELETED')) - ) - - - def test_indexing(self): - self.assertEqual(self.STATUS['PUBLISHED'], 'is published') - - - def test_default(self): - self.assertEqual(self.STATUS.DELETED, 'DELETED') - - - def test_provided(self): - self.assertEqual(self.STATUS.DRAFT, 'DRAFT') - - - def test_len(self): - self.assertEqual(len(self.STATUS), 3) - - - def test_equality(self): - self.assertEqual(self.STATUS, Choices( - ('DRAFT', 'is draft'), - ('PUBLISHED', 'is published'), - 'DELETED', - )) - - - def test_inequality(self): - self.assertNotEqual(self.STATUS, [ - ('DRAFT', 'is draft'), - ('PUBLISHED', 'is published'), - 'DELETED' - ]) - self.assertNotEqual(self.STATUS, Choices('DRAFT')) - - - def test_repr(self): - self.assertEqual(repr(self.STATUS), "Choices" + repr(( - ('DRAFT', 'DRAFT', 'is draft'), - ('PUBLISHED', 'PUBLISHED', 'is published'), - ('DELETED', 'DELETED', 'DELETED'), - ))) - - - def test_contains_value(self): - self.assertTrue('PUBLISHED' in self.STATUS) - self.assertTrue('DRAFT' in self.STATUS) - # This should be True, because both the display value - # and the internal representation are both DELETED. - self.assertTrue('DELETED' in self.STATUS) - - - def test_doesnt_contain_value(self): - self.assertFalse('UNPUBLISHED' in self.STATUS) - - - def test_doesnt_contain_display_value(self): - self.assertFalse('is draft' in self.STATUS) - - - def test_composability(self): - self.assertEqual( - Choices(('DRAFT', 'is draft',)) + Choices(('PUBLISHED', 'is published'), 'DELETED'), - self.STATUS - ) - - self.assertEqual( - (('DRAFT', 'is draft',),) + Choices(('PUBLISHED', 'is published'), 'DELETED'), - self.STATUS - ) - - self.assertEqual( - Choices(('DRAFT', 'is draft',)) + (('PUBLISHED', 'is published'), 'DELETED'), - self.STATUS - ) - - - def test_option_groups(self): - c = Choices( - ('group a', [(1, 'one'), (2, 'two')]), - ['group b', ((3, 'three'),)] - ) - self.assertEqual( - list(c), - [ - ('group a', [(1, 'one'), (2, 'two')]), - ('group b', [(3, 'three')]), - ], - ) - - - -class IdentifierChoicesTests(ChoicesTests): - def setUp(self): - self.STATUS = Choices( - (0, 'DRAFT', 'is draft'), - (1, 'PUBLISHED', 'is published'), - (2, 'DELETED', 'is deleted')) - - - def test_iteration(self): - self.assertEqual(tuple(self.STATUS), ( - (0, 'is draft'), - (1, 'is published'), - (2, 'is deleted'))) - - - def test_indexing(self): - self.assertEqual(self.STATUS[1], 'is published') - - - def test_getattr(self): - self.assertEqual(self.STATUS.DRAFT, 0) - - - def test_len(self): - self.assertEqual(len(self.STATUS), 3) - - - def test_repr(self): - self.assertEqual(repr(self.STATUS), "Choices" + repr(( - (0, 'DRAFT', 'is draft'), - (1, 'PUBLISHED', 'is published'), - (2, 'DELETED', 'is deleted'), - ))) - - - def test_contains_value(self): - self.assertTrue(0 in self.STATUS) - self.assertTrue(1 in self.STATUS) - self.assertTrue(2 in self.STATUS) - - - def test_doesnt_contain_value(self): - self.assertFalse(3 in self.STATUS) - - - def test_doesnt_contain_display_value(self): - self.assertFalse('is draft' in self.STATUS) - - - def test_doesnt_contain_python_attr(self): - self.assertFalse('PUBLISHED' in self.STATUS) - - - def test_equality(self): - self.assertEqual(self.STATUS, Choices( - (0, 'DRAFT', 'is draft'), - (1, 'PUBLISHED', 'is published'), - (2, 'DELETED', 'is deleted') - )) - - - def test_inequality(self): - self.assertNotEqual(self.STATUS, [ - (0, 'DRAFT', 'is draft'), - (1, 'PUBLISHED', 'is published'), - (2, 'DELETED', 'is deleted') - ]) - self.assertNotEqual(self.STATUS, Choices('DRAFT')) - - - def test_composability(self): - self.assertEqual( - Choices( - (0, 'DRAFT', 'is draft'), - (1, 'PUBLISHED', 'is published') - ) + Choices( - (2, 'DELETED', 'is deleted'), - ), - self.STATUS - ) - - self.assertEqual( - Choices( - (0, 'DRAFT', 'is draft'), - (1, 'PUBLISHED', 'is published') - ) + ( - (2, 'DELETED', 'is deleted'), - ), - self.STATUS - ) - - self.assertEqual( - ( - (0, 'DRAFT', 'is draft'), - (1, 'PUBLISHED', 'is published') - ) + Choices( - (2, 'DELETED', 'is deleted'), - ), - self.STATUS - ) - - - def test_option_groups(self): - c = Choices( - ('group a', [(1, 'ONE', 'one'), (2, 'TWO', 'two')]), - ['group b', ((3, 'THREE', 'three'),)] - ) - self.assertEqual( - list(c), - [ - ('group a', [(1, 'one'), (2, 'two')]), - ('group b', [(3, 'three')]), - ], - ) - - -class InheritanceManagerTests(TestCase): - def setUp(self): - self.child1 = InheritanceManagerTestChild1.objects.create() - self.child2 = InheritanceManagerTestChild2.objects.create() - self.grandchild1 = InheritanceManagerTestGrandChild1.objects.create() - self.grandchild1_2 = \ - InheritanceManagerTestGrandChild1_2.objects.create() - - - def get_manager(self): - return InheritanceManagerTestParent.objects - - - def test_normal(self): - children = set([ - InheritanceManagerTestParent(pk=self.child1.pk), - InheritanceManagerTestParent(pk=self.child2.pk), - InheritanceManagerTestParent(pk=self.grandchild1.pk), - InheritanceManagerTestParent(pk=self.grandchild1_2.pk), - ]) - self.assertEqual(set(self.get_manager().all()), children) - - - def test_select_all_subclasses(self): - children = set([self.child1, self.child2]) - if django.VERSION >= (1, 6, 0): - children.add(self.grandchild1) - children.add(self.grandchild1_2) - else: - children.add(InheritanceManagerTestChild1(pk=self.grandchild1.pk)) - children.add(InheritanceManagerTestChild1(pk=self.grandchild1_2.pk)) - self.assertEqual( - set(self.get_manager().select_subclasses()), children) - - - def test_select_subclasses_invalid_relation(self): - """ - If an invalid relation string is provided, we can provide the user - with a list which is valid, rather than just have the select_related() - raise an AttributeError further in. - """ - regex = '^.+? is not in the discovered subclasses, tried:.+$' - with self.assertRaisesRegexp(ValueError, regex): - self.get_manager().select_subclasses('user') - - - def test_select_specific_subclasses(self): - children = set([ - self.child1, - InheritanceManagerTestParent(pk=self.child2.pk), - InheritanceManagerTestChild1(pk=self.grandchild1.pk), - InheritanceManagerTestChild1(pk=self.grandchild1_2.pk), - ]) - self.assertEqual( - set( - self.get_manager().select_subclasses( - "inheritancemanagertestchild1") - ), - children, - ) - - - @skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+") - def test_select_specific_grandchildren(self): - children = set([ - InheritanceManagerTestParent(pk=self.child1.pk), - InheritanceManagerTestParent(pk=self.child2.pk), - self.grandchild1, - InheritanceManagerTestParent(pk=self.grandchild1_2.pk), - ]) - self.assertEqual( - set( - self.get_manager().select_subclasses( - "inheritancemanagertestchild1__inheritancemanagertestgrandchild1" - ) - ), - children, - ) - - - @skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+") - def test_children_and_grandchildren(self): - children = set([ - self.child1, - InheritanceManagerTestParent(pk=self.child2.pk), - self.grandchild1, - InheritanceManagerTestChild1(pk=self.grandchild1_2.pk), - ]) - self.assertEqual( - set( - self.get_manager().select_subclasses( - "inheritancemanagertestchild1", - "inheritancemanagertestchild1__inheritancemanagertestgrandchild1" - ) - ), - children, - ) - - - def test_get_subclass(self): - self.assertEqual( - self.get_manager().get_subclass(pk=self.child1.pk), - self.child1) - - - def test_get_subclass_on_queryset(self): - self.assertEqual( - self.get_manager().all().get_subclass(pk=self.child1.pk), - self.child1) - - - def test_prior_select_related(self): - with self.assertNumQueries(1): - obj = self.get_manager().select_related( - "inheritancemanagertestchild1").select_subclasses( - "inheritancemanagertestchild2").get(pk=self.child1.pk) - obj.inheritancemanagertestchild1 - - - @skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+") - def test_version_determining_any_depth(self): - self.assertIsNone(self.get_manager().all()._get_maximum_depth()) - - - @skipUnless(django.VERSION < (1, 6, 0), "test only applies to Django < 1.6") - def test_version_determining_only_child_depth(self): - self.assertEqual(1, self.get_manager().all()._get_maximum_depth()) - - - @skipUnless(django.VERSION < (1, 6, 0), "test only applies to Django < 1.6") - def test_manually_specifying_parent_fk_only_children(self): - """ - given a Model which inherits from another Model, but also declares - the OneToOne link manually using `related_name` and `parent_link`, - ensure that the relation names and subclasses are obtained correctly. - """ - child3 = InheritanceManagerTestChild3.objects.create() - results = InheritanceManagerTestParent.objects.all().select_subclasses() - - expected_objs = [self.child1, self.child2, - InheritanceManagerTestChild1(pk=self.grandchild1.pk), - InheritanceManagerTestChild1(pk=self.grandchild1_2.pk), - child3] - self.assertEqual(list(results), expected_objs) - - expected_related_names = [ - 'inheritancemanagertestchild1', - 'inheritancemanagertestchild2', - 'manual_onetoone', # this was set via parent_link & related_name - ] - self.assertEqual(set(results.subclasses), - set(expected_related_names)) - - - @skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+") - def test_manually_specifying_parent_fk_including_grandchildren(self): - """ - given a Model which inherits from another Model, but also declares - the OneToOne link manually using `related_name` and `parent_link`, - ensure that the relation names and subclasses are obtained correctly. - """ - child3 = InheritanceManagerTestChild3.objects.create() - results = InheritanceManagerTestParent.objects.all().select_subclasses() - - expected_objs = [self.child1, self.child2, self.grandchild1, - self.grandchild1_2, child3] - self.assertEqual(list(results), expected_objs) - - expected_related_names = [ - 'inheritancemanagertestchild1__inheritancemanagertestgrandchild1', - 'inheritancemanagertestchild1__inheritancemanagertestgrandchild1_2', - 'inheritancemanagertestchild1', - 'inheritancemanagertestchild2', - 'manual_onetoone', # this was set via parent_link & related_name - ] - self.assertEqual(set(results.subclasses), - set(expected_related_names)) - - - def test_manually_specifying_parent_fk_single_subclass(self): - """ - Using a string related_name when the relation is manually defined - instead of implicit should still work in the same way. - """ - related_name = 'manual_onetoone' - child3 = InheritanceManagerTestChild3.objects.create() - results = InheritanceManagerTestParent.objects.all().select_subclasses(related_name) - - expected_objs = [InheritanceManagerTestParent(pk=self.child1.pk), - InheritanceManagerTestParent(pk=self.child2.pk), - InheritanceManagerTestParent(pk=self.grandchild1.pk), - InheritanceManagerTestParent(pk=self.grandchild1_2.pk), - child3] - self.assertEqual(list(results), expected_objs) - expected_related_names = [related_name] - self.assertEqual(set(results.subclasses), - set(expected_related_names)) - - - def test_filter_on_values_queryset(self): - queryset = InheritanceManagerTestChild1.objects.values('id').filter(pk=self.child1.pk) - self.assertEqual(list(queryset), [{'id': self.child1.pk}]) - - - @skipUnless(django.VERSION >= (1, 9, 0), "test only applies to Django 1.9+") - def test_dj19_values_list_on_select_subclasses(self): - """ - Using `select_subclasses` in conjunction with `values_list()` raised an - exception in `_get_sub_obj_recurse()` because the result of `values_list()` - is either a `tuple` or primitive objects if `flat=True` is specified, - because no type checking was done prior to fetching child nodes. - - Django versions below 1.9 are not affected by this bug. - """ - - # Querysets are cast to lists to force immediate evaluation. - # No exceptions must be thrown. - - # No argument to select_subclasses - objs_1 = list( - self.get_manager(). - select_subclasses(). - values_list('id') - ) - - # String argument to select_subclasses - objs_2 = list( - self.get_manager(). - select_subclasses( - "inheritancemanagertestchild2" - ). - values_list('id') - ) - - # String argument to select_subclasses - objs_3 = list( - self.get_manager(). - select_subclasses( - InheritanceManagerTestChild2 - ). - values_list('id') - ) - - assert all(( - isinstance(objs_1, list), - isinstance(objs_2, list), - isinstance(objs_3, list), - )) - - assert objs_1 == objs_2 == objs_3 - - -class InheritanceManagerUsingModelsTests(TestCase): - - def setUp(self): - self.parent1 = InheritanceManagerTestParent.objects.create() - self.child1 = InheritanceManagerTestChild1.objects.create() - self.child2 = InheritanceManagerTestChild2.objects.create() - self.grandchild1 = InheritanceManagerTestGrandChild1.objects.create() - self.grandchild1_2 = InheritanceManagerTestGrandChild1_2.objects.create() - - - def test_select_subclass_by_child_model(self): - """ - Confirm that passing a child model works the same as passing the - select_related manually - """ - objs = InheritanceManagerTestParent.objects.select_subclasses( - "inheritancemanagertestchild1").order_by('pk') - objsmodels = InheritanceManagerTestParent.objects.select_subclasses( - InheritanceManagerTestChild1).order_by('pk') - self.assertEqual(objs.subclasses, objsmodels.subclasses) - self.assertEqual(list(objs), list(objsmodels)) - - - @skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+") - def test_select_subclass_by_grandchild_model(self): - """ - Confirm that passing a grandchild model works the same as passing the - select_related manually - """ - objs = InheritanceManagerTestParent.objects.select_subclasses( - "inheritancemanagertestchild1__inheritancemanagertestgrandchild1")\ - .order_by('pk') - objsmodels = InheritanceManagerTestParent.objects.select_subclasses( - InheritanceManagerTestGrandChild1).order_by('pk') - self.assertEqual(objs.subclasses, objsmodels.subclasses) - self.assertEqual(list(objs), list(objsmodels)) - - - @skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+") - def test_selecting_all_subclasses_specifically_grandchildren(self): - """ - A bare select_subclasses() should achieve the same results as doing - select_subclasses and specifying all possible subclasses. - This test checks grandchildren, so only works on 1.6>= - """ - objs = InheritanceManagerTestParent.objects.select_subclasses().order_by('pk') - objsmodels = InheritanceManagerTestParent.objects.select_subclasses( - InheritanceManagerTestChild1, InheritanceManagerTestChild2, - InheritanceManagerTestChild3, - InheritanceManagerTestGrandChild1, - InheritanceManagerTestGrandChild1_2).order_by('pk') - self.assertEqual(set(objs.subclasses), set(objsmodels.subclasses)) - self.assertEqual(list(objs), list(objsmodels)) - - - def test_selecting_all_subclasses_specifically_children(self): - """ - A bare select_subclasses() should achieve the same results as doing - select_subclasses and specifying all possible subclasses. - - Note: This is sort of the same test as - `test_selecting_all_subclasses_specifically_grandchildren` but it - specifically switches what models are used because that happens - behind the scenes in a bare select_subclasses(), so we need to - emulate it. - """ - objs = InheritanceManagerTestParent.objects.select_subclasses().order_by('pk') - - if django.VERSION >= (1, 6, 0): - models = (InheritanceManagerTestChild1, - InheritanceManagerTestChild2, - InheritanceManagerTestChild3, - InheritanceManagerTestGrandChild1, - InheritanceManagerTestGrandChild1_2) - else: - models = (InheritanceManagerTestChild1, - InheritanceManagerTestChild2, - InheritanceManagerTestChild3) - - objsmodels = InheritanceManagerTestParent.objects.select_subclasses( - *models).order_by('pk') - # order shouldn't matter, I don't think, as long as the resulting - # queryset (when cast to a list) is the same. - self.assertEqual(set(objs.subclasses), set(objsmodels.subclasses)) - self.assertEqual(list(objs), list(objsmodels)) - - - def test_select_subclass_just_self(self): - """ - Passing in the same model as the manager/queryset is bound against - (ie: the root parent) should have no effect on the result set. - """ - objsmodels = InheritanceManagerTestParent.objects.select_subclasses( - InheritanceManagerTestParent).order_by('pk') - self.assertEqual([], objsmodels.subclasses) - self.assertEqual(list(objsmodels), [ - InheritanceManagerTestParent(pk=self.parent1.pk), - InheritanceManagerTestParent(pk=self.child1.pk), - InheritanceManagerTestParent(pk=self.child2.pk), - InheritanceManagerTestParent(pk=self.grandchild1.pk), - InheritanceManagerTestParent(pk=self.grandchild1_2.pk), - ]) - - - def test_select_subclass_invalid_related_model(self): - """ - Confirming that giving a stupid model doesn't work. - """ - regex = '^.+? is not a subclass of .+$' - with self.assertRaisesRegexp(ValueError, regex): - InheritanceManagerTestParent.objects.select_subclasses( - TimeFrame).order_by('pk') - - - - @skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+") - def test_mixing_strings_and_classes_with_grandchildren(self): - """ - Given arguments consisting of both strings and model classes, - ensure the right resolutions take place, accounting for the extra - depth (grandchildren etc) 1.6> allows. - """ - objs = InheritanceManagerTestParent.objects.select_subclasses( - "inheritancemanagertestchild2", - InheritanceManagerTestGrandChild1_2).order_by('pk') - expecting = ['inheritancemanagertestchild1__inheritancemanagertestgrandchild1_2', - 'inheritancemanagertestchild2'] - self.assertEqual(set(objs.subclasses), set(expecting)) - expecting2 = [ - InheritanceManagerTestParent(pk=self.parent1.pk), - InheritanceManagerTestParent(pk=self.child1.pk), - InheritanceManagerTestChild2(pk=self.child2.pk), - InheritanceManagerTestParent(pk=self.grandchild1.pk), - InheritanceManagerTestGrandChild1_2(pk=self.grandchild1_2.pk), - ] - self.assertEqual(list(objs), expecting2) - - - def test_mixing_strings_and_classes_with_children(self): - """ - Given arguments consisting of both strings and model classes, - ensure the right resolutions take place, walking down as far as - children. - """ - objs = InheritanceManagerTestParent.objects.select_subclasses( - "inheritancemanagertestchild2", - InheritanceManagerTestChild1).order_by('pk') - expecting = ['inheritancemanagertestchild1', - 'inheritancemanagertestchild2'] - - self.assertEqual(set(objs.subclasses), set(expecting)) - expecting2 = [ - InheritanceManagerTestParent(pk=self.parent1.pk), - InheritanceManagerTestChild1(pk=self.child1.pk), - InheritanceManagerTestChild2(pk=self.child2.pk), - InheritanceManagerTestChild1(pk=self.grandchild1.pk), - InheritanceManagerTestChild1(pk=self.grandchild1_2.pk), - ] - self.assertEqual(list(objs), expecting2) - - - def test_duplications(self): - """ - Check that even if the same thing is provided as a string and a model - that the right results are retrieved. - """ - # mixing strings and models which evaluate to the same thing is fine. - objs = InheritanceManagerTestParent.objects.select_subclasses( - "inheritancemanagertestchild2", - InheritanceManagerTestChild2).order_by('pk') - self.assertEqual(list(objs), [ - InheritanceManagerTestParent(pk=self.parent1.pk), - InheritanceManagerTestParent(pk=self.child1.pk), - InheritanceManagerTestChild2(pk=self.child2.pk), - InheritanceManagerTestParent(pk=self.grandchild1.pk), - InheritanceManagerTestParent(pk=self.grandchild1_2.pk), - ]) - - - @skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+") - def test_child_doesnt_accidentally_get_parent(self): - """ - Given a Child model which also has an InheritanceManager, - none of the returned objects should be Parent objects. - """ - objs = InheritanceManagerTestChild1.objects.select_subclasses( - InheritanceManagerTestGrandChild1).order_by('pk') - self.assertEqual([ - InheritanceManagerTestChild1(pk=self.child1.pk), - InheritanceManagerTestGrandChild1(pk=self.grandchild1.pk), - InheritanceManagerTestChild1(pk=self.grandchild1_2.pk), - ], list(objs)) - - - def test_manually_specifying_parent_fk_only_specific_child(self): - """ - given a Model which inherits from another Model, but also declares - the OneToOne link manually using `related_name` and `parent_link`, - ensure that the relation names and subclasses are obtained correctly. - """ - child3 = InheritanceManagerTestChild3.objects.create() - results = InheritanceManagerTestParent.objects.all().select_subclasses( - InheritanceManagerTestChild3) - - expected_objs = [InheritanceManagerTestParent(pk=self.parent1.pk), - InheritanceManagerTestParent(pk=self.child1.pk), - InheritanceManagerTestParent(pk=self.child2.pk), - InheritanceManagerTestParent(pk=self.grandchild1.pk), - InheritanceManagerTestParent(pk=self.grandchild1_2.pk), - child3] - self.assertEqual(list(results), expected_objs) - - expected_related_names = ['manual_onetoone'] - self.assertEqual(set(results.subclasses), - set(expected_related_names)) - - def test_extras_descend(self): - """ - Ensure that extra(select=) values are copied onto sub-classes. - """ - results = InheritanceManagerTestParent.objects.select_subclasses().extra( - select={'foo': 'id + 1'} - ) - self.assertTrue(all(result.foo == (result.id + 1) for result in results)) - - -class InheritanceManagerRelatedTests(InheritanceManagerTests): - def setUp(self): - self.related = InheritanceManagerTestRelated.objects.create() - self.child1 = InheritanceManagerTestChild1.objects.create( - related=self.related) - self.child2 = InheritanceManagerTestChild2.objects.create( - related=self.related) - self.grandchild1 = InheritanceManagerTestGrandChild1.objects.create(related=self.related) - self.grandchild1_2 = InheritanceManagerTestGrandChild1_2.objects.create(related=self.related) - - - def get_manager(self): - return self.related.imtests - - - def test_get_method_with_select_subclasses(self): - self.assertEqual( - InheritanceManagerTestParent.objects.select_subclasses().get( - id=self.child1.id), - self.child1) - - - def test_annotate_with_select_subclasses(self): - qs = InheritanceManagerTestParent.objects.select_subclasses().annotate( - models.Count('id')) - self.assertEqual(qs.get(id=self.child1.id).id__count, 1) - - - def test_annotate_with_named_arguments_with_select_subclasses(self): - qs = InheritanceManagerTestParent.objects.select_subclasses().annotate( - test_count=models.Count('id')) - self.assertEqual(qs.get(id=self.child1.id).test_count, 1) - - - def test_annotate_before_select_subclasses(self): - qs = InheritanceManagerTestParent.objects.annotate( - models.Count('id')).select_subclasses() - self.assertEqual(qs.get(id=self.child1.id).id__count, 1) - - - def test_annotate_with_named_arguments_before_select_subclasses(self): - qs = InheritanceManagerTestParent.objects.annotate( - test_count=models.Count('id')).select_subclasses() - self.assertEqual(qs.get(id=self.child1.id).test_count, 1) - - - -class TimeStampedModelTests(TestCase): - def test_created(self): - with freeze_time(datetime(2016, 1, 1)): - t1 = TimeStamp.objects.create() - self.assertEqual(t1.created, datetime(2016, 1, 1)) - - - def test_modified(self): - with freeze_time(datetime(2016, 1, 1)): - t1 = TimeStamp.objects.create() - - with freeze_time(datetime(2016, 1, 2)): - t1.save() - - self.assertEqual(t1.modified, datetime(2016, 1, 2)) - - - -class TimeFramedModelTests(TestCase): - def setUp(self): - self.now = datetime.now() - - - def test_not_yet_begun(self): - TimeFrame.objects.create(start=self.now+timedelta(days=2)) - self.assertEqual(TimeFrame.timeframed.count(), 0) - - - def test_finished(self): - TimeFrame.objects.create(end=self.now-timedelta(days=1)) - self.assertEqual(TimeFrame.timeframed.count(), 0) - - - def test_no_end(self): - TimeFrame.objects.create(start=self.now-timedelta(days=10)) - self.assertEqual(TimeFrame.timeframed.count(), 1) - - - def test_no_start(self): - TimeFrame.objects.create(end=self.now+timedelta(days=2)) - self.assertEqual(TimeFrame.timeframed.count(), 1) - - - def test_within_range(self): - TimeFrame.objects.create(start=self.now-timedelta(days=1), - end=self.now+timedelta(days=1)) - self.assertEqual(TimeFrame.timeframed.count(), 1) - - - -class TimeFrameManagerAddedTests(TestCase): - def test_manager_available(self): - self.assertTrue(isinstance(TimeFrameManagerAdded.timeframed, QueryManager)) - - - def test_conflict_error(self): - with self.assertRaises(ImproperlyConfigured): - class ErrorModel(TimeFramedModel): - timeframed = models.BooleanField() - - - -class StatusModelTests(TestCase): - def setUp(self): - self.model = Status - self.on_hold = Status.STATUS.on_hold - self.active = Status.STATUS.active - - - def test_created(self): - with freeze_time(datetime(2016, 1, 1)): - c1 = self.model.objects.create() - self.assertTrue(c1.status_changed, datetime(2016, 1, 1)) - - c2 = self.model.objects.create() - self.assertEqual(self.model.active.count(), 2) - self.assertEqual(self.model.deleted.count(), 0) - - - def test_modification(self): - t1 = self.model.objects.create() - date_created = t1.status_changed - t1.status = self.on_hold - t1.save() - self.assertEqual(self.model.active.count(), 0) - self.assertEqual(self.model.on_hold.count(), 1) - self.assertTrue(t1.status_changed > date_created) - date_changed = t1.status_changed - t1.save() - self.assertEqual(t1.status_changed, date_changed) - date_active_again = t1.status_changed - t1.status = self.active - t1.save() - self.assertTrue(t1.status_changed > date_active_again) - - - -class StatusModelPlainTupleTests(StatusModelTests): - def setUp(self): - self.model = StatusPlainTuple - self.on_hold = StatusPlainTuple.STATUS[2][0] - self.active = StatusPlainTuple.STATUS[0][0] - - - -class StatusManagerAddedTests(TestCase): - def test_manager_available(self): - self.assertTrue(isinstance(StatusManagerAdded.active, QueryManager)) - - - def test_conflict_error(self): - with self.assertRaises(ImproperlyConfigured): - class ErrorModel(StatusModel): - STATUS = ( - ('active', 'Is Active'), - ('deleted', 'Is Deleted'), - ) - active = models.BooleanField() - - - -class QueryManagerTests(TestCase): - def setUp(self): - data = ((True, True, 0), - (True, False, 4), - (False, False, 2), - (False, True, 3), - (True, True, 1), - (True, False, 5)) - for p, c, o in data: - Post.objects.create(published=p, confirmed=c, order=o) - - - def test_passing_kwargs(self): - qs = Post.public.all() - self.assertEqual([p.order for p in qs], [0, 1, 4, 5]) - - - def test_passing_Q(self): - qs = Post.public_confirmed.all() - self.assertEqual([p.order for p in qs], [0, 1]) - - - def test_ordering(self): - qs = Post.public_reversed.all() - self.assertEqual([p.order for p in qs], [5, 4, 1, 0]) - - - -try: - from south.modelsinspector import introspector -except ImportError: - introspector = None - -@skipUnless(introspector, 'South is not installed') -class SouthFreezingTests(TestCase): - def test_introspector_adds_no_excerpt_field(self): - mf = Article._meta.get_field('body') - args, kwargs = introspector(mf) - self.assertEqual(kwargs['no_excerpt_field'], 'True') - - - def test_no_excerpt_field_works(self): - from .models import NoRendered - with self.assertRaises(FieldDoesNotExist): - NoRendered._meta.get_field('_body_excerpt') - - def test_status_field_no_check_for_status(self): - sf = StatusFieldDefaultFilled._meta.get_field('status') - args, kwargs = introspector(sf) - self.assertEqual(kwargs['no_check_for_status'], 'True') - - -class FieldTrackerTestCase(TestCase): - - tracker = None - - def assertHasChanged(self, **kwargs): - tracker = kwargs.pop('tracker', self.tracker) - for field, value in kwargs.items(): - if value is None: - with self.assertRaises(FieldError): - tracker.has_changed(field) - else: - self.assertEqual(tracker.has_changed(field), value) - - def assertPrevious(self, **kwargs): - tracker = kwargs.pop('tracker', self.tracker) - for field, value in kwargs.items(): - self.assertEqual(tracker.previous(field), value) - - def assertChanged(self, **kwargs): - tracker = kwargs.pop('tracker', self.tracker) - self.assertEqual(tracker.changed(), kwargs) - - def assertCurrent(self, **kwargs): - tracker = kwargs.pop('tracker', self.tracker) - self.assertEqual(tracker.current(), kwargs) - - def update_instance(self, **kwargs): - for field, value in kwargs.items(): - setattr(self.instance, field, value) - self.instance.save() - - -class FieldTrackerCommonTests(object): - - def test_pre_save_previous(self): - self.assertPrevious(name=None, number=None) - self.instance.name = 'new age' - self.instance.number = 8 - self.assertPrevious(name=None, number=None) - - -class FieldTrackerTests(FieldTrackerTestCase, FieldTrackerCommonTests): - - tracked_class = Tracked - - def setUp(self): - self.instance = self.tracked_class() - self.tracker = self.instance.tracker - - def test_descriptor(self): - self.assertTrue(isinstance(self.tracked_class.tracker, FieldTracker)) - - def test_pre_save_changed(self): - self.assertChanged(name=None) - self.instance.name = 'new age' - self.assertChanged(name=None) - self.instance.number = 8 - self.assertChanged(name=None, number=None) - self.instance.name = '' - self.assertChanged(name=None, number=None) - self.instance.mutable = [1,2,3] - self.assertChanged(name=None, number=None, mutable=None) - - def test_pre_save_has_changed(self): - self.assertHasChanged(name=True, number=False, mutable=False) - self.instance.name = 'new age' - self.assertHasChanged(name=True, number=False, mutable=False) - self.instance.number = 7 - self.assertHasChanged(name=True, number=True) - self.instance.mutable = [1,2,3] - self.assertHasChanged(name=True, number=True, mutable=True) - - def test_first_save(self): - self.assertHasChanged(name=True, number=False, mutable=False) - self.assertPrevious(name=None, number=None, mutable=None) - self.assertCurrent(name='', number=None, id=None, mutable=None) - self.assertChanged(name=None) - self.instance.name = 'retro' - self.instance.number = 4 - self.instance.mutable = [1,2,3] - self.assertHasChanged(name=True, number=True, mutable=True) - self.assertPrevious(name=None, number=None, mutable=None) - self.assertCurrent(name='retro', number=4, id=None, mutable=[1,2,3]) - self.assertChanged(name=None, number=None, mutable=None) - # Django 1.4 doesn't have update_fields - if django.VERSION >= (1, 5, 0): - self.instance.save(update_fields=[]) - self.assertHasChanged(name=True, number=True, mutable=True) - self.assertPrevious(name=None, number=None, mutable=None) - self.assertCurrent(name='retro', number=4, id=None, mutable=[1,2,3]) - self.assertChanged(name=None, number=None, mutable=None) - with self.assertRaises(ValueError): - self.instance.save(update_fields=['number']) - - def test_post_save_has_changed(self): - self.update_instance(name='retro', number=4, mutable=[1,2,3]) - self.assertHasChanged(name=False, number=False, mutable=False) - self.instance.name = 'new age' - self.assertHasChanged(name=True, number=False) - self.instance.number = 8 - self.assertHasChanged(name=True, number=True) - self.instance.mutable[1] = 4 - self.assertHasChanged(name=True, number=True, mutable=True) - self.instance.name = 'retro' - self.assertHasChanged(name=False, number=True, mutable=True) - - def test_post_save_previous(self): - self.update_instance(name='retro', number=4, mutable=[1,2,3]) - self.instance.name = 'new age' - self.assertPrevious(name='retro', number=4, mutable=[1,2,3]) - self.instance.mutable[1] = 4 - self.assertPrevious(name='retro', number=4, mutable=[1,2,3]) - - def test_post_save_changed(self): - self.update_instance(name='retro', number=4, mutable=[1,2,3]) - self.assertChanged() - self.instance.name = 'new age' - self.assertChanged(name='retro') - self.instance.number = 8 - self.assertChanged(name='retro', number=4) - self.instance.name = 'retro' - self.assertChanged(number=4) - self.instance.mutable[1] = 4 - self.assertChanged(number=4, mutable=[1,2,3]) - self.instance.mutable = [1,2,3] - self.assertChanged(number=4) - - def test_current(self): - self.assertCurrent(id=None, name='', number=None, mutable=None) - self.instance.name = 'new age' - self.assertCurrent(id=None, name='new age', number=None, mutable=None) - self.instance.number = 8 - self.assertCurrent(id=None, name='new age', number=8, mutable=None) - self.instance.mutable = [1,2,3] - self.assertCurrent(id=None, name='new age', number=8, mutable=[1,2,3]) - self.instance.mutable[1] = 4 - self.assertCurrent(id=None, name='new age', number=8, mutable=[1,4,3]) - self.instance.save() - self.assertCurrent(id=self.instance.id, name='new age', number=8, mutable=[1,4,3]) - - @skipUnless( - django.VERSION >= (1, 5, 0), "Django 1.4 doesn't have update_fields") - def test_update_fields(self): - self.update_instance(name='retro', number=4, mutable=[1,2,3]) - self.assertChanged() - self.instance.name = 'new age' - self.instance.number = 8 - self.instance.mutable = [4,5,6] - self.assertChanged(name='retro', number=4, mutable=[1,2,3]) - self.instance.save(update_fields=[]) - self.assertChanged(name='retro', number=4, mutable=[1,2,3]) - self.instance.save(update_fields=['name']) - in_db = self.tracked_class.objects.get(id=self.instance.id) - self.assertEqual(in_db.name, self.instance.name) - self.assertNotEqual(in_db.number, self.instance.number) - self.assertChanged(number=4, mutable=[1,2,3]) - self.instance.save(update_fields=['number']) - self.assertChanged(mutable=[1,2,3]) - self.instance.save(update_fields=['mutable']) - self.assertChanged() - in_db = self.tracked_class.objects.get(id=self.instance.id) - self.assertEqual(in_db.name, self.instance.name) - self.assertEqual(in_db.number, self.instance.number) - self.assertEqual(in_db.mutable, self.instance.mutable) - - def test_with_deferred(self): - self.instance.name = 'new age' - self.instance.number = 1 - self.instance.save() - item = list(self.tracked_class.objects.only('name').all())[0] - self.assertTrue(item._deferred_fields) - - self.assertEqual(item.tracker.previous('number'), None) - self.assertTrue('number' in item._deferred_fields) - - self.assertEqual(item.number, 1) - self.assertTrue('number' not in item._deferred_fields) - self.assertEqual(item.tracker.previous('number'), 1) - self.assertFalse(item.tracker.has_changed('number')) - - item.number = 2 - self.assertTrue(item.tracker.has_changed('number')) - - -class FieldTrackerMultipleInstancesTests(TestCase): - - def test_with_deferred_fields_access_multiple(self): - Tracked.objects.create(pk=1, name='foo', number=1) - Tracked.objects.create(pk=2, name='bar', number=2) - - queryset = Tracked.objects.only('id') - - for instance in queryset: - instance.name - - -class FieldTrackedModelCustomTests(FieldTrackerTestCase, - FieldTrackerCommonTests): - - tracked_class = TrackedNotDefault - - def setUp(self): - self.instance = self.tracked_class() - self.tracker = self.instance.name_tracker - - def test_pre_save_changed(self): - self.assertChanged(name=None) - self.instance.name = 'new age' - self.assertChanged(name=None) - self.instance.number = 8 - self.assertChanged(name=None) - self.instance.name = '' - self.assertChanged(name=None) - - def test_first_save(self): - self.assertHasChanged(name=True, number=None) - self.assertPrevious(name=None, number=None) - self.assertCurrent(name='') - self.assertChanged(name=None) - self.instance.name = 'retro' - self.instance.number = 4 - self.assertHasChanged(name=True, number=None) - self.assertPrevious(name=None, number=None) - self.assertCurrent(name='retro') - self.assertChanged(name=None) - - def test_pre_save_has_changed(self): - self.assertHasChanged(name=True, number=None) - self.instance.name = 'new age' - self.assertHasChanged(name=True, number=None) - self.instance.number = 7 - self.assertHasChanged(name=True, number=None) - - def test_post_save_has_changed(self): - self.update_instance(name='retro', number=4) - self.assertHasChanged(name=False, number=None) - self.instance.name = 'new age' - self.assertHasChanged(name=True, number=None) - self.instance.number = 8 - self.assertHasChanged(name=True, number=None) - self.instance.name = 'retro' - self.assertHasChanged(name=False, number=None) - - def test_post_save_previous(self): - self.update_instance(name='retro', number=4) - self.instance.name = 'new age' - self.assertPrevious(name='retro', number=None) - - def test_post_save_changed(self): - self.update_instance(name='retro', number=4) - self.assertChanged() - self.instance.name = 'new age' - self.assertChanged(name='retro') - self.instance.number = 8 - self.assertChanged(name='retro') - self.instance.name = 'retro' - self.assertChanged() - - def test_current(self): - self.assertCurrent(name='') - self.instance.name = 'new age' - self.assertCurrent(name='new age') - self.instance.number = 8 - self.assertCurrent(name='new age') - self.instance.save() - self.assertCurrent(name='new age') - - @skipUnless( - django.VERSION >= (1, 5, 0), "Django 1.4 doesn't have update_fields") - def test_update_fields(self): - self.update_instance(name='retro', number=4) - self.assertChanged() - self.instance.name = 'new age' - self.instance.number = 8 - self.instance.save(update_fields=['name', 'number']) - self.assertChanged() - - -class FieldTrackedModelAttributeTests(FieldTrackerTestCase): - - tracked_class = TrackedNonFieldAttr - - def setUp(self): - self.instance = self.tracked_class() - self.tracker = self.instance.tracker - - def test_previous(self): - self.assertPrevious(rounded=None) - self.instance.number = 7.5 - self.assertPrevious(rounded=None) - self.instance.save() - self.assertPrevious(rounded=8) - self.instance.number = 7.2 - self.assertPrevious(rounded=8) - self.instance.save() - self.assertPrevious(rounded=7) - - def test_has_changed(self): - self.assertHasChanged(rounded=False) - self.instance.number = 7.5 - self.assertHasChanged(rounded=True) - self.instance.save() - self.assertHasChanged(rounded=False) - self.instance.number = 7.2 - self.assertHasChanged(rounded=True) - self.instance.number = 7.8 - self.assertHasChanged(rounded=False) - - def test_changed(self): - self.assertChanged() - self.instance.number = 7.5 - self.assertPrevious(rounded=None) - self.instance.save() - self.assertPrevious() - self.instance.number = 7.8 - self.assertPrevious() - self.instance.number = 7.2 - self.assertPrevious(rounded=8) - self.instance.save() - self.assertPrevious() - - def test_current(self): - self.assertCurrent(rounded=None) - self.instance.number = 7.5 - self.assertCurrent(rounded=8) - self.instance.save() - self.assertCurrent(rounded=8) - - -class FieldTrackedModelMultiTests(FieldTrackerTestCase, - FieldTrackerCommonTests): - - tracked_class = TrackedMultiple - - def setUp(self): - self.instance = self.tracked_class() - self.trackers = [self.instance.name_tracker, - self.instance.number_tracker] - - def test_pre_save_changed(self): - self.tracker = self.instance.name_tracker - self.assertChanged(name=None) - self.instance.name = 'new age' - self.assertChanged(name=None) - self.instance.number = 8 - self.assertChanged(name=None) - self.instance.name = '' - self.assertChanged(name=None) - self.tracker = self.instance.number_tracker - self.assertChanged(number=None) - self.instance.name = 'new age' - self.assertChanged(number=None) - self.instance.number = 8 - self.assertChanged(number=None) - - def test_pre_save_has_changed(self): - self.tracker = self.instance.name_tracker - self.assertHasChanged(name=True, number=None) - self.instance.name = 'new age' - self.assertHasChanged(name=True, number=None) - self.tracker = self.instance.number_tracker - self.assertHasChanged(name=None, number=False) - self.instance.name = 'new age' - self.assertHasChanged(name=None, number=False) - - def test_pre_save_previous(self): - for tracker in self.trackers: - self.tracker = tracker - super(FieldTrackedModelMultiTests, self).test_pre_save_previous() - - def test_post_save_has_changed(self): - self.update_instance(name='retro', number=4) - self.assertHasChanged(tracker=self.trackers[0], name=False, number=None) - self.assertHasChanged(tracker=self.trackers[1], name=None, number=False) - self.instance.name = 'new age' - self.assertHasChanged(tracker=self.trackers[0], name=True, number=None) - self.assertHasChanged(tracker=self.trackers[1], name=None, number=False) - self.instance.number = 8 - self.assertHasChanged(tracker=self.trackers[0], name=True, number=None) - self.assertHasChanged(tracker=self.trackers[1], name=None, number=True) - self.instance.name = 'retro' - self.instance.number = 4 - self.assertHasChanged(tracker=self.trackers[0], name=False, number=None) - self.assertHasChanged(tracker=self.trackers[1], name=None, number=False) - - def test_post_save_previous(self): - self.update_instance(name='retro', number=4) - self.instance.name = 'new age' - self.instance.number = 8 - self.assertPrevious(tracker=self.trackers[0], name='retro', number=None) - self.assertPrevious(tracker=self.trackers[1], name=None, number=4) - - def test_post_save_changed(self): - self.update_instance(name='retro', number=4) - self.assertChanged(tracker=self.trackers[0]) - self.assertChanged(tracker=self.trackers[1]) - self.instance.name = 'new age' - self.assertChanged(tracker=self.trackers[0], name='retro') - self.assertChanged(tracker=self.trackers[1]) - self.instance.number = 8 - self.assertChanged(tracker=self.trackers[0], name='retro') - self.assertChanged(tracker=self.trackers[1], number=4) - self.instance.name = 'retro' - self.instance.number = 4 - self.assertChanged(tracker=self.trackers[0]) - self.assertChanged(tracker=self.trackers[1]) - - def test_current(self): - self.assertCurrent(tracker=self.trackers[0], name='') - self.assertCurrent(tracker=self.trackers[1], number=None) - self.instance.name = 'new age' - self.assertCurrent(tracker=self.trackers[0], name='new age') - self.assertCurrent(tracker=self.trackers[1], number=None) - self.instance.number = 8 - self.assertCurrent(tracker=self.trackers[0], name='new age') - self.assertCurrent(tracker=self.trackers[1], number=8) - self.instance.save() - self.assertCurrent(tracker=self.trackers[0], name='new age') - self.assertCurrent(tracker=self.trackers[1], number=8) - - -class FieldTrackerForeignKeyTests(FieldTrackerTestCase): - - fk_class = Tracked - tracked_class = TrackedFK - - def setUp(self): - self.old_fk = self.fk_class.objects.create(number=8) - self.instance = self.tracked_class.objects.create(fk=self.old_fk) - - def test_default(self): - self.tracker = self.instance.tracker - self.assertChanged() - self.assertPrevious() - self.assertCurrent(id=self.instance.id, fk_id=self.old_fk.id) - self.instance.fk = self.fk_class.objects.create(number=8) - self.assertChanged(fk_id=self.old_fk.id) - self.assertPrevious(fk_id=self.old_fk.id) - self.assertCurrent(id=self.instance.id, fk_id=self.instance.fk_id) - - def test_custom(self): - self.tracker = self.instance.custom_tracker - self.assertChanged() - self.assertPrevious() - self.assertCurrent(fk_id=self.old_fk.id) - self.instance.fk = self.fk_class.objects.create(number=8) - self.assertChanged(fk_id=self.old_fk.id) - self.assertPrevious(fk_id=self.old_fk.id) - self.assertCurrent(fk_id=self.instance.fk_id) - - def test_custom_without_id(self): - with self.assertNumQueries(1): - self.tracked_class.objects.get() - self.tracker = self.instance.custom_tracker_without_id - self.assertChanged() - self.assertPrevious() - self.assertCurrent(fk=self.old_fk.id) - self.instance.fk = self.fk_class.objects.create(number=8) - self.assertChanged(fk=self.old_fk.id) - self.assertPrevious(fk=self.old_fk.id) - self.assertCurrent(fk=self.instance.fk_id) - - -class InheritedFieldTrackerTests(FieldTrackerTests): - - tracked_class = InheritedTracked - - def test_child_fields_not_tracked(self): - self.name2 = 'test' - self.assertEqual(self.tracker.previous('name2'), None) - self.assertRaises(FieldError, self.tracker.has_changed, 'name2') - - -class FieldTrackerInheritedForeignKeyTests(FieldTrackerForeignKeyTests): - - tracked_class = InheritedTrackedFK - - -class FieldTrackerFileFieldTests(FieldTrackerTestCase): - - tracked_class = TrackedFileField - - def setUp(self): - self.instance = self.tracked_class() - self.tracker = self.instance.tracker - self.some_file = 'something.txt' - self.another_file = 'another.txt' - - def test_pre_save_changed(self): - self.assertChanged(some_file=None) - self.instance.some_file = self.some_file - self.assertChanged(some_file=None) - - def test_pre_save_has_changed(self): - self.assertHasChanged(some_file=True) - self.instance.some_file = self.some_file - self.assertHasChanged(some_file=True) - - def test_pre_save_previous(self): - self.assertPrevious(some_file=None) - self.instance.some_file = self.some_file - self.assertPrevious(some_file=None) - - def test_post_save_changed(self): - self.update_instance(some_file=self.some_file) - self.assertChanged() - previous_file = self.instance.some_file - self.instance.some_file = self.another_file - self.assertChanged(some_file=previous_file) - # test deferred file field - deferred_instance = self.tracked_class.objects.defer('some_file')[0] - deferred_instance.some_file # access field to fetch from database - self.assertChanged(tracker=deferred_instance.tracker) - - previous_file = deferred_instance.some_file - deferred_instance.some_file = self.another_file - self.assertChanged( - tracker=deferred_instance.tracker, - some_file=previous_file, - ) - - def test_post_save_has_changed(self): - self.update_instance(some_file=self.some_file) - self.assertHasChanged(some_file=False) - self.instance.some_file = self.another_file - self.assertHasChanged(some_file=True) - - # test deferred file field - deferred_instance = self.tracked_class.objects.defer('some_file')[0] - deferred_instance.some_file # access field to fetch from database - self.assertHasChanged( - tracker=deferred_instance.tracker, - some_file=False, - ) - - deferred_instance.some_file = self.another_file - self.assertHasChanged( - tracker=deferred_instance.tracker, - some_file=True, - ) - - def test_post_save_previous(self): - self.update_instance(some_file=self.some_file) - previous_file = self.instance.some_file - self.instance.some_file = self.another_file - self.assertPrevious(some_file=previous_file) - - # test deferred file field - deferred_instance = self.tracked_class.objects.defer('some_file')[0] - deferred_instance.some_file # access field to fetch from database - self.assertPrevious( - tracker=deferred_instance.tracker, - some_file=previous_file, - ) - - deferred_instance.some_file = self.another_file - self.assertPrevious( - tracker=deferred_instance.tracker, - some_file=previous_file, - ) - - def test_current(self): - self.assertCurrent(some_file=self.instance.some_file, id=None) - self.instance.some_file = self.some_file - self.assertCurrent(some_file=self.instance.some_file, id=None) - - # test deferred file field - self.instance.save() - deferred_instance = self.tracked_class.objects.defer('some_file')[0] - deferred_instance.some_file # access field to fetch from database - self.assertCurrent( - some_file=self.instance.some_file, - id=self.instance.id, - ) - - self.instance.some_file = self.another_file - self.assertCurrent( - some_file=self.instance.some_file, - id=self.instance.id, - ) - - -class ModelTrackerTests(FieldTrackerTests): - - tracked_class = ModelTracked - - def test_pre_save_changed(self): - self.assertChanged() - self.instance.name = 'new age' - self.assertChanged() - self.instance.number = 8 - self.assertChanged() - self.instance.name = '' - self.assertChanged() - self.instance.mutable = [1,2,3] - self.assertChanged() - - def test_first_save(self): - self.assertHasChanged(name=True, number=True, mutable=True) - self.assertPrevious(name=None, number=None, mutable=None) - self.assertCurrent(name='', number=None, id=None, mutable=None) - self.assertChanged() - self.instance.name = 'retro' - self.instance.number = 4 - self.instance.mutable = [1,2,3] - self.assertHasChanged(name=True, number=True, mutable=True) - self.assertPrevious(name=None, number=None, mutable=None) - self.assertCurrent(name='retro', number=4, id=None, mutable=[1,2,3]) - self.assertChanged() - # Django 1.4 doesn't have update_fields - if django.VERSION >= (1, 5, 0): - self.instance.save(update_fields=[]) - self.assertHasChanged(name=True, number=True, mutable=True) - self.assertPrevious(name=None, number=None, mutable=None) - self.assertCurrent(name='retro', number=4, id=None, mutable=[1,2,3]) - self.assertChanged() - with self.assertRaises(ValueError): - self.instance.save(update_fields=['number']) - - def test_pre_save_has_changed(self): - self.assertHasChanged(name=True, number=True) - self.instance.name = 'new age' - self.assertHasChanged(name=True, number=True) - self.instance.number = 7 - self.assertHasChanged(name=True, number=True) - - -class ModelTrackedModelCustomTests(FieldTrackedModelCustomTests): - - tracked_class = ModelTrackedNotDefault - - def test_first_save(self): - self.assertHasChanged(name=True, number=True) - self.assertPrevious(name=None, number=None) - self.assertCurrent(name='') - self.assertChanged() - self.instance.name = 'retro' - self.instance.number = 4 - self.assertHasChanged(name=True, number=True) - self.assertPrevious(name=None, number=None) - self.assertCurrent(name='retro') - self.assertChanged() - - def test_pre_save_has_changed(self): - self.assertHasChanged(name=True, number=True) - self.instance.name = 'new age' - self.assertHasChanged(name=True, number=True) - self.instance.number = 7 - self.assertHasChanged(name=True, number=True) - - def test_pre_save_changed(self): - self.assertChanged() - self.instance.name = 'new age' - self.assertChanged() - self.instance.number = 8 - self.assertChanged() - self.instance.name = '' - self.assertChanged() - - -class ModelTrackedModelMultiTests(FieldTrackedModelMultiTests): - - tracked_class = ModelTrackedMultiple - - def test_pre_save_has_changed(self): - self.tracker = self.instance.name_tracker - self.assertHasChanged(name=True, number=True) - self.instance.name = 'new age' - self.assertHasChanged(name=True, number=True) - self.tracker = self.instance.number_tracker - self.assertHasChanged(name=True, number=True) - self.instance.name = 'new age' - self.assertHasChanged(name=True, number=True) - - def test_pre_save_changed(self): - self.tracker = self.instance.name_tracker - self.assertChanged() - self.instance.name = 'new age' - self.assertChanged() - self.instance.number = 8 - self.assertChanged() - self.instance.name = '' - self.assertChanged() - self.tracker = self.instance.number_tracker - self.assertChanged() - self.instance.name = 'new age' - self.assertChanged() - self.instance.number = 8 - self.assertChanged() - - -class ModelTrackerForeignKeyTests(FieldTrackerForeignKeyTests): - - fk_class = ModelTracked - tracked_class = ModelTrackedFK - - def test_custom_without_id(self): - with self.assertNumQueries(2): - self.tracked_class.objects.get() - self.tracker = self.instance.custom_tracker_without_id - self.assertChanged() - self.assertPrevious() - self.assertCurrent(fk=self.old_fk) - self.instance.fk = self.fk_class.objects.create(number=8) - self.assertNotEqual(self.instance.fk, self.old_fk) - self.assertChanged(fk=self.old_fk) - self.assertPrevious(fk=self.old_fk) - self.assertCurrent(fk=self.instance.fk) - - -class InheritedModelTrackerTests(ModelTrackerTests): - - tracked_class = InheritedModelTracked - - def test_child_fields_not_tracked(self): - self.name2 = 'test' - self.assertEqual(self.tracker.previous('name2'), None) - self.assertTrue(self.tracker.has_changed('name2')) - - -class SoftDeletableModelTests(TestCase): - - def test_can_only_see_not_removed_entries(self): - SoftDeletable.objects.create(name='a', is_removed=True) - SoftDeletable.objects.create(name='b', is_removed=False) - - queryset = SoftDeletable.objects.all() - - self.assertEqual(queryset.count(), 1) - self.assertEqual(queryset[0].name, 'b') - - def test_instance_cannot_be_fully_deleted(self): - instance = SoftDeletable.objects.create(name='a') - - instance.delete() - - self.assertEqual(SoftDeletable.objects.count(), 0) - self.assertEqual(SoftDeletable.all_objects.count(), 1) - - def test_instance_cannot_be_fully_deleted_via_queryset(self): - SoftDeletable.objects.create(name='a') - - SoftDeletable.objects.all().delete() - - self.assertEqual(SoftDeletable.objects.count(), 0) - self.assertEqual(SoftDeletable.all_objects.count(), 1) - - def test_delete_instance_no_connection(self): - obj = SoftDeletable.objects.create(name='a') - - self.assertRaises(ConnectionDoesNotExist, obj.delete, using='other') +# Needed for Django 1.4/1.5 test runner +from .test_fields import * +from .test_managers import * +from .test_models import * +from .test_choices import * +from .test_miscellaneous import *