import pytest from httpx import AsyncClient pytestmark = pytest.mark.asyncio @pytest.fixture async def team_admin(client: AsyncClient, test_db): """Create a team admin user (gets own account via registration).""" from uuid import UUID as PyUUID from sqlalchemy import select from app.models.user import User data = { "email": "teamadmin@example.com", "password": "TeamAdmin123!", "name": "Team Admin" } response = await client.post("/api/v1/auth/register", json=data) assert response.status_code in (200, 201), f"Failed: {response.text}" user_id = PyUUID(response.json()["id"]) result = await test_db.execute(select(User).where(User.id == user_id)) user = result.scalar_one() user.is_team_admin = True await test_db.commit() return {"email": data["email"], "password": data["password"], "user_data": response.json()} @pytest.fixture async def team_admin_headers(client: AsyncClient, team_admin: dict): """Auth headers for team admin.""" response = await client.post( "/api/v1/auth/login/json", json={"email": team_admin["email"], "password": team_admin["password"]}, ) assert response.status_code == 200 return {"Authorization": f"Bearer {response.json()['access_token']}"} async def test_team_analytics_success(client: AsyncClient, admin_auth_headers: dict): """Super admin can access team analytics.""" response = await client.get( "/api/v1/analytics/team?period=30d", headers=admin_auth_headers, ) assert response.status_code == 200 data = response.json() assert "summary" in data assert "time_series" in data assert "top_flows" in data assert "top_engineers" in data assert "total_sessions" in data["summary"] assert "completion_rate" in data["summary"] assert "median_duration_minutes" in data["summary"] assert "active_engineers" in data["summary"] async def test_team_analytics_team_admin(client: AsyncClient, team_admin_headers: dict): """Team admin can also access team analytics.""" response = await client.get( "/api/v1/analytics/team", headers=team_admin_headers, ) assert response.status_code == 200 async def test_team_analytics_forbidden_for_engineer(client: AsyncClient, auth_headers: dict): """Regular engineers cannot access team analytics.""" response = await client.get( "/api/v1/analytics/team", headers=auth_headers, ) assert response.status_code == 403 async def test_personal_analytics_success(client: AsyncClient, auth_headers: dict): """Any user can access personal analytics.""" response = await client.get( "/api/v1/analytics/me?period=30d", headers=auth_headers, ) assert response.status_code == 200 data = response.json() assert "summary" in data assert "time_series" in data assert "top_flows" in data assert "median_duration_minutes" in data["summary"] async def test_personal_analytics_empty(client: AsyncClient, auth_headers: dict): """Personal analytics with no sessions returns zeroes.""" response = await client.get( "/api/v1/analytics/me?period=7d", headers=auth_headers, ) assert response.status_code == 200 data = response.json() assert data["summary"]["total_sessions"] == 0 assert data["summary"]["completion_rate"] == 0.0 assert data["summary"]["median_duration_minutes"] == 0.0 assert data["summary"]["active_engineers"] == 1 # always 1 for personal async def test_flow_analytics_success(client: AsyncClient, auth_headers: dict, test_tree: dict): """Can access analytics for a visible flow.""" response = await client.get( f"/api/v1/analytics/flows/{test_tree['id']}", headers=auth_headers, ) assert response.status_code == 200 data = response.json() assert "summary" in data assert "step_feedback" in data assert "recent_comments" in data assert "avg_csat" in data assert "total_ratings" in data async def test_flow_analytics_404(client: AsyncClient, auth_headers: dict): """Non-existent flow returns 404.""" import uuid response = await client.get( f"/api/v1/analytics/flows/{uuid.uuid4()}", headers=auth_headers, ) assert response.status_code == 404 async def test_invalid_period_rejected(client: AsyncClient, auth_headers: dict): """Invalid period values are rejected.""" response = await client.get( "/api/v1/analytics/me?period=1y", headers=auth_headers, ) assert response.status_code == 422