From 70bcb3abea76e76d53c8f58d998960593cd7d833 Mon Sep 17 00:00:00 2001 From: Tom Sparrow <793763+sparrowt@users.noreply.github.com> Date: Mon, 15 Mar 2021 11:07:30 +0000 Subject: [PATCH] TDD: tests which fail due to index reinstatement bugs --- .../0012_test_indexes_retained_part1.py | 21 +++++++++ .../0013_test_indexes_retained_part2.py | 26 ++++++++++ testapp/models.py | 14 +++++- testapp/tests/test_indexes.py | 47 +++++++++++++++++++ 4 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 testapp/migrations/0012_test_indexes_retained_part1.py create mode 100644 testapp/migrations/0013_test_indexes_retained_part2.py create mode 100644 testapp/tests/test_indexes.py diff --git a/testapp/migrations/0012_test_indexes_retained_part1.py b/testapp/migrations/0012_test_indexes_retained_part1.py new file mode 100644 index 00000000..13c34299 --- /dev/null +++ b/testapp/migrations/0012_test_indexes_retained_part1.py @@ -0,0 +1,21 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('testapp', '0011_test_unique_constraints'), + ] + + # Issue #58 test prep + operations = [ + migrations.CreateModel( + name='TestIndexesRetained', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('a', models.IntegerField(db_index=True)), + ('b', models.IntegerField(db_index=True)), + ('c', models.IntegerField(db_index=True)), + ], + ), + ] diff --git a/testapp/migrations/0013_test_indexes_retained_part2.py b/testapp/migrations/0013_test_indexes_retained_part2.py new file mode 100644 index 00000000..7d844c61 --- /dev/null +++ b/testapp/migrations/0013_test_indexes_retained_part2.py @@ -0,0 +1,26 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('testapp', '0012_test_indexes_retained_part1'), + ] + + # Issue #58 test operations which should leave index intact + operations = [ + migrations.AlterField( + model_name='testindexesretained', + name='a', + field=models.IntegerField(db_index=True, null=True), + ), + migrations.RenameField( + model_name='testindexesretained', + old_name='b', + new_name='b_renamed', + ), + migrations.RenameModel( + old_name='TestIndexesRetained', + new_name='TestIndexesRetainedRenamed', + ), + ] diff --git a/testapp/models.py b/testapp/models.py index 8049309e..52890ea5 100644 --- a/testapp/models.py +++ b/testapp/models.py @@ -71,12 +71,22 @@ class Meta: class TestRemoveOneToOneFieldModel(models.Model): - # Fields used for testing removing OneToOne field. Verifies that delete_unique do not try to remove indexes - # thats already is removed. + # Fields used for testing removing OneToOne field. Verifies that delete_unique does not try to remove + # indexes that have already been removed (Issue #51) # b = models.OneToOneField('self', on_delete=models.SET_NULL, null=True) a = models.CharField(max_length=50) +class TestIndexesRetainedRenamed(models.Model): + # Issue #58 - in all these cases the column index should still exist afterwards + # case (a) `a` starts out not nullable, but then is changed to be nullable + a = models.IntegerField(db_index=True, null=True) + # case (b) column originally called `b` is renamed + b_renamed = models.IntegerField(db_index=True) + # case (c) this entire model is renamed - this is just a column whose index can be checked afterwards + c = models.IntegerField(db_index=True) + + class Topping(models.Model): name = models.UUIDField(primary_key=True, default=uuid.uuid4) diff --git a/testapp/tests/test_indexes.py b/testapp/tests/test_indexes.py new file mode 100644 index 00000000..5639ae16 --- /dev/null +++ b/testapp/tests/test_indexes.py @@ -0,0 +1,47 @@ +import django.db +from django.test import TestCase + +from ..models import ( + TestIndexesRetainedRenamed +) + + +class TestIndexesRetained(TestCase): + """ + Indexes dropped during a migration should be re-created afterwards + assuming the field still has `db_index=True` (issue #58) + """ + + @classmethod + def setUpClass(cls): + super().setUpClass() + # Pre-fetch which indexes exist for the relevant test model + # now that all the test migrations have run + connection = django.db.connections[django.db.DEFAULT_DB_ALIAS] + cls.constraints = connection.introspection.get_constraints( + connection.cursor(), + table_name=TestIndexesRetainedRenamed._meta.db_table + ) + cls.indexes = {k: v for k, v in cls.constraints.items() if v['index'] is True} + + def _assert_index_exists(self, columns): + matching = {k: v for k, v in self.indexes.items() if set(v['columns']) == columns} + assert len(matching) == 1, ( + "Expected 1 index for columns %s but found %d %s" % ( + columns, + len(matching), + ', '.join(matching.keys()) + ) + ) + + def test_field_made_nullable(self): + # Issue #58 case (a) + self._assert_index_exists({'a'}) + + def test_field_renamed(self): + # Issue #58 case (b) + self._assert_index_exists({'b_renamed'}) + + def test_table_renamed(self): + # Issue #58 case (c) + self._assert_index_exists({'c'})