"""Security tests — auth required and error hygiene.""" from __future__ import annotations import pytest from httpx import ASGITransport, AsyncClient from app.core.config import settings from app.core.security import get_current_user from app.main import app from app.tests.conftest import TENANT_A, tenant_headers from shared.exceptions import UnauthorizedError from shared.security import CurrentUser @pytest.mark.asyncio async def test_unauthenticated_denied_when_auth_required(db_setup, monkeypatch): monkeypatch.setattr(settings, "auth_required", True) async def _deny(): raise UnauthorizedError("توکن احراز هویت ارائه نشده است") app.dependency_overrides[get_current_user] = _deny try: transport = ASGITransport(app=app) async with AsyncClient(transport=transport, base_url="http://testserver") as ac: resp = await ac.get( "/api/v1/programs", headers=tenant_headers(TENANT_A) ) assert resp.status_code == 401 body = resp.json() assert "error" in body assert "traceback" not in str(body).lower() assert "superapp_password" not in str(body) finally: app.dependency_overrides.pop(get_current_user, None) monkeypatch.setattr(settings, "auth_required", False) @pytest.mark.asyncio async def test_permission_denied_for_viewer_role(db_setup, monkeypatch): monkeypatch.setattr(settings, "auth_required", True) async def _viewer(): return CurrentUser(user_id="viewer", roles=["tenant_viewer"]) app.dependency_overrides[get_current_user] = _viewer try: transport = ASGITransport(app=app) async with AsyncClient(transport=transport, base_url="http://testserver") as ac: resp = await ac.post( "/api/v1/programs", json={"code": "SEC", "name": "Secured"}, headers=tenant_headers(TENANT_A), ) assert resp.status_code == 403 assert resp.json()["error"]["code"] in {"forbidden", "permission_denied"} finally: app.dependency_overrides.pop(get_current_user, None) monkeypatch.setattr(settings, "auth_required", False) @pytest.mark.asyncio async def test_invalid_tenant_header(client): resp = await client.get( "/api/v1/programs", headers={"X-Tenant-ID": "not-a-uuid"} ) assert resp.status_code == 400 assert resp.json()["error"]["code"] == "invalid_tenant_id"