- Add pytest config in backend/pyproject.toml with asyncio mode and coverage - Add backend/requirements-test.txt with pytest, pytest-cov, pytest-asyncio, aiosqlite - Write 86 backend unit tests covering auth (JWT tokens), schemas (Pydantic models), config (Settings env overrides), log_parser (log parsing, IP resolution, geo data), models (SQLAlchemy table definitions + column defaults), and theme export - Add Vitest config for Angular frontend (vitest.config.ts, src/test-setup.ts) - Add .gitlab-ci.yml with stages: backend-test, frontend-test, backend-lint, docker-build, and quality-gate - CI enforces 10% minimum coverage threshold for backend
178 lines
4.9 KiB
Python
178 lines
4.9 KiB
Python
"""Tests for Pydantic schemas — validation, defaults, and computed fields."""
|
|
|
|
from datetime import date
|
|
import pytest
|
|
|
|
from app.schemas import (
|
|
EventCreate,
|
|
EventUpdate,
|
|
EventResponse,
|
|
StationConfigUpdate,
|
|
ShowScheduleResponse,
|
|
ShowCreate,
|
|
ShowScheduleCreate,
|
|
HistoryEntryCreate,
|
|
TeamMemberCreate,
|
|
UnderwriterCreate,
|
|
DAY_NAMES,
|
|
)
|
|
|
|
|
|
class TestEventSchemas:
|
|
"""Tests for Event Pydantic schemas."""
|
|
|
|
def test_event_create_validates_required_fields(self):
|
|
event = EventCreate(
|
|
date=date(2026, 8, 15),
|
|
title="Summer Concert",
|
|
description="Live music",
|
|
location="Main Stage",
|
|
icon="🎵",
|
|
)
|
|
assert event.title == "Summer Concert"
|
|
assert event.rsvp_url is None
|
|
|
|
def test_event_create_with_rsvp(self):
|
|
event = EventCreate(
|
|
date=date(2026, 9, 1),
|
|
title="Festival",
|
|
description="Big event",
|
|
location="Park",
|
|
icon="🎉",
|
|
rsvp_url="https://example.com/rsvp",
|
|
)
|
|
assert event.rsvp_url == "https://example.com/rsvp"
|
|
|
|
def test_event_update_all_optional(self):
|
|
update = EventUpdate()
|
|
assert update.title is None
|
|
assert update.active is None
|
|
|
|
def test_event_update_partial(self):
|
|
update = EventUpdate(active=False, title="Updated Title")
|
|
assert update.active is False
|
|
assert update.title == "Updated Title"
|
|
assert update.date is None
|
|
|
|
|
|
class TestStationConfigUpdate:
|
|
"""Tests for StationConfigUpdate schema."""
|
|
|
|
def test_all_fields_optional(self):
|
|
update = StationConfigUpdate()
|
|
assert update.callsign is None
|
|
assert update.color_primary is None
|
|
|
|
def test_partial_update(self):
|
|
update = StationConfigUpdate(
|
|
callsign="KXYZ",
|
|
color_primary="#ff0000",
|
|
)
|
|
assert update.callsign == "KXYZ"
|
|
assert update.color_primary == "#ff0000"
|
|
assert update.name_primary is None
|
|
|
|
|
|
class TestShowScheduleResponse:
|
|
"""Tests for ShowScheduleResponse computed field."""
|
|
|
|
def test_day_label_mapping(self):
|
|
for day_num, expected_label in DAY_NAMES.items():
|
|
schedule = ShowScheduleResponse(
|
|
id=1,
|
|
show_id=1,
|
|
day_of_week=day_num,
|
|
time="10:00",
|
|
)
|
|
assert schedule.day_label == expected_label
|
|
|
|
def test_unknown_day_returns_unknown(self):
|
|
schedule = ShowScheduleResponse(
|
|
id=1,
|
|
show_id=1,
|
|
day_of_week=99,
|
|
time="10:00",
|
|
)
|
|
assert schedule.day_label == "Unknown"
|
|
|
|
|
|
class TestShowCreate:
|
|
"""Tests for ShowCreate schema."""
|
|
|
|
def test_show_create_with_schedules(self):
|
|
show = ShowCreate(
|
|
title="Morning Show",
|
|
description="Morning music",
|
|
host="DJ Alex",
|
|
genre="Pop",
|
|
schedules=[
|
|
ShowScheduleCreate(day_of_week=1, time="08:00"),
|
|
ShowScheduleCreate(day_of_week=3, time="08:00"),
|
|
],
|
|
)
|
|
assert len(show.schedules) == 2
|
|
assert show.schedules[0].day_of_week == 1
|
|
assert show.display_order == 0
|
|
|
|
def test_show_create_defaults(self):
|
|
show = ShowCreate(
|
|
title="Show",
|
|
description="Desc",
|
|
host="Host",
|
|
genre="Genre",
|
|
schedules=[ShowScheduleCreate(day_of_week=1, time="09:00")],
|
|
)
|
|
assert show.show_art_url is None
|
|
assert show.hero_image_url is None
|
|
assert show.display_order == 0
|
|
|
|
|
|
class TestHistoryEntryCreate:
|
|
"""Tests for HistoryEntryCreate schema."""
|
|
|
|
def test_valid_creation(self):
|
|
entry = HistoryEntryCreate(
|
|
year=1987,
|
|
title="Station Founded",
|
|
body="The station began broadcasting.",
|
|
display_order=1,
|
|
)
|
|
assert entry.year == 1987
|
|
assert entry.title == "Station Founded"
|
|
|
|
|
|
class TestTeamMemberCreate:
|
|
"""Tests for TeamMemberCreate schema."""
|
|
|
|
def test_required_fields(self):
|
|
member = TeamMemberCreate(
|
|
name="Jane Doe",
|
|
title="Engineer",
|
|
display_order=1,
|
|
)
|
|
assert member.name == "Jane Doe"
|
|
assert member.bio is None
|
|
assert member.photo_url is None
|
|
|
|
|
|
class TestUnderwriterCreate:
|
|
"""Tests for UnderwriterCreate schema."""
|
|
|
|
def test_defaults_display_order_to_zero(self):
|
|
uw = UnderwriterCreate(
|
|
name="Sponsor Co",
|
|
description="A great sponsor",
|
|
)
|
|
assert uw.display_order == 0
|
|
assert uw.website_url is None
|
|
assert uw.logo_url is None
|
|
|
|
def test_custom_display_order(self):
|
|
uw = UnderwriterCreate(
|
|
name="Premium Sponsor",
|
|
description="Best sponsor",
|
|
display_order=5,
|
|
website_url="https://sponsor.example.com",
|
|
)
|
|
assert uw.display_order == 5
|