Files
Salon_Assistant/tests/unit/test_fixture_provider.py
Ty 15f6a6c713 Implement S6 doctor and A1 daily-board with fixtures.
Add operator health checks (make doctor) wrapping platform CLIs, and the fixtures-only daily board skill library with unit tests (make verify).
2026-07-27 12:41:40 -07:00

157 lines
5.3 KiB
Python

"""Tests for the scheduling fixture provider."""
from __future__ import annotations
import json
import pathlib
import tempfile
import pytest
import sys
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[2] / "skills" / "_lib"))
from lumina_skills.domain import Appointment, AppointmentStatus
from lumina_skills.providers.scheduling.fixture_provider import (
load_fixtures,
load_fixture_metadata,
)
# Path to the real fixture file.
_FIXTURE_PATH = pathlib.Path(__file__).resolve().parents[2] / "data" / "fixtures" / "scheduling" / "claire_bennett_2026-07-28.json"
def _make_fixture_file(tmp_path: pathlib.Path, data: dict) -> pathlib.Path:
"""Write a fixture dict to a temp JSON file."""
p = tmp_path / "test_fixture.json"
p.write_text(json.dumps(data), encoding="utf-8")
return p
# ── load_fixtures ──────────────────────────────────────────────────────────
def test_load_real_fixture():
"""Load the Claire Bennett fixture file."""
apts = load_fixtures(_FIXTURE_PATH)
assert len(apts) == 7 # 7 appointments in the fixture
assert all(isinstance(a, Appointment) for a in apts)
def test_load_fixture_statuses():
"""Fixture statuses are correctly parsed."""
apts = load_fixtures(_FIXTURE_PATH)
statuses = {a.appointment_id: a.status for a in apts}
assert statuses["APT-001"] == AppointmentStatus.CONFIRMED
assert statuses["APT-002"] == AppointmentStatus.PENDING
assert statuses["APT-007"] == AppointmentStatus.CANCELLED
def test_load_fixture_needs_confirmation():
"""needs_confirmation flag is loaded from fixture."""
apts = load_fixtures(_FIXTURE_PATH)
flags = {a.appointment_id: a.needs_confirmation for a in apts}
assert flags["APT-001"] is False
assert flags["APT-002"] is True
assert flags["APT-004"] is True
def test_load_fixture_notes():
"""Notes are loaded from fixture."""
apts = load_fixtures(_FIXTURE_PATH)
notes = {a.appointment_id: a.notes for a in apts}
assert "ammonia" in notes["APT-001"].lower()
assert "Wedding" in notes["APT-002"]
def test_load_fixture_missing_file(tmp_path: pathlib.Path):
"""FileNotFoundError for missing fixture."""
with pytest.raises(FileNotFoundError):
load_fixtures(tmp_path / "nonexistent.json")
def test_load_fixture_empty_appointments(tmp_path: pathlib.Path):
"""Empty appointments list returns empty list."""
data = {"salon_name": "Test", "date": "2026-01-01", "appointments": []}
path = _make_fixture_file(tmp_path, data)
apts = load_fixtures(path)
assert apts == []
def test_load_fixture_default_status(tmp_path: pathlib.Path):
"""Missing status field defaults to PENDING (via .get default)."""
data = {
"appointments": [{
"id": "APT-X",
"start": "2026-01-01T09:00:00",
"end": "2026-01-01T10:00:00",
"client_name": "Test",
"service_name": "Test",
"staff_name": "Test",
# No status field.
}]
}
path = _make_fixture_file(tmp_path, data)
apts = load_fixtures(path)
assert apts[0].status == AppointmentStatus.PENDING
def test_load_fixture_unknown_status_raises(tmp_path: pathlib.Path):
"""Unknown status string raises ValueError."""
data = {
"appointments": [{
"id": "APT-X",
"start": "2026-01-01T09:00:00",
"end": "2026-01-01T10:00:00",
"client_name": "Test",
"service_name": "Test",
"staff_name": "Test",
"status": "typo_status",
}]
}
path = _make_fixture_file(tmp_path, data)
with pytest.raises(ValueError, match="Unknown appointment status"):
load_fixtures(path)
def test_load_fixture_default_needs_confirmation(tmp_path: pathlib.Path):
"""Missing needs_confirmation defaults to False."""
data = {
"appointments": [{
"id": "APT-X",
"start": "2026-01-01T09:00:00",
"end": "2026-01-01T10:00:00",
"client_name": "Test",
"service_name": "Test",
"staff_name": "Test",
"status": "confirmed",
# No needs_confirmation field.
}]
}
path = _make_fixture_file(tmp_path, data)
apts = load_fixtures(path)
assert apts[0].needs_confirmation is False
def test_load_fixture_malformed_json(tmp_path: pathlib.Path):
"""ValueError for invalid JSON."""
p = tmp_path / "bad.json"
p.write_text("{not valid json}", encoding="utf-8")
with pytest.raises(ValueError):
load_fixtures(p)
# ── load_fixture_metadata ─────────────────────────────────────────────────
def test_load_metadata():
meta = load_fixture_metadata(_FIXTURE_PATH)
assert meta["salon_name"] == "Lumina Hair Studio & Spa"
assert meta["date"] == "2026-07-28"
assert meta["business_hours"]["open"] == "09:00"
assert meta["business_hours"]["close"] == "18:00"
assert len(meta["staff"]) == 2
def test_load_metadata_missing_file(tmp_path: pathlib.Path):
with pytest.raises(FileNotFoundError):
load_fixture_metadata(tmp_path / "nonexistent.json")