from datetime import date, datetime, time import pytest import sqlalchemy as sa from sqlalchemy.exc import IntegrityError from app import db from app.models import TimeSlot, WorkEntry, WorkplacePresenceEvent def make_entry() -> WorkEntry: return WorkEntry(date=date(2026, 8, 13), day_type="WORK") def make_event(entry: WorkEntry, key: str = "ha-arrival-1") -> WorkplacePresenceEvent: return WorkplacePresenceEvent( idempotency_key=key, event_type="arrival", received_at=datetime(2026, 8, 13, 6, 23, 10), occurred_at=datetime(2026, 8, 13, 8, 23, 10), local_date=date(2026, 8, 13), entry=entry, ) def test_presence_event_creation_and_relations(app): with app.app_context(): entry = make_entry() slot = TimeSlot(start_time=time(8), end_time=time(12), entry=entry) event = make_event(entry) event.time_slot = slot db.session.add(entry) db.session.commit() assert event.entry is entry assert event in entry.presence_events assert event.time_slot is slot assert event in slot.presence_events assert event.processed_at is None def test_idempotency_key_is_unique(app): with app.app_context(): entry = make_entry() db.session.add_all([entry, make_event(entry), make_event(entry, "ha-arrival-1")]) with pytest.raises(IntegrityError): db.session.commit() db.session.rollback() def test_event_type_check_constraint(app): with app.app_context(): entry = make_entry() event = make_event(entry) event.event_type = "unknown" db.session.add_all([entry, event]) with pytest.raises(IntegrityError): db.session.commit() db.session.rollback() def test_time_slot_link_is_nullable_and_set_null_on_slot_delete(app): with app.app_context(): entry = make_entry() slot = TimeSlot(start_time=time(8), end_time=time(12), entry=entry) event = make_event(entry) event.time_slot = slot db.session.add(entry) db.session.commit() db.session.delete(slot) db.session.commit() assert db.session.get(WorkplacePresenceEvent, event.id).time_slot_id is None def test_events_cascade_when_work_entry_is_deleted(app): with app.app_context(): entry = make_entry() db.session.add(make_event(entry)) db.session.commit() event_id = entry.presence_events[0].id db.session.delete(entry) db.session.commit() assert db.session.get(WorkplacePresenceEvent, event_id) is None def test_create_all_adds_presence_table_without_losing_existing_entries(app): with app.app_context(): db.session.add(make_entry()) db.session.commit() db.session.execute(sa.text("DROP TABLE workplace_presence_events")) db.session.commit() db.create_all() assert db.session.scalar(sa.select(sa.func.count()).select_from(WorkEntry)) == 1 assert sa.inspect(db.engine).has_table("workplace_presence_events") def test_create_all_creates_all_tables_on_empty_sqlite_database(app): with app.app_context(): db.drop_all() db.create_all() inspector = sa.inspect(db.engine) assert inspector.has_table("work_entries") assert inspector.has_table("time_slots") assert inspector.has_table("workplace_presence_events")