78 lines
2.6 KiB
Python
78 lines
2.6 KiB
Python
"""
|
|
Tests for the SLA engine (app/utils/sla.py).
|
|
|
|
sla_status/deadline/hours_remaining read only .status, .severity, .reported_at,
|
|
so a lightweight stand-in object is enough — no DB or app context needed. Time
|
|
is controlled by positioning reported_at relative to now_eastern().
|
|
"""
|
|
from datetime import timedelta
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
from app.utils.sla import (
|
|
sla_status, sla_deadline, sla_hours_remaining, SLA_HOURS, AT_RISK_THRESHOLD,
|
|
)
|
|
from app.utils.time_utils import now_eastern
|
|
|
|
|
|
def issue(severity='high', status='open', hours_ago=0):
|
|
return SimpleNamespace(
|
|
severity=severity,
|
|
status=status,
|
|
reported_at=now_eastern() - timedelta(hours=hours_ago),
|
|
)
|
|
|
|
|
|
def test_resolved_issue_has_no_sla():
|
|
assert sla_status(issue(status='resolved', hours_ago=999)) is None
|
|
|
|
|
|
def test_unknown_severity_returns_none():
|
|
assert sla_status(issue(severity='nonsense', hours_ago=1)) is None
|
|
|
|
|
|
def test_fresh_issue_is_ok():
|
|
# high = 24h window, at-risk at 18h; 1h in is comfortably OK
|
|
assert sla_status(issue(severity='high', hours_ago=1)) == 'ok'
|
|
|
|
|
|
def test_issue_past_at_risk_threshold():
|
|
# high at-risk at 18h; 20h in is at_risk but not yet breached (24h)
|
|
assert sla_status(issue(severity='high', hours_ago=20)) == 'at_risk'
|
|
|
|
|
|
def test_issue_past_deadline_is_breached():
|
|
assert sla_status(issue(severity='high', hours_ago=25)) == 'breached'
|
|
|
|
|
|
@pytest.mark.parametrize('severity', list(SLA_HOURS.keys()))
|
|
def test_at_risk_boundary_for_each_severity(severity):
|
|
window = SLA_HOURS[severity]
|
|
# Just past the at-risk fraction, still before the deadline.
|
|
at_risk_hours = window * AT_RISK_THRESHOLD
|
|
assert sla_status(issue(severity=severity, hours_ago=at_risk_hours + 0.1)) == 'at_risk'
|
|
# Just before the at-risk fraction is still OK.
|
|
assert sla_status(issue(severity=severity, hours_ago=at_risk_hours - 0.5)) == 'ok'
|
|
|
|
|
|
def test_deadline_is_reported_at_plus_window():
|
|
iss = issue(severity='medium', hours_ago=0)
|
|
expected = iss.reported_at + timedelta(hours=SLA_HOURS['medium'])
|
|
assert sla_deadline(iss) == expected
|
|
|
|
|
|
def test_hours_remaining_positive_when_within_window():
|
|
remaining = sla_hours_remaining(issue(severity='critical', hours_ago=1))
|
|
# critical window is 4h; ~3h should remain
|
|
assert 2.5 < remaining < 3.5
|
|
|
|
|
|
def test_hours_remaining_negative_when_overdue():
|
|
remaining = sla_hours_remaining(issue(severity='critical', hours_ago=6))
|
|
assert remaining < 0
|
|
|
|
|
|
def test_hours_remaining_none_for_resolved():
|
|
assert sla_hours_remaining(issue(status='resolved', hours_ago=1)) is None
|