Skip to content

Commit

Permalink
Refs #34553 -- Split constraint escaping test in subtests.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
charettes authored and felixxm committed May 10, 2023
1 parent 6e32d1f commit e0f8104
Showing 1 changed file with 38 additions and 41 deletions.
79 changes: 38 additions & 41 deletions tests/migrations/test_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -3652,47 +3652,44 @@ def test_add_constraint_percent_escaping(self):
),
]
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")
# 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")
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")
with self.assertRaises(IntegrityError), transaction.atomic():
Author.objects.create(name="Albert", surname="Alberto")
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.
(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)
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(**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):
Expand Down

0 comments on commit e0f8104

Please sign in to comment.