Sep 4 - Add link relavant issues function

This commit is contained in:
2026-09-04 16:53:53 -04:00
parent d291dfc513
commit e9005b9b9c
20 changed files with 3208 additions and 72 deletions
+199
View File
@@ -0,0 +1,199 @@
"""
tests/test_sla_candidate_query.py
---------------------------------
Equivalence test for the SQL prefilter in send_sla_alerts().
The cron used to read EVERY open issue and decide in Python which ones deserved
a notification. It now narrows to candidates in SQL first.
The contract is a CONSERVATIVE SUPERSET, not equality:
* Nothing the old loop would have notified may be missed. This is the safety
property — a miss is a silently unsent SLA alert, with nothing in any log
to say it did not happen.
* The SQL may return extra rows, because it deliberately does not replicate
the "already notified at_risk and still only at_risk" skip (that would mean
writing the per-severity deadline arithmetic a second time, in SQL). The
Python loop still applies that skip, so no extra notification is ever sent
— only a handful of extra rows are read.
The reference predicate below is the old loop's skip logic, written out.
Multi-tenant note: the cron runs once per tenant database, so this prefilter is
worth more here than in ST — the saving multiplies by the tenant count.
"""
from datetime import timedelta
import pytest
@pytest.fixture
def ctx(app):
with app.app_context():
from app import db
db.drop_all()
db.create_all()
yield app
db.session.remove()
def _candidate_query():
"""The prefilter exactly as send_sla_alerts() builds it."""
from app import db
from app.models.issue import Issue
from app.utils.sla import SLA_HOURS, AT_RISK_THRESHOLD
from app.utils.time_utils import now_eastern
now = now_eastern()
age_clauses = [
db.and_(
Issue.severity == severity,
Issue.reported_at <= now - timedelta(hours=hours * AT_RISK_THRESHOLD),
)
for severity, hours in SLA_HOURS.items()
]
return Issue.query.filter(
Issue.status.in_(['open', 'in_progress', 'pending_verification']),
Issue.reported_at.isnot(None),
db.or_(Issue.sla_notified.is_(None), Issue.sla_notified != 'breached'),
db.or_(*age_clauses),
)
def _old_loop_would_act(issue):
"""The pre-change Python logic: which rows survived to send a notification."""
from app.utils.sla import sla_status
if issue.status not in ('open', 'in_progress', 'pending_verification'):
return False
if issue.reported_at is None:
return False # would have raised TypeError — see the NULL test
status = sla_status(issue)
if status not in ('at_risk', 'breached'):
return False
already = issue.sla_notified
if already == 'breached':
return False
if already == 'at_risk' and status == 'at_risk':
return False
return True
def _add(severity, hours_ago, status='open', sla_notified=None):
from app import db
from app.models.issue import Issue
from app.utils.time_utils import now_eastern
issue = Issue(
severity=severity,
description='x',
status=status,
sla_notified=sla_notified,
reported_at=now_eastern() - timedelta(hours=hours_ago),
)
db.session.add(issue)
return issue
@pytest.fixture
def population(ctx):
"""One issue per interesting combination of severity / age / state."""
from app import db
from app.utils.sla import SLA_HOURS, AT_RISK_THRESHOLD
rows = []
for severity, window in SLA_HOURS.items():
at_risk_at = window * AT_RISK_THRESHOLD
rows += [
_add(severity, 0), # fresh -> ok
_add(severity, at_risk_at * 0.5), # halfway -> ok
_add(severity, at_risk_at + 1), # at risk
_add(severity, window + 1), # breached
# already-notified variants
_add(severity, at_risk_at + 1, sla_notified='at_risk'),
_add(severity, window + 1, sla_notified='at_risk'),
_add(severity, window + 1, sla_notified='breached'),
# non-actionable / other statuses
_add(severity, window + 1, status='resolved'),
_add(severity, window + 1, status='in_progress'),
_add(severity, window + 1, status='pending_verification'),
]
db.session.commit()
return rows
def test_prefilter_never_misses_an_actionable_issue(population):
"""Safety property: every row the old loop notified is still selected."""
expected = {i.id for i in population if _old_loop_would_act(i)}
actual = {i.id for i in _candidate_query().all()}
assert expected, 'fixture built no actionable issues — test is vacuous'
assert expected <= actual, (
'the prefilter drops issues the old loop would have alerted on: '
f'{sorted(expected - actual)}'
)
def test_prefilter_extras_are_all_skipped_by_the_loop(population):
"""The extra rows the SQL lets through produce no extra notifications.
Each must be a row the Python loop independently skips, so the set of alerts
actually sent is unchanged.
"""
from app.utils.sla import sla_status
by_id = {i.id: i for i in population}
expected = {i.id for i in population if _old_loop_would_act(i)}
extras = {i.id for i in _candidate_query().all()} - expected
for iid in extras:
issue = by_id[iid]
assert not _old_loop_would_act(issue)
# and the only reason it is allowed through is the at_risk bookkeeping
assert issue.sla_notified == 'at_risk' and sla_status(issue) == 'at_risk', (
f'issue {iid} is an unexplained extra: status={issue.status} '
f'severity={issue.severity} sla_notified={issue.sla_notified} '
f'sla_status={sla_status(issue)}'
)
def test_prefilter_excludes_the_bulk_of_open_issues(population):
"""The point of the change: most open issues are never read."""
from app.models.issue import Issue
total_open = Issue.query.filter(
Issue.status.in_(['open', 'in_progress', 'pending_verification'])
).count()
candidates = _candidate_query().count()
assert candidates < total_open
def test_null_reported_at_is_excluded_not_crashed(ctx):
"""reported_at is nullable and sla_status() raises TypeError on NULL.
Before the prefilter one such row aborted the whole cron run — and in MT it
would abort that tenant's run entirely. It must now be excluded in SQL.
"""
from app import db
from app.models.issue import Issue
from app.utils.sla import sla_status
issue = Issue(severity='high', description='x', status='open')
db.session.add(issue)
db.session.commit()
# The column carries default=now_eastern, so an ORM insert can never leave
# it NULL — the row has to be forced, which is exactly how a legacy or
# raw-SQL-inserted row would look.
db.session.execute(
db.text('UPDATE issues SET reported_at = NULL WHERE id = :i'),
{'i': issue.id},
)
db.session.commit()
db.session.expire(issue)
assert issue.reported_at is None
assert issue.id not in {i.id for i in _candidate_query().all()}
with pytest.raises(TypeError):
sla_status(issue) # confirms the crash the filter is avoiding