"""Tests for API key authentication.""" import os import pytest from fastapi import FastAPI, Depends from fastapi.testclient import TestClient from src.network.auth import ( APIKeyAuth, APIKeyHeader, require_api_key, API_KEY_HEADER, ) class TestAPIKeyAuth: """Tests for APIKeyAuth.""" def test_validate_correct_key(self): auth = APIKeyAuth(keys=["my-secret-key"]) assert auth.validate("my-secret-key") def test_validate_wrong_key(self): auth = APIKeyAuth(keys=["my-secret-key"]) assert not auth.validate("wrong-key") def test_validate_empty_key(self): auth = APIKeyAuth(keys=["my-secret-key"]) assert not auth.validate("") assert not auth.validate(None) def test_multiple_keys(self): auth = APIKeyAuth(keys=["key-a", "key-b", "key-c"]) assert auth.validate("key-a") assert auth.validate("key-b") assert auth.validate("key-c") assert not auth.validate("key-d") assert auth.get_keys_count() == 3 def test_empty_keys_list(self): auth = APIKeyAuth(keys=[]) assert not auth.validate("anything") assert auth.get_keys_count() == 0 def test_key_with_whitespace(self): auth = APIKeyAuth(keys=[" key-with-spaces "]) assert auth.validate("key-with-spaces") def test_constant_time_comparison(self): """Ensure timing doesn't leak key info (basic check).""" auth = APIKeyAuth(keys=["a" * 32]) # Should not raise for _ in range(10): auth.validate("b" * 32) auth.validate("a" * 32) def test_key_from_env(self, monkeypatch): monkeypatch.setenv("MIXER_API_KEY", "env-secret") auth = APIKeyAuth() assert auth.validate("env-secret") assert not auth.validate("wrong") def test_multikey_from_env(self, monkeypatch): monkeypatch.setenv("MIXER_API_KEY", "key1, key2,key3") auth = APIKeyAuth() assert auth.validate("key1") assert auth.validate("key2") assert auth.validate("key3") assert auth.get_keys_count() == 3 class TestRequireAPIKey: """Tests for require_api_key FastAPI dependency.""" @pytest.fixture def app(self): auth = APIKeyAuth(keys=["test-key"]) app = FastAPI() @app.get("/protected") async def protected(_: None = Depends(require_api_key(auth))): return {"ok": True} @app.get("/public") async def public(): return {"ok": True} return app @pytest.fixture def client(self, app): return TestClient(app) def test_public_endpoint_no_key(self, client): resp = client.get("/public") assert resp.status_code == 200 assert resp.json() == {"ok": True} def test_protected_no_key(self, client): resp = client.get("/protected") assert resp.status_code == 401 assert "Missing" in resp.json()["detail"] def test_protected_wrong_key(self, client): resp = client.get("/protected", headers={API_KEY_HEADER: "wrong"}) assert resp.status_code == 403 def test_protected_correct_key(self, client): resp = client.get("/protected", headers={API_KEY_HEADER: "test-key"}) assert resp.status_code == 200 assert resp.json() == {"ok": True}