summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorHonza Král <honza.kral@gmail.com>2009-12-07 01:41:26 +0000
committerHonza Král <honza.kral@gmail.com>2009-12-07 01:41:26 +0000
commit3b895d4a9a68a841ec517b84451da496bb258b3f (patch)
tree1913ed9144ff29b5cb359822c7b6a4eecd3ba0fb /tests
parent30ea350dabad28b0e524feabce434d446e013d6f (diff)
[soc2009/model-validation] Merged to trunk at r11791
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/model-validation@11798 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'tests')
-rw-r--r--tests/modeltests/model_package/tests.py27
-rw-r--r--tests/regressiontests/admin_validation/models.py59
-rw-r--r--tests/regressiontests/bash_completion/__init__.py1
-rw-r--r--tests/regressiontests/bash_completion/management/__init__.py1
-rw-r--r--tests/regressiontests/bash_completion/management/commands/__init__.py1
-rw-r--r--tests/regressiontests/bash_completion/management/commands/test_command.py14
-rw-r--r--tests/regressiontests/bash_completion/models.py1
-rw-r--r--tests/regressiontests/bash_completion/tests.py87
-rw-r--r--tests/regressiontests/defer_regress/models.py17
-rw-r--r--tests/regressiontests/forms/fields.py25
-rw-r--r--tests/regressiontests/forms/regressions.py30
-rw-r--r--tests/regressiontests/many_to_one_regress/models.py6
-rw-r--r--tests/regressiontests/queries/models.py12
-rw-r--r--tests/regressiontests/urlpatterns_reverse/middleware.py7
-rw-r--r--tests/regressiontests/urlpatterns_reverse/tests.py37
-rw-r--r--tests/regressiontests/urlpatterns_reverse/urlconf_inner.py12
-rw-r--r--tests/regressiontests/urlpatterns_reverse/urlconf_outer.py9
17 files changed, 342 insertions, 4 deletions
diff --git a/tests/modeltests/model_package/tests.py b/tests/modeltests/model_package/tests.py
index 6e8c158a68..7fd4b6f679 100644
--- a/tests/modeltests/model_package/tests.py
+++ b/tests/modeltests/model_package/tests.py
@@ -1,4 +1,13 @@
-"""
+from django.db import models
+
+class Advertisment(models.Model):
+ customer = models.CharField(max_length=100)
+ publications = models.ManyToManyField("model_package.Publication", null=True, blank=True)
+
+ class Meta:
+ app_label = 'model_package'
+
+__test__ = {'API_TESTS': """
>>> from models.publication import Publication
>>> from models.article import Article
>>> from django.contrib.auth.views import Site
@@ -19,7 +28,6 @@
>>> a.save()
>>> a.publications.add(p)
>>> a.sites.add(current_site)
->>> a.save()
>>> a = Article.objects.get(id=1)
>>> a
@@ -29,6 +37,19 @@
>>> a.sites.count()
1
-"""
+# Regression for #12248 - Models can exist in the test package, too
+
+>>> ad = Advertisment(customer="Lawrence Journal-World")
+>>> ad.save()
+>>> ad.publications.add(p)
+
+>>> ad = Advertisment.objects.get(id=1)
+>>> ad
+<Advertisment: Advertisment object>
+
+>>> ad.publications.count()
+1
+
+"""}
diff --git a/tests/regressiontests/admin_validation/models.py b/tests/regressiontests/admin_validation/models.py
index 5506114841..eb53a9dd6e 100644
--- a/tests/regressiontests/admin_validation/models.py
+++ b/tests/regressiontests/admin_validation/models.py
@@ -26,6 +26,21 @@ class TwoAlbumFKAndAnE(models.Model):
e = models.CharField(max_length=1)
+class Author(models.Model):
+ name = models.CharField(max_length=100)
+
+
+class Book(models.Model):
+ name = models.CharField(max_length=100)
+ subtitle = models.CharField(max_length=100)
+ price = models.FloatField()
+ authors = models.ManyToManyField(Author, through='AuthorsBooks')
+
+
+class AuthorsBooks(models.Model):
+ author = models.ForeignKey(Author)
+ book = models.ForeignKey(Book)
+
__test__ = {'API_TESTS':"""
@@ -95,4 +110,48 @@ Exception: <class 'regressiontests.admin_validation.models.TwoAlbumFKAndAnE'> ha
>>> validate_inline(TwoAlbumFKAndAnEInline, None, Album)
+# Regression test for #12203/#12237 - Fail more gracefully when a M2M field that
+# specifies the 'through' option is included in the 'fields' or the 'fieldsets'
+# ModelAdmin options.
+
+>>> class BookAdmin(admin.ModelAdmin):
+... fields = ['authors']
+
+>>> validate(BookAdmin, Book)
+Traceback (most recent call last):
+ ...
+ImproperlyConfigured: 'BookAdmin.fields' can't include the ManyToManyField field 'authors' because 'authors' manually specifies a 'through' model.
+
+>>> class FieldsetBookAdmin(admin.ModelAdmin):
+... fieldsets = (
+... ('Header 1', {'fields': ('name',)}),
+... ('Header 2', {'fields': ('authors',)}),
+... )
+
+>>> validate(FieldsetBookAdmin, Book)
+Traceback (most recent call last):
+ ...
+ImproperlyConfigured: 'FieldsetBookAdmin.fieldsets[1][1]['fields']' can't include the ManyToManyField field 'authors' because 'authors' manually specifies a 'through' model.
+
+>>> class NestedFieldsetAdmin(admin.ModelAdmin):
+... fieldsets = (
+... ('Main', {'fields': ('price', ('name', 'subtitle'))}),
+... )
+
+>>> validate(NestedFieldsetAdmin, Book)
+
+# Regression test for #12209 -- If the explicitly provided through model
+# is specified as a string, the admin should still be able use
+# Model.m2m_field.through
+
+>>> class AuthorsInline(admin.TabularInline):
+... model = Book.authors.through
+
+>>> class BookAdmin(admin.ModelAdmin):
+... inlines = [AuthorsInline]
+
+# If the through model is still a string (and hasn't been resolved to a model)
+# the validation will fail.
+>>> validate(BookAdmin, Book)
+
"""}
diff --git a/tests/regressiontests/bash_completion/__init__.py b/tests/regressiontests/bash_completion/__init__.py
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/tests/regressiontests/bash_completion/__init__.py
@@ -0,0 +1 @@
+
diff --git a/tests/regressiontests/bash_completion/management/__init__.py b/tests/regressiontests/bash_completion/management/__init__.py
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/tests/regressiontests/bash_completion/management/__init__.py
@@ -0,0 +1 @@
+
diff --git a/tests/regressiontests/bash_completion/management/commands/__init__.py b/tests/regressiontests/bash_completion/management/commands/__init__.py
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/tests/regressiontests/bash_completion/management/commands/__init__.py
@@ -0,0 +1 @@
+
diff --git a/tests/regressiontests/bash_completion/management/commands/test_command.py b/tests/regressiontests/bash_completion/management/commands/test_command.py
new file mode 100644
index 0000000000..5cb8820a8f
--- /dev/null
+++ b/tests/regressiontests/bash_completion/management/commands/test_command.py
@@ -0,0 +1,14 @@
+import sys, os
+from optparse import OptionParser, make_option
+
+from django.core.management.base import BaseCommand
+
+
+class Command(BaseCommand):
+ option_list = BaseCommand.option_list + (
+ make_option("--list", action="store_true", dest="list",
+ help="Print all options"),
+ )
+
+ def handle(self, *args, **options):
+ pass
diff --git a/tests/regressiontests/bash_completion/models.py b/tests/regressiontests/bash_completion/models.py
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/tests/regressiontests/bash_completion/models.py
@@ -0,0 +1 @@
+
diff --git a/tests/regressiontests/bash_completion/tests.py b/tests/regressiontests/bash_completion/tests.py
new file mode 100644
index 0000000000..24c8b1d3f3
--- /dev/null
+++ b/tests/regressiontests/bash_completion/tests.py
@@ -0,0 +1,87 @@
+"""
+A series of tests to establish that the command-line bash completion works.
+"""
+import os
+import unittest
+import sys
+import StringIO
+
+from django.conf import settings
+from django.core.management import ManagementUtility
+
+class BashCompletionTests(unittest.TestCase):
+ """
+ Testing the Python level bash completion code.
+ This requires setting up the environment as if we got passed data
+ from bash.
+ """
+
+ def setUp(self):
+ self.old_DJANGO_AUTO_COMPLETE = os.environ.get('DJANGO_AUTO_COMPLETE')
+ os.environ['DJANGO_AUTO_COMPLETE'] = '1'
+ self.output = StringIO.StringIO()
+ self.old_stdout = sys.stdout
+ sys.stdout = self.output
+
+ def tearDown(self):
+ sys.stdout = self.old_stdout
+ if self.old_DJANGO_AUTO_COMPLETE:
+ os.environ['DJANGO_AUTO_COMPLETE'] = self.old_DJANGO_AUTO_COMPLETE
+ else:
+ del os.environ['DJANGO_AUTO_COMPLETE']
+
+ def _user_input(self, input_str):
+ os.environ['COMP_WORDS'] = input_str
+ os.environ['COMP_CWORD'] = str(len(input_str.split()) - 1)
+ sys.argv = input_str.split(' ')
+
+ def _run_autocomplete(self):
+ util = ManagementUtility(argv=sys.argv)
+ try:
+ util.autocomplete()
+ except SystemExit:
+ pass
+ return self.output.getvalue().strip().split('\n')
+
+ def test_django_admin_py(self):
+ "django_admin.py will autocomplete option flags"
+ self._user_input('django-admin.py sqlall --v')
+ output = self._run_autocomplete()
+ self.assertEqual(output, ['--verbosity='])
+
+ def test_manage_py(self):
+ "manage.py will autocomplete option flags"
+ self._user_input('manage.py sqlall --v')
+ output = self._run_autocomplete()
+ self.assertEqual(output, ['--verbosity='])
+
+ def test_custom_command(self):
+ "A custom command can autocomplete option flags"
+ self._user_input('django-admin.py test_command --l')
+ output = self._run_autocomplete()
+ self.assertEqual(output, ['--list'])
+
+ def test_subcommands(self):
+ "Subcommands can be autocompleted"
+ self._user_input('django-admin.py sql')
+ output = self._run_autocomplete()
+ self.assertEqual(output, ['sql sqlall sqlclear sqlcustom sqlflush sqlindexes sqlinitialdata sqlreset sqlsequencereset'])
+
+ def test_help(self):
+ "No errors, just an empty list if there are no autocomplete options"
+ self._user_input('django-admin.py help --')
+ output = self._run_autocomplete()
+ self.assertEqual(output, [''])
+
+ def test_runfcgi(self):
+ "Command arguments will be autocompleted"
+ self._user_input('django-admin.py runfcgi h')
+ output = self._run_autocomplete()
+ self.assertEqual(output, ['host='])
+
+ def test_app_completion(self):
+ "Application names will be autocompleted for an AppCommand"
+ self._user_input('django-admin.py sqlall a')
+ output = self._run_autocomplete()
+ app_labels = [name.split('.')[-1] for name in settings.INSTALLED_APPS]
+ self.assertEqual(output, sorted(label for label in app_labels if label.startswith('a')))
diff --git a/tests/regressiontests/defer_regress/models.py b/tests/regressiontests/defer_regress/models.py
index da9822ab88..a1cd19788d 100644
--- a/tests/regressiontests/defer_regress/models.py
+++ b/tests/regressiontests/defer_regress/models.py
@@ -115,6 +115,23 @@ u'c1'
>>> results[0].second_child.name
u'c2'
+# Test for #12163 - Pickling error saving session with unsaved model instances.
+>>> from django.contrib.sessions.backends.db import SessionStore
+>>> SESSION_KEY = '2b1189a188b44ad18c35e1baac6ceead'
+>>> item = Item()
+>>> item._deferred
+False
+>>> s = SessionStore(SESSION_KEY)
+>>> s.clear()
+>>> s['item'] = item
+>>> s.save()
+>>> s = SessionStore(SESSION_KEY)
+>>> s.modified = True
+>>> s.save()
+>>> i2 = s['item']
+>>> i2._deferred # Item must still be non-deferred
+False
+
# Finally, we need to flush the app cache for the defer module.
# Using only/defer creates some artifical entries in the app cache
# that messes up later tests. Purge all entries, just to be sure.
diff --git a/tests/regressiontests/forms/fields.py b/tests/regressiontests/forms/fields.py
index b02b4612a2..eeff56761d 100644
--- a/tests/regressiontests/forms/fields.py
+++ b/tests/regressiontests/forms/fields.py
@@ -1,4 +1,29 @@
# -*- coding: utf-8 -*-
+"""
+##########
+# Fields #
+##########
+
+Each Field class does some sort of validation. Each Field has a clean() method,
+which either raises django.forms.ValidationError or returns the "clean"
+data -- usually a Unicode object, but, in some rare cases, a list.
+
+Each Field's __init__() takes at least these parameters:
+ required -- Boolean that specifies whether the field is required.
+ True by default.
+ widget -- A Widget class, or instance of a Widget class, that should be
+ used for this Field when displaying it. Each Field has a default
+ Widget that it'll use if you don't specify this. In most cases,
+ the default widget is TextInput.
+ label -- A verbose name for this field, for use in displaying this field in
+ a form. By default, Django will use a "pretty" version of the form
+ field name, if the Field is part of a Form.
+ initial -- A value to use in this Field's initial display. This value is
+ *not* used as a fallback if data isn't given.
+
+Other than that, the Field subclasses have class-specific options for
+__init__(). For example, CharField has a max_length option.
+"""
import datetime
import time
import re
diff --git a/tests/regressiontests/forms/regressions.py b/tests/regressiontests/forms/regressions.py
index 51aa41d2fb..9471932057 100644
--- a/tests/regressiontests/forms/regressions.py
+++ b/tests/regressiontests/forms/regressions.py
@@ -102,4 +102,34 @@ u'<ul class="errorlist"><li>(Hidden field data) This field is required.</li></ul
>>> f.as_table()
u'<tr><td colspan="2"><ul class="errorlist"><li>(Hidden field data) This field is required.</li></ul><input type="hidden" name="data" id="id_data" /></td></tr>'
+###################################################
+# Tests for XSS vulnerabilities in error messages #
+###################################################
+
+# The forms layer doesn't escape input values directly because error messages
+# might be presented in non-HTML contexts. Instead, the message is just marked
+# for escaping by the template engine. So we'll need to construct a little
+# silly template to trigger the escaping.
+
+>>> from django.template import Template, Context
+>>> t = Template('{{ form.errors }}')
+
+>>> class SomeForm(Form):
+... field = ChoiceField(choices=[('one', 'One')])
+>>> f = SomeForm({'field': '<script>'})
+>>> t.render(Context({'form': f}))
+u'<ul class="errorlist"><li>field<ul class="errorlist"><li>Select a valid choice. &lt;script&gt; is not one of the available choices.</li></ul></li></ul>'
+
+>>> class SomeForm(Form):
+... field = MultipleChoiceField(choices=[('one', 'One')])
+>>> f = SomeForm({'field': ['<script>']})
+>>> t.render(Context({'form': f}))
+u'<ul class="errorlist"><li>field<ul class="errorlist"><li>Select a valid choice. &lt;script&gt; is not one of the available choices.</li></ul></li></ul>'
+
+>>> from regressiontests.forms.models import ChoiceModel
+>>> class SomeForm(Form):
+... field = ModelMultipleChoiceField(ChoiceModel.objects.all())
+>>> f = SomeForm({'field': ['<script>']})
+>>> t.render(Context({'form': f}))
+u'<ul class="errorlist"><li>field<ul class="errorlist"><li>&quot;&lt;script&gt;&quot; is not a valid value for a primary key.</li></ul></li></ul>'
"""
diff --git a/tests/regressiontests/many_to_one_regress/models.py b/tests/regressiontests/many_to_one_regress/models.py
index 3f35bef62c..d491f002c2 100644
--- a/tests/regressiontests/many_to_one_regress/models.py
+++ b/tests/regressiontests/many_to_one_regress/models.py
@@ -167,4 +167,10 @@ Traceback (most recent call last):
...
ValueError: Cannot assign "<Child: Child object>": "Child.parent" must be a "Parent" instance.
+# Regression for #12190 -- Should be able to instantiate a FK
+# outside of a model, and interrogate its related field.
+>>> cat = models.ForeignKey(Category)
+>>> cat.rel.get_related_field().name
+'id'
+
"""}
diff --git a/tests/regressiontests/queries/models.py b/tests/regressiontests/queries/models.py
index cf21d5210b..ddac509ede 100644
--- a/tests/regressiontests/queries/models.py
+++ b/tests/regressiontests/queries/models.py
@@ -755,10 +755,20 @@ Bug #6180, #6203 -- dates with limits and/or counts
>>> Item.objects.dates('created', 'day')[0]
datetime.datetime(2007, 12, 19, 0, 0)
-Bug #7087 -- dates with extra select columns
+Bug #7087/#12242 -- dates with extra select columns
>>> Item.objects.dates('created', 'day').extra(select={'a': 1})
[datetime.datetime(2007, 12, 19, 0, 0), datetime.datetime(2007, 12, 20, 0, 0)]
+>>> Item.objects.extra(select={'a': 1}).dates('created', 'day')
+[datetime.datetime(2007, 12, 19, 0, 0), datetime.datetime(2007, 12, 20, 0, 0)]
+
+>>> name="one"
+>>> Item.objects.dates('created', 'day').extra(where=['name=%s'], params=[name])
+[datetime.datetime(2007, 12, 19, 0, 0)]
+
+>>> Item.objects.extra(where=['name=%s'], params=[name]).dates('created', 'day')
+[datetime.datetime(2007, 12, 19, 0, 0)]
+
Bug #7155 -- nullable dates
>>> Item.objects.dates('modified', 'day')
[datetime.datetime(2007, 12, 19, 0, 0)]
diff --git a/tests/regressiontests/urlpatterns_reverse/middleware.py b/tests/regressiontests/urlpatterns_reverse/middleware.py
new file mode 100644
index 0000000000..7bb9aa924f
--- /dev/null
+++ b/tests/regressiontests/urlpatterns_reverse/middleware.py
@@ -0,0 +1,7 @@
+from django.core.urlresolvers import set_urlconf
+
+import urlconf_inner
+
+class ChangeURLconfMiddleware(object):
+ def process_request(self, request):
+ request.urlconf = urlconf_inner.__name__
diff --git a/tests/regressiontests/urlpatterns_reverse/tests.py b/tests/regressiontests/urlpatterns_reverse/tests.py
index d4f281ba81..34c9db25d3 100644
--- a/tests/regressiontests/urlpatterns_reverse/tests.py
+++ b/tests/regressiontests/urlpatterns_reverse/tests.py
@@ -16,11 +16,16 @@ ImproperlyConfigured: The included urlconf regressiontests.urlpatterns_reverse.n
import unittest
+from django.conf import settings
from django.core.urlresolvers import reverse, resolve, NoReverseMatch, Resolver404
from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
from django.shortcuts import redirect
from django.test import TestCase
+import urlconf_outer
+import urlconf_inner
+import middleware
+
test_data = (
('places', '/places/3/', [3], {}),
('places', '/places/3/', ['3'], {}),
@@ -239,3 +244,35 @@ class NamespaceTests(TestCase):
self.assertEquals('/other1/inner/37/42/', reverse('nodefault:urlobject-view', args=[37,42], current_app='other-ns1'))
self.assertEquals('/other1/inner/42/37/', reverse('nodefault:urlobject-view', kwargs={'arg1':42, 'arg2':37}, current_app='other-ns1'))
+
+class RequestURLconfTests(TestCase):
+ def setUp(self):
+ self.root_urlconf = settings.ROOT_URLCONF
+ self.middleware_classes = settings.MIDDLEWARE_CLASSES
+ settings.ROOT_URLCONF = urlconf_outer.__name__
+
+ def tearDown(self):
+ settings.ROOT_URLCONF = self.root_urlconf
+ settings.MIDDLEWARE_CLASSES = self.middleware_classes
+
+ def test_urlconf(self):
+ response = self.client.get('/test/me/')
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.content, 'outer:/test/me/,'
+ 'inner:/inner_urlconf/second_test/')
+ response = self.client.get('/inner_urlconf/second_test/')
+ self.assertEqual(response.status_code, 200)
+ response = self.client.get('/second_test/')
+ self.assertEqual(response.status_code, 404)
+
+ def test_urlconf_overridden(self):
+ settings.MIDDLEWARE_CLASSES += (
+ '%s.ChangeURLconfMiddleware' % middleware.__name__,
+ )
+ response = self.client.get('/test/me/')
+ self.assertEqual(response.status_code, 404)
+ response = self.client.get('/inner_urlconf/second_test/')
+ self.assertEqual(response.status_code, 404)
+ response = self.client.get('/second_test/')
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.content, 'outer:,inner:/second_test/')
diff --git a/tests/regressiontests/urlpatterns_reverse/urlconf_inner.py b/tests/regressiontests/urlpatterns_reverse/urlconf_inner.py
new file mode 100644
index 0000000000..d188e06421
--- /dev/null
+++ b/tests/regressiontests/urlpatterns_reverse/urlconf_inner.py
@@ -0,0 +1,12 @@
+from django.conf.urls.defaults import *
+from django.template import Template, Context
+from django.http import HttpResponse
+
+def inner_view(request):
+ content = Template('{% url outer as outer_url %}outer:{{ outer_url }},'
+ '{% url inner as inner_url %}inner:{{ inner_url }}').render(Context())
+ return HttpResponse(content)
+
+urlpatterns = patterns('',
+ url(r'^second_test/$', inner_view, name='inner'),
+) \ No newline at end of file
diff --git a/tests/regressiontests/urlpatterns_reverse/urlconf_outer.py b/tests/regressiontests/urlpatterns_reverse/urlconf_outer.py
new file mode 100644
index 0000000000..506e03666e
--- /dev/null
+++ b/tests/regressiontests/urlpatterns_reverse/urlconf_outer.py
@@ -0,0 +1,9 @@
+from django.conf.urls.defaults import *
+
+import urlconf_inner
+
+
+urlpatterns = patterns('',
+ url(r'^test/me/$', urlconf_inner.inner_view, name='outer'),
+ url(r'^inner_urlconf/', include(urlconf_inner.__name__))
+) \ No newline at end of file