Files
stack/apps/coordinator/tests/conftest.py
Jason Woltje e23c09f1f2 feat(#157): Set up webhook receiver endpoint
Implement FastAPI webhook receiver for Gitea issue assignment events
with HMAC SHA256 signature verification and event routing.

Implementation details:
- FastAPI application with /webhook/gitea POST endpoint
- HMAC SHA256 signature verification in security.py
- Event routing for assigned, unassigned, closed actions
- Comprehensive logging for all webhook events
- Health check endpoint at /health
- Docker containerization with health checks
- 91% test coverage (exceeds 85% requirement)

TDD workflow followed:
- Wrote 16 tests first (RED phase)
- Implemented features to pass tests (GREEN phase)
- All tests passing with 91% coverage
- Type checking with mypy: success
- Linting with ruff: success

Files created:
- apps/coordinator/src/main.py - FastAPI application
- apps/coordinator/src/webhook.py - Webhook handlers
- apps/coordinator/src/security.py - HMAC verification
- apps/coordinator/src/config.py - Configuration management
- apps/coordinator/tests/ - Comprehensive test suite
- apps/coordinator/Dockerfile - Production container
- apps/coordinator/pyproject.toml - Python project config

Configuration:
- Updated .env.example with GITEA_WEBHOOK_SECRET
- Updated docker-compose.yml with coordinator service

Testing:
- 16 unit and integration tests
- Security tests for signature verification
- Event handler tests for all supported actions
- Health check endpoint tests
- All tests passing with 91% coverage

This unblocks issue #158 (issue parser).

Fixes #157

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-01 17:41:46 -06:00

121 lines
3.1 KiB
Python

"""Pytest fixtures for coordinator tests."""
import pytest
from fastapi.testclient import TestClient
@pytest.fixture
def webhook_secret() -> str:
"""Return a test webhook secret."""
return "test-webhook-secret-12345"
@pytest.fixture
def gitea_url() -> str:
"""Return a test Gitea URL."""
return "https://git.mosaicstack.dev"
@pytest.fixture
def sample_assigned_payload() -> dict[str, object]:
"""Return a sample Gitea 'assigned' issue webhook payload."""
return {
"action": "assigned",
"number": 157,
"issue": {
"id": 157,
"number": 157,
"title": "[COORD-001] Set up webhook receiver endpoint",
"state": "open",
"assignee": {
"id": 1,
"login": "mosaic",
"full_name": "Mosaic Bot",
},
},
"repository": {
"name": "stack",
"full_name": "mosaic/stack",
"owner": {"login": "mosaic"},
},
"sender": {
"id": 2,
"login": "admin",
"full_name": "Admin User",
},
}
@pytest.fixture
def sample_unassigned_payload() -> dict[str, object]:
"""Return a sample Gitea 'unassigned' issue webhook payload."""
return {
"action": "unassigned",
"number": 157,
"issue": {
"id": 157,
"number": 157,
"title": "[COORD-001] Set up webhook receiver endpoint",
"state": "open",
"assignee": None,
},
"repository": {
"name": "stack",
"full_name": "mosaic/stack",
"owner": {"login": "mosaic"},
},
"sender": {
"id": 2,
"login": "admin",
"full_name": "Admin User",
},
}
@pytest.fixture
def sample_closed_payload() -> dict[str, object]:
"""Return a sample Gitea 'closed' issue webhook payload."""
return {
"action": "closed",
"number": 157,
"issue": {
"id": 157,
"number": 157,
"title": "[COORD-001] Set up webhook receiver endpoint",
"state": "closed",
"assignee": {
"id": 1,
"login": "mosaic",
"full_name": "Mosaic Bot",
},
},
"repository": {
"name": "stack",
"full_name": "mosaic/stack",
"owner": {"login": "mosaic"},
},
"sender": {
"id": 2,
"login": "admin",
"full_name": "Admin User",
},
}
@pytest.fixture
def client(webhook_secret: str, gitea_url: str, monkeypatch: pytest.MonkeyPatch) -> TestClient:
"""Create a FastAPI test client with test configuration."""
# Set test environment variables
monkeypatch.setenv("GITEA_WEBHOOK_SECRET", webhook_secret)
monkeypatch.setenv("GITEA_URL", gitea_url)
monkeypatch.setenv("LOG_LEVEL", "debug")
# Force reload of settings
from src import config
import importlib
importlib.reload(config)
# Import app after settings are configured
from src.main import app
return TestClient(app)