feat: implement location events & error handling (Step 10.2)

- Add LocationService with create/rename/archive methods
- Add LocationProjection for event handling
- Add admin-only location management routes at /locations
- Add error response helpers (error_response, error_toast, success_toast)
- Add toast handler JS to base template for HX-Trigger notifications
- Update seeds.py to emit LocationCreated events per spec §23
- Archived locations block animal moves with 422 error

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-31 17:48:16 +00:00
parent 5ba068b36a
commit 229842fb45
14 changed files with 1896 additions and 28 deletions

View File

@@ -240,6 +240,9 @@ class TestAnimalServiceTransactionIntegrity:
def test_no_partial_data_on_projection_error(self, seeded_db, event_store, valid_location_id):
"""If projection fails, event is not persisted."""
# Count events before the test (seeds create LocationCreated events)
event_count_before = seeded_db.execute("SELECT COUNT(*) FROM events").fetchone()[0]
# Create a registry with a failing projection
from animaltrack.projections import Projection, ProjectionError
@@ -264,9 +267,9 @@ class TestAnimalServiceTransactionIntegrity:
with pytest.raises(ProjectionError):
service.create_cohort(payload, ts_utc, "test_user")
# Verify nothing was persisted
event_count = seeded_db.execute("SELECT COUNT(*) FROM events").fetchone()[0]
assert event_count == 0
# Verify no new events were persisted (count unchanged from before)
event_count_after = seeded_db.execute("SELECT COUNT(*) FROM events").fetchone()[0]
assert event_count_after == event_count_before
animal_count = seeded_db.execute("SELECT COUNT(*) FROM animal_registry").fetchone()[0]
assert animal_count == 0