From 50c26da76160c50026418859e8133d301621bbee Mon Sep 17 00:00:00 2001 From: Alexandre Moore Date: Tue, 21 Jul 2026 09:55:13 +0200 Subject: [PATCH] Fix /entries/ 500 on dlc and pending entries EntryResponse and EntryFilter still validated entry_type against manual|auto|wishlist and status against success|failed, so listing history on a database containing dlc entries (created by the DLC priority scan) or pending rows crashed with a pydantic ValidationError. Widen both patterns and cover them with regression tests. --- backend/src/api/schemas/entry.py | 16 ++++++------ backend/tests/unit/test_schemas_entry.py | 31 ++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/backend/src/api/schemas/entry.py b/backend/src/api/schemas/entry.py index 2e9871b..286df53 100644 --- a/backend/src/api/schemas/entry.py +++ b/backend/src/api/schemas/entry.py @@ -29,14 +29,14 @@ class EntryBase(BaseModel): ) entry_type: str = Field( ..., - description="Type of entry (manual, auto, wishlist)", - pattern="^(manual|auto|wishlist)$", + description="Type of entry (manual, auto, wishlist, dlc)", + pattern="^(manual|auto|wishlist|dlc)$", examples=["manual"], ) status: str = Field( ..., - description="Entry status (success, failed)", - pattern="^(success|failed)$", + description="Entry status (success, failed, pending)", + pattern="^(success|failed|pending)$", examples=["success"], ) error_message: str | None = Field( @@ -138,14 +138,14 @@ class EntryFilter(BaseModel): entry_type: str | None = Field( default=None, - description="Filter by entry type (manual, auto, wishlist)", - pattern="^(manual|auto|wishlist)$", + description="Filter by entry type (manual, auto, wishlist, dlc)", + pattern="^(manual|auto|wishlist|dlc)$", examples=["auto"], ) status: str | None = Field( default=None, - description="Filter by status (success, failed)", - pattern="^(success|failed)$", + description="Filter by status (success, failed, pending)", + pattern="^(success|failed|pending)$", examples=["success"], ) giveaway_id: int | None = Field( diff --git a/backend/tests/unit/test_schemas_entry.py b/backend/tests/unit/test_schemas_entry.py index 2e3af91..4bdb4e7 100644 --- a/backend/tests/unit/test_schemas_entry.py +++ b/backend/tests/unit/test_schemas_entry.py @@ -74,10 +74,11 @@ def test_entry_base_validates_status(): # Valid statuses EntryBase(giveaway_id=1, points_spent=50, entry_type="manual", status="success") EntryBase(giveaway_id=1, points_spent=50, entry_type="manual", status="failed") + EntryBase(giveaway_id=1, points_spent=50, entry_type="manual", status="pending") # Invalid status with pytest.raises(ValidationError): - EntryBase(giveaway_id=1, points_spent=50, entry_type="manual", status="pending") + EntryBase(giveaway_id=1, points_spent=50, entry_type="manual", status="unknown") def test_entry_response(): @@ -151,10 +152,11 @@ def test_entry_filter_validates_status(): # Valid EntryFilter(status="success") EntryFilter(status="failed") + EntryFilter(status="pending") # Invalid with pytest.raises(ValidationError): - EntryFilter(status="pending") + EntryFilter(status="unknown") def test_entry_stats(): @@ -251,3 +253,28 @@ def test_entry_history(): def test_entry_response_orm_mode(): """Test EntryResponse has ORM mode enabled.""" assert EntryResponse.model_config.get("from_attributes") is True + + +def test_entry_base_accepts_dlc_type_and_pending_status(): + """Regression: dlc entries (and pending status) must serialize. + + The /entries/ endpoint 500'd on real databases because EntryResponse + still rejected entry_type='dlc' rows created by the DLC priority scan. + """ + entry = EntryBase( + giveaway_id=123, + points_spent=25, + entry_type="dlc", + status="pending", + ) + + assert entry.entry_type == "dlc" + assert entry.status == "pending" + + +def test_entry_filter_accepts_dlc_and_pending(): + """The filter schema accepts the same values the list endpoint does.""" + filters = EntryFilter(entry_type="dlc", status="pending") + + assert filters.entry_type == "dlc" + assert filters.status == "pending"