Skip to content

build: configure pytest-django and add dev dependencies#40

Open
SherryShijiarui wants to merge 32 commits intodevfrom
feat/web-view-pytest
Open

build: configure pytest-django and add dev dependencies#40
SherryShijiarui wants to merge 32 commits intodevfrom
feat/web-view-pytest

Conversation

@SherryShijiarui
Copy link

No description provided.

Copy link
Contributor

@A-lexisL A-lexisL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. tests should be comprehensive, so more tests are needed. Please arrange them into classes based on API and Authenticated status.
    e.g.
class TestCourseListAuthenticated
class TestCourseListNotAuthenticated
class TestCourseDetailAuthenticated
...

assert "results" in response.data

def test_filter_courses_by_department(self, base_client, course_factory):
"""Verify filtering courses by department code."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now, only filter courses by department code for nonauthenticated user exists. But tests for all the following params are needed for authenticated/nonauthenticated users.

            - department (string): Filter by department code (case-insensitive)
            - code (string): Filter by course code (partial match)
            - min_quality (integer): Filter by minimum quality score (authenticated only)
            - min_difficulty (integer): Filter by minimum difficulty score (authenticated only)
            - sort_by (string): Sort field ("course_code", "review_count"),("quality_score", "difficulty_score")(authenticated only)
            - sort_order (string): "asc" or "desc" (default: "asc")
            - page (integer): Page number for pagination

(docstring of CoursesListAPI in apps/web/views.py)

Same for other apis

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added coverage for all documented query params across auth states in apps/web/tests/test_course.py (filters, min_quality/min_difficulty, review_count/quality_score/difficulty_score sorts, sort_order). Evidence: .venv/bin/pytest apps/web/tests -> 52 passed.

Copy link
Contributor

@zzjc1234 zzjc1234 Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added concrete diff for parameter coverage:

+@pytest.mark.django_db
+class TestCourseAPIUnauthenticated:
+    def test_filter_courses_by_department(...):
+    def test_filter_courses_by_code(...):
+    def test_sort_courses_by_review_count_anonymous(...):
+    def test_sort_courses_by_score_anonymous(...):
+    def test_filter_courses_by_score_anonymous(...):
+    def test_filter_courses_by_difficulty_anonymous(...):
+
+@pytest.mark.django_db
+class TestCourseAPIAuthenticated:
+    def test_filter_courses_by_department_authenticated(...):
+    def test_filter_courses_by_code_authenticated(...):
+    def test_sort_by_review_count(...):
+    def test_sort_courses_by_score(...):
+    def test_sort_courses_by_difficulty_score(...):
+    def test_filter_courses_by_quality(...):
+    def test_filter_courses_by_difficulty(...):
+    def test_sort_order_asc_and_desc(...):

These tests are now split by auth state and cover the documented query parameters.

url = reverse("user_review_api", kwargs={"review_id": review.id})
response = auth_client.delete(url)
assert response.status_code == 204
assert Review.objects.filter(id=review.id).count() == 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing tests for base_client

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added unauthenticated coverage in TestReviewAPIUnauthenticated for list, personal list, delete, and detail in apps/web/tests/test_review.py. Evidence: .venv/bin/pytest apps/web/tests -> 52 passed.

Copy link
Contributor

@zzjc1234 zzjc1234 Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added concrete diff for missing base_client tests:

+class TestReviewAPIUnauthenticated:
+    def test_get_course_reviews_anonymous(...):
+    def test_get_personal_reviews_anonymous(...):
+    def test_delete_review_anonymous_forbidden(...):
+    def test_review_detail_anonymous_forbidden(...):

This adds unauthenticated coverage for the review endpoints.


assert response.status_code == 200
# Verify the title matches the fixture-created course
assert response.data["course_title"] == course.course_title
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

base_client and auth_client will receive different response data:

  • auth_client: full details
  • base_client: "quality_score" ,"difficulty_score","difficulty_vote","quality_vote","quality_vote_count","difficulty_vote_count" fields are removed(the docstrings for CoursesDetailAPI is not complete)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added course detail field assertions for auth vs unauth in apps/web/tests/test_course.py (scores and votes hidden for base_client, present for auth_client). Evidence: .venv/bin/pytest apps/web/tests -> 52 passed.

Copy link
Contributor

@zzjc1234 zzjc1234 Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added concrete diff for auth vs unauth response fields:

+def test_course_detail_fields_unauthenticated(self, base_client, course):
+    hidden_fields = [
+        "quality_score", "difficulty_score",
+        "difficulty_vote", "quality_vote",
+        "quality_vote_count", "difficulty_vote_count",
+    ]
+    for field in hidden_fields:
+        assert field not in response.data
+
+def test_course_detail_fields_authenticated(self, auth_client, course):
+    required_fields = [
+        "quality_score", "difficulty_score",
+        "difficulty_vote", "quality_vote",
+        "quality_vote_count", "difficulty_vote_count",
+    ]
+    for field in required_fields:
+        assert field in response.data

This directly validates the serializer behavior difference.

@A-lexisL A-lexisL moved this from Todo to In Progress in Course Review Jan 19, 2026
Copy link
Contributor

@A-lexisL A-lexisL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests are comprehensive. But some test cases still need polishing.

term_data
and term_data.group("year") == "16"
and term_data.group("term") == "w"
and term_data.group("term") == "F"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed the term regex test to check 16W, 16w, and 16F separately in apps/web/tests/lib_tests/test_terms.py. Evidence: .venv/bin/pytest apps/web/tests -> 52 passed.

Copy link
Contributor

@zzjc1234 zzjc1234 Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added concrete diff for the term assertion fix:

 term_data = terms.term_regex.match("16w")
 self.assertTrue(
     term_data
     and term_data.group("year") == "16"
-    and term_data.group("term") == "w"
-    and term_data.group("term") == "F"
+    and term_data.group("term") == "w"
 )
+term_data = terms.term_regex.match("16F")
+self.assertTrue(
+    term_data
+    and term_data.group("year") == "16"
+    and term_data.group("term") == "F"
+)

This removes the contradictory condition and tests both cases explicitly.

@pytest.fixture
def department_mixed_courses(db):
"""Returns a mixed set of courses for filtering/sorting tests."""
# Note: Using 'title' to match your original course.py
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since title is changed to course_title, this comment is outdated

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the fixture comment to reference course_title in apps/web/tests/conftest.py. Evidence: .venv/bin/pytest apps/web/tests -> 52 passed.

Copy link
Contributor

@zzjc1234 zzjc1234 Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added concrete diff for the outdated fixture comment:

-# Note: Using title to match your original course.py
+# Note: Using course_title to match current course model field

resp_p1 = auth_client.get(url, {"page": 1})

assert resp_p1.status_code == 200
assert len(resp_p1.data["results"]) == 10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

10 is hardcoded.
Its not very necessary to test pagination, as it has nothing to do with data correctness/authentication access

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the pagination hardcode expectation; course list tests now focus on filter and sort behavior in apps/web/tests/test_course.py. Evidence: .venv/bin/pytest apps/web/tests -> 52 passed.

Copy link
Contributor

@zzjc1234 zzjc1234 Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added concrete diff for removing pagination hardcode:

-def test_pagination_with_default_settings(self, auth_client, course_factory):
-    for i in range(11):
-        course_factory(course_code=f"CODE_{i:02d}")
-    resp_p1 = auth_client.get(reverse("courses_api"), {"page": 1})
-    assert len(resp_p1.data["results"]) == 10

Course tests now focus on correctness of filtering and sorting behavior.

status.HTTP_403_FORBIDDEN,
]

def test_department_api_empty(self, base_client, db):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be removed: department_api is already tested in test_course.py

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed department_api checks from apps/web/tests/test_review.py; department coverage is in apps/web/tests/test_course.py. Evidence: .venv/bin/pytest apps/web/tests -> 52 passed.

Copy link
Contributor

@zzjc1234 zzjc1234 Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added concrete diff for removing department API assertions from review tests:

-def test_department_api_empty(self, base_client, db):
-    ...
-
-def test_department_api_sorting(self, base_client, db):
-    ...

Department API coverage is kept in apps/web/tests/test_course.py.

"""5. Verify user can list their own reviews."""
response = auth_client.get(personal_reviews_list_url)
assert response.status_code == status.HTTP_200_OK
assert any(r["id"] == review.id for r in response.data)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should check if the api returns and only returns personal reviews. e.g. review + other_review and the api only returns review

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated test_list_personal_reviews to include other_review and assert only the current user review is returned in apps/web/tests/test_review.py. Evidence: .venv/bin/pytest apps/web/tests -> 52 passed.

Copy link
Contributor

@zzjc1234 zzjc1234 Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added concrete diff below:

-def test_list_personal_reviews(self, auth_client, personal_reviews_list_url, review):
+def test_list_personal_reviews(self, auth_client, personal_reviews_list_url, review, other_review):
     response = auth_client.get(personal_reviews_list_url)
     assert response.status_code == status.HTTP_200_OK
     assert any(r["id"] == review.id for r in response.data)
+    assert all(r["id"] != other_review.id for r in response.data)

This now verifies the API returns only personal reviews.


ReviewFactory(course=course, professor="UniqueProf", comments="c" * min_len)
response = auth_client.get(course_reviews_url, {"q": "UniqueProf"})
assert any(r["professor"] == "UniqueProf" for r in response.data)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be all
Although search review could also match partial UniqueProf in professor/comment, in this case only reviews that ["professor"] == "UniqueProf" would be returned

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Search test now uses all(...) and adds a different professor so only UniqueProf results are returned. Evidence: .venv/bin/pytest apps/web/tests -> 52 passed.

Copy link
Contributor

@zzjc1234 zzjc1234 Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added concrete diff for strict search assertion:

 ReviewFactory(course=course, professor="UniqueProf", comments="c" * min_len)
+ReviewFactory(course=course, professor="OtherProf", comments="c" * min_len)
 response = auth_client.get(course_reviews_url, {"q": "UniqueProf"})
-assert any(r["professor"] == "UniqueProf" for r in response.data)
+assert all(r["professor"] == "UniqueProf" for r in response.data)

self, auth_client, personal_review_detail_url, review, valid_review_data
):
"""9. Verify successful update of user's own review."""
valid_review_data["comments"] = "Updated content that is long enough."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

min length is according to config. So this comment may not be long enough. Use 'b' * min_len

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated comment length usage to min_len so validation aligns with config in apps/web/tests/test_review.py. Evidence: .venv/bin/pytest apps/web/tests -> 52 passed.

Copy link
Contributor

@zzjc1234 zzjc1234 Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added concrete diff for config-safe update payload length:

-valid_review_data["comments"] = "Updated content that is long enough."
+valid_review_data["comments"] = "b" * len(valid_review_data["comments"])

This keeps the updated value aligned with the configured minimum length.

]
assert Review.objects.filter(id=review.id).exists()

def test_department_api_sorting(self, base_client, db):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could be removed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the redundant non-existent delete test in apps/web/tests/test_review.py. Evidence: .venv/bin/pytest apps/web/tests -> 52 passed.

Copy link
Contributor

@zzjc1234 zzjc1234 Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added concrete diff for removing the redundant test:

-def test_delete_non_existent_id(self, auth_client):
-    url = reverse("user_review_api", kwargs={"review_id": 99999})
-    response = auth_client.delete(url)
-    assert response.status_code == status.HTTP_404_NOT_FOUND

):
"""14. Verify PUT fails if required fields (professor) are missing."""
response = auth_client.put(
personal_review_detail_url, {"comments": "Valid length..."}, format="json"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still min length problem.
Dont know whether 400 is caused by min length or missing fields

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing-field validation test now uses min_len comments so 400 is due to missing required fields, not length. Evidence: .venv/bin/pytest apps/web/tests -> 52 passed.

Copy link
Contributor

@zzjc1234 zzjc1234 Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added concrete diff to isolate missing-field validation from length validation:

-def test_update_validation_missing_field(self, auth_client, personal_review_detail_url):
+def test_update_validation_missing_field(self, auth_client, personal_review_detail_url, min_len):
     response = auth_client.put(
         personal_review_detail_url,
-        {"comments": "Valid length..."},
+        {"comments": "b" * min_len},
         format="json",
     )\n```\nNow the 400 is specifically from missing required fields.

):
"""19. Verify vote statistics are included in the response."""
response = auth_client.get(personal_review_detail_url)
assert "kudos_count" in response.data
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also test dislike_count

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added dislike_count assertion in test_review_response_contains_votes in apps/web/tests/test_review.py. Evidence: .venv/bin/pytest apps/web/tests -> 52 passed.

Copy link
Contributor

@zzjc1234 zzjc1234 Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added concrete diff for vote stats assertion:

 response = auth_client.get(personal_review_detail_url)
 assert "kudos_count" in response.data
+assert "dislike_count" in response.data

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

5 participants