fix(ci): tolerate empty source link canary response
This commit is contained in:
@@ -430,6 +430,53 @@ class AlertChainSmokeMetricTest(unittest.TestCase):
|
||||
self.assertIn(["target_incident_id", "INC-20260505-25E744"], tags)
|
||||
self.assertEqual(calls[0]["headers"]["X-AwoooP-Operator-Key"], "secret")
|
||||
|
||||
def test_source_link_canary_accepts_empty_2xx_for_downstream_readback(self):
|
||||
def fake_post(url, payload, *, headers=None, timeout=None):
|
||||
self.assertTrue(url.endswith("/api/v1/webhooks/sentry/error"))
|
||||
self.assertEqual(payload["data"]["issue"]["title"], "AwoooPSourceLinkCanary")
|
||||
return alert_chain_smoke_test.HttpGetResult(204, "")
|
||||
|
||||
original_post = alert_chain_smoke_test.http_post_json
|
||||
try:
|
||||
alert_chain_smoke_test.http_post_json = fake_post
|
||||
result = alert_chain_smoke_test.send_source_link_canary(
|
||||
"https://awoooi.example",
|
||||
target_incident_id="INC-20260505-25E744",
|
||||
operator_key="secret",
|
||||
operator_id="gitea-e2e-health",
|
||||
run_ref="run/123",
|
||||
)
|
||||
finally:
|
||||
alert_chain_smoke_test.http_post_json = original_post
|
||||
|
||||
self.assertTrue(result.passed)
|
||||
self.assertIn("source-correlation smoke must verify readback", result.message)
|
||||
|
||||
def test_source_link_canary_reports_http_error_before_json_parse(self):
|
||||
def fake_post(url, payload, *, headers=None, timeout=None):
|
||||
self.assertTrue(url.endswith("/api/v1/webhooks/sentry/error"))
|
||||
return alert_chain_smoke_test.HttpGetResult(
|
||||
502,
|
||||
"<html><body>bad gateway</body></html>",
|
||||
)
|
||||
|
||||
original_post = alert_chain_smoke_test.http_post_json
|
||||
try:
|
||||
alert_chain_smoke_test.http_post_json = fake_post
|
||||
result = alert_chain_smoke_test.send_source_link_canary(
|
||||
"https://awoooi.example",
|
||||
target_incident_id="INC-20260505-25E744",
|
||||
operator_key="secret",
|
||||
operator_id="gitea-e2e-health",
|
||||
run_ref="run/123",
|
||||
)
|
||||
finally:
|
||||
alert_chain_smoke_test.http_post_json = original_post
|
||||
|
||||
self.assertFalse(result.passed)
|
||||
self.assertIn("sentry HTTP 502", result.message)
|
||||
self.assertIn("bad gateway", result.message)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -137,6 +137,22 @@ def _http_error_message(error: Exception) -> str:
|
||||
return str(error)
|
||||
|
||||
|
||||
def _response_body_preview(text: str, limit: int = 240) -> str:
|
||||
cleaned = " ".join((text or "").split())
|
||||
if not cleaned:
|
||||
return "<empty body>"
|
||||
if len(cleaned) <= limit:
|
||||
return cleaned
|
||||
return f"{cleaned[:limit]}..."
|
||||
|
||||
|
||||
def _response_json(resp: HttpGetResult) -> dict[str, Any] | None:
|
||||
text = (resp.text or "").strip()
|
||||
if not text:
|
||||
return None
|
||||
return resp.json()
|
||||
|
||||
|
||||
def _api_health_probe_summary(attempt: int) -> str:
|
||||
return (
|
||||
f"attempts={attempt}/{API_HEALTH_ATTEMPTS}, "
|
||||
@@ -782,18 +798,43 @@ def send_source_provider_upstream_canary(
|
||||
},
|
||||
timeout=TIMEOUT,
|
||||
)
|
||||
data = resp.json()
|
||||
except (URLError, TimeoutError, OSError, json.JSONDecodeError) as e:
|
||||
except (URLError, TimeoutError, OSError) as e:
|
||||
return CheckResult(
|
||||
"Source Provider Upstream Canary",
|
||||
False,
|
||||
f"{provider} upstream canary failed: {_http_error_message(e)}",
|
||||
)
|
||||
if resp.status_code >= 400:
|
||||
try:
|
||||
data = _response_json(resp)
|
||||
except json.JSONDecodeError:
|
||||
data = None
|
||||
detail = (
|
||||
data.get("detail")
|
||||
if isinstance(data, dict)
|
||||
else _response_body_preview(resp.text)
|
||||
)
|
||||
return CheckResult(
|
||||
"Source Provider Upstream Canary",
|
||||
False,
|
||||
f"{provider} HTTP {resp.status_code}: {data.get('detail', resp.text) if isinstance(data, dict) else resp.text}",
|
||||
f"{provider} HTTP {resp.status_code}: {detail}",
|
||||
)
|
||||
try:
|
||||
data = _response_json(resp)
|
||||
except json.JSONDecodeError:
|
||||
return CheckResult(
|
||||
"Source Provider Upstream Canary",
|
||||
False,
|
||||
(
|
||||
f"{provider} upstream canary returned non-JSON "
|
||||
f"HTTP {resp.status_code}: {_response_body_preview(resp.text)}"
|
||||
),
|
||||
)
|
||||
if data is None:
|
||||
return CheckResult(
|
||||
"Source Provider Upstream Canary",
|
||||
False,
|
||||
f"{provider} upstream canary returned empty HTTP {resp.status_code}",
|
||||
)
|
||||
validation_error = _validate_upstream_canary_response(provider, data)
|
||||
if validation_error:
|
||||
@@ -850,18 +891,46 @@ def send_source_link_canary(
|
||||
},
|
||||
timeout=TIMEOUT,
|
||||
)
|
||||
data = resp.json()
|
||||
except (URLError, TimeoutError, OSError, json.JSONDecodeError) as e:
|
||||
except (URLError, TimeoutError, OSError) as e:
|
||||
return CheckResult(
|
||||
"Source Link Canary",
|
||||
False,
|
||||
f"sentry source-link canary failed: {_http_error_message(e)}",
|
||||
)
|
||||
if resp.status_code >= 400:
|
||||
try:
|
||||
data = _response_json(resp)
|
||||
except json.JSONDecodeError:
|
||||
data = None
|
||||
detail = (
|
||||
data.get("detail")
|
||||
if isinstance(data, dict)
|
||||
else _response_body_preview(resp.text)
|
||||
)
|
||||
return CheckResult(
|
||||
"Source Link Canary",
|
||||
False,
|
||||
f"sentry HTTP {resp.status_code}: {data.get('detail', resp.text) if isinstance(data, dict) else resp.text}",
|
||||
f"sentry HTTP {resp.status_code}: {detail}",
|
||||
)
|
||||
if not (resp.text or "").strip():
|
||||
return CheckResult(
|
||||
"Source Link Canary",
|
||||
True,
|
||||
(
|
||||
"accepted sentry source-link canary post with empty "
|
||||
f"HTTP {resp.status_code}; source-correlation smoke must verify readback"
|
||||
),
|
||||
)
|
||||
try:
|
||||
data = resp.json()
|
||||
except json.JSONDecodeError:
|
||||
return CheckResult(
|
||||
"Source Link Canary",
|
||||
False,
|
||||
(
|
||||
"sentry source-link canary returned non-JSON "
|
||||
f"HTTP {resp.status_code}: {_response_body_preview(resp.text)}"
|
||||
),
|
||||
)
|
||||
validation_error = _validate_upstream_canary_response("sentry", data)
|
||||
if validation_error:
|
||||
|
||||
Reference in New Issue
Block a user