mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-04 02:04:06 +00:00
test(api): reduce report, provider, auth, and sentry test costs (#11888)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user