Skip to content

Commit ee61ba1

Browse files
committed
Add some missing test coverage
`TestLimitedWorker` is missing unit test coverage for a couple of branches in `LimitedWorker`. The reason why coverage isn't failing on `main` is that `TestUserPurger` (the unit tests for `UserPurger`) calls through to the real `LimitedWorker` code (doesn't mock it) and those `UserPurger` unit tests were adding coverage to `LimitedWorker`. In a future PR (#9049) I want to make some changes to `TestUserPurger` that would cause them to no longer cover certain branches of `LimitedWorker` and would cause a coverage failure. It isn't `TestUserPurger`'s job to cover every line and branch of `LimitedWorker`, that's `TestLimitedWorker`'s job, so I'm adding the missing coverage to `TestLimitedWorker` now to prevent those coverage failures in future. This illustrates some of the risks of integrated tests: * You make changes to the tests for one unit and that causes coverage failures in *other* units, creating extra work to fix them. If we used integrated tests frequently this could happen often. * You can have missing unit tests and not know it because the lines and branches are accidentally covered by the tests for *other* units. * You might have a unit test that you think is covering a branch but actually isn't going down that branch. Even though the test is wrong it could still be passing. Missing coverage could save you but won't if tests for *other* units are covering the branch that your broken test is supposed to be covering. In this case I wouldn't want `TestUserPurger` to mock `LimitedWorker`: `UserPurger` contains lots of complex SQL queries that it executes through `LimitedWorker` rather than directly. We want these queries to be executed against the real DB so we can test that various objects are deleted or not-deleted correctly. I think the ideal solution might be for `TestUserPurger` to mock `LimitedWorker`, intercept the SQLAlchemy queries that `UserPurger` passes to the mock `LimitedWorker`, and for the _tests_ to then execute those queries against the DB and assert that they return the right results. But I'm not going to bite off that relatively major change right now.
1 parent b3e3cfd commit ee61ba1

File tree

1 file changed

+53
-0
lines changed

1 file changed

+53
-0
lines changed

tests/unit/h/services/user_delete_test.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,35 @@ def user(self, factories, db_session):
468468

469469

470470
class TestLimitedWorker:
471+
def test_update(self, caplog, db_session, factories):
472+
annotations = factories.Annotation.create_batch(size=2, text="ORIGINAL")
473+
worker = LimitedWorker(db_session, limit=3)
474+
475+
updated_annotation_ids = worker.update(
476+
Annotation, select(Annotation.id), {"text": "UPDATED"}
477+
)
478+
479+
assert sorted(updated_annotation_ids) == sorted(
480+
[annotation.id for annotation in annotations]
481+
)
482+
assert worker.limit == 1
483+
for annotation in annotations:
484+
assert annotation.text == "UPDATED"
485+
assert caplog.record_tuples == [
486+
("h.services.user_delete", logging.INFO, "Updated 2 rows from annotation")
487+
]
488+
489+
def test_update_when_no_matching_rows(self, caplog, db_session):
490+
worker = LimitedWorker(db_session, limit=3)
491+
492+
updated_annotation_ids = worker.update(
493+
Annotation, select(Annotation.id), {"text": "UPDATED"}
494+
)
495+
496+
assert updated_annotation_ids == []
497+
assert worker.limit == 3
498+
assert caplog.record_tuples == []
499+
471500
def test_update_when_limit_exceeded(self, db_session, factories):
472501
annotation = factories.Annotation()
473502
original_text = annotation.text
@@ -525,6 +554,30 @@ def test_update_when_limit_reached(self, caplog, db_session, factories):
525554
("h.services.user_delete", logging.INFO, "Updated 1 rows from annotation")
526555
]
527556

557+
def test_delete(self, caplog, db_session, factories):
558+
annotations = factories.Annotation.create_batch(size=2)
559+
worker = LimitedWorker(db_session, limit=3)
560+
561+
deleted_annotation_ids = worker.delete(Annotation, select(Annotation.id))
562+
563+
assert worker.limit == 1
564+
assert sorted(deleted_annotation_ids) == sorted(
565+
[annotation.id for annotation in annotations]
566+
)
567+
assert caplog.record_tuples == [
568+
("h.services.user_delete", logging.INFO, "Deleted 2 rows from annotation")
569+
]
570+
assert db_session.scalars(select(Annotation)).all() == []
571+
572+
def test_delete_when_no_matching_rows(self, caplog, db_session):
573+
worker = LimitedWorker(db_session, limit=3)
574+
575+
deleted_annotation_ids = worker.delete(Annotation, select(Annotation.id))
576+
577+
assert worker.limit == 3
578+
assert deleted_annotation_ids == []
579+
assert caplog.record_tuples == []
580+
528581
def test_delete_when_limit_exceeded(self, db_session, factories):
529582
annotation = factories.Annotation()
530583
worker = LimitedWorker(db_session, limit=0)

0 commit comments

Comments
 (0)