from __future__ import annotations from pathlib import Path from fastapi.testclient import TestClient from app.core.config import Settings from app.security.service import APIKeyService from main import create_app def test_sms_route_requires_authentication_and_execution_scope( tmp_path: Path, monkeypatch ) -> None: monkeypatch.delenv("HTTPSMS_API_KEY", raising=False) admin = APIKeyService.generate_material("test") settings = Settings( _env_file=None, temp_dir=tmp_path / "temp", output_dir=tmp_path / "outputs", database_url=f"sqlite+aiosqlite:///{tmp_path / 'security.db'}", auth_enabled=True, auth_bootstrap_key_hash=admin.key_hash, auth_bootstrap_key_prefix=admin.key_prefix, auth_bootstrap_environment="test", auth_default_requests_per_minute=1000, cleanup_interval_seconds=3600, whisper_model="tiny", max_workers=1, ) with TestClient(create_app(settings)) as client: payload = { "from": "+2348012345678", "to": "+2348098765432", "content": "authorization regression test", } unauthenticated = client.post("/v1/sms/send", json=payload) assert unauthenticated.status_code == 401 reader = client.post( "/v1/api-keys", headers={"Authorization": f"Bearer {admin.api_key}"}, json={ "name": "Template Reader", "environment": "test", "role": None, "scopes": ["templates:read"], }, ) assert reader.status_code == 201 forbidden = client.post( "/v1/sms/send", headers={"Authorization": f"Bearer {reader.json()['api_key']}"}, json=payload, ) assert forbidden.status_code == 403 executor = client.post( "/v1/api-keys", headers={"Authorization": f"Bearer {admin.api_key}"}, json={ "name": "SMS Executor", "environment": "test", "role": None, "scopes": ["operations:execute"], }, ) assert executor.status_code == 201 not_configured = client.post( "/v1/sms/send", headers={"Authorization": f"Bearer {executor.json()['api_key']}"}, json=payload, ) assert not_configured.status_code == 503