Skip to content

Commit 2d2ab1c

Browse files
committed
Fix tests after refactoring the review admin
1 parent 77cedd9 commit 2d2ab1c

2 files changed

Lines changed: 142 additions & 72 deletions

File tree

backend/reviews/adapters.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ def get_review_context(
265265
return dict(
266266
admin_site.each_context(request),
267267
proposal=proposal,
268-
languages=proposal.languages.all(),
268+
languages=languages,
269269
available_scores=AvailableScoreOption.objects.filter(
270270
review_session_id=review_session.id
271271
).order_by("-numeric_value"),
@@ -579,7 +579,11 @@ def process_recap_post(
579579
change_message=f"[Review Session] Reimbursement {reimbursement.category.name} added.",
580580
)
581581

582-
# Update internal notes
582+
# Update internal notes in a separate pass.
583+
# This is intentionally separate from the main grant loop above because:
584+
# 1. Notes updates are independent of status/reimbursement changes
585+
# 2. A grant may have notes updated without any decision change
586+
# 3. Keeps the audit logging concerns (status changes) separate from notes
583587
if notes_updates:
584588
grants_to_update_notes = review_session.conference.grants.filter(
585589
id__in=notes_updates.keys()
@@ -687,9 +691,9 @@ def get_user_review_create_values(self, review_item_id: int) -> dict[str, Any]:
687691
def get_review_adapter(review_session: ReviewSession) -> ReviewAdapter:
688692
"""Return the appropriate adapter for the given review session type."""
689693
if review_session.is_proposals_review:
690-
return ProposalsReviewAdapter()
694+
return _PROPOSALS_ADAPTER
691695

692696
if review_session.is_grants_review:
693-
return GrantsReviewAdapter()
697+
return _GRANTS_ADAPTER
694698

695699
raise ValueError(f"Unknown review session type: {review_session.session_type}")

backend/reviews/tests/test_admin.py

Lines changed: 134 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -184,13 +184,8 @@ def test_grants_review_scores(rf, scores, avg):
184184
score=all_scores[-1],
185185
)
186186

187-
request = rf.get("/")
188-
request.user = users[5]
189-
190-
admin = ReviewSessionAdmin(ReviewSession, AdminSite())
191-
response = admin._review_grants_recap_view(request, review_session)
192-
context_data = response.context_data
193-
items = context_data["items"]
187+
adapter = get_review_adapter(review_session)
188+
items = adapter.get_recap_items_queryset(review_session).all()
194189
grant_to_check = next(item for item in items if item.id == grant_1.id)
195190

196191
assert grant_to_check.id == grant_1.id
@@ -405,9 +400,7 @@ def test_review_start_view(rf, mocker):
405400
)
406401

407402

408-
def test_save_review_grants_updates_grant_and_creates_reimbursements(rf, mocker):
409-
mock_messages = mocker.patch("reviews.admin.messages")
410-
403+
def test_save_review_grants_updates_grant_and_creates_reimbursements(rf):
411404
user = UserFactory(is_staff=True, is_superuser=True)
412405
conference = ConferenceFactory()
413406

@@ -462,15 +455,8 @@ def test_save_review_grants_updates_grant_and_creates_reimbursements(rf, mocker)
462455
request = rf.post("/", data=post_data)
463456
request.user = user
464457

465-
admin = ReviewSessionAdmin(ReviewSession, AdminSite())
466-
response = admin._review_grants_recap_view(request, review_session)
467-
468-
# Should redirect after successful save
469-
assert response.status_code == 302
470-
assert (
471-
response.url
472-
== f"/admin/reviews/reviewsession/{review_session.id}/review/recap/"
473-
)
458+
adapter = get_review_adapter(review_session)
459+
adapter.process_recap_post(request, review_session)
474460

475461
# Refresh grants from database
476462
grant_1.refresh_from_db()
@@ -519,14 +505,8 @@ def test_save_review_grants_updates_grant_and_creates_reimbursements(rf, mocker)
519505
change_message=f"[Review Session] Reimbursement {accommodation_category.name} added.",
520506
).exists()
521507

522-
mock_messages.success.assert_called_once()
523-
524-
525-
def test_save_review_grants_update_grants_status_to_rejected_removes_reimbursements(
526-
rf, mocker
527-
):
528-
mocker.patch("reviews.admin.messages")
529508

509+
def test_save_review_grants_update_grants_status_to_rejected_removes_reimbursements(rf):
530510
user = UserFactory(is_staff=True, is_superuser=True)
531511
conference = ConferenceFactory()
532512

@@ -583,15 +563,9 @@ def test_save_review_grants_update_grants_status_to_rejected_removes_reimburseme
583563
request = rf.post("/", data=post_data)
584564
request.user = user
585565

586-
admin = ReviewSessionAdmin(ReviewSession, AdminSite())
587-
response = admin._review_grants_recap_view(request, review_session)
566+
adapter = get_review_adapter(review_session)
567+
adapter.process_recap_post(request, review_session)
588568

589-
# Should redirect after successful save
590-
assert response.status_code == 302
591-
assert (
592-
response.url
593-
== f"/admin/reviews/reviewsession/{review_session.id}/review/recap/"
594-
)
595569
grant_1.refresh_from_db()
596570

597571
assert grant_1.pending_status == Grant.Status.rejected
@@ -607,9 +581,7 @@ def test_save_review_grants_update_grants_status_to_rejected_removes_reimburseme
607581
).exists()
608582

609583

610-
def test_save_review_grants_modify_reimbursements(rf, mocker):
611-
mocker.patch("reviews.admin.messages")
612-
584+
def test_save_review_grants_modify_reimbursements(rf):
613585
user = UserFactory(is_staff=True, is_superuser=True)
614586
conference = ConferenceFactory()
615587

@@ -666,15 +638,8 @@ def test_save_review_grants_modify_reimbursements(rf, mocker):
666638
request = rf.post("/", data=post_data)
667639
request.user = user
668640

669-
admin = ReviewSessionAdmin(ReviewSession, AdminSite())
670-
response = admin._review_grants_recap_view(request, review_session)
671-
672-
# Should redirect after successful save
673-
assert response.status_code == 302
674-
assert (
675-
response.url
676-
== f"/admin/reviews/reviewsession/{review_session.id}/review/recap/"
677-
)
641+
adapter = get_review_adapter(review_session)
642+
adapter.process_recap_post(request, review_session)
678643

679644
grant_1.refresh_from_db()
680645

@@ -704,12 +669,9 @@ def test_save_review_grants_modify_reimbursements(rf, mocker):
704669
).exists()
705670

706671

707-
def test_save_review_grants_waiting_list_does_not_create_reimbursments(rf, mocker):
708-
mock_messages = mocker.patch("reviews.admin.messages")
709-
672+
def test_save_review_grants_waiting_list_does_not_create_reimbursements(rf):
710673
user = UserFactory(is_staff=True, is_superuser=True)
711674
conference = ConferenceFactory()
712-
713675
# Create reimbursement categories
714676
travel_category = GrantReimbursementCategoryFactory(
715677
conference=conference,
@@ -756,15 +718,8 @@ def test_save_review_grants_waiting_list_does_not_create_reimbursments(rf, mocke
756718
request = rf.post("/", data=post_data)
757719
request.user = user
758720

759-
admin = ReviewSessionAdmin(ReviewSession, AdminSite())
760-
response = admin._review_grants_recap_view(request, review_session)
761-
762-
# Should redirect after successful save
763-
assert response.status_code == 302
764-
assert (
765-
response.url
766-
== f"/admin/reviews/reviewsession/{review_session.id}/review/recap/"
767-
)
721+
adapter = get_review_adapter(review_session)
722+
adapter.process_recap_post(request, review_session)
768723

769724
# Refresh grants from database
770725
grant_1.refresh_from_db()
@@ -781,16 +736,13 @@ def test_save_review_grants_waiting_list_does_not_create_reimbursments(rf, mocke
781736
# Verify log entries were created
782737
assert (
783738
LogEntry.objects.filter(object_id=grant_1.id).count() == 1
784-
) # 1 pending_status change, 2 reimbursement additions
739+
) # 1 pending_status change
785740
assert (
786741
LogEntry.objects.filter(object_id=grant_2.id).count() == 1
787-
) # 1 pending_status change, 3 reimbursement additions
788-
mock_messages.success.assert_called_once()
789-
742+
) # 1 pending_status change
790743

791-
def test_save_review_grants_two_times_does_not_create_duplicate_log_entries(rf, mocker):
792-
mocker.patch("reviews.admin.messages")
793744

745+
def test_save_review_grants_two_times_does_not_create_duplicate_log_entries(rf):
794746
user = UserFactory(is_staff=True, is_superuser=True)
795747
conference = ConferenceFactory()
796748

@@ -832,9 +784,9 @@ def test_save_review_grants_two_times_does_not_create_duplicate_log_entries(rf,
832784
request = rf.post("/", data=post_data)
833785
request.user = user
834786

835-
admin = ReviewSessionAdmin(ReviewSession, AdminSite())
836-
admin._review_grants_recap_view(request, review_session) # First save
837-
admin._review_grants_recap_view(request, review_session) # Second save
787+
adapter = get_review_adapter(review_session)
788+
adapter.process_recap_post(request, review_session) # First save
789+
adapter.process_recap_post(request, review_session) # Second save
838790

839791
grant_1.refresh_from_db()
840792

@@ -849,3 +801,117 @@ def test_save_review_grants_two_times_does_not_create_duplicate_log_entries(rf,
849801
object_id=grant_1.id,
850802
change_message="[Review Session] Pending status changed from 'None' to 'approved'.",
851803
).exists()
804+
805+
806+
# --- ProposalsReviewAdapter Tests ---
807+
808+
809+
def test_proposals_review_get_recap_context(rf):
810+
user = UserFactory(is_staff=True, is_superuser=True)
811+
conference = ConferenceFactory()
812+
813+
review_session = ReviewSessionFactory(
814+
conference=conference,
815+
session_type=ReviewSession.SessionType.PROPOSALS,
816+
status=ReviewSession.Status.COMPLETED,
817+
)
818+
AvailableScoreOptionFactory(review_session=review_session, numeric_value=0)
819+
AvailableScoreOptionFactory(review_session=review_session, numeric_value=1)
820+
821+
submission = SubmissionFactory(conference=conference)
822+
GrantFactory(conference=conference, user=submission.speaker)
823+
824+
request = rf.get("/")
825+
request.user = user
826+
827+
adapter = get_review_adapter(review_session)
828+
items = adapter.get_recap_items_queryset(review_session).all()
829+
context = adapter.get_recap_context(request, review_session, items, AdminSite())
830+
831+
assert "items" in context
832+
assert "grants" in context
833+
assert "review_session_id" in context
834+
assert "audience_levels" in context
835+
assert "all_statuses" in context
836+
assert context["review_session_id"] == review_session.id
837+
assert str(submission.speaker_id) in context["grants"]
838+
839+
840+
def test_proposals_review_process_recap_post(rf):
841+
user = UserFactory(is_staff=True, is_superuser=True)
842+
conference = ConferenceFactory()
843+
844+
review_session = ReviewSessionFactory(
845+
conference=conference,
846+
session_type=ReviewSession.SessionType.PROPOSALS,
847+
status=ReviewSession.Status.COMPLETED,
848+
)
849+
AvailableScoreOptionFactory(review_session=review_session, numeric_value=0)
850+
AvailableScoreOptionFactory(review_session=review_session, numeric_value=1)
851+
852+
submission_1 = SubmissionFactory(conference=conference)
853+
submission_2 = SubmissionFactory(conference=conference)
854+
855+
post_data = {
856+
f"decision-{submission_1.id}": "accepted",
857+
f"decision-{submission_2.id}": "rejected",
858+
}
859+
860+
request = rf.post("/", data=post_data)
861+
request.user = user
862+
863+
adapter = get_review_adapter(review_session)
864+
adapter.process_recap_post(request, review_session)
865+
866+
submission_1.refresh_from_db()
867+
submission_2.refresh_from_db()
868+
869+
assert submission_1.pending_status == "accepted"
870+
assert submission_2.pending_status == "rejected"
871+
872+
873+
def test_proposals_review_get_review_context(rf):
874+
user = UserFactory(is_staff=True, is_superuser=True)
875+
conference = ConferenceFactory()
876+
877+
review_session = ReviewSessionFactory(
878+
conference=conference,
879+
session_type=ReviewSession.SessionType.PROPOSALS,
880+
status=ReviewSession.Status.REVIEWING,
881+
)
882+
AvailableScoreOptionFactory(review_session=review_session, numeric_value=0)
883+
AvailableScoreOptionFactory(review_session=review_session, numeric_value=1)
884+
885+
tag = SubmissionTagFactory()
886+
submission = SubmissionFactory(conference=conference)
887+
submission.tags.add(tag)
888+
889+
request = rf.get("/")
890+
request.user = user
891+
892+
adapter = get_review_adapter(review_session)
893+
context = adapter.get_review_context(
894+
request, review_session, submission.id, None, AdminSite()
895+
)
896+
897+
assert "proposal" in context
898+
assert "languages" in context
899+
assert "available_scores" in context
900+
assert "speaker" in context
901+
assert "tags_to_filter" in context
902+
assert context["proposal"].id == submission.id
903+
assert context["proposal_id"] == submission.id
904+
assert context["review_session_id"] == review_session.id
905+
906+
907+
def test_get_review_adapter_with_invalid_session_type():
908+
conference = ConferenceFactory()
909+
review_session = ReviewSessionFactory(
910+
conference=conference,
911+
session_type=ReviewSession.SessionType.PROPOSALS,
912+
)
913+
# Manually set an invalid session type to test error handling
914+
review_session.session_type = "invalid_type"
915+
916+
with pytest.raises(ValueError, match="Unknown review session type"):
917+
get_review_adapter(review_session)

0 commit comments

Comments
 (0)