test(api): reduce report, provider, auth, and sentry test costs (#11888)

This commit is contained in:
Josema Camacho
2026-07-08 14:31:53 +02:00
committed by GitHub
parent d874fc573d
commit 3461f9ac49
29 changed files with 1561 additions and 1315 deletions
+15 -3
View File
@@ -31,7 +31,7 @@ allowed-tools: Read, Edit, Write, Glob, Grep, Bash, WebFetch, WebSearch, Task
```text
create_test_user (session) ─► tenants_fixture (function) ─► authenticated_client
│
└─► providers_fixture ─► scans_fixture ─► findings_fixture
└─► aws_provider ─► scans_fixture ─► findings_fixture
```
### Key Fixtures
@@ -40,8 +40,12 @@ create_test_user (session) ─► tenants_fixture (function) ─► authenticate
|---------|-------------|
| `create_test_user` | Session user (`dev@prowler.com`) |
| `tenants_fixture` | 3 tenants: [0],[1] have membership, [2] isolated |
| `authenticated_client` | JWT client for tenant[0] |
| `providers_fixture` | 9 providers in tenant[0] |
| `authenticated_client` | Django test client with JWT for tenant[0] |
| `authenticated_client_for_tenant_factory` | Creates a Django test client with JWT for a specific user and tenant |
| `provider_factory` | Creates one validated provider with provider-specific defaults |
| `aws_provider` | 1 AWS provider in tenant[0] |
| `aws_provider_pair` | 2 AWS providers in tenant[0] |
| `all_provider_types_fixture` | 1 provider for every supported provider type |
| `tasks_fixture` | 2 Celery tasks with TaskResult |
### RBAC Fixtures
@@ -52,6 +56,14 @@ create_test_user (session) ─► tenants_fixture (function) ─► authenticate
| `authenticated_client_rbac_noroles` | Membership but NO roles |
| `authenticated_client_no_permissions_rbac` | All permissions = False |
Use `authenticated_client` for normal view behavior tests. It uses a cheap JWT
and still runs the real request authentication path. Use serializer-generated
JWTs or API-key clients only when the test is specifically about token
obtain/refresh, invalid tokens, expired tokens, tenant switching by token, API
keys, or unauthenticated 401 behavior. Use
`authenticated_client_for_tenant_factory` when a test needs a cheap JWT client
for a different user or tenant.
---
## 2. JSON:API Requests
+24 -24
View File
@@ -22,12 +22,12 @@ from api.rls import Tenant
class TestProviderViewSet:
"""Example API tests for Provider endpoints."""
def test_list_providers(self, authenticated_client, providers_fixture):
def test_list_providers(self, authenticated_client, aws_provider):
"""GET list returns all providers for authenticated tenant."""
response = authenticated_client.get(reverse("provider-list"))
assert response.status_code == status.HTTP_200_OK
assert len(response.json()["data"]) == len(providers_fixture)
assert len(response.json()["data"]) == 1
def test_create_provider(self, authenticated_client):
"""POST with JSON:API format creates provider."""
@@ -49,9 +49,9 @@ class TestProviderViewSet:
assert response.status_code == status.HTTP_201_CREATED
assert response.json()["data"]["attributes"]["uid"] == "123456789012"
def test_update_provider(self, authenticated_client, providers_fixture):
def test_update_provider(self, authenticated_client, aws_provider):
"""PATCH with JSON:API format updates provider."""
provider = providers_fixture[0]
provider = aws_provider
payload = {
"data": {
@@ -95,7 +95,7 @@ class TestRLSIsolation:
assert response.status_code == status.HTTP_404_NOT_FOUND
def test_list_excludes_other_tenants(
self, authenticated_client, providers_fixture, tenants_fixture
self, authenticated_client, aws_provider, tenants_fixture
):
"""List endpoints only return resources from user's tenants."""
# Create provider in isolated tenant
@@ -109,8 +109,8 @@ class TestRLSIsolation:
response = authenticated_client.get(reverse("provider-list"))
assert response.status_code == status.HTTP_200_OK
# Should only see providers_fixture (9 providers in tenant[0])
assert len(response.json()["data"]) == len(providers_fixture)
# Should only see the AWS provider in tenant[0]
assert len(response.json()["data"]) == 1
@pytest.mark.django_db
@@ -136,7 +136,7 @@ class TestRBACPermissions:
response = authenticated_client_rbac_noroles.get(reverse("user-list"))
assert response.status_code == status.HTTP_403_FORBIDDEN
def test_admin_sees_all(self, authenticated_client_rbac, providers_fixture):
def test_admin_sees_all(self, authenticated_client_rbac, aws_provider):
"""Admin with unlimited_visibility=True sees all providers."""
response = authenticated_client_rbac.get(reverse("provider-list"))
assert response.status_code == status.HTTP_200_OK
@@ -153,11 +153,11 @@ class TestAsyncOperations:
mock_delete_task,
mock_task_get,
authenticated_client,
providers_fixture,
aws_provider,
tasks_fixture,
):
"""DELETE returns 202 Accepted with Content-Location header."""
provider = providers_fixture[0]
provider = aws_provider
prowler_task = tasks_fixture[0]
# Mock the Celery task
@@ -184,11 +184,11 @@ class TestAsyncOperations:
mock_scan_task,
mock_task_get,
authenticated_client,
providers_fixture,
aws_provider,
tasks_fixture,
):
"""POST to scan trigger returns 202 with task location."""
provider = providers_fixture[0]
provider = aws_provider
prowler_task = tasks_fixture[0]
task_mock = Mock()
@@ -208,9 +208,9 @@ class TestAsyncOperations:
class TestJSONAPIResponses:
"""Example JSON:API response handling."""
def test_read_single_resource(self, authenticated_client, providers_fixture):
def test_read_single_resource(self, authenticated_client, aws_provider):
"""Read data from single resource response."""
provider = providers_fixture[0]
provider = aws_provider
response = authenticated_client.get(
reverse("provider-detail", kwargs={"pk": provider.id})
)
@@ -222,12 +222,12 @@ class TestJSONAPIResponses:
assert resource_id == str(provider.id)
assert attrs["provider"] == provider.provider
def test_read_list_response(self, authenticated_client, providers_fixture):
def test_read_list_response(self, authenticated_client, aws_provider):
"""Read data from list response."""
response = authenticated_client.get(reverse("provider-list"))
items = response.json()["data"]
assert len(items) == len(providers_fixture)
assert len(items) == 1
def test_read_relationships(self, authenticated_client, scans_fixture):
"""Read relationship data."""
@@ -262,9 +262,9 @@ class TestJSONAPIResponses:
class TestSoftDelete:
"""Example soft-delete manager tests."""
def test_objects_excludes_soft_deleted(self, providers_fixture):
def test_objects_excludes_soft_deleted(self, aws_provider):
"""Default manager excludes soft-deleted records."""
provider = providers_fixture[0]
provider = aws_provider
provider.is_deleted = True
provider.save()
@@ -284,12 +284,12 @@ class TestSoftDelete:
class TestCeleryTaskLogic:
"""Example: Testing Celery task logic directly with apply()."""
def test_task_logic_directly(self, tenants_fixture, providers_fixture):
def test_task_logic_directly(self, tenants_fixture, aws_provider):
"""Use apply() for synchronous execution without Celery worker."""
from tasks.tasks import check_provider_connection_task
tenant = tenants_fixture[0]
provider = providers_fixture[0]
provider = aws_provider
# Execute task synchronously (no broker needed)
result = check_provider_connection_task.apply(
@@ -328,12 +328,12 @@ class TestSetTenantDecorator:
"""Example: Testing @set_tenant decorator behavior."""
@patch("api.decorators.connection")
def test_sets_rls_context(self, mock_conn, tenants_fixture, providers_fixture):
def test_sets_rls_context(self, mock_conn, tenants_fixture, aws_provider):
"""Verify @set_tenant sets RLS context via SET_CONFIG_QUERY."""
from tasks.tasks import check_provider_connection_task
tenant = tenants_fixture[0]
provider = providers_fixture[0]
provider = aws_provider
# Call task with tenant_id - decorator sets RLS and pops it
check_provider_connection_task.apply(
@@ -349,13 +349,13 @@ class TestBeatScheduling:
"""Example: Testing Beat scheduled task creation."""
@patch("tasks.beat.perform_scheduled_scan_task.apply_async")
def test_schedule_provider_scan(self, mock_apply, providers_fixture):
def test_schedule_provider_scan(self, mock_apply, aws_provider):
"""Verify periodic task is created with correct settings."""
from django_celery_beat.models import PeriodicTask
from tasks.beat import schedule_provider_scan
provider = providers_fixture[0]
provider = aws_provider
mock_apply.return_value = Mock(id="task-123")
schedule_provider_scan(provider)
@@ -24,7 +24,7 @@ create_test_user (session)
│ └─► authenticated_client
│ └─► (most API tests use this)
│
├─► providers_fixture
├─► aws_provider
│ └─► scans_fixture
│ └─► findings_fixture
│
@@ -102,12 +102,20 @@ Authentication tests:
```python
@pytest.mark.django_db
class TestProviderViewSet:
def test_list(self, authenticated_client, providers_fixture):
# authenticated_client has JWT for tenant[0]
# providers_fixture has 9 providers in tenant[0]
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
+2 -2
View File
@@ -164,9 +164,9 @@ class Test_ec2_ami_public:
```python
@pytest.mark.django_db
class TestResourceModel:
def test_create_resource_with_tags(self, providers_fixture):
def test_create_resource_with_tags(self, aws_provider):
# Given
provider, *_ = providers_fixture
provider = aws_provider
tenant_id = provider.tenant_id
# When