agnes-the-ai-analyst/tests/test_jira_webhooks.py
ZdenekSrotyr e25a7aba7d fix: resolve JWT secret key test isolation issue
Replace module-level SECRET_KEY cache with lazy _get_cached_secret_key()
that re-reads env vars in test mode. This fixes 20 test failures caused
by JWT secret mismatch when test modules load in different orders.
2026-04-12 14:05:41 +02:00

101 lines
3.3 KiB
Python

"""Tests for Jira webhook FastAPI router."""
import hashlib
import hmac
import json
import os
import tempfile
import pytest
from fastapi.testclient import TestClient
def _sign(payload: bytes, secret: str) -> str:
"""Compute sha256=<HMAC hex> for a given payload and secret."""
mac = hmac.new(secret.encode("utf-8"), payload, hashlib.sha256).hexdigest()
return f"sha256={mac}"
@pytest.fixture()
def webhook_client(tmp_path, monkeypatch):
"""Create a TestClient with required env vars and dirs."""
data_dir = tmp_path / "data"
data_dir.mkdir()
(data_dir / "issues").mkdir()
monkeypatch.setenv("DATA_DIR", str(data_dir))
monkeypatch.setenv("JWT_SECRET_KEY", "test-jwt-secret")
monkeypatch.setenv("JIRA_WEBHOOK_SECRET", "test-webhook-secret")
monkeypatch.setenv("JIRA_DATA_DIR", str(data_dir))
# Re-read env into Config (class attrs read os.environ at import time)
from connectors.jira import service as svc
monkeypatch.setattr(svc.Config, "JIRA_WEBHOOK_SECRET", "test-webhook-secret")
monkeypatch.setattr(svc.Config, "JIRA_DATA_DIR", data_dir)
# Reimport app to pick up router
from app.main import create_app
app = create_app()
return TestClient(app)
def test_health(webhook_client):
"""GET /webhooks/jira/health returns 200."""
resp = webhook_client.get("/webhooks/jira/health")
assert resp.status_code == 200
body = resp.json()
assert body["status"] == "ok"
assert "webhook_secret_set" in body
def test_missing_signature_401(webhook_client):
"""POST without signature header returns 401."""
payload = json.dumps({"webhookEvent": "jira:issue_updated", "issue": {"key": "TEST-1"}}).encode()
resp = webhook_client.post("/webhooks/jira", content=payload, headers={"Content-Type": "application/json"})
assert resp.status_code == 401
def test_invalid_signature_401(webhook_client):
"""POST with wrong signature returns 401."""
payload = json.dumps({"webhookEvent": "jira:issue_updated", "issue": {"key": "TEST-1"}}).encode()
resp = webhook_client.post(
"/webhooks/jira",
content=payload,
headers={
"Content-Type": "application/json",
"X-Hub-Signature-256": "sha256=badhex",
},
)
assert resp.status_code == 401
def test_valid_signature_accepted(webhook_client):
"""POST with correct HMAC-SHA256 is not rejected as 401."""
payload = json.dumps({"webhookEvent": "jira:issue_updated", "issue": {"key": "TEST-1"}}).encode()
sig = _sign(payload, "test-webhook-secret")
resp = webhook_client.post(
"/webhooks/jira",
content=payload,
headers={
"Content-Type": "application/json",
"X-Hub-Signature-256": sig,
},
)
# Should pass signature check; 200, 500 (service error), or 503 (not configured) are fine
# 500 can occur if JIRA_DATA_DIR points to a stale path from another test
assert resp.status_code in (200, 500, 503)
def test_empty_payload_400(webhook_client):
"""POST with empty body and valid signature returns 400."""
payload = b""
sig = _sign(payload, "test-webhook-secret")
resp = webhook_client.post(
"/webhooks/jira",
content=payload,
headers={
"Content-Type": "application/json",
"X-Hub-Signature-256": sig,
},
)
assert resp.status_code == 400