Add user_defaults table and repository for persisting form defaults across sessions. Feed and egg forms now load/save user preferences. Changes: - Add migration 0009-user-defaults.sql with table schema - Add UserDefault model and UserDefaultsRepository - Integrate defaults into feed route (location, feed_type, amount) - Integrate defaults into egg route (location) - Add repository unit tests and route integration tests 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
159 lines
5.4 KiB
Python
159 lines
5.4 KiB
Python
# ABOUTME: Tests for UserDefaultsRepository.
|
|
# ABOUTME: Validates CRUD operations for user form defaults.
|
|
|
|
import time
|
|
|
|
import pytest
|
|
|
|
from animaltrack.models.reference import UserDefault
|
|
from animaltrack.repositories.user_defaults import UserDefaultsRepository
|
|
|
|
|
|
@pytest.fixture
|
|
def now_utc():
|
|
"""Current time in milliseconds since epoch."""
|
|
return int(time.time() * 1000)
|
|
|
|
|
|
class TestUserDefaultsRepository:
|
|
"""Tests for UserDefaultsRepository."""
|
|
|
|
def test_get_returns_none_for_missing(self, seeded_db):
|
|
"""get returns None when no defaults exist."""
|
|
repo = UserDefaultsRepository(seeded_db)
|
|
result = repo.get("ppetru", "collect_egg")
|
|
assert result is None
|
|
|
|
def test_upsert_creates_new_record(self, seeded_db, now_utc):
|
|
"""upsert creates a new defaults record."""
|
|
repo = UserDefaultsRepository(seeded_db)
|
|
defaults = UserDefault(
|
|
username="ppetru",
|
|
action="collect_egg",
|
|
location_id="01ARZ3NDEKTSV4RRFFQ69G5FAV",
|
|
updated_at_utc=now_utc,
|
|
)
|
|
repo.upsert(defaults)
|
|
|
|
result = repo.get("ppetru", "collect_egg")
|
|
assert result is not None
|
|
assert result.username == "ppetru"
|
|
assert result.action == "collect_egg"
|
|
assert result.location_id == "01ARZ3NDEKTSV4RRFFQ69G5FAV"
|
|
|
|
def test_upsert_updates_existing_record(self, seeded_db, now_utc):
|
|
"""upsert updates an existing defaults record."""
|
|
repo = UserDefaultsRepository(seeded_db)
|
|
defaults = UserDefault(
|
|
username="ppetru",
|
|
action="feed_given",
|
|
location_id="01ARZ3NDEKTSV4RRFFQ69G5FAV",
|
|
feed_type_code="layer",
|
|
amount_kg=20,
|
|
updated_at_utc=now_utc,
|
|
)
|
|
repo.upsert(defaults)
|
|
|
|
updated = UserDefault(
|
|
username="ppetru",
|
|
action="feed_given",
|
|
location_id="01ARZ3NDEKTSV4RRFFQ69G5ABC",
|
|
feed_type_code="grower",
|
|
amount_kg=25,
|
|
updated_at_utc=now_utc + 1000,
|
|
)
|
|
repo.upsert(updated)
|
|
|
|
result = repo.get("ppetru", "feed_given")
|
|
assert result.location_id == "01ARZ3NDEKTSV4RRFFQ69G5ABC"
|
|
assert result.feed_type_code == "grower"
|
|
assert result.amount_kg == 25
|
|
|
|
def test_get_returns_all_fields(self, seeded_db, now_utc):
|
|
"""get returns all stored fields correctly."""
|
|
repo = UserDefaultsRepository(seeded_db)
|
|
defaults = UserDefault(
|
|
username="ppetru",
|
|
action="feed_given",
|
|
location_id="01ARZ3NDEKTSV4RRFFQ69G5FAV",
|
|
species="duck",
|
|
animal_filter="location:strip1",
|
|
feed_type_code="layer",
|
|
amount_kg=20,
|
|
bag_size_kg=25,
|
|
updated_at_utc=now_utc,
|
|
)
|
|
repo.upsert(defaults)
|
|
|
|
result = repo.get("ppetru", "feed_given")
|
|
assert result.location_id == "01ARZ3NDEKTSV4RRFFQ69G5FAV"
|
|
assert result.species == "duck"
|
|
assert result.animal_filter == "location:strip1"
|
|
assert result.feed_type_code == "layer"
|
|
assert result.amount_kg == 20
|
|
assert result.bag_size_kg == 25
|
|
|
|
def test_different_actions_are_independent(self, seeded_db, now_utc):
|
|
"""Different actions for same user are stored independently."""
|
|
repo = UserDefaultsRepository(seeded_db)
|
|
egg_defaults = UserDefault(
|
|
username="ppetru",
|
|
action="collect_egg",
|
|
location_id="01ARZ3NDEKTSV4RRFFQ69G5EGG",
|
|
updated_at_utc=now_utc,
|
|
)
|
|
feed_defaults = UserDefault(
|
|
username="ppetru",
|
|
action="feed_given",
|
|
location_id="01ARZ3NDEKTSV4RRFFQ69G5FED",
|
|
updated_at_utc=now_utc,
|
|
)
|
|
repo.upsert(egg_defaults)
|
|
repo.upsert(feed_defaults)
|
|
|
|
egg_result = repo.get("ppetru", "collect_egg")
|
|
feed_result = repo.get("ppetru", "feed_given")
|
|
|
|
assert egg_result.location_id == "01ARZ3NDEKTSV4RRFFQ69G5EGG"
|
|
assert feed_result.location_id == "01ARZ3NDEKTSV4RRFFQ69G5FED"
|
|
|
|
def test_different_users_are_independent(self, seeded_db, now_utc):
|
|
"""Different users have independent defaults."""
|
|
repo = UserDefaultsRepository(seeded_db)
|
|
ppetru_defaults = UserDefault(
|
|
username="ppetru",
|
|
action="collect_egg",
|
|
location_id="01ARZ3NDEKTSV4RRFFQ69G5PPP",
|
|
updated_at_utc=now_utc,
|
|
)
|
|
ines_defaults = UserDefault(
|
|
username="ines",
|
|
action="collect_egg",
|
|
location_id="01ARZ3NDEKTSV4RRFFQ69G5III",
|
|
updated_at_utc=now_utc,
|
|
)
|
|
repo.upsert(ppetru_defaults)
|
|
repo.upsert(ines_defaults)
|
|
|
|
ppetru_result = repo.get("ppetru", "collect_egg")
|
|
ines_result = repo.get("ines", "collect_egg")
|
|
|
|
assert ppetru_result.location_id == "01ARZ3NDEKTSV4RRFFQ69G5PPP"
|
|
assert ines_result.location_id == "01ARZ3NDEKTSV4RRFFQ69G5III"
|
|
|
|
def test_null_fields_preserved(self, seeded_db, now_utc):
|
|
"""Null fields are stored and retrieved correctly."""
|
|
repo = UserDefaultsRepository(seeded_db)
|
|
defaults = UserDefault(
|
|
username="ppetru",
|
|
action="collect_egg",
|
|
location_id=None,
|
|
species=None,
|
|
updated_at_utc=now_utc,
|
|
)
|
|
repo.upsert(defaults)
|
|
|
|
result = repo.get("ppetru", "collect_egg")
|
|
assert result.location_id is None
|
|
assert result.species is None
|