"""Tests for the l1_session_cleanup job.""" import uuid from datetime import datetime, timedelta, timezone import pytest from sqlalchemy.ext.asyncio import AsyncSession from app.models.l1_walk_session import L1WalkSession from app.models.account import Account from app.models.user import User from app.services.l1_session_cleanup import flip_stale_sessions # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- async def _make_account(db: AsyncSession) -> Account: import secrets import string code = "".join(secrets.choice(string.ascii_uppercase + string.digits) for _ in range(8)) a = Account(id=uuid.uuid4(), name="Test", display_code=code) db.add(a) await db.flush() return a async def _make_user(db: AsyncSession, *, account_id: uuid.UUID) -> User: u = User( id=uuid.uuid4(), email=f"user-{uuid.uuid4()}@example.com", name="L1", account_id=account_id, account_role="l1_tech", role="engineer", is_active=True, ) db.add(u) await db.flush() return u async def _make_session( db: AsyncSession, *, account_id: uuid.UUID, user_id: uuid.UUID, status: str = "active", last_step_at: datetime | None = None, ) -> L1WalkSession: now = datetime.now(timezone.utc) session = L1WalkSession( id=uuid.uuid4(), account_id=account_id, created_by_user_id=user_id, ticket_id="t", ticket_kind="internal", session_kind="adhoc", status=status, started_at=now, last_step_at=last_step_at or now, ) db.add(session) await db.flush() return session # --------------------------------------------------------------------------- # Tests # --------------------------------------------------------------------------- @pytest.mark.asyncio async def test_flip_stale_sessions_only_affects_old_active_rows(test_db: AsyncSession): account = await _make_account(test_db) user = await _make_user(test_db, account_id=account.id) # 1. Stale active (>24h ago) — should flip stale = await _make_session( test_db, account_id=account.id, user_id=user.id, status="active", last_step_at=datetime.now(timezone.utc) - timedelta(hours=25), ) # 2. Fresh active (1h ago) — should stay active fresh = await _make_session( test_db, account_id=account.id, user_id=user.id, status="active", last_step_at=datetime.now(timezone.utc) - timedelta(hours=1), ) # 3. Already-resolved (old) — should stay resolved, not flip already_resolved = await _make_session( test_db, account_id=account.id, user_id=user.id, status="resolved", last_step_at=datetime.now(timezone.utc) - timedelta(hours=48), ) await test_db.commit() count = await flip_stale_sessions(test_db) assert count == 1 await test_db.refresh(stale) await test_db.refresh(fresh) await test_db.refresh(already_resolved) assert stale.status == "abandoned" assert fresh.status == "active" assert already_resolved.status == "resolved" @pytest.mark.asyncio async def test_flip_stale_sessions_returns_zero_when_none_stale(test_db: AsyncSession): account = await _make_account(test_db) user = await _make_user(test_db, account_id=account.id) await _make_session( test_db, account_id=account.id, user_id=user.id, status="active", last_step_at=datetime.now(timezone.utc) - timedelta(hours=1), ) await test_db.commit() count = await flip_stale_sessions(test_db) assert count == 0