""" Tests for POST /issues/ ingestion endpoint. """ import os import tempfile import uuid from pathlib import Path import pytest pytest.importorskip("fastapi") pytest.importorskip("httpx") from fastapi.testclient import TestClient from issue_core.api.app import create_app API_KEY = "test-key-not-a-real-secret-only-for-pytest" @pytest.fixture def tmp_issue_store(monkeypatch, tmp_path): """Point cli.utils.get_config_dir at a tmp dir and the default backend at local.""" config_dir = tmp_path / "issue-core" config_dir.mkdir() monkeypatch.setattr( "issue_core.cli.utils.get_config_dir", lambda: config_dir, raising=True ) monkeypatch.setattr( "issue_core.api.ingest.get_config_dir", lambda: config_dir, raising=True ) db_path = str(config_dir / "issues.db") monkeypatch.setattr( "issue_core.cli.utils.load_backend_configs", lambda: {"default": "local", "local": {"type": "local", "db_path": db_path}}, raising=True, ) monkeypatch.setattr( "issue_core.api.ingest.load_backend_configs", lambda: {"default": "local", "local": {"type": "local", "db_path": db_path}}, raising=True, ) return config_dir @pytest.fixture def client(monkeypatch, tmp_issue_store): monkeypatch.setenv("ISSUE_CORE_API_KEY", API_KEY) return TestClient(create_app()) @pytest.fixture def valid_payload(): return { "title": "Fix the parser", "description": "Parser fails on multi-line input.", "target_repo": "coulomb/parser", "priority": "high", "labels": ["bug"], "due_in_days": 7, "source_type": "rule", "source_id": "rule:parse-failure", "triggering_event_id": str(uuid.uuid4()), "activity_definition_id": "ad:parser-monitor", } @pytest.mark.unit def test_healthz(client): response = client.get("/healthz") assert response.status_code == 200 assert response.json()["status"] == "ok" @pytest.mark.unit def test_ingest_rejects_missing_api_key(client, valid_payload): response = client.post("/issues/", json=valid_payload) assert response.status_code == 401 @pytest.mark.unit def test_ingest_rejects_wrong_api_key(client, valid_payload): response = client.post( "/issues/", json=valid_payload, headers={"Authorization": "Bearer wrong"} ) assert response.status_code == 401 @pytest.mark.unit def test_ingest_creates_issue_with_bearer(client, valid_payload): response = client.post( "/issues/", json=valid_payload, headers={"Authorization": f"Bearer {API_KEY}"}, ) assert response.status_code == 201, response.text body = response.json() assert body["backend"] == "sqlite" assert body["issue_id"] @pytest.mark.unit def test_ingest_creates_issue_with_x_api_key(client, valid_payload): response = client.post( "/issues/", json=valid_payload, headers={"X-API-Key": API_KEY}, ) assert response.status_code == 201, response.text @pytest.mark.unit def test_ingest_rejects_invalid_payload(client): bad = {"title": "no required fields"} response = client.post( "/issues/", json=bad, headers={"Authorization": f"Bearer {API_KEY}"} ) assert response.status_code == 422 @pytest.mark.unit def test_ingest_rejects_invalid_priority(client, valid_payload): valid_payload["priority"] = "urgent" response = client.post( "/issues/", json=valid_payload, headers={"Authorization": f"Bearer {API_KEY}"}, ) assert response.status_code == 422 @pytest.mark.unit def test_ingest_persists_traceability_metadata(client, valid_payload, tmp_issue_store): """Check that triggering_event_id, source_*, target_repo are stored on the issue.""" response = client.post( "/issues/", json=valid_payload, headers={"Authorization": f"Bearer {API_KEY}"}, ) assert response.status_code == 201, response.text issue_id = response.json()["issue_id"] from issue_core.backends.local import LocalSQLiteBackend backend = LocalSQLiteBackend() backend.connect({"type": "local", "db_path": str(tmp_issue_store / "issues.db")}) try: stored = backend.get_issue(issue_id) assert stored is not None ingestion = stored.sync_metadata.get("ingestion") or {} assert ingestion["target_repo"] == valid_payload["target_repo"] assert ingestion["source_type"] == valid_payload["source_type"] assert ingestion["source_id"] == valid_payload["source_id"] assert ingestion["triggering_event_id"] == valid_payload["triggering_event_id"] assert ingestion["activity_definition_id"] == valid_payload["activity_definition_id"] label_names = {label.name for label in stored.labels} assert f"priority:{valid_payload['priority']}" in label_names assert f"source:{valid_payload['source_type']}" in label_names assert f"repo:{valid_payload['target_repo']}" in label_names assert "bug" in label_names finally: backend.disconnect() @pytest.mark.unit def test_app_refuses_without_api_key_env(monkeypatch, tmp_issue_store, valid_payload): monkeypatch.delenv("ISSUE_CORE_API_KEY", raising=False) app = create_app() client = TestClient(app) response = client.post( "/issues/", json=valid_payload, headers={"Authorization": "Bearer anything"} ) assert response.status_code == 503 assert "ISSUE_CORE_API_KEY" in response.json()["detail"]