Files
awoooi/apps/api/tests/test_telegram_canonical_routing_registry.py
ogt ff8d917247
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 1s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / build-and-deploy (push) Has been cancelled
CD Pipeline / tests (push) Has been cancelled
CD Pipeline / post-deploy-checks (push) Has been cancelled
fix(telegram): bind product alerts to canonical routes
2026-07-15 01:08:05 +08:00

650 lines
21 KiB
Python

from __future__ import annotations
import copy
import json
import re
from pathlib import Path
import pytest
import yaml
from src.services.notification_matrix import (
get_trusted_alert_canonical_route_context,
load_canonical_telegram_routing_registry,
resolve_canonical_telegram_route,
)
EXPECTED_PRODUCTS = {
"2026fifa",
"agent-bounty-protocol",
"awooogo",
"awoooi",
"bitan-pharmacy",
"clawbot-openclaw",
"momo-pro",
"stockplatform-v2",
"tsenyang-website",
"vibework",
"vtuber",
}
REPO_ROOT = Path(__file__).resolve().parents[3]
def test_namespaced_product_ingress_route_is_explicit_and_policy_blocked() -> None:
context = get_trusted_alert_canonical_route_context(
{
"awoooi_product_id": "momo-pro",
"awoooi_signal_family": "crawler_and_data_freshness",
"awoooi_route_severity": "P2",
},
source="prometheus_alertmanager",
source_severity="warning",
)
assert context == {
"product_id": "momo-pro",
"signal_family": "crawler_and_data_freshness",
"severity": "P2",
"route_source": "prometheus_alertmanager",
}
assert resolve_canonical_telegram_route(**{
key: context[key] for key in ("product_id", "signal_family", "severity")
})["decision"] == "block"
@pytest.mark.parametrize(
("labels", "source", "severity"),
[
({}, "prometheus_alertmanager", "critical"),
(
{
"awoooi_product_id": "momo-pro",
"awoooi_signal_family": "crawler_and_data_freshness",
},
"untrusted_ingress",
"critical",
),
(
{
"awoooi_product_id": "awoooi",
"awoooi_signal_family": "incident_lifecycle",
"awoooi_route_severity": "P1",
},
"signoz_webhook",
"critical",
),
(
{
"awoooi_product_id": "momo-pro",
"awoooi_signal_family": "crawler_and_data_freshness",
"awoooi_route_severity": "SEV-1",
},
"prometheus_alertmanager",
"critical",
),
],
)
def test_product_ingress_route_fails_closed_without_complete_trusted_context(
labels: dict[str, str], source: str, severity: str
) -> None:
assert get_trusted_alert_canonical_route_context(
labels,
source=source,
source_severity=severity,
) is None
@pytest.mark.parametrize("rule_file", ["alerts-unified.yml", "alerts.yml"])
def test_product_alert_rules_carry_canonical_route_labels(rule_file: str) -> None:
payload = yaml.safe_load((REPO_ROOT / "ops/monitoring" / rule_file).read_text())
rules = {
rule["alert"]: rule["labels"]
for group in payload["groups"]
for rule in group.get("rules", [])
if "alert" in rule
}
expected = {
"OpenClawDown": ("clawbot-openclaw", "raw_monitoring_alert", "P1"),
"MoWoooWorkDown": ("momo-pro", "scheduler_and_sales_pipeline", "P1"),
"TsenyangWebsiteDown": (
"tsenyang-website",
"public_site_and_agent_action_health",
"P1",
),
"StockWoooWorkDown": (
"stockplatform-v2",
"market_data_and_recommendation_freshness",
"P1",
),
"BitanWoooWorkDown": (
"bitan-pharmacy",
"container_and_product_health",
"P1",
),
"MomoScraperSuccessLow": (
"momo-pro",
"crawler_and_data_freshness",
"P2",
),
}
for alertname, route in expected.items():
labels = rules[alertname]
assert (
labels["awoooi_product_id"],
labels["awoooi_signal_family"],
labels["awoooi_route_severity"],
) == route
def test_specialized_openclaw_rule_cannot_fall_back_to_shared_sre() -> None:
payload = yaml.safe_load(
(REPO_ROOT / "k8s/monitoring/k3s-alerts-supplemental.yaml").read_text()
)
rule = next(
rule
for group in payload["groups"]
for rule in group.get("rules", [])
if rule.get("alert") == "OpenClawDown"
)
labels = rule["labels"]
assert {
key: labels[key]
for key in (
"awoooi_product_id",
"awoooi_signal_family",
"awoooi_route_severity",
)
} == {
"awoooi_product_id": "clawbot-openclaw",
"awoooi_signal_family": "raw_monitoring_alert",
"awoooi_route_severity": "P2",
}
def test_active_source_has_no_raw_numeric_telegram_destination_defaults() -> None:
candidate_paths = [
REPO_ROOT / "docker-compose.yml",
REPO_ROOT / "k8s/awoooi-prod/04-configmap.yaml",
REPO_ROOT / "scripts/ops/deploy-docker-health-monitor.sh",
REPO_ROOT / "scripts/ops/docker-health-monitor.sh",
*(REPO_ROOT / ".gitea/workflows").glob("*.y*ml"),
]
raw_numeric_identifier = re.compile(r"-?\d{8,}")
violations: list[str] = []
for path in candidate_paths:
for line_number, line in enumerate(path.read_text().splitlines(), 1):
if (
any(key in line for key in ("CHAT_ID", "USER_WHITELIST"))
and raw_numeric_identifier.search(line)
):
violations.append(f"{path.relative_to(REPO_ROOT)}:{line_number}")
assert violations == []
def test_registry_covers_all_11_products_and_reconciles_rollups() -> None:
registry = load_canonical_telegram_routing_registry()
assert {
product["product_id"] for product in registry["products"]
} == EXPECTED_PRODUCTS
assert registry["rollups"] == {
"product_count": 11,
"route_count": 20,
"allowed_route_count": 4,
"blocked_route_count": 13,
"disabled_route_count": 2,
"not_implemented_route_count": 1,
"shared_sre_allowed_route_count": 4,
"raw_numeric_destination_count": 0,
"runtime_route_change_count": 0,
"telegram_send_count": 0,
}
@pytest.mark.parametrize(
"signal_family",
[
"shared_infrastructure",
"security_incident",
"disaster_recovery",
"incident_lifecycle",
],
)
@pytest.mark.parametrize("severity", ["P0", "P1"])
def test_shared_sre_accepts_only_allowlisted_p0_p1_lifecycle(
signal_family: str, severity: str
) -> None:
decision = resolve_canonical_telegram_route("awoooi", signal_family, severity)
assert decision["decision"] == "allow"
assert decision["chat_alias"] == "awoooi_sre_war_room"
assert decision["allowed_destination"] == (
"telegram_chat_alias:awoooi_sre_war_room"
)
assert decision["receipt_backend"] == (
"awoooi.alert_operation_log+telegram_outbound_receipts"
)
@pytest.mark.parametrize(
("signal_family", "severity"),
[
("product_raw_monitoring", "P2"),
("product_raw_monitoring", "P3"),
("marketing", "P1"),
],
)
def test_shared_sre_blocks_raw_p2_p3_and_marketing(
signal_family: str, severity: str
) -> None:
decision = resolve_canonical_telegram_route("awoooi", signal_family, severity)
assert decision["decision"] == "block"
assert decision["allowed_destination"] == "blocked"
@pytest.mark.parametrize("product_id", ["awooogo", "vibework", "vtuber"])
def test_disabled_or_not_implemented_products_never_resolve_to_egress(
product_id: str,
) -> None:
decision = resolve_canonical_telegram_route(product_id, "any-signal", "P0")
assert decision["decision"] == "block"
assert decision["egress_status"] in {"disabled", "not_implemented"}
assert decision["allowed_destination"] == "blocked"
@pytest.mark.parametrize(
("product_id", "signal_family", "severity"),
[
("unknown-product", "shared_infrastructure", "P0"),
("awoooi", "unknown-family", "P0"),
("awoooi", "shared_infrastructure", "P3"),
("awoooi", "shared_infrastructure", "SEV-1"),
],
)
def test_unknown_route_dimensions_are_fail_closed(
product_id: str, signal_family: str, severity: str
) -> None:
decision = resolve_canonical_telegram_route(product_id, signal_family, severity)
assert decision["decision"] == "block"
assert decision["route_id"] is None
assert decision["allowed_destination"] == "blocked"
assert decision["reason"] == "unknown_product_signal_or_severity"
def test_requested_destination_must_match_the_canonical_alias() -> None:
decision = resolve_canonical_telegram_route(
"awoooi",
"security_incident",
"P0",
requested_destination="telegram_chat_alias:some_other_room",
)
assert decision["decision"] == "block"
assert decision["reason"] == "requested_destination_not_allowed"
def test_explicit_empty_registry_is_invalid_instead_of_loading_default() -> None:
with pytest.raises(ValueError, match="schema_version"):
resolve_canonical_telegram_route(
"awoooi",
"security_incident",
"P0",
registry={},
)
def test_product_level_blocked_disposition_prevents_route_egress() -> None:
decision = resolve_canonical_telegram_route(
"agent-bounty-protocol",
"a2a_task_lifecycle",
"P1",
)
assert decision["decision"] == "block"
assert decision["egress_status"] == "blocked"
assert decision["allowed_destination"] == "blocked"
def test_registry_contains_only_symbolic_aliases_not_runtime_identifiers() -> None:
registry = load_canonical_telegram_routing_registry()
serialized = json.dumps(registry, ensure_ascii=False)
assert '"chat_id"' not in serialized
assert '"bot_token"' not in serialized
for route in registry["routes"]:
for field in ("bot_alias", "chat_alias", "allowed_destination"):
assert not str(route[field]).lstrip("-").isdigit()
def test_shared_monitoring_routes_bind_tsenyang_owner_and_destination_policy() -> None:
registry = load_canonical_telegram_routing_registry()
shared_routes = [
route
for route in registry["routes"]
if route["egress_status"] == "allowed" and route["scope"] == "shared"
]
assert shared_routes
assert {route["bot_alias"] for route in shared_routes} == {"tsenyang_bot"}
owner_role = next(
role
for role in registry["destination_roles"]
if role["chat_alias"] == "awoooi_sre_war_room"
and role["bot_alias"] == "tsenyang_bot"
)
assert owner_role["bot_owner"] == "sre_shared_runtime"
assert owner_role["monitoring_owner"] is True
assert owner_role["monitoring_egress_policy"] == "allowlist_only"
@pytest.mark.parametrize(
("field", "value", "error"),
[
("bot_owner", "ai_agent_runtime", "bot ownership"),
("monitoring_owner", False, "not the monitoring owner"),
(
"monitoring_egress_policy",
"interaction_reply_only_no_monitoring_ownership",
"not monitoring allowlisted",
),
],
)
def test_tampered_shared_destination_ownership_is_rejected(
tmp_path: Path,
field: str,
value: object,
error: str,
) -> None:
registry = copy.deepcopy(load_canonical_telegram_routing_registry())
role = next(
role
for role in registry["destination_roles"]
if role["chat_alias"] == "awoooi_sre_war_room"
and role["bot_alias"] == "tsenyang_bot"
)
role[field] = value
_write_registry(tmp_path, registry)
with pytest.raises(ValueError, match=error):
load_canonical_telegram_routing_registry(tmp_path)
def test_tampered_shared_route_cannot_switch_to_openclaw_bot(tmp_path: Path) -> None:
registry = copy.deepcopy(load_canonical_telegram_routing_registry())
route = next(
route
for route in registry["routes"]
if route["route_id"] == "awoooi-security-incident"
)
route["bot_alias"] = "openclaw_bot"
_write_registry(tmp_path, registry)
with pytest.raises(ValueError, match="shared SRE P0/P1"):
load_canonical_telegram_routing_registry(tmp_path)
@pytest.mark.parametrize("visible_label", ["TsenYang", "小O_小龍蝦"])
def test_visible_labels_do_not_claim_proven_runtime_alias(
tmp_path: Path,
visible_label: str,
) -> None:
registry = copy.deepcopy(load_canonical_telegram_routing_registry())
role = next(
role
for role in registry["destination_roles"]
if role["visible_label"] == visible_label
)
assert role["runtime_alias_evidence"] == "visible_label_only"
assert role["proven_runtime_alias"] == "none"
role["proven_runtime_alias"] = role["chat_alias"]
_write_registry(tmp_path, registry)
with pytest.raises(ValueError, match="visible label must not claim"):
load_canonical_telegram_routing_registry(tmp_path)
def test_direct_egress_scanners_cover_agent99_and_reboot_recovery() -> None:
repo_root = Path(__file__).resolve().parents[3]
scanner_paths = [
repo_root / "scripts/security/telegram-notification-egress-inventory.py",
repo_root
/ "scripts/security/telegram-notification-egress-no-new-bypass-guard.py",
]
for scanner_path in scanner_paths:
source = scanner_path.read_text(encoding="utf-8")
assert 'Path("agent99-control-plane.ps1")' in source
assert 'Path("scripts/reboot-recovery")' in source
assert '".ps1"' in source
guarded_source = "\n".join(
[
(repo_root / "agent99-control-plane.ps1").read_text(encoding="utf-8"),
*(
path.read_text(encoding="utf-8", errors="replace")
for path in sorted((repo_root / "scripts/reboot-recovery").rglob("*"))
if path.is_file() and path.suffix in {".ps1", ".sh", ".py"}
),
]
)
assert "api.telegram.org/bot" not in guarded_source
def test_tampered_shared_sre_route_is_rejected(tmp_path: Path) -> None:
registry = copy.deepcopy(load_canonical_telegram_routing_registry())
route = next(
route
for route in registry["routes"]
if route["route_id"] == "awoooi-shared-infrastructure"
)
route["severity"].append("P3")
_write_registry(tmp_path, registry)
with pytest.raises(ValueError, match="shared SRE P0/P1"):
load_canonical_telegram_routing_registry(tmp_path)
def test_tampered_allowed_route_without_receipt_is_rejected(tmp_path: Path) -> None:
registry = copy.deepcopy(load_canonical_telegram_routing_registry())
route = next(
route for route in registry["routes"] if route["egress_status"] == "allowed"
)
route["receipt_backend"] = "none"
_write_registry(tmp_path, registry)
with pytest.raises(ValueError, match="receipt backend"):
load_canonical_telegram_routing_registry(tmp_path)
def test_tampered_unknown_destination_is_rejected(tmp_path: Path) -> None:
registry = copy.deepcopy(load_canonical_telegram_routing_registry())
route = next(
route for route in registry["routes"] if route["egress_status"] == "blocked"
)
route["allowed_destination"] = "telegram_chat_alias:guessed_room"
_write_registry(tmp_path, registry)
with pytest.raises(ValueError, match="non-allowed route must be blocked"):
load_canonical_telegram_routing_registry(tmp_path)
def test_tampered_raw_chat_identifier_is_rejected(tmp_path: Path) -> None:
registry = copy.deepcopy(load_canonical_telegram_routing_registry())
registry["routes"][0]["chat_alias"] = str(-(10**12))
_write_registry(tmp_path, registry)
with pytest.raises(ValueError, match="raw numeric runtime identifier"):
load_canonical_telegram_routing_registry(tmp_path)
def test_tampered_blocked_product_cannot_gain_an_allowed_route(tmp_path: Path) -> None:
registry = copy.deepcopy(load_canonical_telegram_routing_registry())
route = next(
route
for route in registry["routes"]
if route["route_id"] == "bitan-product-health"
)
route.update(
{
"bot_alias": "bitan_bot",
"chat_alias": "bitan_product_ops",
"allowed_destination": "telegram_chat_alias:bitan_product_ops",
"receipt_backend": "bitan.delivery_receipts",
"egress_status": "allowed",
}
)
_write_registry(tmp_path, registry)
with pytest.raises(
ValueError, match="bitan-pharmacy must not have an allowed route"
):
load_canonical_telegram_routing_registry(tmp_path)
def test_recursive_guard_rejects_sensitive_runtime_key(tmp_path: Path) -> None:
registry = copy.deepcopy(load_canonical_telegram_routing_registry())
registry["destination_roles"][0]["bot_token"] = "redacted-is-still-forbidden"
_write_registry(tmp_path, registry)
with pytest.raises(ValueError, match="forbidden sensitive runtime key"):
load_canonical_telegram_routing_registry(tmp_path)
@pytest.mark.parametrize(
"sensitive_key",
[
"api_key",
"password",
"credential",
"private_key",
"cookie",
"session",
],
)
def test_recursive_guard_rejects_extended_sensitive_runtime_keys(
tmp_path: Path,
sensitive_key: str,
) -> None:
registry = copy.deepcopy(load_canonical_telegram_routing_registry())
registry["destination_roles"][0][sensitive_key] = "redacted"
_write_registry(tmp_path, registry)
with pytest.raises(ValueError, match="forbidden sensitive runtime key"):
load_canonical_telegram_routing_registry(tmp_path)
def test_recursive_guard_rejects_secret_shaped_runtime_value(tmp_path: Path) -> None:
registry = copy.deepcopy(load_canonical_telegram_routing_registry())
registry["destination_roles"][0]["role"] = f"{10**7}:{'x' * 24}"
_write_registry(tmp_path, registry)
with pytest.raises(ValueError, match="secret-shaped runtime value"):
load_canonical_telegram_routing_registry(tmp_path)
@pytest.mark.parametrize(
("section", "field"),
[
("top", "status"),
("top", "destination_roles"),
("product", "owner"),
("route", "signal_family"),
("policy", "default_decision"),
("rollups", "route_count"),
("destination_role", "monitoring_egress_policy"),
("supersedes", "owner"),
("execution_boundaries", "telegram_send_allowed"),
],
)
def test_runtime_validator_rejects_missing_fields_at_every_object_level(
tmp_path: Path,
section: str,
field: str,
) -> None:
registry = copy.deepcopy(load_canonical_telegram_routing_registry())
target = _registry_object_for_section(registry, section)
target.pop(field)
_write_registry(tmp_path, registry)
with pytest.raises(ValueError, match="missing required fields"):
load_canonical_telegram_routing_registry(tmp_path)
@pytest.mark.parametrize(
"section",
[
"top",
"product",
"route",
"policy",
"rollups",
"destination_role",
"supersedes",
"execution_boundaries",
],
)
def test_runtime_validator_rejects_unexpected_fields_at_every_object_level(
tmp_path: Path,
section: str,
) -> None:
registry = copy.deepcopy(load_canonical_telegram_routing_registry())
target = _registry_object_for_section(registry, section)
target["unexpected_field"] = "must-fail"
_write_registry(tmp_path, registry)
with pytest.raises(ValueError, match="unexpected fields"):
load_canonical_telegram_routing_registry(tmp_path)
def test_schema_closes_every_data_object_and_validates_snapshot() -> None:
import jsonschema
repo_root = Path(__file__).resolve().parents[3]
schema = json.loads(
(
repo_root
/ "docs/schemas/telegram_canonical_routing_registry_v1.schema.json"
).read_text(encoding="utf-8")
)
def assert_closed_objects(node: object, path: str = "schema") -> None:
if isinstance(node, dict):
if node.get("type") == "object":
assert node.get("additionalProperties") is False, path
for key, nested in node.items():
assert_closed_objects(nested, f"{path}.{key}")
elif isinstance(node, list):
for index, nested in enumerate(node):
assert_closed_objects(nested, f"{path}[{index}]")
assert_closed_objects(schema)
jsonschema.Draft202012Validator.check_schema(schema)
jsonschema.Draft202012Validator(schema).validate(
load_canonical_telegram_routing_registry()
)
def _registry_object_for_section(registry: dict, section: str) -> dict:
if section == "top":
return registry
if section == "product":
return next(
product
for product in registry["products"]
if product["product_id"] == "awoooi"
)
if section == "route":
return registry["routes"][0]
if section == "destination_role":
return registry["destination_roles"][0]
return registry[section]
def _write_registry(directory: Path, registry: dict) -> None:
(directory / "telegram-canonical-routing-registry.snapshot.json").write_text(
json.dumps(registry),
encoding="utf-8",
)