flip_stale_sessions flips L1WalkSession.status from 'active' to 'abandoned' for rows where last_step_at is older than 24h. Preserves the row for audit; removes it from the L1 dashboard's 'Resume in progress' widget. Runs hourly via APScheduler with max_instances=1 (Lesson 1). Uses the admin session factory (no RLS context at startup). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
120 lines
3.6 KiB
Python
120 lines
3.6 KiB
Python
"""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
|