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.fetch = AsyncMock(return_value=[]) 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