# 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`
