1
0
mirror of https://github.com/django/django.git synced 2025-01-03 15:06:09 +00:00

Refs #34553 -- Split constraint escaping test in subtests.

This ensures that constraint violations are tested in isolation from
each other as an IntegrityError only ensures a least one constraint is
violated.

For example, the assertion added in 42e8cf4 break both the
name_constraint_rhs and the rebate_constraint constraints and thus
doesn't constitute a proper regression test. Refs #32369.
This commit is contained in:
Simon Charette 2023-05-09 22:18:05 -04:00 committed by Mariusz Felisiak
parent 6e32d1fa1d
commit e0f8104a96

View File

@ -3652,47 +3652,44 @@ class OperationTests(OperationTestBase):
),
]
from_state = self.apply_operations(app_label, ProjectState(), operations)
# "%" generated in startswith lookup should be escaped in a way that is
# considered a leading wildcard.
check = models.Q(name__startswith="Albert")
constraint = models.CheckConstraint(check=check, name="name_constraint")
operation = migrations.AddConstraint("Author", constraint)
to_state = from_state.clone()
operation.state_forwards(app_label, to_state)
with connection.schema_editor() as editor:
operation.database_forwards(app_label, editor, from_state, to_state)
Author = to_state.apps.get_model(app_label, "Author")
with self.assertRaises(IntegrityError), transaction.atomic():
Author.objects.create(name="Artur")
checks = [
# "%" generated in startswith lookup should be escaped in a way
# that is considered a leading wildcard.
(
models.Q(name__startswith="Albert"),
{"name": "Alberta"},
{"name": "Artur"},
),
# Literal "%" should be escaped in a way that is not a considered a
# wildcard.
check = models.Q(rebate__endswith="%")
constraint = models.CheckConstraint(check=check, name="rebate_constraint")
(models.Q(rebate__endswith="%"), {"rebate": "10%"}, {"rebate": "10$"}),
# Right-hand-side baked "%" literals should not be used for
# parameters interpolation.
(
~models.Q(surname__startswith=models.F("name")),
{"name": "Albert"},
{"name": "Albert", "surname": "Alberto"},
),
]
for check, valid, invalid in checks:
with self.subTest(check=check, valid=valid, invalid=invalid):
constraint = models.CheckConstraint(check=check, name="constraint")
operation = migrations.AddConstraint("Author", constraint)
from_state = to_state
to_state = from_state.clone()
operation.state_forwards(app_label, to_state)
Author = to_state.apps.get_model(app_label, "Author")
with connection.schema_editor() as editor:
operation.database_forwards(app_label, editor, from_state, to_state)
Author = to_state.apps.get_model(app_label, "Author")
with self.assertRaises(IntegrityError), transaction.atomic():
Author.objects.create(name="Albert", rebate="10$")
author = Author.objects.create(name="Albert", rebate="10%")
self.assertEqual(Author.objects.get(), author)
# Right-hand-side baked "%" literals should not be used for parameters
# interpolation.
check = ~models.Q(surname__startswith=models.F("name"))
constraint = models.CheckConstraint(check=check, name="name_constraint_rhs")
operation = migrations.AddConstraint("Author", constraint)
from_state = to_state
to_state = from_state.clone()
operation.state_forwards(app_label, to_state)
with connection.schema_editor() as editor:
operation.database_forwards(app_label, editor, from_state, to_state)
Author = to_state.apps.get_model(app_label, "Author")
try:
with transaction.atomic():
Author.objects.create(**valid).delete()
with self.assertRaises(IntegrityError), transaction.atomic():
Author.objects.create(name="Albert", surname="Alberto")
Author.objects.create(**invalid)
finally:
with connection.schema_editor() as editor:
operation.database_backwards(
app_label, editor, from_state, to_state
)
@skipUnlessDBFeature("supports_table_check_constraints")
def test_add_or_constraint(self):