# API Test Documentation Reference ## File Locations | Type | Path | |------|------| | Central fixtures | `api/src/backend/conftest.py` | | API unit tests | `api/src/backend/api/tests/` | | Integration tests | `api/src/backend/api/tests/integration/` | | Task tests | `api/src/backend/tasks/tests/` | | Dev fixtures (JSON) | `api/src/backend/api/fixtures/dev/` | --- ## Fixture Dependency Graph ```text create_test_user (session) │ └─► tenants_fixture (function) │ ├─► set_user_admin_roles_fixture │ │ │ └─► authenticated_client │ └─► (most API tests use this) │ ├─► aws_provider │ └─► scans_fixture │ └─► findings_fixture │ └─► RBAC fixtures (create their own tenants/users): ├─► create_test_user_rbac │ └─► authenticated_client_rbac │ ├─► create_test_user_rbac_no_roles │ └─► authenticated_client_rbac_noroles │ ├─► create_test_user_rbac_limited │ └─► authenticated_client_no_permissions_rbac │ ├─► create_test_user_rbac_manage_account │ └─► authenticated_client_rbac_manage_account │ └─► create_test_user_rbac_manage_users_only └─► authenticated_client_rbac_manage_users_only ``` --- ## Test File Contents ### `api/src/backend/api/tests/test_views.py` Main ViewSet tests covering: - `TestUserViewSet` - User CRUD, password validation, deletion cascades - `TestTenantViewSet` - Tenant operations - `TestProviderViewSet` - Provider CRUD, async deletion, connection testing - `TestScanViewSet` - Scan trigger, list, filter - `TestFindingViewSet` - Finding queries, filters - `TestResourceViewSet` - Resource listing with tags - `TestTaskViewSet` - Celery task status - `TestIntegrationViewSet` - S3/Security Hub integrations - `TestComplianceOverviewViewSet` - Compliance data - And many more... ### `api/src/backend/api/tests/test_rbac.py` RBAC permission tests covering: - Permission checks for each ViewSet - Role-based access patterns - `unlimited_visibility` behavior - Provider group visibility filtering - Self-access patterns (`/me` endpoint) ### `api/src/backend/api/tests/integration/test_rls_transaction.py` RLS enforcement tests: - `rls_transaction` context manager - Invalid UUID validation - Custom parameter names ### `api/src/backend/api/tests/integration/test_providers.py` Provider integration tests: - Delete + recreate flow with async tasks - End-to-end provider lifecycle ### `api/src/backend/api/tests/integration/test_authentication.py` Authentication tests: - JWT token flow - API key authentication - Social login (SAML, OAuth) - Cross-tenant token isolation --- ## Key Test Classes and Their Fixtures ### Standard API Tests ```python @pytest.mark.django_db class TestProviderViewSet: def test_list(self, authenticated_client, aws_provider): # authenticated_client is a Django test client with JWT for tenant[0] # aws_provider creates one validated AWS provider in tenant[0] ... ``` Use serializer-generated JWTs or API-key clients for authentication behavior tests only: token obtain/refresh, invalid or expired tokens, token-scoped tenant switching, API keys, and unauthenticated 401 responses. Regular view tests should use `authenticated_client` so they still exercise `request.user`, `request.auth["tenant_id"]`, RLS, and RBAC without paying token serializer cost. Use `authenticated_client_for_tenant_factory` when a test needs the same cheap JWT path for a different user or tenant. ### RBAC Tests ```python @pytest.mark.django_db class TestProviderRBAC: def test_with_permission(self, authenticated_client_rbac, ...): # Has all permissions ... def test_without_permission(self, authenticated_client_no_permissions_rbac, ...): # Has no permissions (all False) ... ``` ### Cross-Tenant Tests ```python @pytest.mark.django_db class TestCrossTenantIsolation: def test_cannot_access_other_tenant(self, authenticated_client, tenants_fixture): other_tenant = tenants_fixture[2] # Isolated tenant # Create resource in other_tenant # Try to access with authenticated_client # Expect 404 ``` ### Async Task Tests ```python @pytest.mark.django_db class TestAsyncOperations: @patch("api.v1.views.Task.objects.get") @patch("api.v1.views.some_task.delay") def test_async_operation(self, mock_task, mock_task_get, tasks_fixture, ...): prowler_task = tasks_fixture[0] mock_task.return_value = Mock(id=prowler_task.id) mock_task_get.return_value = prowler_task # Execute and verify 202 response ``` --- ## Constants Available from conftest ```python from conftest import ( API_JSON_CONTENT_TYPE, # "application/vnd.api+json" NO_TENANT_HTTP_STATUS, # status.HTTP_401_UNAUTHORIZED TEST_USER, # "dev@prowler.com" TEST_PASSWORD, # "testing_psswd" TODAY, # str(datetime.today().date()) today_after_n_days, # Function: (n: int) -> str get_api_tokens, # Function: (client, email, password, tenant_id?) -> (access, refresh) get_authorization_header, # Function: (token) -> {"Authorization": f"Bearer {token}"} ) ``` --- ## Running Tests ```bash # Full test suite cd api && uv run pytest # Fast fail on first error cd api && uv run pytest -x # Short traceback cd api && uv run pytest --tb=short # Specific file cd api && uv run pytest api/src/backend/api/tests/test_views.py # Pattern match cd api && uv run pytest -k "Provider" # Verbose with print output cd api && uv run pytest -v -s # With coverage cd api && uv run pytest --cov=api --cov-report=html # Parallel execution cd api && uv run pytest -n auto ``` --- ## pytest Configuration From `api/pyproject.toml`: ```toml [tool.pytest.ini_options] DJANGO_SETTINGS_MODULE = "config.settings" python_files = "test_*.py" addopts = "--reuse-db" ``` Key points: - Uses `--reuse-db` for faster test runs - Settings from `config.settings` - Test files must match `test_*.py`