Files
onGuard24/tests/test_ingress.py
Alexandr a8ccf1d35c
Some checks failed
Deploy / deploy (push) Has been cancelled
CI / test (push) Successful in 37s
release: v1.9.0 — IRM-алерты отдельно от инцидентов
- Alembic 005: таблицы irm_alerts и incident_alert_links
- Модуль alerts: API/UI, Ack/Resolve, привязка к инциденту через alert_ids
- Вебхук Grafana: одна транзакция ingress + irm_alerts; разбор payload в grafana_payload
- По умолчанию инцидент из вебхука не создаётся (AUTO_INCIDENT_FROM_ALERT)
- Документация IRM_GRAFANA_PARITY.md, обновления IRM.md и CHANGELOG

Made-with: Cursor
2026-04-03 15:26:38 +03:00

181 lines
5.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import json
from unittest.mock import AsyncMock, MagicMock, patch
from fastapi.testclient import TestClient
def _webhook_mock_pool(mock_conn: AsyncMock) -> MagicMock:
"""Пул с транзакцией и execute — как после вставки ingress + irm_alerts."""
tx = AsyncMock()
tx.__aenter__ = AsyncMock(return_value=None)
tx.__aexit__ = AsyncMock(return_value=None)
mock_conn.transaction = MagicMock(return_value=tx)
mock_conn.execute = AsyncMock()
mock_cm = AsyncMock()
mock_cm.__aenter__ = AsyncMock(return_value=mock_conn)
mock_cm.__aexit__ = AsyncMock(return_value=None)
mock_pool = MagicMock()
mock_pool.acquire = MagicMock(return_value=mock_cm)
return mock_pool
def test_grafana_webhook_no_db(client: TestClient) -> None:
"""Без пула БД — 202, запись не падает."""
r = client.post(
"/api/v1/ingress/grafana",
content=json.dumps({"title": "t"}),
headers={"Content-Type": "application/json"},
)
assert r.status_code == 202
def test_grafana_webhook_unauthorized_when_secret_set(client: TestClient) -> None:
app = client.app
real = app.state.settings.grafana_webhook_secret
app.state.settings.grafana_webhook_secret = "s3cr3t"
try:
r = client.post(
"/api/v1/ingress/grafana",
content=b"{}",
headers={"Content-Type": "application/json"},
)
assert r.status_code == 401
r2 = client.post(
"/api/v1/ingress/grafana",
content=b"{}",
headers={"Content-Type": "application/json", "X-OnGuard-Secret": "s3cr3t"},
)
assert r2.status_code == 202
finally:
app.state.settings.grafana_webhook_secret = real
def test_grafana_webhook_inserts_with_mock_pool(client: TestClient) -> None:
from uuid import uuid4
mock_conn = AsyncMock()
uid = uuid4()
mock_conn.fetchrow = AsyncMock(return_value={"id": uid})
mock_pool = _webhook_mock_pool(mock_conn)
app = client.app
real_pool = app.state.pool
app.state.pool = mock_pool
try:
r = client.post(
"/api/v1/ingress/grafana",
content=json.dumps({"a": 1}),
headers={"Content-Type": "application/json"},
)
assert r.status_code == 202
mock_conn.fetchrow.assert_called_once()
mock_conn.execute.assert_called_once()
finally:
app.state.pool = real_pool
def test_grafana_webhook_auto_org_from_external_url(client: TestClient) -> None:
from uuid import uuid4
mock_conn = AsyncMock()
uid = uuid4()
mock_conn.fetchrow = AsyncMock(return_value={"id": uid})
mock_pool = _webhook_mock_pool(mock_conn)
app = client.app
real_pool = app.state.pool
app.state.pool = mock_pool
try:
r = client.post(
"/api/v1/ingress/grafana",
content=json.dumps(
{"externalURL": "https://grafana-adibrov.example.com/", "title": "x"}
),
headers={"Content-Type": "application/json"},
)
assert r.status_code == 202
assert mock_conn.fetchrow.call_args[0][3] == "grafana-adibrov.example.com"
finally:
app.state.pool = real_pool
def test_grafana_webhook_publishes_alert_received(client: TestClient) -> None:
from unittest.mock import patch
from uuid import uuid4
mock_conn = AsyncMock()
uid = uuid4()
mock_conn.fetchrow = AsyncMock(return_value={"id": uid})
mock_pool = _webhook_mock_pool(mock_conn)
app = client.app
bus = app.state.event_bus
with patch.object(bus, "publish_alert_received", new_callable=AsyncMock) as spy:
real_pool = app.state.pool
app.state.pool = mock_pool
try:
r = client.post(
"/api/v1/ingress/grafana",
content=json.dumps({"title": "x"}),
headers={"Content-Type": "application/json"},
)
assert r.status_code == 202
spy.assert_awaited_once()
assert spy.await_args.kwargs.get("raw_payload_ref") == uid
finally:
app.state.pool = real_pool
def test_grafana_webhook_org_any_slug_without_json_config(client: TestClient) -> None:
"""Путь /{slug} не требует GRAFANA_SOURCES_JSON — slug просто сохраняется."""
from uuid import uuid4
mock_conn = AsyncMock()
uid = uuid4()
mock_conn.fetchrow = AsyncMock(return_value={"id": uid})
mock_pool = _webhook_mock_pool(mock_conn)
app = client.app
real_pool = app.state.pool
app.state.pool = mock_pool
try:
r = client.post(
"/api/v1/ingress/grafana/other",
content=b"{}",
headers={"Content-Type": "application/json"},
)
assert r.status_code == 202
assert mock_conn.fetchrow.call_args[0][3] == "other"
finally:
app.state.pool = real_pool
def test_grafana_webhook_org_ok(client: TestClient) -> None:
from uuid import uuid4
mock_conn = AsyncMock()
uid = uuid4()
mock_conn.fetchrow = AsyncMock(return_value={"id": uid})
mock_pool = _webhook_mock_pool(mock_conn)
app = client.app
real_json = app.state.settings.grafana_sources_json
real_pool = app.state.pool
app.state.settings.grafana_sources_json = (
'[{"slug":"adibrov","api_url":"http://192.168.0.1:3000","api_token":"","webhook_secret":""}]'
)
app.state.pool = mock_pool
try:
r = client.post(
"/api/v1/ingress/grafana/adibrov",
content='{"title":"t"}',
headers={"Content-Type": "application/json"},
)
assert r.status_code == 202
call = mock_conn.fetchrow.call_args
assert "org_slug" in call[0][0].lower() or "org_slug" in str(call)
assert call[0][1] == "grafana"
assert call[0][3] == "adibrov"
finally:
app.state.settings.grafana_sources_json = real_json
app.state.pool = real_pool