Reorganized comparison db function tests.

This commit is contained in:
Nick Pope 2018-08-16 01:06:19 +01:00 committed by Tim Graham
parent 3288985822
commit 5c3db0ef6a
6 changed files with 261 additions and 301 deletions

View File

@ -10,7 +10,7 @@ from django.test import (
TestCase, ignore_warnings, override_settings, skipUnlessDBFeature, TestCase, ignore_warnings, override_settings, skipUnlessDBFeature,
) )
from .models import Author from ..models import Author
class CastTests(TestCase): class CastTests(TestCase):

View File

@ -0,0 +1,72 @@
from django.db.models import TextField
from django.db.models.functions import Coalesce, Lower
from django.test import TestCase
from django.utils import timezone
from ..models import Article, Author
lorem_ipsum = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua."""
class CoalesceTests(TestCase):
def test_basic(self):
Author.objects.create(name='John Smith', alias='smithj')
Author.objects.create(name='Rhonda')
authors = Author.objects.annotate(display_name=Coalesce('alias', 'name'))
self.assertQuerysetEqual(
authors.order_by('name'), ['smithj', 'Rhonda'],
lambda a: a.display_name
)
def test_gt_two_expressions(self):
with self.assertRaisesMessage(ValueError, 'Coalesce must take at least two expressions'):
Author.objects.annotate(display_name=Coalesce('alias'))
def test_mixed_values(self):
a1 = Author.objects.create(name='John Smith', alias='smithj')
a2 = Author.objects.create(name='Rhonda')
ar1 = Article.objects.create(
title='How to Django',
text=lorem_ipsum,
written=timezone.now(),
)
ar1.authors.add(a1)
ar1.authors.add(a2)
# mixed Text and Char
article = Article.objects.annotate(
headline=Coalesce('summary', 'text', output_field=TextField()),
)
self.assertQuerysetEqual(
article.order_by('title'), [lorem_ipsum],
lambda a: a.headline
)
# mixed Text and Char wrapped
article = Article.objects.annotate(
headline=Coalesce(Lower('summary'), Lower('text'), output_field=TextField()),
)
self.assertQuerysetEqual(
article.order_by('title'), [lorem_ipsum.lower()],
lambda a: a.headline
)
def test_ordering(self):
Author.objects.create(name='John Smith', alias='smithj')
Author.objects.create(name='Rhonda')
authors = Author.objects.order_by(Coalesce('alias', 'name'))
self.assertQuerysetEqual(
authors, ['Rhonda', 'John Smith'],
lambda a: a.name
)
authors = Author.objects.order_by(Coalesce('alias', 'name').asc())
self.assertQuerysetEqual(
authors, ['Rhonda', 'John Smith'],
lambda a: a.name
)
authors = Author.objects.order_by(Coalesce('alias', 'name').desc())
self.assertQuerysetEqual(
authors, ['John Smith', 'Rhonda'],
lambda a: a.name
)

View File

@ -0,0 +1,91 @@
from datetime import datetime, timedelta
from decimal import Decimal
from unittest import skipIf, skipUnless
from django.db import connection
from django.db.models.expressions import RawSQL
from django.db.models.functions import Coalesce, Greatest
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
from django.utils import timezone
from ..models import Article, Author, DecimalModel, Fan
class GreatestTests(TestCase):
def test_basic(self):
now = timezone.now()
before = now - timedelta(hours=1)
Article.objects.create(title='Testing with Django', written=before, published=now)
articles = Article.objects.annotate(last_updated=Greatest('written', 'published'))
self.assertEqual(articles.first().last_updated, now)
@skipUnlessDBFeature('greatest_least_ignores_nulls')
def test_ignores_null(self):
now = timezone.now()
Article.objects.create(title='Testing with Django', written=now)
articles = Article.objects.annotate(last_updated=Greatest('written', 'published'))
self.assertEqual(articles.first().last_updated, now)
@skipIfDBFeature('greatest_least_ignores_nulls')
def test_propagates_null(self):
Article.objects.create(title='Testing with Django', written=timezone.now())
articles = Article.objects.annotate(last_updated=Greatest('written', 'published'))
self.assertIsNone(articles.first().last_updated)
@skipIf(connection.vendor == 'mysql', "This doesn't work on MySQL")
def test_coalesce_workaround(self):
past = datetime(1900, 1, 1)
now = timezone.now()
Article.objects.create(title='Testing with Django', written=now)
articles = Article.objects.annotate(
last_updated=Greatest(
Coalesce('written', past),
Coalesce('published', past),
),
)
self.assertEqual(articles.first().last_updated, now)
@skipUnless(connection.vendor == 'mysql', "MySQL-specific workaround")
def test_coalesce_workaround_mysql(self):
past = datetime(1900, 1, 1)
now = timezone.now()
Article.objects.create(title='Testing with Django', written=now)
past_sql = RawSQL("cast(%s as datetime)", (past,))
articles = Article.objects.annotate(
last_updated=Greatest(
Coalesce('written', past_sql),
Coalesce('published', past_sql),
),
)
self.assertEqual(articles.first().last_updated, now)
def test_all_null(self):
Article.objects.create(title='Testing with Django', written=timezone.now())
articles = Article.objects.annotate(last_updated=Greatest('published', 'updated'))
self.assertIsNone(articles.first().last_updated)
def test_one_expressions(self):
with self.assertRaisesMessage(ValueError, 'Greatest must take at least two expressions'):
Greatest('written')
def test_related_field(self):
author = Author.objects.create(name='John Smith', age=45)
Fan.objects.create(name='Margaret', age=50, author=author)
authors = Author.objects.annotate(highest_age=Greatest('age', 'fans__age'))
self.assertEqual(authors.first().highest_age, 50)
def test_update(self):
author = Author.objects.create(name='James Smith', goes_by='Jim')
Author.objects.update(alias=Greatest('name', 'goes_by'))
author.refresh_from_db()
self.assertEqual(author.alias, 'Jim')
def test_decimal_filter(self):
obj = DecimalModel.objects.create(n1=Decimal('1.1'), n2=Decimal('1.2'))
self.assertCountEqual(
DecimalModel.objects.annotate(
greatest=Greatest('n1', 'n2'),
).filter(greatest=Decimal('1.2')),
[obj],
)

View File

@ -0,0 +1,93 @@
from datetime import datetime, timedelta
from decimal import Decimal
from unittest import skipIf, skipUnless
from django.db import connection
from django.db.models.expressions import RawSQL
from django.db.models.functions import Coalesce, Least
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
from django.utils import timezone
from ..models import Article, Author, DecimalModel, Fan
class LeastTests(TestCase):
def test_basic(self):
now = timezone.now()
before = now - timedelta(hours=1)
Article.objects.create(title='Testing with Django', written=before, published=now)
articles = Article.objects.annotate(first_updated=Least('written', 'published'))
self.assertEqual(articles.first().first_updated, before)
@skipUnlessDBFeature('greatest_least_ignores_nulls')
def test_ignores_null(self):
now = timezone.now()
Article.objects.create(title='Testing with Django', written=now)
articles = Article.objects.annotate(
first_updated=Least('written', 'published'),
)
self.assertEqual(articles.first().first_updated, now)
@skipIfDBFeature('greatest_least_ignores_nulls')
def test_propagates_null(self):
Article.objects.create(title='Testing with Django', written=timezone.now())
articles = Article.objects.annotate(first_updated=Least('written', 'published'))
self.assertIsNone(articles.first().first_updated)
@skipIf(connection.vendor == 'mysql', "This doesn't work on MySQL")
def test_coalesce_workaround(self):
future = datetime(2100, 1, 1)
now = timezone.now()
Article.objects.create(title='Testing with Django', written=now)
articles = Article.objects.annotate(
last_updated=Least(
Coalesce('written', future),
Coalesce('published', future),
),
)
self.assertEqual(articles.first().last_updated, now)
@skipUnless(connection.vendor == 'mysql', "MySQL-specific workaround")
def test_coalesce_workaround_mysql(self):
future = datetime(2100, 1, 1)
now = timezone.now()
Article.objects.create(title='Testing with Django', written=now)
future_sql = RawSQL("cast(%s as datetime)", (future,))
articles = Article.objects.annotate(
last_updated=Least(
Coalesce('written', future_sql),
Coalesce('published', future_sql),
),
)
self.assertEqual(articles.first().last_updated, now)
def test_all_null(self):
Article.objects.create(title='Testing with Django', written=timezone.now())
articles = Article.objects.annotate(first_updated=Least('published', 'updated'))
self.assertIsNone(articles.first().first_updated)
def test_one_expressions(self):
with self.assertRaisesMessage(ValueError, 'Least must take at least two expressions'):
Least('written')
def test_related_field(self):
author = Author.objects.create(name='John Smith', age=45)
Fan.objects.create(name='Margaret', age=50, author=author)
authors = Author.objects.annotate(lowest_age=Least('age', 'fans__age'))
self.assertEqual(authors.first().lowest_age, 45)
def test_update(self):
author = Author.objects.create(name='James Smith', goes_by='Jim')
Author.objects.update(alias=Least('name', 'goes_by'))
author.refresh_from_db()
self.assertEqual(author.alias, 'James Smith')
def test_decimal_filter(self):
obj = DecimalModel.objects.create(n1=Decimal('1.1'), n2=Decimal('1.2'))
self.assertCountEqual(
DecimalModel.objects.annotate(
least=Least('n1', 'n2'),
).filter(least=Decimal('1.1')),
[obj],
)

View File

@ -1,17 +1,11 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
from decimal import Decimal
from unittest import skipIf, skipUnless
from django.db import connection from django.db.models import CharField, Value as V
from django.db.models import CharField, TextField, Value as V from django.db.models.functions import Coalesce, Length, Now, Upper
from django.db.models.expressions import RawSQL from django.test import TestCase
from django.db.models.functions import (
Coalesce, Greatest, Least, Length, Lower, Now, Upper,
)
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
from django.utils import timezone from django.utils import timezone
from .models import Article, Author, DecimalModel, Fan from .models import Article, Author
lorem_ipsum = """ lorem_ipsum = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
@ -20,296 +14,6 @@ lorem_ipsum = """
class FunctionTests(TestCase): class FunctionTests(TestCase):
def test_coalesce(self):
Author.objects.create(name='John Smith', alias='smithj')
Author.objects.create(name='Rhonda')
authors = Author.objects.annotate(display_name=Coalesce('alias', 'name'))
self.assertQuerysetEqual(
authors.order_by('name'), [
'smithj',
'Rhonda',
],
lambda a: a.display_name
)
with self.assertRaisesMessage(ValueError, 'Coalesce must take at least two expressions'):
Author.objects.annotate(display_name=Coalesce('alias'))
def test_coalesce_mixed_values(self):
a1 = Author.objects.create(name='John Smith', alias='smithj')
a2 = Author.objects.create(name='Rhonda')
ar1 = Article.objects.create(
title="How to Django",
text=lorem_ipsum,
written=timezone.now(),
)
ar1.authors.add(a1)
ar1.authors.add(a2)
# mixed Text and Char
article = Article.objects.annotate(
headline=Coalesce('summary', 'text', output_field=TextField()),
)
self.assertQuerysetEqual(
article.order_by('title'), [
lorem_ipsum,
],
lambda a: a.headline
)
# mixed Text and Char wrapped
article = Article.objects.annotate(
headline=Coalesce(Lower('summary'), Lower('text'), output_field=TextField()),
)
self.assertQuerysetEqual(
article.order_by('title'), [
lorem_ipsum.lower(),
],
lambda a: a.headline
)
def test_coalesce_ordering(self):
Author.objects.create(name='John Smith', alias='smithj')
Author.objects.create(name='Rhonda')
authors = Author.objects.order_by(Coalesce('alias', 'name'))
self.assertQuerysetEqual(
authors, [
'Rhonda',
'John Smith',
],
lambda a: a.name
)
authors = Author.objects.order_by(Coalesce('alias', 'name').asc())
self.assertQuerysetEqual(
authors, [
'Rhonda',
'John Smith',
],
lambda a: a.name
)
authors = Author.objects.order_by(Coalesce('alias', 'name').desc())
self.assertQuerysetEqual(
authors, [
'John Smith',
'Rhonda',
],
lambda a: a.name
)
def test_greatest(self):
now = timezone.now()
before = now - timedelta(hours=1)
Article.objects.create(
title="Testing with Django",
written=before,
published=now,
)
articles = Article.objects.annotate(
last_updated=Greatest('written', 'published'),
)
self.assertEqual(articles.first().last_updated, now)
@skipUnlessDBFeature('greatest_least_ignores_nulls')
def test_greatest_ignores_null(self):
now = timezone.now()
Article.objects.create(title="Testing with Django", written=now)
articles = Article.objects.annotate(
last_updated=Greatest('written', 'published'),
)
self.assertEqual(articles.first().last_updated, now)
@skipIfDBFeature('greatest_least_ignores_nulls')
def test_greatest_propagates_null(self):
now = timezone.now()
Article.objects.create(title="Testing with Django", written=now)
articles = Article.objects.annotate(
last_updated=Greatest('written', 'published'),
)
self.assertIsNone(articles.first().last_updated)
@skipIf(connection.vendor == 'mysql', "This doesn't work on MySQL")
def test_greatest_coalesce_workaround(self):
past = datetime(1900, 1, 1)
now = timezone.now()
Article.objects.create(title="Testing with Django", written=now)
articles = Article.objects.annotate(
last_updated=Greatest(
Coalesce('written', past),
Coalesce('published', past),
),
)
self.assertEqual(articles.first().last_updated, now)
@skipUnless(connection.vendor == 'mysql', "MySQL-specific workaround")
def test_greatest_coalesce_workaround_mysql(self):
past = datetime(1900, 1, 1)
now = timezone.now()
Article.objects.create(title="Testing with Django", written=now)
past_sql = RawSQL("cast(%s as datetime)", (past,))
articles = Article.objects.annotate(
last_updated=Greatest(
Coalesce('written', past_sql),
Coalesce('published', past_sql),
),
)
self.assertEqual(articles.first().last_updated, now)
def test_greatest_all_null(self):
Article.objects.create(title="Testing with Django", written=timezone.now())
articles = Article.objects.annotate(last_updated=Greatest('published', 'updated'))
self.assertIsNone(articles.first().last_updated)
def test_greatest_one_expressions(self):
with self.assertRaisesMessage(ValueError, 'Greatest must take at least two expressions'):
Greatest('written')
def test_greatest_related_field(self):
author = Author.objects.create(name='John Smith', age=45)
Fan.objects.create(name='Margaret', age=50, author=author)
authors = Author.objects.annotate(
highest_age=Greatest('age', 'fans__age'),
)
self.assertEqual(authors.first().highest_age, 50)
def test_greatest_update(self):
author = Author.objects.create(name='James Smith', goes_by='Jim')
Author.objects.update(alias=Greatest('name', 'goes_by'))
author.refresh_from_db()
self.assertEqual(author.alias, 'Jim')
def test_greatest_decimal_filter(self):
obj = DecimalModel.objects.create(n1=Decimal('1.1'), n2=Decimal('1.2'))
self.assertCountEqual(
DecimalModel.objects.annotate(
greatest=Greatest('n1', 'n2'),
).filter(greatest=Decimal('1.2')),
[obj],
)
def test_least(self):
now = timezone.now()
before = now - timedelta(hours=1)
Article.objects.create(
title="Testing with Django",
written=before,
published=now,
)
articles = Article.objects.annotate(
first_updated=Least('written', 'published'),
)
self.assertEqual(articles.first().first_updated, before)
@skipUnlessDBFeature('greatest_least_ignores_nulls')
def test_least_ignores_null(self):
now = timezone.now()
Article.objects.create(title="Testing with Django", written=now)
articles = Article.objects.annotate(
first_updated=Least('written', 'published'),
)
self.assertEqual(articles.first().first_updated, now)
@skipIfDBFeature('greatest_least_ignores_nulls')
def test_least_propagates_null(self):
now = timezone.now()
Article.objects.create(title="Testing with Django", written=now)
articles = Article.objects.annotate(
first_updated=Least('written', 'published'),
)
self.assertIsNone(articles.first().first_updated)
@skipIf(connection.vendor == 'mysql', "This doesn't work on MySQL")
def test_least_coalesce_workaround(self):
future = datetime(2100, 1, 1)
now = timezone.now()
Article.objects.create(title="Testing with Django", written=now)
articles = Article.objects.annotate(
last_updated=Least(
Coalesce('written', future),
Coalesce('published', future),
),
)
self.assertEqual(articles.first().last_updated, now)
@skipUnless(connection.vendor == 'mysql', "MySQL-specific workaround")
def test_least_coalesce_workaround_mysql(self):
future = datetime(2100, 1, 1)
now = timezone.now()
Article.objects.create(title="Testing with Django", written=now)
future_sql = RawSQL("cast(%s as datetime)", (future,))
articles = Article.objects.annotate(
last_updated=Least(
Coalesce('written', future_sql),
Coalesce('published', future_sql),
),
)
self.assertEqual(articles.first().last_updated, now)
def test_least_all_null(self):
Article.objects.create(title="Testing with Django", written=timezone.now())
articles = Article.objects.annotate(first_updated=Least('published', 'updated'))
self.assertIsNone(articles.first().first_updated)
def test_least_one_expressions(self):
with self.assertRaisesMessage(ValueError, 'Least must take at least two expressions'):
Least('written')
def test_least_related_field(self):
author = Author.objects.create(name='John Smith', age=45)
Fan.objects.create(name='Margaret', age=50, author=author)
authors = Author.objects.annotate(
lowest_age=Least('age', 'fans__age'),
)
self.assertEqual(authors.first().lowest_age, 45)
def test_least_update(self):
author = Author.objects.create(name='James Smith', goes_by='Jim')
Author.objects.update(alias=Least('name', 'goes_by'))
author.refresh_from_db()
self.assertEqual(author.alias, 'James Smith')
def test_least_decimal_filter(self):
obj = DecimalModel.objects.create(n1=Decimal('1.1'), n2=Decimal('1.2'))
self.assertCountEqual(
DecimalModel.objects.annotate(
least=Least('n1', 'n2'),
).filter(least=Decimal('1.1')),
[obj],
)
def test_nested_function_ordering(self): def test_nested_function_ordering(self):
Author.objects.create(name='John Smith') Author.objects.create(name='John Smith')
Author.objects.create(name='Rhonda Simpson', alias='ronny') Author.objects.create(name='Rhonda Simpson', alias='ronny')