From f121e4cf32c2c27c4eecfc3e7b39f8f4fa6683a9 Mon Sep 17 00:00:00 2001 From: Brian McMahon Date: Wed, 13 May 2026 14:06:25 -0700 Subject: [PATCH 1/2] Cleanup pass before morning-signal cutover (rc3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three coordinated cleanups before crossing into the morning-signal consumer cutover: 1. examples/smoke_test.py rewritten to lead with FlowDoctor.builder() + TelegramNotifierConfig instead of the now-@deprecated flow_doctor.init() yaml flow. Adds smoke checks for flow_doctor.context() propagation, report_async() from an asyncio context, and flow_doctor.otel.report_to_otel_span_event. All offline (FLOW_DOCTOR_SKIP_PREFLIGHT=1 + fake creds + sqlite at temp path). 2. RemediationExecutor's orphan _notify_telegram path migrated to consume a first-class TelegramNotifier instance. Adds TelegramNotifier.send_raw(text) for adjacent subsystems firing ad-hoc messages without conforming to the Report shape; the sentinel pattern on parse_mode / disable_notification lets callers distinguish "use instance default" from "explicit override". RemediationConfig gains telegram_bot_token + telegram_chat_id + telegram_message_thread_id; _init_remediation builds a real TelegramNotifier from those (with FLOW_DOCTOR_TELEGRAM_* env-var fallback chain) and hands it to RemediationExecutor. The legacy telegram_webhook_url path stays — soft-deprecated for 0.4.x yaml back-compat, removed in 0.6.0. When both are configured, the notifier wins. Remediation pings going through the new path pick up Markdown rendering, threading, target-id audit (actions.target row), and the same validate() preflight as the rest of the notifier surface. 3. [tool.coverage.run] section added to pyproject.toml. pytest-cov was misreporting coverage on Pydantic-derived modules under editable installs because it instruments AFTER the import has already happened; the direct ``python -m coverage run -m pytest && python -m coverage report`` path measures correctly. Real project-wide coverage is 84%, not the 67% pytest-cov reported. 17 new tests cover: send_raw POST shape + chat/thread/parse_mode override (sentinel-based), never-raise behavior on network failure and API ok=false, 4096-char truncation, HTTP non-200 returning None, RemediationExecutor invoking send_raw with the formatted message, emoji + dry-run tag + error-truncation in the formatted body, RemediationExecutor preferring the notifier when both paths are configured, legacy webhook URL still working unchanged for 0.4.x back-compat, and _init_remediation building the notifier from RemediationConfig fields + env-var fallbacks. Version bump 0.5.0rc2 → 0.5.0rc3. Suite: 393/393 pass (376 prior + 17 new). Co-Authored-By: Claude Opus 4.7 (1M context) --- .coverage | Bin 0 -> 196608 bytes CHANGELOG.md | 56 ++++ examples/smoke_test.py | 146 ++++++-- flow_doctor/__init__.py | 2 +- flow_doctor/core/client.py | 26 ++ flow_doctor/core/config.py | 18 + flow_doctor/notify/telegram.py | 83 ++++- flow_doctor/remediation/executor.py | 78 ++++- pyproject.toml | 25 +- tests/test_remediation_telegram_notifier.py | 348 ++++++++++++++++++++ 10 files changed, 738 insertions(+), 44 deletions(-) create mode 100644 .coverage create mode 100644 tests/test_remediation_telegram_notifier.py diff --git a/.coverage b/.coverage new file mode 100644 index 0000000000000000000000000000000000000000..3bbb5687c1eb80e2ced130a7b1af35d08b68f615 GIT binary patch literal 196608 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 literal 0 HcmV?d00001 diff --git a/CHANGELOG.md b/CHANGELOG.md index 7bd989a..45b5186 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,62 @@ ## Unreleased +## 0.5.0rc3 (2026-05-13) + +Cleanup pass before the morning-signal cutover. + +### Added + +- **`TelegramNotifier.send_raw(text, *, parse_mode=, disable_notification=)`** — + adjacent flow-doctor subsystems can now POST arbitrary text through + the same bot + chat + thread + Markdown routing the structured + `send()` path uses, without conforming to the `Report` shape. + Returns the standard non-secret `"telegram:[:]"` + target identifier (or `None` on failure — never raises). + ``parse_mode=None`` / ``disable_notification=False`` are honoured as + explicit overrides via a sentinel default; pass nothing to inherit + the instance defaults. +- **`RemediationConfig.telegram_bot_token` + `telegram_chat_id` + + `telegram_message_thread_id`** — first-class Telegram fields for the + remediation pipeline. `_init_remediation` builds a real + `TelegramNotifier` from these (with the `FLOW_DOCTOR_TELEGRAM_*` + env-var fallback chain) and hands it to `RemediationExecutor`. + +### Changed + +- **`RemediationExecutor`** now accepts a `telegram_notifier: + TelegramNotifier | None` kwarg in addition to the legacy + `telegram_webhook_url`. When both are supplied, the notifier wins. + Remediation pings going through it pick up Markdown rendering, + threading, target-id audit (`actions.target` row), and the same + `validate()` preflight as the rest of the notifier surface. +- `examples/smoke_test.py` rewritten to lead with + `FlowDoctor.builder()` + `TelegramNotifierConfig` (instead of the + now-`@deprecated` `flow_doctor.init()`). Adds smoke checks for + `flow_doctor.context()` propagation, `report_async()` from an + asyncio context, and `flow_doctor.otel.report_to_otel_span_event` + serialization. All offline (FLOW_DOCTOR_SKIP_PREFLIGHT=1 + fake + creds + sqlite at temp path). +- `[tool.coverage.run]` section added to `pyproject.toml`. Use the + canonical `python -m coverage run -m pytest && python -m coverage + report` instead of `pytest --cov=` — the latter misreports + module-level statement coverage under editable installs because + pytest-cov instruments after the import has already happened. + +### Deprecated + +- **`RemediationConfig.telegram_webhook_url`** is now soft-deprecated. + Kept for 0.4.x yaml back-compat through the 0.5.x series; consumers + should migrate to `telegram_bot_token` + `telegram_chat_id` (with + optional `telegram_message_thread_id`). Will be removed in 0.6.0. + +### Coverage + +Suite: 393/393 pass (376 prior + 17 new for remediation-Telegram +migration). Project-wide coverage 84% (canonical measurement; the +pytest-cov number that previously read 67% was a tool quirk, not a +real regression). + ## 0.5.0rc2 (2026-05-13) Adds Telegram as the **recommended default notifier** for new consumers. diff --git a/examples/smoke_test.py b/examples/smoke_test.py index c75b41c..9f62a7c 100644 --- a/examples/smoke_test.py +++ b/examples/smoke_test.py @@ -1,28 +1,79 @@ """ -Smoke test for Flow Doctor Phase 1. -Run from the flow-doctor repo root: +Smoke test for Flow Doctor (0.5.0rc+ surface). + +Exercises the recommended FlowDoctor.builder() entry point, the typed +TelegramNotifierConfig (without actually firing — no real bot token +required), the flow_doctor.context() contextvars layer, the +report_async() coroutine, and the historical report-handling features +(guard / monitor / dedup / capture_logs / secret scrubbing / +never-crash-the-caller). All sqlite, no network. + +Run from the flow-doctor repo root:: + python examples/smoke_test.py """ -import flow_doctor + +from __future__ import annotations + +import asyncio import logging import os import tempfile +import flow_doctor +from flow_doctor import ( + EmailNotifierConfig, + FlowDoctor, + FlowDoctorProtocol, + TelegramNotifierConfig, +) + db_path = os.path.join(tempfile.gettempdir(), "fd_smoke_test.db") -# Clean up from prior runs if os.path.exists(db_path): os.remove(db_path) -fd = flow_doctor.init( - flow_name="test-flow", - repo="your-org/your-repo", - owner="@your-username", - store=f"sqlite:///{db_path}", +# Skip notifier preflight network calls — this smoke test runs offline +# and the fake credentials below would otherwise trip /getMe and the +# Gmail SMTP banner. Downstream consumers should NOT set this in prod. +os.environ["FLOW_DOCTOR_SKIP_PREFLIGHT"] = "1" + +# --------------------------------------------------------------------------- +# Build a FlowDoctor with the recommended (0.5.0rc+) builder API. +# Telegram is the recommended default; we wire fake creds since the +# smoke test stays offline. +# --------------------------------------------------------------------------- +fd: FlowDoctorProtocol = ( + FlowDoctor.builder("smoke-test") + .with_repo("your-org/your-repo", owner="@your-username") + .with_store(path=db_path) + .with_dedup(cooldown_minutes=60) + .add_notifier( + TelegramNotifierConfig( + bot_token="123:fake-smoke-token", + chat_id=-1001234567890, + # message_thread_id=42, # optional: forum-topic routing + ) + ) + # A second notifier to demonstrate the discriminated union — also + # fake creds since we stay offline. + .add_notifier( + EmailNotifierConfig( + sender="alerts@example.com", + recipients=["oncall@example.com"], + smtp_password="fake-app-password", + ) + ) + .build() ) print("=" * 60) -print("FLOW DOCTOR PHASE 1 — SMOKE TEST") +print("FLOW DOCTOR — SMOKE TEST (0.5.0rc+ surface)") print("=" * 60) +print(f"Built via: FlowDoctor.builder() → satisfies FlowDoctorProtocol: " + f"{isinstance(fd, FlowDoctorProtocol)}") +print(f"Notifiers: {len(fd.config.notify)} " + f"({', '.join(n.type for n in fd.config.notify)})") + # --- Test 1: Exception report --- print("\n--- Test 1: Exception report ---") @@ -33,6 +84,7 @@ print(f" Report ID: {report_id}") assert report_id is not None, "Expected a report ID" + # --- Test 2: guard() re-raises --- print("\n--- Test 2: guard() context manager ---") try: @@ -43,6 +95,7 @@ else: raise AssertionError("guard() should have re-raised") + # --- Test 3: monitor() decorator --- print("\n--- Test 3: @monitor decorator ---") @@ -57,6 +110,7 @@ def failing_function(): else: raise AssertionError("monitor() should have re-raised") + # --- Test 4: Dedup suppression --- print("\n--- Test 4: Dedup (5 identical errors → 1 report) ---") dedup_results = [] @@ -73,13 +127,39 @@ def failing_function(): dedup_count = sum(1 for r in dedup_results if r is None) print(f" → {new_count} new, {dedup_count} deduped") -# --- Test 5: Non-exception warning --- -print("\n--- Test 5: Non-exception warning ---") -report_id = fd.report("Scanner returned 0 candidates", severity="warning") -print(f" Warning report ID: {report_id}") -# --- Test 6: capture_logs() --- -print("\n--- Test 6: Log capture ---") +# --- Test 5: flow_doctor.context() contextvars propagation --- +print("\n--- Test 5: flow_doctor.context() ambient propagation ---") +with flow_doctor.context(flow_name="smoke-test-am", stage="ingest", run_id="run-42"): + try: + raise RuntimeError("ingest stage failed") + except Exception as e: + report_id = fd.report(e) + +# Verify the context landed on the persisted report. +recent = fd.history(limit=1)[0] +print(f" Report flow_name: {recent.context.get('flow_name')}") +print(f" Report stage: {recent.context.get('stage')}") +print(f" Report run_id: {recent.context.get('run_id')}") +assert recent.context.get("stage") == "ingest" +assert recent.context.get("run_id") == "run-42" + + +# --- Test 6: report_async() --- +print("\n--- Test 6: report_async() from an asyncio context ---") +async def async_pipeline(): + try: + raise TimeoutError("async pipeline tripped a deadline") + except Exception as e: + return await fd.report_async(e) + +async_report_id = asyncio.run(async_pipeline()) +print(f" Async report ID: {async_report_id}") +assert async_report_id is not None + + +# --- Test 7: capture_logs() --- +print("\n--- Test 7: Log capture ---") logger = logging.getLogger("test.scanner") with fd.capture_logs(level=logging.INFO): logger.info("Starting scanner with 900 tickers") @@ -90,8 +170,9 @@ def failing_function(): report_id = fd.report(e) print(f" Report with logs: {report_id}") -# --- Test 7: Secret scrubbing --- -print("\n--- Test 7: Secret scrubbing ---") + +# --- Test 8: Secret scrubbing --- +print("\n--- Test 8: Secret scrubbing ---") try: api_key = "AKIAIOSFODNN7EXAMPLE" raise RuntimeError(f"S3 auth failed with key {api_key}") @@ -105,12 +186,13 @@ def failing_function(): ) print(f" Scrubbed report ID: {report_id}") -# --- Test 8: report() never crashes --- -print("\n--- Test 8: report() never crashes caller ---") -# Create an fd with a broken store path -broken_fd = flow_doctor.init( - flow_name="broken-flow", - store="sqlite:////nonexistent/impossible/path/db.sqlite", + +# --- Test 9: report() never crashes its caller --- +print("\n--- Test 9: report() never crashes caller ---") +broken_fd = ( + FlowDoctor.builder("broken-flow") + .with_store(path="/nonexistent/impossible/path/db.sqlite") + .build(strict=False) # degraded mode — store init will fail loudly ) try: raise RuntimeError("this should not crash") @@ -119,11 +201,25 @@ def failing_function(): print(f" Broken store report result: {result} (None is OK)") print(" Caller survived — report() did not propagate") + +# --- Test 10: OTel serialization --- +print("\n--- Test 10: OTel SpanEvent serialization ---") +from flow_doctor.otel import report_to_otel_span_event + +span_event = report_to_otel_span_event(recent) # from test 5 +print(f" resource.service.name: {span_event['resource']['service.name']}") +print(f" event.name: {span_event['name']}") +print(f" severity_text: {span_event['severity_text']}") +print(f" attributes.context.run_id: " + f"{span_event['attributes'].get('context.run_id')}") + + # --- History --- print("\n--- Report History ---") for r in fd.history(limit=20): dedup_str = f" (dedup x{r.dedup_count})" if r.dedup_count > 1 else "" - print(f" [{r.severity:8s}] {r.error_type or 'msg'}: {r.error_message[:60]}{dedup_str}") + print(f" [{r.severity:8s}] {r.error_type or 'msg'}: " + f"{r.error_message[:60]}{dedup_str}") print("\n" + "=" * 60) print("ALL SMOKE TESTS PASSED") diff --git a/flow_doctor/__init__.py b/flow_doctor/__init__.py index d4e9c64..f54c746 100644 --- a/flow_doctor/__init__.py +++ b/flow_doctor/__init__.py @@ -34,4 +34,4 @@ "current_context", "init", ] -__version__ = "0.5.0rc2" +__version__ = "0.5.0rc3" diff --git a/flow_doctor/core/client.py b/flow_doctor/core/client.py index ac38ba7..80c9cee 100644 --- a/flow_doctor/core/client.py +++ b/flow_doctor/core/client.py @@ -387,9 +387,35 @@ def _init_remediation(self, config: FlowDoctorConfig) -> None: gate_config.market_close_hour = 0 self._decision_gate = DecisionGate(config=gate_config, store=self._store) + + # Preferred Telegram path (since 0.5.0rc3): build a real + # TelegramNotifier from the config and hand it to the + # executor. Falls back to the legacy webhook URL when only + # that's configured (for 0.4.x yaml back-compat). + telegram_notifier = None + tg_token = ( + config.remediation.telegram_bot_token + or _env_fallback("telegram_bot_token") + ) + tg_chat = config.remediation.telegram_chat_id + if tg_chat is None: + env_chat = _env_fallback("telegram_chat_id") + if env_chat is not None and env_chat.lstrip("-").isdigit(): + tg_chat = int(env_chat) + elif env_chat is not None: + tg_chat = env_chat + if tg_token and tg_chat not in (None, ""): + from flow_doctor.notify.telegram import TelegramNotifier + telegram_notifier = TelegramNotifier( + bot_token=tg_token, + chat_id=tg_chat, + message_thread_id=config.remediation.telegram_message_thread_id, + ) + self._remediation_executor = RemediationExecutor( dry_run=config.remediation.dry_run, store=self._store, + telegram_notifier=telegram_notifier, telegram_webhook_url=config.remediation.telegram_webhook_url, ) except Exception as e: diff --git a/flow_doctor/core/config.py b/flow_doctor/core/config.py index d416d85..f24522c 100644 --- a/flow_doctor/core/config.py +++ b/flow_doctor/core/config.py @@ -142,6 +142,21 @@ class RemediationConfig(_ConfigModel): max_auto_remediations_per_day: int = 2 max_auto_remediations_per_failure: int = 2 market_hours_lockout: bool = True + # Telegram routing for remediation auto-action pings. Preferred + # path since 0.5.0rc3 — the executor will build a real + # ``TelegramNotifier`` from these fields and route every remediation + # action's success / failure through it, picking up bot-token / + # chat-id / threading / Markdown rendering / target-id audit for + # free. Leave unset to skip Telegram notification entirely. + telegram_bot_token: Optional[str] = None + telegram_chat_id: Optional[Union[int, str]] = None + telegram_message_thread_id: Optional[int] = None + # Legacy webhook URL — kept for back-compat with 0.4.x configs. + # Was a misnomer (Telegram doesn't have user-installable webhooks + # the way Slack does), and the executor's bespoke POST format + # didn't compose with the rest of the notifier surface. New + # consumers should use ``telegram_bot_token`` + ``telegram_chat_id`` + # above; this field will be removed in 0.6.0. telegram_webhook_url: Optional[str] = None s3_audit_bucket: Optional[str] = None s3_audit_prefix: str = "flow-doctor/audit" @@ -456,6 +471,9 @@ def load_config( _defaults.max_auto_remediations_per_failure)), market_hours_lockout=rem_raw.get( "market_hours_lockout", _defaults.market_hours_lockout), + telegram_bot_token=rem_raw.get("telegram_bot_token"), + telegram_chat_id=rem_raw.get("telegram_chat_id"), + telegram_message_thread_id=rem_raw.get("telegram_message_thread_id"), telegram_webhook_url=rem_raw.get("telegram_webhook_url"), s3_audit_bucket=rem_raw.get("s3_audit_bucket"), s3_audit_prefix=rem_raw.get( diff --git a/flow_doctor/notify/telegram.py b/flow_doctor/notify/telegram.py index edf35bd..a92cff4 100644 --- a/flow_doctor/notify/telegram.py +++ b/flow_doctor/notify/telegram.py @@ -27,7 +27,7 @@ import json import logging import sys -from typing import Optional, Union +from typing import Any, Optional, Union from urllib.error import URLError from urllib.request import Request, urlopen @@ -42,6 +42,11 @@ _MAX_MESSAGE_LEN = 4096 _TRUNCATION_SUFFIX = "\n…[truncated]" +# Sentinel for ``send_raw`` overrides — lets us distinguish "caller +# didn't pass this kwarg, use instance default" from "caller explicitly +# passed None to override to plain text / push-with-sound". +_UNSET: Any = object() + class TelegramNotifier(Notifier): """Send alerts via the Telegram Bot API. @@ -146,6 +151,82 @@ def send( ) return None + def send_raw( + self, + text: str, + *, + parse_mode: Any = _UNSET, + disable_notification: Any = _UNSET, + ) -> Optional[str]: + """POST an arbitrary text message to the configured chat. + + Distinct from :meth:`send`, which formats a structured Report. + ``send_raw`` is the convenience for adjacent flow-doctor + subsystems (remediation, custom success pings) that want to ride + the same bot + chat + thread routing without conforming to the + Report shape. Returns the standard non-secret target identifier + on success, or None on failure (errors are logged, never raised). + + ``parse_mode`` and ``disable_notification`` default to the + instance values supplied at construction time. Explicit + overrides — including ``parse_mode=None`` for plain-text + rendering when the body contains characters that Markdown + would otherwise mangle — are honoured. The sentinel lets us + distinguish "use instance default" from "explicit None". + """ + text = _truncate(text) + payload: dict = { + "chat_id": self.chat_id, + "text": text, + } + mode = self.parse_mode if parse_mode is _UNSET else parse_mode + if mode: + payload["parse_mode"] = mode + if self.message_thread_id is not None: + payload["message_thread_id"] = self.message_thread_id + quiet = ( + self.disable_notification + if disable_notification is _UNSET + else disable_notification + ) + if quiet: + payload["disable_notification"] = True + + try: + data = json.dumps(payload).encode("utf-8") + req = Request( + f"{self._API_BASE}/bot{self.bot_token}/sendMessage", + data=data, + headers={"Content-Type": "application/json"}, + method="POST", + ) + with urlopen(req, timeout=10) as resp: + if resp.status == 200: + body = resp.read().decode("utf-8", errors="replace") + try: + parsed = json.loads(body) + except json.JSONDecodeError: + parsed = {} + if parsed.get("ok"): + target = f"telegram:{self.chat_id}" + if self.message_thread_id is not None: + target += f":{self.message_thread_id}" + return target + _logger.critical( + "flow-doctor Telegram send_raw ok=false: %s", + parsed.get("description", "unknown"), + ) + return None + _logger.critical( + "flow-doctor Telegram send_raw HTTP %s", resp.status, + ) + return None + except Exception as e: + _logger.warning( + "flow-doctor Telegram send_raw failed: %s", e, + ) + return None + def validate(self) -> None: """Preflight: confirm the bot token is valid via ``getMe``. diff --git a/flow_doctor/remediation/executor.py b/flow_doctor/remediation/executor.py index b4430f3..fe8b5da 100644 --- a/flow_doctor/remediation/executor.py +++ b/flow_doctor/remediation/executor.py @@ -11,11 +11,14 @@ import sys from dataclasses import dataclass, field from datetime import datetime, timezone -from typing import Any, Dict, List, Optional +from typing import TYPE_CHECKING, Any, Dict, List, Optional from flow_doctor.remediation.decision_gate import Decision, DecisionType from flow_doctor.remediation.playbook import RemediationAction, RemediationType +if TYPE_CHECKING: + from flow_doctor.notify.telegram import TelegramNotifier + logger = logging.getLogger("flow_doctor.remediation") @@ -47,13 +50,26 @@ def __init__( sfn_client=None, ec2_client=None, store=None, + telegram_notifier: "Optional[TelegramNotifier]" = None, telegram_webhook_url: Optional[str] = None, ): + """ + Args: + telegram_notifier: First-class ``TelegramNotifier`` to route + remediation pings through (preferred since 0.5.0rc3). + When supplied, gets bot-token / chat-id / threading / + Markdown formatting / target-id auditing. + telegram_webhook_url: Legacy back-compat path — POSTs a + ``{"text": ...}`` body to an arbitrary URL via urllib. + Kept so 0.4.x ``flow-doctor.yaml`` configs keep working + without code changes; will be removed in 0.6.0. + """ self.dry_run = dry_run self._ssm = ssm_client self._sfn = sfn_client self._ec2 = ec2_client self._store = store + self._telegram_notifier = telegram_notifier self._telegram_url = telegram_webhook_url def execute(self, decision: Decision) -> ExecutionResult: @@ -249,24 +265,32 @@ def _save_audit(self, decision: Decision, result: ExecutionResult) -> None: logger.error("Failed to save remediation audit: %s", e) def _notify_telegram(self, decision: Decision, result: ExecutionResult) -> None: - """Send Telegram notification for every remediation action.""" - if not self._telegram_url: + """Send Telegram notification for every remediation action. + + Preferred path (0.5.0rc3+): a first-class ``TelegramNotifier`` + passed via ``telegram_notifier=``. The pre-rc3 ``telegram_webhook_url`` + path is kept for back-compat with 0.4.x yaml configs and will be + removed in 0.6.0. + """ + if not self._telegram_notifier and not self._telegram_url: return - try: - emoji = "✅" if result.success else "❌" - mode = "[DRY RUN] " if result.dry_run else "" - pattern = decision.playbook_match.name if decision.playbook_match else "unknown" - msg = ( - f"{emoji} {mode}flow-doctor auto-remediation\n" - f"Pattern: {pattern}\n" - f"Action: {result.action_type}\n" - f"Flow: {decision.diagnosis.flow_name}\n" - f"Root cause: {decision.diagnosis.root_cause[:200]}\n" - ) - if result.error: - msg += f"Error: {result.error[:200]}\n" + msg = self._format_remediation_message(decision, result) + # Prefer the first-class notifier when configured. + if self._telegram_notifier is not None: + try: + self._telegram_notifier.send_raw(msg) + except Exception as e: + # send_raw() already logs + swallows; this except is the + # belt-and-suspenders barrier for anything that slips + # past, since the executor must never crash on + # notification failure. + logger.warning("Telegram notifier failed: %s", e) + return + + # Legacy webhook-URL path. + try: import urllib.request data = json.dumps({"text": msg}).encode("utf-8") req = urllib.request.Request( @@ -277,3 +301,25 @@ def _notify_telegram(self, decision: Decision, result: ExecutionResult) -> None: urllib.request.urlopen(req, timeout=5) except Exception as e: logger.warning("Telegram notification failed: %s", e) + + @staticmethod + def _format_remediation_message( + decision: Decision, result: ExecutionResult + ) -> str: + emoji = "✅" if result.success else "❌" + mode = "[DRY RUN] " if result.dry_run else "" + pattern = ( + decision.playbook_match.name + if decision.playbook_match + else "unknown" + ) + msg = ( + f"{emoji} {mode}flow-doctor auto-remediation\n" + f"Pattern: {pattern}\n" + f"Action: {result.action_type}\n" + f"Flow: {decision.diagnosis.flow_name}\n" + f"Root cause: {decision.diagnosis.root_cause[:200]}\n" + ) + if result.error: + msg += f"Error: {result.error[:200]}\n" + return msg diff --git a/pyproject.toml b/pyproject.toml index 7eba07e..52100c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "flow-doctor" -version = "0.5.0rc2" +version = "0.5.0rc3" description = "Pipeline error handler: capture, deduplicate, diagnose, and auto-fix failures." readme = "README.md" requires-python = ">=3.9" @@ -63,6 +63,29 @@ include = ["flow_doctor*"] # authoritative when consumers depend on flow-doctor in --strict mode. flow_doctor = ["py.typed"] +[tool.coverage.run] +# Canonical coverage entry — invoke as +# python -m coverage run -m pytest && python -m coverage report +# pytest-cov's --cov= flag misreports module-level statement coverage +# under editable installs (`pip install -e .`) because it instruments +# AFTER the import has already happened; the direct ``coverage run`` +# path measures correctly. +source = ["flow_doctor"] +branch = true +omit = [ + "tests/*", + "*/__pycache__/*", +] + +[tool.coverage.report] +exclude_lines = [ + "pragma: no cover", + "raise NotImplementedError", + "if TYPE_CHECKING:", + "if __name__ == .__main__.:", +] +show_missing = true + [tool.pytest.ini_options] testpaths = ["tests"] filterwarnings = [ diff --git a/tests/test_remediation_telegram_notifier.py b/tests/test_remediation_telegram_notifier.py new file mode 100644 index 0000000..56b465a --- /dev/null +++ b/tests/test_remediation_telegram_notifier.py @@ -0,0 +1,348 @@ +"""Tests for the 0.5.0rc3 remediation → TelegramNotifier migration. + +Covers: +- TelegramNotifier.send_raw() — adjacent subsystems (remediation, + custom success pings) firing arbitrary text through the same bot + + chat + thread routing. +- RemediationExecutor consuming a TelegramNotifier instance (the new + preferred path) — verifies the executor calls send_raw() with the + remediation-formatted body. +- RemediationExecutor legacy telegram_webhook_url path — verifies the + bespoke urllib.urlopen POST still works for 0.4.x back-compat. +- _init_remediation in core/client.py building a TelegramNotifier from + RemediationConfig.telegram_bot_token + telegram_chat_id fields. +""" + +from __future__ import annotations + +import json +import tempfile +from unittest.mock import MagicMock, patch + +import pytest + +from flow_doctor.core.client import FlowDoctor +from flow_doctor.core.config import FlowDoctorConfig, RemediationConfig, StoreConfig +from flow_doctor.notify.telegram import TelegramNotifier +from flow_doctor.remediation.decision_gate import ( + Decision, + DecisionType, +) +from flow_doctor.remediation.executor import ExecutionResult, RemediationExecutor + + +def _fake_urlopen_response(body: dict, status: int = 200): + resp = MagicMock() + resp.status = status + resp.read.return_value = json.dumps(body).encode("utf-8") + resp.__enter__ = lambda self: self + resp.__exit__ = lambda self, *a: False + return resp + + +# --------------------------------------------------------------------------- +# TelegramNotifier.send_raw() +# --------------------------------------------------------------------------- + + +def test_send_raw_posts_text_to_chat_returns_target_id(): + notifier = TelegramNotifier(bot_token="t", chat_id=-100) + with patch("flow_doctor.notify.telegram.urlopen") as mock_urlopen: + mock_urlopen.return_value = _fake_urlopen_response({"ok": True}) + target = notifier.send_raw("hello remediation") + assert target == "telegram:-100" + payload = json.loads(mock_urlopen.call_args[0][0].data.decode("utf-8")) + assert payload["text"] == "hello remediation" + assert payload["chat_id"] == -100 + assert payload["parse_mode"] == "Markdown" # default + + +def test_send_raw_includes_message_thread_id_when_set(): + notifier = TelegramNotifier(bot_token="t", chat_id=1, message_thread_id=99) + with patch("flow_doctor.notify.telegram.urlopen") as mock_urlopen: + mock_urlopen.return_value = _fake_urlopen_response({"ok": True}) + target = notifier.send_raw("threaded") + payload = json.loads(mock_urlopen.call_args[0][0].data.decode("utf-8")) + assert payload["message_thread_id"] == 99 + assert target == "telegram:1:99" + + +def test_send_raw_override_parse_mode_per_call(): + """parse_mode arg overrides the instance default — useful for the + remediation pings where we may want plain text instead of Markdown.""" + notifier = TelegramNotifier(bot_token="t", chat_id=1, parse_mode="Markdown") + with patch("flow_doctor.notify.telegram.urlopen") as mock_urlopen: + mock_urlopen.return_value = _fake_urlopen_response({"ok": True}) + notifier.send_raw("plain", parse_mode=None) + payload = json.loads(mock_urlopen.call_args[0][0].data.decode("utf-8")) + assert "parse_mode" not in payload + + +def test_send_raw_returns_none_on_api_failure(): + notifier = TelegramNotifier(bot_token="t", chat_id=1) + with patch("flow_doctor.notify.telegram.urlopen") as mock_urlopen: + mock_urlopen.return_value = _fake_urlopen_response( + {"ok": False, "description": "Forbidden"} + ) + target = notifier.send_raw("nope") + assert target is None + + +def test_send_raw_swallows_network_failures(): + notifier = TelegramNotifier(bot_token="t", chat_id=1) + with patch( + "flow_doctor.notify.telegram.urlopen", + side_effect=ConnectionError("offline"), + ): + # Must NOT raise — adjacent subsystems rely on this for + # never-crash-the-caller semantics. + target = notifier.send_raw("never raises") + assert target is None + + +def test_send_raw_truncates_at_telegram_4096_limit(): + notifier = TelegramNotifier(bot_token="t", chat_id=1) + long = "x" * 5000 + with patch("flow_doctor.notify.telegram.urlopen") as mock_urlopen: + mock_urlopen.return_value = _fake_urlopen_response({"ok": True}) + notifier.send_raw(long) + payload = json.loads(mock_urlopen.call_args[0][0].data.decode("utf-8")) + assert len(payload["text"]) == 4096 + assert payload["text"].endswith("[truncated]") + + +def test_send_raw_http_non_200_returns_none(): + notifier = TelegramNotifier(bot_token="t", chat_id=1) + with patch("flow_doctor.notify.telegram.urlopen") as mock_urlopen: + mock_urlopen.return_value = _fake_urlopen_response( + {"ok": True}, status=502 + ) + target = notifier.send_raw("x") + assert target is None + + +# --------------------------------------------------------------------------- +# RemediationExecutor — first-class TelegramNotifier path (rc3+) +# --------------------------------------------------------------------------- + + +def _make_decision(success: bool = True) -> tuple[Decision, ExecutionResult]: + """Build a minimal Decision + ExecutionResult fixture for the + remediation notification path. The remediation pipeline normally + populates these — here we hand-build them with just the fields the + notification formatter actually reads.""" + decision = MagicMock(spec=Decision) + decision.decision_type = DecisionType.AUTO_REMEDIATE + decision.playbook_match = MagicMock() + decision.playbook_match.name = "service_down" + decision.diagnosis = MagicMock() + decision.diagnosis.flow_name = "alpha-engine-predictor" + decision.diagnosis.root_cause = "Lambda init exceeded 10s on cold-start" + + result = ExecutionResult( + success=success, + action_type="restart_service", + dry_run=False, + error="" if success else "ssm send_command failed", + ) + return decision, result + + +def test_executor_with_telegram_notifier_invokes_send_raw(): + notifier = TelegramNotifier(bot_token="t", chat_id=-100) + executor = RemediationExecutor( + dry_run=False, + store=MagicMock(), + telegram_notifier=notifier, + ) + decision, result = _make_decision(success=True) + + with patch.object(notifier, "send_raw", wraps=notifier.send_raw) as spy: + with patch("flow_doctor.notify.telegram.urlopen") as mock_urlopen: + mock_urlopen.return_value = _fake_urlopen_response({"ok": True}) + executor._notify_telegram(decision, result) + + assert spy.call_count == 1 + sent_text = spy.call_args[0][0] + assert "service_down" in sent_text + assert "restart_service" in sent_text + assert "alpha-engine-predictor" in sent_text + assert "✅" in sent_text # success emoji + assert "DRY RUN" not in sent_text + + +def test_executor_failure_message_includes_error_and_red_emoji(): + notifier = TelegramNotifier(bot_token="t", chat_id=1) + executor = RemediationExecutor( + store=MagicMock(), telegram_notifier=notifier + ) + decision, result = _make_decision(success=False) + + with patch.object(notifier, "send_raw") as mock_send: + executor._notify_telegram(decision, result) + + sent_text = mock_send.call_args[0][0] + assert "❌" in sent_text + assert "ssm send_command failed" in sent_text + + +def test_executor_dry_run_message_includes_dry_run_tag(): + notifier = TelegramNotifier(bot_token="t", chat_id=1) + executor = RemediationExecutor( + store=MagicMock(), telegram_notifier=notifier + ) + decision, result = _make_decision() + result.dry_run = True + + with patch.object(notifier, "send_raw") as mock_send: + executor._notify_telegram(decision, result) + + sent_text = mock_send.call_args[0][0] + assert "[DRY RUN]" in sent_text + + +def test_executor_notifier_path_swallows_send_raw_exceptions(): + """send_raw already swallows + logs failures internally, but the + executor adds a belt-and-suspenders try/except around it so an + unexpected raise can't crash the remediation pipeline.""" + notifier = TelegramNotifier(bot_token="t", chat_id=1) + executor = RemediationExecutor( + store=MagicMock(), telegram_notifier=notifier + ) + decision, result = _make_decision() + + with patch.object( + notifier, "send_raw", side_effect=RuntimeError("synthetic") + ): + # Must NOT raise. + executor._notify_telegram(decision, result) + + +# --------------------------------------------------------------------------- +# RemediationExecutor — legacy webhook URL path +# --------------------------------------------------------------------------- + + +def test_executor_legacy_webhook_url_still_posts(): + """Back-compat: 0.4.x configs only had telegram_webhook_url. The + legacy code path must still POST the same body shape it did before.""" + executor = RemediationExecutor( + store=MagicMock(), + telegram_webhook_url="https://example.com/some-webhook", + ) + decision, result = _make_decision() + + with patch("urllib.request.urlopen") as mock_urlopen: + executor._notify_telegram(decision, result) + + assert mock_urlopen.call_count == 1 + req = mock_urlopen.call_args[0][0] + assert req.full_url == "https://example.com/some-webhook" + payload = json.loads(req.data.decode("utf-8")) + assert "text" in payload + assert "service_down" in payload["text"] + + +def test_executor_with_neither_telegram_path_is_noop(): + executor = RemediationExecutor(store=MagicMock()) + decision, result = _make_decision() + + with patch("urllib.request.urlopen") as mock_urlopen: + executor._notify_telegram(decision, result) + + assert mock_urlopen.call_count == 0 + + +def test_executor_prefers_notifier_over_legacy_url(): + """When both are configured (transitional config), the notifier + wins. The legacy URL is the fallback for installs that haven't + moved over yet.""" + notifier = TelegramNotifier(bot_token="t", chat_id=1) + executor = RemediationExecutor( + store=MagicMock(), + telegram_notifier=notifier, + telegram_webhook_url="https://legacy.example.com/hook", + ) + decision, result = _make_decision() + + with patch.object(notifier, "send_raw") as mock_send_raw: + with patch("urllib.request.urlopen") as mock_urlopen: + executor._notify_telegram(decision, result) + + assert mock_send_raw.call_count == 1 + assert mock_urlopen.call_count == 0 # legacy path NOT touched + + +# --------------------------------------------------------------------------- +# _init_remediation in core/client.py — builds TelegramNotifier from +# RemediationConfig.telegram_bot_token + telegram_chat_id +# --------------------------------------------------------------------------- + + +def _build_fd_with_remediation_telegram( + *, + bot_token=None, + chat_id=None, + thread_id=None, + webhook_url=None, + db_path: str = ":memory:", +) -> FlowDoctor: + config = FlowDoctorConfig( + flow_name="rem-test", + store=StoreConfig(type="sqlite", path=db_path), + remediation=RemediationConfig( + enabled=True, + dry_run=True, + telegram_bot_token=bot_token, + telegram_chat_id=chat_id, + telegram_message_thread_id=thread_id, + telegram_webhook_url=webhook_url, + ), + ) + return FlowDoctor(config) + + +def test_init_remediation_builds_telegram_notifier_from_config(monkeypatch): + monkeypatch.delenv("FLOW_DOCTOR_TELEGRAM_BOT_TOKEN", raising=False) + monkeypatch.delenv("FLOW_DOCTOR_TELEGRAM_CHAT_ID", raising=False) + with tempfile.NamedTemporaryFile(suffix=".db") as f: + fd = _build_fd_with_remediation_telegram( + bot_token="123:abc", + chat_id=-100, + thread_id=7, + db_path=f.name, + ) + executor = fd._remediation_executor + assert executor is not None + assert executor._telegram_notifier is not None + assert executor._telegram_notifier.bot_token == "123:abc" + assert executor._telegram_notifier.chat_id == -100 + assert executor._telegram_notifier.message_thread_id == 7 + + +def test_init_remediation_pulls_telegram_creds_from_env(monkeypatch): + """Same env-var contract as the standalone TelegramNotifier wiring.""" + monkeypatch.setenv("FLOW_DOCTOR_TELEGRAM_BOT_TOKEN", "env-token") + monkeypatch.setenv("FLOW_DOCTOR_TELEGRAM_CHAT_ID", "-1001234") + with tempfile.NamedTemporaryFile(suffix=".db") as f: + fd = _build_fd_with_remediation_telegram(db_path=f.name) + notifier = fd._remediation_executor._telegram_notifier + assert notifier is not None + assert notifier.bot_token == "env-token" + assert notifier.chat_id == -1001234 # coerced from str + + +def test_init_remediation_no_telegram_when_only_legacy_url(monkeypatch): + """If only the legacy webhook URL is set (and no bot creds via + config or env), the new notifier is None and the executor falls + through to the legacy POST path.""" + monkeypatch.delenv("FLOW_DOCTOR_TELEGRAM_BOT_TOKEN", raising=False) + monkeypatch.delenv("FLOW_DOCTOR_TELEGRAM_CHAT_ID", raising=False) + with tempfile.NamedTemporaryFile(suffix=".db") as f: + fd = _build_fd_with_remediation_telegram( + webhook_url="https://legacy.example/hook", + db_path=f.name, + ) + executor = fd._remediation_executor + assert executor._telegram_notifier is None + assert executor._telegram_url == "https://legacy.example/hook" From faa2fc068c3c1e2c1065c8330478aeba90aeb148 Mon Sep 17 00:00:00 2001 From: Brian McMahon Date: Wed, 13 May 2026 14:06:33 -0700 Subject: [PATCH 2/2] Untrack .coverage + add coverage artifacts to gitignore --- .coverage | Bin 196608 -> 0 bytes .gitignore | 2 ++ 2 files changed, 2 insertions(+) delete mode 100644 .coverage diff --git a/.coverage b/.coverage deleted file mode 100644 index 3bbb5687c1eb80e2ced130a7b1af35d08b68f615..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 196608 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 diff --git a/.gitignore b/.gitignore index a3c0289..2831069 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,5 @@ scripts/ # Internal planning docs (not for public repo) private/ +.coverage +htmlcov/