Files
shawn 9cd8292acc
Lint & Validate / lint (push) Has been cancelled
Phase 1-4: Audio stack, mixer engine, MIDI, and network API
P2-R1: ALSA + JACK2 low-latency config (scripts, quirks, tuning)
P2-R2: Carla integration (build scripts, 8ch rack config, NAM LV2 support)
P2-R3: Plugin manager, categories, blacklist, NAM model support
P3-R1: Mixer DSP engine (channel strip, routing matrix, bus mgr, automation)
P4-R1: MIDI engine (learn mode, clock sync, HID discovery, mapping store)
P4-R2: Network API (OSC server, FastAPI REST, WebSocket, auth, rate limiter)
P5-R1: Touchscreen UI evaluation + main entry point
docs: Audio stack, Carla integration, MIDI support, UI evaluation
tests: Full test suite (292 passing)
2026-05-19 20:39:17 -04:00

113 lines
3.3 KiB
Python

"""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}