summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/migrations/test_autodetector.py10
-rw-r--r--tests/migrations/test_operations.py61
-rw-r--r--tests/migrations/test_state.py8
-rw-r--r--tests/model_indexes/__init__.py0
-rw-r--r--tests/model_indexes/models.py7
-rw-r--r--tests/model_indexes/tests.py62
-rw-r--r--tests/schema/tests.py21
7 files changed, 161 insertions, 8 deletions
diff --git a/tests/migrations/test_autodetector.py b/tests/migrations/test_autodetector.py
index cb20927e07..1a1f6a1250 100644
--- a/tests/migrations/test_autodetector.py
+++ b/tests/migrations/test_autodetector.py
@@ -1263,7 +1263,9 @@ class AutodetectorTests(TestCase):
# Right number/type of migrations?
self.assertNumberMigrations(changes, "testapp", 1)
self.assertOperationTypes(changes, "testapp", 0, ["CreateModel"])
- self.assertOperationAttributes(changes, "testapp", 0, 0, name="AuthorProxy", options={"proxy": True})
+ self.assertOperationAttributes(
+ changes, "testapp", 0, 0, name="AuthorProxy", options={"proxy": True, "indexes": []}
+ )
# Now, we test turning a proxy model into a non-proxy model
# It should delete the proxy then make the real one
changes = self.get_changes(
@@ -1273,7 +1275,7 @@ class AutodetectorTests(TestCase):
self.assertNumberMigrations(changes, "testapp", 1)
self.assertOperationTypes(changes, "testapp", 0, ["DeleteModel", "CreateModel"])
self.assertOperationAttributes(changes, "testapp", 0, 0, name="AuthorProxy")
- self.assertOperationAttributes(changes, "testapp", 0, 1, name="AuthorProxy", options={})
+ self.assertOperationAttributes(changes, "testapp", 0, 1, name="AuthorProxy", options={"indexes": []})
def test_proxy_custom_pk(self):
"""
@@ -1296,7 +1298,9 @@ class AutodetectorTests(TestCase):
# Right number/type of migrations?
self.assertNumberMigrations(changes, 'testapp', 1)
self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel"])
- self.assertOperationAttributes(changes, 'testapp', 0, 0, name="AuthorUnmanaged", options={"managed": False})
+ self.assertOperationAttributes(
+ changes, 'testapp', 0, 0, name="AuthorUnmanaged", options={"managed": False, "indexes": []}
+ )
def test_unmanaged_to_managed(self):
# Now, we test turning an unmanaged model into a managed model
diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py
index 3707caf106..01a9c8d886 100644
--- a/tests/migrations/test_operations.py
+++ b/tests/migrations/test_operations.py
@@ -51,7 +51,7 @@ class OperationTestBase(MigrationTestBase):
return project_state, new_state
def set_up_test_model(
- self, app_label, second_model=False, third_model=False,
+ self, app_label, second_model=False, third_model=False, multicol_index=False,
related_model=False, mti_model=False, proxy_model=False, manager_model=False,
unique_together=False, options=False, db_table=None, index_together=False):
"""
@@ -96,6 +96,11 @@ class OperationTestBase(MigrationTestBase):
],
options=model_options,
)]
+ if multicol_index:
+ operations.append(migrations.AddIndex(
+ "Pony",
+ models.Index(fields=["pink", "weight"], name="pony_test_idx")
+ ))
if second_model:
operations.append(migrations.CreateModel(
"Stable",
@@ -1375,6 +1380,60 @@ class OperationTests(OperationTestBase):
operation = migrations.AlterUniqueTogether("Pony", None)
self.assertEqual(operation.describe(), "Alter unique_together for Pony (0 constraint(s))")
+ def test_add_index(self):
+ """
+ Test the AddIndex operation.
+ """
+ project_state = self.set_up_test_model("test_adin")
+ index = models.Index(fields=["pink"])
+ operation = migrations.AddIndex("Pony", index)
+ self.assertEqual(operation.describe(), "Create index on field(s) pink of model Pony")
+ new_state = project_state.clone()
+ operation.state_forwards("test_adin", new_state)
+ # Test the database alteration
+ self.assertEqual(len(new_state.models["test_adin", "pony"].options['indexes']), 1)
+ self.assertIndexNotExists("test_adin_pony", ["pink"])
+ with connection.schema_editor() as editor:
+ operation.database_forwards("test_adin", editor, project_state, new_state)
+ self.assertIndexExists("test_adin_pony", ["pink"])
+ # And test reversal
+ with connection.schema_editor() as editor:
+ operation.database_backwards("test_adin", editor, new_state, project_state)
+ self.assertIndexNotExists("test_adin_pony", ["pink"])
+ # And deconstruction
+ definition = operation.deconstruct()
+ self.assertEqual(definition[0], "AddIndex")
+ self.assertEqual(definition[1], [])
+ self.assertEqual(definition[2], {'model_name': "Pony", 'index': index})
+
+ def test_remove_index(self):
+ """
+ Test the RemoveIndex operation.
+ """
+ project_state = self.set_up_test_model("test_rmin", multicol_index=True)
+ self.assertTableExists("test_rmin_pony")
+ self.assertIndexExists("test_rmin_pony", ["pink", "weight"])
+ operation = migrations.RemoveIndex("Pony", "pony_test_idx")
+ self.assertEqual(operation.describe(), "Remove index pony_test_idx from Pony")
+ new_state = project_state.clone()
+ operation.state_forwards("test_rmin", new_state)
+ # Test the state alteration
+ self.assertEqual(len(new_state.models["test_rmin", "pony"].options['indexes']), 0)
+ self.assertIndexExists("test_rmin_pony", ["pink", "weight"])
+ # Test the database alteration
+ with connection.schema_editor() as editor:
+ operation.database_forwards("test_rmin", editor, project_state, new_state)
+ self.assertIndexNotExists("test_rmin_pony", ["pink", "weight"])
+ # And test reversal
+ with connection.schema_editor() as editor:
+ operation.database_backwards("test_rmin", editor, new_state, project_state)
+ self.assertIndexExists("test_rmin_pony", ["pink", "weight"])
+ # And deconstruction
+ definition = operation.deconstruct()
+ self.assertEqual(definition[0], "RemoveIndex")
+ self.assertEqual(definition[1], [])
+ self.assertEqual(definition[2], {'model_name': "Pony", 'name': "pony_test_idx"})
+
def test_alter_index_together(self):
"""
Tests the AlterIndexTogether operation.
diff --git a/tests/migrations/test_state.py b/tests/migrations/test_state.py
index d165221aa6..dae4fabb67 100644
--- a/tests/migrations/test_state.py
+++ b/tests/migrations/test_state.py
@@ -125,7 +125,7 @@ class StateTests(SimpleTestCase):
self.assertIs(author_state.fields[3][1].null, True)
self.assertEqual(
author_state.options,
- {"unique_together": {("name", "bio")}, "index_together": {("bio", "age")}}
+ {"unique_together": {("name", "bio")}, "index_together": {("bio", "age")}, "indexes": []}
)
self.assertEqual(author_state.bases, (models.Model, ))
@@ -135,13 +135,13 @@ class StateTests(SimpleTestCase):
self.assertEqual(book_state.fields[1][1].max_length, 1000)
self.assertIs(book_state.fields[2][1].null, False)
self.assertEqual(book_state.fields[3][1].__class__.__name__, "ManyToManyField")
- self.assertEqual(book_state.options, {"verbose_name": "tome", "db_table": "test_tome"})
+ self.assertEqual(book_state.options, {"verbose_name": "tome", "db_table": "test_tome", "indexes": []})
self.assertEqual(book_state.bases, (models.Model, ))
self.assertEqual(author_proxy_state.app_label, "migrations")
self.assertEqual(author_proxy_state.name, "AuthorProxy")
self.assertEqual(author_proxy_state.fields, [])
- self.assertEqual(author_proxy_state.options, {"proxy": True, "ordering": ["name"]})
+ self.assertEqual(author_proxy_state.options, {"proxy": True, "ordering": ["name"], "indexes": []})
self.assertEqual(author_proxy_state.bases, ("migrations.author", ))
self.assertEqual(sub_author_state.app_label, "migrations")
@@ -960,7 +960,7 @@ class ModelStateTests(SimpleTestCase):
self.assertEqual(author_state.fields[1][1].max_length, 255)
self.assertIs(author_state.fields[2][1].null, False)
self.assertIs(author_state.fields[3][1].null, True)
- self.assertEqual(author_state.options, {'swappable': 'TEST_SWAPPABLE_MODEL'})
+ self.assertEqual(author_state.options, {'swappable': 'TEST_SWAPPABLE_MODEL', 'indexes': []})
self.assertEqual(author_state.bases, (models.Model, ))
self.assertEqual(author_state.managers, [])
diff --git a/tests/model_indexes/__init__.py b/tests/model_indexes/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/model_indexes/__init__.py
diff --git a/tests/model_indexes/models.py b/tests/model_indexes/models.py
new file mode 100644
index 0000000000..598a4cb808
--- /dev/null
+++ b/tests/model_indexes/models.py
@@ -0,0 +1,7 @@
+from django.db import models
+
+
+class Book(models.Model):
+ title = models.CharField(max_length=50)
+ author = models.CharField(max_length=50)
+ pages = models.IntegerField(db_column='page_count')
diff --git a/tests/model_indexes/tests.py b/tests/model_indexes/tests.py
new file mode 100644
index 0000000000..71bd8c91c0
--- /dev/null
+++ b/tests/model_indexes/tests.py
@@ -0,0 +1,62 @@
+from django.db import models
+from django.test import TestCase
+
+from .models import Book
+
+
+class IndexesTests(TestCase):
+
+ def test_repr(self):
+ index = models.Index(fields=['title'])
+ multi_col_index = models.Index(fields=['title', 'author'])
+ self.assertEqual(repr(index), "<Index: fields='title'>")
+ self.assertEqual(repr(multi_col_index), "<Index: fields='title, author'>")
+
+ def test_eq(self):
+ index = models.Index(fields=['title'])
+ same_index = models.Index(fields=['title'])
+ another_index = models.Index(fields=['title', 'author'])
+ self.assertEqual(index, same_index)
+ self.assertNotEqual(index, another_index)
+
+ def test_raises_error_without_field(self):
+ msg = 'At least one field is required to define an index.'
+ with self.assertRaisesMessage(ValueError, msg):
+ models.Index()
+
+ def test_max_name_length(self):
+ msg = 'Index names cannot be longer than 30 characters.'
+ with self.assertRaisesMessage(ValueError, msg):
+ models.Index(fields=['title'], name='looooooooooooong_index_name_idx')
+
+ def test_name_constraints(self):
+ msg = 'Index names cannot start with an underscore (_).'
+ with self.assertRaisesMessage(ValueError, msg):
+ models.Index(fields=['title'], name='_name_starting_with_underscore')
+
+ msg = 'Index names cannot start with a number (0-9).'
+ with self.assertRaisesMessage(ValueError, msg):
+ models.Index(fields=['title'], name='5name_starting_with_number')
+
+ def test_name_auto_generation(self):
+ index = models.Index(fields=['author'])
+ index.model = Book
+ self.assertEqual(index.name, 'model_index_author_0f5565_idx')
+
+ # fields may be truncated in the name. db_column is used for naming.
+ long_field_index = models.Index(fields=['pages'])
+ long_field_index.model = Book
+ self.assertEqual(long_field_index.name, 'model_index_page_co_69235a_idx')
+
+ # suffix can't be longer than 3 characters.
+ long_field_index.suffix = 'suff'
+ msg = 'Index too long for multiple database support. Is self.suffix longer than 3 characters?'
+ with self.assertRaisesMessage(AssertionError, msg):
+ long_field_index.get_name()
+
+ def test_deconstruction(self):
+ index = models.Index(fields=['title'])
+ path, args, kwargs = index.deconstruct()
+ self.assertEqual(path, 'django.db.models.Index')
+ self.assertEqual(args, ())
+ self.assertEqual(kwargs, {'fields': ['title']})
diff --git a/tests/schema/tests.py b/tests/schema/tests.py
index 8b35b77c7b..a3e4f8227a 100644
--- a/tests/schema/tests.py
+++ b/tests/schema/tests.py
@@ -16,6 +16,7 @@ from django.db.models.fields import (
from django.db.models.fields.related import (
ForeignKey, ForeignObject, ManyToManyField, OneToOneField,
)
+from django.db.models.indexes import Index
from django.db.transaction import atomic
from django.test import (
TransactionTestCase, mock, skipIfDBFeature, skipUnlessDBFeature,
@@ -1443,6 +1444,26 @@ class SchemaTests(TransactionTestCase):
columns = self.column_classes(Author)
self.assertEqual(columns['name'][0], "CharField")
+ def test_add_remove_index(self):
+ """
+ Tests index addition and removal
+ """
+ # Create the table
+ with connection.schema_editor() as editor:
+ editor.create_model(Author)
+ # Ensure the table is there and has no index
+ self.assertNotIn('title', self.get_indexes(Author._meta.db_table))
+ # Add the index
+ index = Index(fields=['name'], name='author_title_idx')
+ index.model = Author
+ with connection.schema_editor() as editor:
+ editor.add_index(index)
+ self.assertIn('name', self.get_indexes(Author._meta.db_table))
+ # Drop the index
+ with connection.schema_editor() as editor:
+ editor.remove_index(index)
+ self.assertNotIn('name', self.get_indexes(Author._meta.db_table))
+
def test_indexes(self):
"""
Tests creation/altering of indexes