"""Reminder cron: nudges stale pending reports, bounded + dedup'd.""" from __future__ import annotations import uuid from datetime import UTC, date, datetime, timedelta import pytest from sqlalchemy.ext.asyncio import AsyncSession from app.domains.attendance import constants as c from app.domains.attendance.models import AttendanceCycle, AttendanceReport from app.domains.attendance.scheduler_handlers import ( run_attendance_pending_reminder, ) from app.identity.person_models import IdentityPerson async def _person(db, email): p = IdentityPerson( id=uuid.uuid4(), work_email=email, display_name=email, employment_status="active", source_system="successfactors", hris_person_id=f"sf-{uuid.uuid4().hex[:8]}", ) db.add(p) await db.flush() return p async def _cycle(db): cyc = AttendanceCycle( id=uuid.uuid4(), cycle_key="2026-06", start_date=date(2026, 5, 21), end_date=date(2026, 6, 20), ) db.add(cyc) await db.flush() return cyc async def _report(db, cyc, *, status, last_sent_ago_h=None, confirmed_ago_h=None): intern = await _person(db, f"i-{uuid.uuid4().hex[:6]}@x.com") leader = await _person(db, f"l-{uuid.uuid4().hex[:6]}@x.com") now = datetime.now(UTC) r = AttendanceReport( id=uuid.uuid4(), cycle_id=cyc.id, person_id=intern.id, leader_person_id=leader.id, status=status, version=1, snapshot_json={"summary": {}, "days": []}, last_sent_at=now - timedelta(hours=last_sent_ago_h) if last_sent_ago_h else None, intern_confirmed_at=now - timedelta(hours=confirmed_ago_h) if confirmed_ago_h else None, ) db.add(r) await db.flush() return r @pytest.mark.asyncio async def test_reminds_stale_skips_recent(db_session: AsyncSession): cyc = await _cycle(db_session) stale_intern = await _report(db_session, cyc, status=c.STATUS_PENDING_INTERN, last_sent_ago_h=72) recent_intern = await _report(db_session, cyc, status=c.STATUS_PENDING_INTERN, last_sent_ago_h=2) stale_leader = await _report(db_session, cyc, status=c.STATUS_PENDING_LEADER, confirmed_ago_h=72) completed = await _report(db_session, cyc, status=c.STATUS_COMPLETED, last_sent_ago_h=72) await db_session.commit() result = await run_attendance_pending_reminder(db_session, {}) assert result["status"] == "ok" assert result["reminded"] == 2 # stale intern + stale leader for r in (stale_intern, stale_leader): await db_session.refresh(r) assert r.last_reminded_at is not None for r in (recent_intern, completed): await db_session.refresh(r) assert r.last_reminded_at is None @pytest.mark.asyncio async def test_disabled_via_config(db_session: AsyncSession): cyc = await _cycle(db_session) await _report(db_session, cyc, status=c.STATUS_PENDING_INTERN, last_sent_ago_h=72) await db_session.commit() result = await run_attendance_pending_reminder(db_session, {"enabled": False}) assert result["status"] == "skipped" assert result["reminded"] == 0 @pytest.mark.asyncio async def test_not_renagged_within_window(db_session: AsyncSession): cyc = await _cycle(db_session) await _report(db_session, cyc, status=c.STATUS_PENDING_INTERN, last_sent_ago_h=72) await db_session.commit() first = await run_attendance_pending_reminder(db_session, {}) assert first["reminded"] == 1 # Immediate second run — already reminded within the window. second = await run_attendance_pending_reminder(db_session, {}) assert second["reminded"] == 0