-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add failing test for saving with a new sort_order #36
Changes from all commits
8ab614e
7515b69
e61ed80
5f7ccab
0a42578
c6cea48
9f864ba
6e69fc3
497679a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,4 @@ | |
*.pot | ||
*.pyc | ||
local_settings.py | ||
.hypothesis/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
from django.test import TestCase | ||
from hypothesis import example, given | ||
from hypothesis.extra.django import TestCase | ||
from hypothesis.strategies import integers, lists | ||
|
||
from .models import SubTask, Task | ||
|
||
|
@@ -50,12 +52,14 @@ def test_insert_on_create(self): | |
old_3 = Task.objects.create(sort_order=3) | ||
|
||
# Insert between old_1 and old_2 | ||
with self.assertNumQueries(4): | ||
with self.assertNumQueries(6): | ||
# Queries: | ||
# Savepoint | ||
# Savepoint | ||
# Bump old_2 to position 3 | ||
# Release savepoint | ||
# Release savepoint | ||
# Save new in position 2 | ||
# Commit | ||
new = Task.objects.create(sort_order=old_2.sort_order) | ||
|
||
tasks = Task.objects.all() | ||
|
@@ -114,14 +118,16 @@ def test_decrease_order(self): | |
item5 = Task.objects.create(sort_order=5) | ||
|
||
# Move item4 to position 2 | ||
with self.assertNumQueries(6): | ||
with self.assertNumQueries(8): | ||
# Queries: | ||
# Savepoint | ||
# Find end of list | ||
# Move item4 to end of list | ||
# Savepoint | ||
# Bump item2 and item3 on by one | ||
# Release savepoint | ||
# Release savepoint | ||
# Save item4 to new desired position | ||
# Commit | ||
item4.sort_order = item2.sort_order | ||
item4.save() | ||
|
||
|
@@ -207,3 +213,16 @@ def test_changing_parent(self): | |
subtask_2.task = task | ||
subtask_2.save() | ||
self.assertSequenceEqual(task.subtask_set.all(), [subtask, subtask_2]) | ||
|
||
@given(lists(integers(min_value=1), min_size=1, unique=True)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does this do? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Populates the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than installing Hypothesis, wouldn't it be better just to do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (With There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is much more thorough. I don't trust us to catch all the edge cases without hypothesis. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll also note that it found an overflow in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Found the bugs how? It looks like it improved on the hardcoded list in the test that was there already, but wouldn't changing that to And yes - the point being that there probably aren't many more edge cases or failures in the exact list of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That sounds good, although I'm a bit surprised/alarmed that Is there a test for this? What else did it find? If I'm missing information about what you've actually done here, please post it either in the issue or in the code itself. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
@example([2, 3, 1]) | ||
@example([2, 3, 4]) | ||
def test_save_subtask_no_errors(self, sort_orders): | ||
"""Ensure Orderable.save does not raise IntegrityError.""" | ||
task = Task.objects.create() | ||
|
||
for order in sort_orders: | ||
subtask = SubTask.objects.create(task=task, sort_order=order) | ||
|
||
subtask.sort_order = 2 | ||
subtask.save() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this test fail or not? Slightly confused by the issue title. Travis seems to think it's all good. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great, ta. Could you update the description? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added a docstring. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean the issue description, it's misleading (but good idea, thanks!) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
universal = 1 | ||
[flake8] | ||
application-import-names = orderable | ||
ignore = D100,D101,D102,D203,D204 | ||
ignore = D100,D101,D102,D104,D105,D203,D204 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good old There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm happy to remove it, but that might as well be a different PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Though actually, there's still a lot of errors we do catch: http://pep257.readthedocs.org/en/latest/error_codes.html There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, no rush. (We'll want to remove it in our other projects as well, if everyone's on board with the idea.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's mostly missing docstrings we ignore, along with whether there are blank lines before/after class docstrings. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking at the list of errors, I think there are only one or two that are particularly significant. A lot of them are things we all do by habit, although they're still useful for newcomers in the future so that's not a good reason to remove it. (There's also a couple of stupid ones like "First line should be in imperative mood" :P) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We do do most of these by habit, but that's the point of linting - enforcing good habits. |
||
import-order-style = google | ||
max-complexity = 10 | ||
max-line-length = 90 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is ugly, but it works...
Unfortunately, Django doesn't have support for
DEFERRABLE
unique constraints. Ideally, we'd be able to set the constraint asUNIQUE DEFERRABLE INITIALLY IMMEDIATE
and then run theupdate
withDEFERRED
set.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't seem right to me -- why is there an integrity error in the first place? The "shift" should have cleared the way, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Please bear in mind that I don't really understand the failing case here.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Say we have a
qs
wheresort_order
looks like this:1, 2, 3, 4
.We add a new item with
sort_order == 2
.Orderable._save
createsto_shift
which looks like:2, 3, 4
.The old code then tried to call
to_shift.update(sort_order = F('sort_order') + 1)
.This works fine without any
unique
constraints, but ifsort_order
isunique_together
with another field (as inSubTask
) theupdate
will raiseIntegrityError
when it increments2
to3
. The internal state ofto_shift
is now3, 3, 4
, which is invalid.If Django (and the db) supported
DEFERRED
, we could defer the unique check until theupdate
had completed (3, 4, 5
) and everything would work as in the case withoutunique
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds like a manifestation of #7
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It definitely looks related to #26 / #33.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anyway, I agree this isn't really the "right" fix, but I don't think I can implement the "right" fix without
DEFERRED
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about defining the new sort order by:
Max(sort_order) + sort_order + 1
? Would that work?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice idea. I tried a few variants of it - the first just led to
IntegrityError
for a slightly more complex example. The second causedsort_order
to increase exponentially - I getdjango.db.utils.DataError
eventually.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cool thanks :)