kiwi/tests/api/test_feedback.py
pyr0ball 6791ea22b2 feat: complete feedback button — status probe, requests dep, tests
- feedback.py: add GET /feedback/status endpoint (returns {enabled: bool})
  so frontend can probe on mount instead of optimistic-enable; remove
  unused get_db import
- FeedbackButton.vue: probe /feedback/status on mount, start hidden;
  drop redundant 503-hide path (status probe makes it redundant)
- pyproject.toml: declare requests>=2.31 (used by feedback.py Forgejo calls)
- tests/api/test_feedback.py: 7 tests — status endpoint (no-token, token,
  demo mode), POST 503/403, happy path with mocked Forgejo, 502 on error
2026-04-03 16:52:40 -07:00

111 lines
4.1 KiB
Python

"""Tests for the /feedback endpoints."""
from __future__ import annotations
from unittest.mock import MagicMock, patch
import pytest
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
# ── /feedback/status ──────────────────────────────────────────────────────────
def test_status_disabled_when_no_token(monkeypatch):
monkeypatch.delenv("FORGEJO_API_TOKEN", raising=False)
monkeypatch.setattr("app.core.config.settings.DEMO_MODE", False)
res = client.get("/api/v1/feedback/status")
assert res.status_code == 200
assert res.json() == {"enabled": False}
def test_status_enabled_when_token_set(monkeypatch):
monkeypatch.setenv("FORGEJO_API_TOKEN", "test-token")
monkeypatch.setattr("app.core.config.settings.DEMO_MODE", False)
res = client.get("/api/v1/feedback/status")
assert res.status_code == 200
assert res.json() == {"enabled": True}
def test_status_disabled_in_demo_mode(monkeypatch):
monkeypatch.setenv("FORGEJO_API_TOKEN", "test-token")
monkeypatch.setattr("app.core.config.settings.DEMO_MODE", True)
res = client.get("/api/v1/feedback/status")
assert res.status_code == 200
assert res.json() == {"enabled": False}
# ── POST /feedback ────────────────────────────────────────────────────────────
def test_submit_returns_503_when_no_token(monkeypatch):
monkeypatch.delenv("FORGEJO_API_TOKEN", raising=False)
res = client.post("/api/v1/feedback", json={
"title": "Test", "description": "desc", "type": "bug",
})
assert res.status_code == 503
def test_submit_returns_403_in_demo_mode(monkeypatch):
monkeypatch.setenv("FORGEJO_API_TOKEN", "test-token")
monkeypatch.setattr("app.core.config.settings.DEMO_MODE", True)
res = client.post("/api/v1/feedback", json={
"title": "Test", "description": "desc", "type": "bug",
})
assert res.status_code == 403
def test_submit_creates_issue(monkeypatch):
monkeypatch.setenv("FORGEJO_API_TOKEN", "test-token")
monkeypatch.setenv("FORGEJO_REPO", "Circuit-Forge/kiwi")
monkeypatch.setattr("app.core.config.settings.DEMO_MODE", False)
# Mock the two Forgejo HTTP calls: label fetch + issue create
label_response = MagicMock()
label_response.ok = True
label_response.json.return_value = [
{"id": 1, "name": "beta-feedback"},
{"id": 2, "name": "needs-triage"},
{"id": 3, "name": "bug"},
]
issue_response = MagicMock()
issue_response.ok = True
issue_response.json.return_value = {"number": 42, "html_url": "https://example.com/issues/42"}
with patch("app.api.endpoints.feedback.requests.get", return_value=label_response), \
patch("app.api.endpoints.feedback.requests.post", return_value=issue_response):
res = client.post("/api/v1/feedback", json={
"title": "Something broke",
"description": "It broke when I tapped X",
"type": "bug",
"repro": "1. Open app\n2. Tap X",
"tab": "pantry",
})
assert res.status_code == 200
data = res.json()
assert data["issue_number"] == 42
assert data["issue_url"] == "https://example.com/issues/42"
def test_submit_returns_502_on_forgejo_error(monkeypatch):
monkeypatch.setenv("FORGEJO_API_TOKEN", "test-token")
monkeypatch.setattr("app.core.config.settings.DEMO_MODE", False)
label_response = MagicMock()
label_response.ok = True
label_response.json.return_value = []
bad_response = MagicMock()
bad_response.ok = False
bad_response.text = "forbidden"
with patch("app.api.endpoints.feedback.requests.get", return_value=label_response), \
patch("app.api.endpoints.feedback.requests.post", return_value=bad_response):
res = client.post("/api/v1/feedback", json={
"title": "Oops", "description": "desc", "type": "other",
})
assert res.status_code == 502