test(api): request-model validation for the S1b 500-fixes (R6 S1b)
Pure-logic tests that the three ROADMAP-named 500-producers (channel priority, tag_id, setup password) and admin quota now reject bad input at the model boundary, and that the PATCH models separate absent from explicit-null via model_fields_set. Mutation-checked: reverting a field type turns them red.
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
"""R6 S1b: the request models turn payloads that used to 500 into clean validation errors,
|
||||
and PATCH models distinguish an absent field from an explicit null. Pure-logic (no DB) — it
|
||||
exercises the Pydantic models the route modules define, not the endpoints."""
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from app.routes.channels import AttachTagIn, ChannelUpdateIn
|
||||
from app.routes.downloads import AdminSetQuotaIn
|
||||
from app.routes.setup import SetupAdminIn
|
||||
from app.routes.tags import TagUpdateIn
|
||||
|
||||
|
||||
# --- The three ROADMAP-named 500-producers are now 422s (ValidationError at the boundary). ---
|
||||
|
||||
def test_channel_priority_rejects_non_numeric():
|
||||
# Was `int(payload["priority"])` → 500 on garbage. Now a validation error.
|
||||
with pytest.raises(ValidationError):
|
||||
ChannelUpdateIn(priority="not-a-number")
|
||||
|
||||
|
||||
def test_channel_priority_accepts_int_and_numeric_string():
|
||||
assert ChannelUpdateIn(priority=5).priority == 5
|
||||
assert ChannelUpdateIn(priority="5").priority == 5 # lax numeric-string coercion, as before
|
||||
|
||||
|
||||
def test_channel_update_fields_all_optional():
|
||||
m = ChannelUpdateIn()
|
||||
assert m.priority is None and m.hidden is None and m.deep_requested is None
|
||||
|
||||
|
||||
def test_attach_tag_rejects_non_int_id():
|
||||
# Was a str tag_id into db.get(Tag, ...) — a DB cast 500. Now rejected up front.
|
||||
with pytest.raises(ValidationError):
|
||||
AttachTagIn(tag_id="abc")
|
||||
|
||||
|
||||
def test_setup_admin_password_must_be_a_string():
|
||||
# Was a non-string password reaching len()/argon2 → 500. `str` rejects it.
|
||||
with pytest.raises(ValidationError):
|
||||
SetupAdminIn(email="a@b.c", password=12345)
|
||||
|
||||
|
||||
def test_admin_quota_rejects_non_numeric_limit():
|
||||
# Was `int(payload["max_bytes"])` → 500. Now a validation error.
|
||||
with pytest.raises(ValidationError):
|
||||
AdminSetQuotaIn(max_bytes="lots")
|
||||
|
||||
|
||||
# --- PATCH models: model_fields_set separates "absent" from "explicit null". ---
|
||||
|
||||
def test_tag_update_absent_vs_null_color():
|
||||
absent = TagUpdateIn()
|
||||
assert "color" not in absent.model_fields_set # untouched on PATCH
|
||||
|
||||
cleared = TagUpdateIn(color=None)
|
||||
assert "color" in cleared.model_fields_set # an explicit null CLEARS it
|
||||
assert cleared.color is None
|
||||
Reference in New Issue
Block a user