"""API endpoint tests (no YubiKey required).""" import pytest from fastapi.testclient import TestClient from main import app ADMIN = {"username": "certauth", "password": "test-admin-pass-123"} @pytest.fixture(scope="module") def client(): with TestClient(app) as c: yield c @pytest.fixture(scope="module") def auth(client): r = client.post("/api/token", json=ADMIN) assert r.status_code == 200 return {"Authorization": f"Bearer {r.json()['access_token']}"} def test_health(client): r = client.get("/api/health") assert r.status_code == 200 assert r.json() == {"status": "ok"} def test_token_wrong_password(client): r = client.post("/api/token", json={"username": "certauth", "password": "wrong"}) assert r.status_code == 401 def test_token_unknown_user(client): r = client.post("/api/token", json={"username": "nobody", "password": "x"}) assert r.status_code == 401 def test_me(client, auth): r = client.get("/api/me", headers=auth) assert r.status_code == 200 assert r.json() == {"username": "certauth"} def test_me_without_token(client): assert client.get("/api/me").status_code == 401 def test_login_page_renders_with_csrf(client): r = client.get("/login") assert r.status_code == 200 assert "csrf_token" in r.text def test_dashboard_redirects_when_unauthenticated(client): r = client.get("/", follow_redirects=False) assert r.status_code == 302 assert r.headers["location"] == "/login" def test_login_requires_valid_csrf(client): r = client.post( "/login", data={"username": "certauth", "password": "test-admin-pass-123", "csrf_token": "bogus"}, follow_redirects=False, ) assert r.status_code == 200 assert "Invalid request" in r.text def test_domain_create_and_list(client, auth): r = client.post( "/api/domains", data={"name": "test.example.ms", "description": "unit test domain"}, headers=auth, ) assert r.status_code == 200 r = client.get("/api/domains", headers=auth) assert r.status_code == 200 names = [d["name"] for d in r.json()] assert "test.example.ms" in names def test_domain_requires_auth(client): r = client.post("/api/domains", data={"name": "x.ms"}) assert r.status_code == 401 def test_cert_request_lifecycle(client, auth): client.post( "/api/domains", data={"name": "certtest.example.ms"}, headers=auth, ) r = client.post( "/api/certs/request", data={"cn": "certtest.example.ms", "sans": "alt.example.ms"}, headers=auth, ) assert r.status_code == 200 cert_id = r.json()["id"] r = client.get("/api/certs", headers=auth) row = next(c for c in r.json() if c["id"] == cert_id) assert row["status"] == "pending" assert row["subject"] == "certtest.example.ms" # Signing requires a physical YubiKey; must fail cleanly, not 500-crash. r = client.post(f"/api/certs/{cert_id}/sign", headers=auth) assert r.status_code == 500 def test_sign_missing_cert(client, auth): assert client.post("/api/certs/99999/sign", headers=auth).status_code == 400