Closes the bug class cucx-docs flagged at Bingham — a CTI Route
Point's CFNA destination points at a number that is structurally
unreachable from the configured CFNA-CSS, so the failsafe forward
fires but finds no matching pattern and the call dies. Invisible
from any single-record inspection (CTI RP record looks fine,
destination pattern exists in some partition, CSS is fine — defect
lives in the relationship between CFNA-CSS and destination's
partition).
The motivating Bingham finding (life-safety severity):
912-CTI-RP (Secondary CER) CFNA + CFUR → "10911" via 911CER-CSS
Pattern "10.911" exists in CER911-PT
911CER-CSS does NOT contain CER911-PT
→ failsafe is structurally broken; both CER servers down would
produce fast-busy on 911 calls instead of routing through ELIN-10
to the PSAP
Implementation per axl/agent-threads/cti-audit-prompts/002:
- Tool, not prompt — output is structured + deterministic; same
shape as route_patterns_targeting (Q1 confirmed as proposed)
- Three-tier severity: HIGH for life-safety descriptions, MEDIUM
for non-life-safety, no LOW (Q2 refined from cucx-docs's
binary proposal — every broken forward is a real bug, just not
all are 911)
- Scope: CFNA + CFUR only for v1; CFB excluded by design (Q3
confirmed — CTI RPs rarely go busy)
- Lives in route_plan.py alongside route_patterns_targeting +
device_grep + translation_chain (Q5 — defer cti.py namespace
until adjacent prompts land)
- Named cti_failsafe_reachability not _audit (Q4 — drops the
_audit suffix per the established tool-vs-prompt naming split;
tools use direct-action names, prompts use _audit)
Life-safety token list (case-insensitive substring match against
name AND description):
("emergency", "911", "cer", "psap", "panic", "alert")
Suggested-fix message names the partition where the destination's
pattern lives and proposes either "add partition X to CSS Y" or
"change CSS to a CSS containing partition X." Falls back to a
generic "manual investigation needed" message when the destination
matches no exact-literal pattern in any partition (often means a
wildcard pattern is the actual target).
Tests: 26 in TestLifeSafetyDetection + TestCtiFailsafeReachability:
- 16 token-matching cases (10 positive, 4 negative, 2 sentinel)
- 10 tool-level cases including the canonical Bingham bug
reproduced verbatim (assertion compares the entire finding dict
to the expected output from cucx-docs's 001 message)
Full mcaxl suite: 238 → 264 passing (+26 from this work).
Adjacent prompts cucx-docs flagged as lower-priority follow-ups
(cti_route_point_audit, cti_port_pool_audit,
cti_application_user_audit) deferred but tracked.
316 lines
13 KiB
Python
316 lines
13 KiB
Python
"""Tests for cti_failsafe_reachability — find broken CFNA/CFUR forwards.
|
|
|
|
Source: cucx-docs handoff at
|
|
``axl/agent-threads/cti-audit-prompts/001-cucx-cfna-reachability-audit.md``
|
|
documenting a real life-safety bug at Bingham (912-CTI-RP CFNA →
|
|
'10911' under 911CER-CSS, where '10.911' lives in CER911-PT which
|
|
911CER-CSS doesn't reach).
|
|
|
|
The tool composes three SQL queries per broken forward:
|
|
1. Top-level forwards SQL (fetch CTI RPs with CFNA/CFUR set)
|
|
2. translation_chain's SQL (per-forward reachability check)
|
|
3. _suggest_failsafe_fix's partition-lookup SQL (one per finding)
|
|
|
|
The FakeAxlClient dispatches by query content rather than sequence
|
|
because the order of (2) and (3) interleaves across multiple findings.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from mcaxl.route_plan import (
|
|
_LIFE_SAFETY_TOKENS,
|
|
_is_life_safety_cti,
|
|
cti_failsafe_reachability,
|
|
)
|
|
|
|
|
|
class FakeAxlClient:
|
|
"""Dispatching fake — returns canned responses keyed on SQL content.
|
|
|
|
Constructor takes:
|
|
- cti_rp_rows: rows for the top-level "find CTI RPs with forwards" query
|
|
- reachable_destinations: set of (destination, css) pairs that have a
|
|
matching pattern (translation_chain returns match_count > 0 for these)
|
|
- destination_partitions: dict {destination: [partition_name, ...]}
|
|
used by the _suggest_failsafe_fix's partition-lookup query
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
cti_rp_rows: list[dict],
|
|
reachable_destinations: set[tuple[str, str]] | None = None,
|
|
destination_partitions: dict[str, list[str]] | None = None,
|
|
):
|
|
self._cti_rows = cti_rp_rows
|
|
self._reachable = reachable_destinations or set()
|
|
self._dest_partitions = destination_partitions or {}
|
|
self.queries: list[str] = []
|
|
|
|
def execute_sql_query(self, sql: str) -> dict:
|
|
self.queries.append(sql)
|
|
|
|
# Dispatch 1: top-level "find CTI RPs with CFNA/CFUR" query
|
|
if "tc.name = 'CTI Route Point'" in sql and "cfnadestination" in sql:
|
|
return {"row_count": len(self._cti_rows), "rows": self._cti_rows}
|
|
|
|
# Dispatch 2: translation_chain's reachability check
|
|
# Recognizable by `tkpatternusage IN (3, 5, 7)` from route_plan.py
|
|
if "tkpatternusage IN (3, 5, 7)" in sql:
|
|
# Extract the destination + CSS from the SQL to figure out
|
|
# whether to return a "match" row or no rows. The destination
|
|
# appears in the called-side filter; the CSS appears in the
|
|
# callingsearchspace WHERE clause.
|
|
#
|
|
# Simplest dispatch: scan the query for the (dest, css) pairs
|
|
# we know are reachable. If any match, return a fake matching
|
|
# pattern row.
|
|
for dest, css in self._reachable:
|
|
if f"name = '{css}'" in sql:
|
|
# For each reachable destination, the test fake returns
|
|
# a single pattern that exactly equals the destination
|
|
# so translation_chain's wildcard matcher resolves it.
|
|
return {
|
|
"row_count": 1,
|
|
"rows": [{
|
|
"pattern": dest,
|
|
"pattern_type": "Translation",
|
|
"partition_name": "Reachable-PT",
|
|
"calling_party_xform_mask": None,
|
|
"called_party_xform_mask": None,
|
|
"prefix_digits_out": None,
|
|
"digit_discard_instructions": None,
|
|
"route_filter": None,
|
|
"description": "fake-reachable",
|
|
}],
|
|
}
|
|
return {"row_count": 0, "rows": []}
|
|
|
|
# Dispatch 3: _suggest_failsafe_fix's partition-lookup query
|
|
if "rp.name IS NOT NULL" in sql and "np.dnorpattern" in sql:
|
|
# Extract the dnorpattern literal from the SQL
|
|
for dest, parts in self._dest_partitions.items():
|
|
if f"np.dnorpattern = '{dest}'" in sql:
|
|
rows = [{"partition": p} for p in parts]
|
|
return {"row_count": len(rows), "rows": rows}
|
|
return {"row_count": 0, "rows": []}
|
|
|
|
# Anything else — empty (unexpected query path; fail loud later)
|
|
return {"row_count": 0, "rows": []}
|
|
|
|
|
|
def _cti_row(name, description, cfna=None, cfur=None, cfna_css=None, cfur_css=None):
|
|
return {
|
|
"name": name,
|
|
"description": description,
|
|
"cfnadestination": cfna,
|
|
"cfurdestination": cfur,
|
|
"cfna_css_name": cfna_css,
|
|
"cfur_css_name": cfur_css,
|
|
}
|
|
|
|
|
|
# ─── Life-safety token detection (helper in isolation) ────────────────
|
|
|
|
class TestLifeSafetyDetection:
|
|
@pytest.mark.parametrize("description", [
|
|
"Primary CER Server",
|
|
"911 CTI Route Point",
|
|
"Emergency CER",
|
|
"PSAP gateway",
|
|
"PANIC button receiver",
|
|
"Code BLUE Alert",
|
|
])
|
|
def test_life_safety_tokens_match(self, description):
|
|
assert _is_life_safety_cti("some-name", description) is True
|
|
|
|
@pytest.mark.parametrize("name", [
|
|
"911-CTI-RP",
|
|
"EMERGENCY-RP",
|
|
"CER-Primary",
|
|
"psap-gateway",
|
|
])
|
|
def test_token_matched_in_name_field(self, name):
|
|
# Tokens match against name OR description — some clusters tag
|
|
# the role in the name field rather than the description
|
|
assert _is_life_safety_cti(name, "Generic CTI Route Point") is True
|
|
|
|
@pytest.mark.parametrize("description", [
|
|
"Patient Intake CTI Route Point",
|
|
"Voicemail Pilot",
|
|
"Receptionist Hunt Pilot",
|
|
"Generic application route point",
|
|
])
|
|
def test_non_life_safety_descriptions(self, description):
|
|
assert _is_life_safety_cti("regular-rp", description) is False
|
|
|
|
def test_null_name_and_description_does_not_match(self):
|
|
assert _is_life_safety_cti(None, None) is False
|
|
assert _is_life_safety_cti("", "") is False
|
|
|
|
def test_advertised_token_list_is_what_we_implement(self):
|
|
# If the token list grows or shrinks, the docstring + agent-thread
|
|
# reply must be updated alongside. Catches accidental drift.
|
|
assert _LIFE_SAFETY_TOKENS == (
|
|
"emergency", "911", "cer", "psap", "panic", "alert",
|
|
)
|
|
|
|
|
|
# ─── Tool-level integration ──────────────────────────────────────────
|
|
|
|
class TestCtiFailsafeReachability:
|
|
|
|
def test_no_cti_route_points_returns_empty_findings(self):
|
|
client = FakeAxlClient(cti_rp_rows=[])
|
|
result = cti_failsafe_reachability(client)
|
|
assert result["total_cti_route_points"] == 0
|
|
assert result["broken_cfna"] == 0
|
|
assert result["broken_cfur"] == 0
|
|
assert result["findings"] == []
|
|
|
|
def test_working_cfna_produces_no_finding(self):
|
|
client = FakeAxlClient(
|
|
cti_rp_rows=[
|
|
_cti_row("Working-RP", "Patient intake", cfna="5550100", cfna_css="Internal-CSS"),
|
|
],
|
|
reachable_destinations={("5550100", "Internal-CSS")},
|
|
)
|
|
result = cti_failsafe_reachability(client)
|
|
assert result["broken_cfna"] == 0
|
|
assert result["findings"] == []
|
|
|
|
def test_broken_cfna_non_life_safety_is_medium(self):
|
|
client = FakeAxlClient(
|
|
cti_rp_rows=[
|
|
_cti_row("Generic-RP", "Patient intake", cfna="5550100", cfna_css="BadCSS"),
|
|
],
|
|
reachable_destinations=set(), # nothing reachable
|
|
destination_partitions={"5550100": ["Internal-PT"]},
|
|
)
|
|
result = cti_failsafe_reachability(client)
|
|
assert result["broken_cfna"] == 1
|
|
assert len(result["findings"]) == 1
|
|
finding = result["findings"][0]
|
|
assert finding["device"] == "Generic-RP"
|
|
assert finding["forward_kind"] == "cfna"
|
|
assert finding["destination"] == "5550100"
|
|
assert finding["css"] == "BadCSS"
|
|
assert finding["match_count"] == 0
|
|
assert finding["severity"] == "MEDIUM"
|
|
assert "Internal-PT" in finding["suggested_fix"]
|
|
assert "BadCSS" in finding["suggested_fix"]
|
|
|
|
def test_broken_cfna_life_safety_is_high(self):
|
|
client = FakeAxlClient(
|
|
cti_rp_rows=[
|
|
_cti_row("911-CTI-RP", "Emergency dispatch", cfna="10911", cfna_css="911CER-CSS"),
|
|
],
|
|
destination_partitions={"10911": ["CER911-PT"]},
|
|
)
|
|
result = cti_failsafe_reachability(client)
|
|
assert result["findings"][0]["severity"] == "HIGH"
|
|
|
|
def test_broken_cfna_and_cfur_produce_two_findings(self):
|
|
# Same device with both forwards broken — should produce TWO entries
|
|
# (per-forward, not per-device, per the design decision)
|
|
client = FakeAxlClient(
|
|
cti_rp_rows=[
|
|
_cti_row(
|
|
"912-CTI-RP", "CTI RP for Secondary CER Server",
|
|
cfna="10911", cfna_css="911CER-CSS",
|
|
cfur="10911", cfur_css="911CER-CSS",
|
|
),
|
|
],
|
|
destination_partitions={"10911": ["CER911-PT"]},
|
|
)
|
|
result = cti_failsafe_reachability(client)
|
|
assert result["broken_cfna"] == 1
|
|
assert result["broken_cfur"] == 1
|
|
assert len(result["findings"]) == 2
|
|
kinds = {f["forward_kind"] for f in result["findings"]}
|
|
assert kinds == {"cfna", "cfur"}
|
|
# Both should be HIGH (description contains "CER")
|
|
assert all(f["severity"] == "HIGH" for f in result["findings"])
|
|
|
|
def test_only_cfna_set_does_not_check_cfur(self):
|
|
# CFUR null → don't check it (not a finding)
|
|
client = FakeAxlClient(
|
|
cti_rp_rows=[
|
|
_cti_row("Half-RP", "Generic", cfna="9999", cfna_css="BadCSS"),
|
|
],
|
|
destination_partitions={"9999": ["Some-PT"]},
|
|
)
|
|
result = cti_failsafe_reachability(client)
|
|
assert result["broken_cfna"] == 1
|
|
assert result["broken_cfur"] == 0
|
|
|
|
def test_canonical_bingham_bug_reproduced(self):
|
|
"""The canary scenario from cucx-docs's 001 — verifies the tool
|
|
produces exactly the expected output for the motivating bug."""
|
|
client = FakeAxlClient(
|
|
cti_rp_rows=[
|
|
_cti_row(
|
|
"912-CTI-RP", "CTI RP for Secondary CER Server",
|
|
cfna="10911", cfna_css="911CER-CSS",
|
|
cfur="10911", cfur_css="911CER-CSS",
|
|
),
|
|
],
|
|
destination_partitions={"10911": ["CER911-PT"]},
|
|
)
|
|
result = cti_failsafe_reachability(client)
|
|
|
|
cfna_finding = next(f for f in result["findings"] if f["forward_kind"] == "cfna")
|
|
assert cfna_finding == {
|
|
"device": "912-CTI-RP",
|
|
"description": "CTI RP for Secondary CER Server",
|
|
"forward_kind": "cfna",
|
|
"destination": "10911",
|
|
"css": "911CER-CSS",
|
|
"match_count": 0,
|
|
"severity": "HIGH", # description contains "CER"
|
|
"suggested_fix": (
|
|
"Pattern '10911' lives in partition 'CER911-PT'. "
|
|
"Either add 'CER911-PT' to CSS '911CER-CSS', "
|
|
"OR change the forward CSS to a CSS that already "
|
|
"contains 'CER911-PT'."
|
|
),
|
|
}
|
|
|
|
def test_suggested_fix_when_no_partition_holds_destination(self):
|
|
# Edge case: destination doesn't match any literal pattern
|
|
# (might match a wildcard, but not an exact-literal). Suggest_fix
|
|
# falls back to a generic message.
|
|
client = FakeAxlClient(
|
|
cti_rp_rows=[
|
|
_cti_row("Wild-RP", "Generic", cfna="orphan-dest", cfna_css="BadCSS"),
|
|
],
|
|
destination_partitions={}, # no partition holds 'orphan-dest'
|
|
)
|
|
result = cti_failsafe_reachability(client)
|
|
fix = result["findings"][0]["suggested_fix"]
|
|
assert "matches no exact-literal pattern" in fix
|
|
assert "wildcard" in fix.lower()
|
|
|
|
def test_suggested_fix_when_destination_in_multiple_partitions(self):
|
|
# Edge case: destination matches in multiple partitions; the
|
|
# fix message lists them and asks the operator to pick.
|
|
client = FakeAxlClient(
|
|
cti_rp_rows=[
|
|
_cti_row("Multi-RP", "Generic", cfna="5555", cfna_css="BadCSS"),
|
|
],
|
|
destination_partitions={"5555": ["Site-A-PT", "Site-B-PT"]},
|
|
)
|
|
result = cti_failsafe_reachability(client)
|
|
fix = result["findings"][0]["suggested_fix"]
|
|
assert "multiple partitions" in fix
|
|
assert "Site-A-PT" in fix
|
|
assert "Site-B-PT" in fix
|
|
|
|
def test_response_includes_scope_note(self):
|
|
client = FakeAxlClient(cti_rp_rows=[])
|
|
result = cti_failsafe_reachability(client)
|
|
assert "_note" in result
|
|
# Scope discipline visible at the call site — CFB exclusion is
|
|
# documented, and the life-safety token list is named.
|
|
assert "CFB" in result["_note"]
|
|
assert "emergency" in result["_note"]
|