ghost-shopper-api / python /tests /test_assessment_outlet.py
azzaraqi
Deploy FastAPI backend with Supabase pooler support
b84ea83
Raw
History Blame Contribute Delete
6 kB
"""
Unit Tests β€” Assessment & Outlet Pydantic Schemas
Tests:
- AssessmentCreate: required fields, validation (score range, category)
- AssessmentResponse: ORM β†’ Pydantic round-trip
- OutletCreate: required fields, validation
- OutletResponse: ORM β†’ Pydantic round-trip
"""
from datetime import datetime, timezone
import pytest
from pydantic import ValidationError
from models.assessment_model import Assessment, AssessmentCreate, AssessmentResponse
from models.outlet_model import Outlet, OutletCreate, OutletResponse
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# AssessmentCreate
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
class TestAssessmentCreate:
def _valid(self, **overrides):
return {
"category": "A",
"item_no": 1,
"criteria": "Welcoming Kasir",
"score": 4.0,
"raw_value": "4",
**overrides,
}
def test_valid_minimal(self):
a = AssessmentCreate(**self._valid())
assert a.category == "A"
assert a.item_no == 1
assert a.score == 4.0
assert a.notes is None
def test_valid_with_notes(self):
a = AssessmentCreate(**self._valid(notes="Sangat baik", description="Kasir ramah"))
assert a.notes == "Sangat baik"
assert a.description == "Kasir ramah"
def test_category_required(self):
data = self._valid()
del data["category"]
with pytest.raises(ValidationError) as exc_info:
AssessmentCreate(**data)
assert any(e["loc"] == ("category",) for e in exc_info.value.errors())
def test_category_max_length_1(self):
with pytest.raises(ValidationError):
AssessmentCreate(**self._valid(category="AB"))
def test_category_empty_string_invalid(self):
with pytest.raises(ValidationError):
AssessmentCreate(**self._valid(category=""))
def test_score_below_minimum(self):
with pytest.raises(ValidationError):
AssessmentCreate(**self._valid(score=0.5))
def test_score_above_maximum(self):
with pytest.raises(ValidationError):
AssessmentCreate(**self._valid(score=5.5))
def test_score_boundary_min(self):
a = AssessmentCreate(**self._valid(score=1.0))
assert a.score == 1.0
def test_score_boundary_max(self):
a = AssessmentCreate(**self._valid(score=5.0))
assert a.score == 5.0
def test_item_no_below_minimum(self):
with pytest.raises(ValidationError):
AssessmentCreate(**self._valid(item_no=0))
def test_item_no_above_maximum(self):
with pytest.raises(ValidationError):
AssessmentCreate(**self._valid(item_no=11))
def test_criteria_required(self):
data = self._valid()
del data["criteria"]
with pytest.raises(ValidationError):
AssessmentCreate(**data)
def test_criteria_empty_invalid(self):
with pytest.raises(ValidationError):
AssessmentCreate(**self._valid(criteria=""))
def test_criteria_max_length(self):
with pytest.raises(ValidationError):
AssessmentCreate(**self._valid(criteria="x" * 501))
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# AssessmentResponse β€” ORM round-trip
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
class TestAssessmentResponse:
@pytest.mark.asyncio
async def test_from_orm(self, sample_assessment: Assessment):
resp = AssessmentResponse.model_validate(sample_assessment)
assert str(resp.id) == str(sample_assessment.id)
assert resp.category == "A"
assert resp.item_no == 1
assert resp.score == 4.0
assert resp.raw_value == "4"
assert resp.notes == "Baik"
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# OutletCreate
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
class TestOutletCreate:
def test_valid_minimal(self):
o = OutletCreate(name="Outlet A")
assert o.name == "Outlet A"
assert o.location is None
assert o.description is None
def test_valid_full(self):
o = OutletCreate(
name="Outlet B",
location="Jl. Merdeka",
description="Outlet cabang",
)
assert o.location == "Jl. Merdeka"
def test_name_required(self):
with pytest.raises(ValidationError) as exc_info:
OutletCreate()
assert any(e["loc"] == ("name",) for e in exc_info.value.errors())
def test_name_empty_invalid(self):
with pytest.raises(ValidationError):
OutletCreate(name="")
def test_name_max_length(self):
with pytest.raises(ValidationError):
OutletCreate(name="x" * 256)
def test_location_max_length(self):
with pytest.raises(ValidationError):
OutletCreate(name="OK", location="L" * 256)
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# OutletResponse β€” ORM round-trip
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
class TestOutletResponse:
@pytest.mark.asyncio
async def test_from_orm(self, sample_outlet: Outlet):
resp = OutletResponse.model_validate(sample_outlet)
assert str(resp.id) == str(sample_outlet.id)
assert resp.name == "Outlet Gedung A"
assert resp.location == "Jl. Sudirman No. 1"