Skip to main content Testing patterns for Prowler API: JSON:API, Celery tasks, RLS isolation, RBAC.
Trigger: When writing tests for api/ (JSON:API requests/assertions, cross-tenant isolation, RBAC, Celery tasks, viewsets/serializers).
npx skills add prowler-cloud/prowler --skill prowler-test-api Read, Edit, Write, Glob, Grep, Bash, WebFetch, WebSearch, Task security security-tools security-audit security-hardening hardening aws
Critical Rules
ALWAYS use response.json()["data"] not response.data
ALWAYS use content_type = "application/vnd.api+json" for PATCH/PUT requests
ALWAYS use format="vnd.api+json" for POST requests
ALWAYS test cross-tenant isolation - RLS returns 404, NOT 403
NEVER skip RLS isolation tests when adding new endpoints
NEVER use realistic-looking API keys in tests (TruffleHog will flag them)
ALWAYS mock BOTH .delay() AND Task.objects.get for async task tests
1. Fixture Dependency Chain
create_test_user (session) ─► tenants_fixture (function) ─► authenticated_client
│
└─► aws_provider ─► scans_fixture ─► findings_fixture
Key Fixtures Fixture Description create_test_userSession user ([email protected] ) tenants_fixture3 tenants: [0],[1] have membership, [2] isolated authenticated_clientDjango test client with JWT for tenant[0] authenticated_client_for_tenant_factoryCreates a Django test client with JWT for a specific user and tenant provider_factoryCreates one validated provider with provider-specific defaults aws_provider1 AWS provider in tenant[0] aws_provider_pair2 AWS providers in tenant[0] all_provider_types_fixture1 provider for every supported provider type tasks_fixture2 Celery tasks with TaskResult
RBAC Fixtures Fixture Permissions authenticated_client_rbacAll permissions (admin) authenticated_client_rbac_norolesMembership but NO roles authenticated_client_no_permissions_rbacAll 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
POST (Create) response = client.post(
reverse("provider-list"),
data={"data": {"type": "providers", "attributes": {...}}},
format="vnd.api+json", # NOT content_type!
)
PATCH (Update) response = client.patch(
reverse("provider-detail", kwargs={"pk": provider.id}),
data={"data": {"type": "providers", "id": str(provider.id), "attributes": {...}}},
content_type="application/vnd.api+json", # NOT format!
)
Reading Responses data = response.json()["data"]
attrs = data["attributes"]
errors = response.json()["errors"] # For 400 responses
3. RLS Isolation (Cross-Tenant) RLS returns 404, NOT 403 - the resource is invisible, not forbidden.
def test_cross_tenant_access_denied(self, authenticated_client, tenants_fixture):
other_tenant = tenants_fixture[2] # Isolated tenant
foreign_provider = Provider.objects.create(tenant_id=other_tenant.id, ...)
response = authenticated_client.get(reverse("provider-detail", args=[foreign_provider.id]))
assert response.status_code == status.HTTP_404_NOT_FOUND # NOT 403!
4. Celery Task Testing
Testing Strategies Strategy Use For Mock .delay() + Task.objects.get Testing views that trigger tasks task.apply()Synchronous task logic testing Mock chain/group Testing Canvas orchestration Mock connection Testing @set_tenant decorator Mock apply_async Testing Beat scheduled tasks
Why NOT task_always_eager Problem Impact No task serialization Misses argument type errors No broker interaction Hides connection issues Different execution context self.request behaves differently
Instead, use: task.apply() for sync execution, mocking for isolation.
Full examples: See assets/api_test.py for TestCeleryTaskLogic, TestCeleryCanvas, TestSetTenantDecorator, TestBeatScheduling.
5. Fake Secrets (TruffleHog) # BAD - TruffleHog flags these:
api_key = "sk-test1234567890T3BlbkFJtest1234567890"
# GOOD - obviously fake:
api_key = "sk-fake-test-key-for-unit-testing-only"
6. Response Status Codes Scenario Code Successful GET 200 Successful POST 201 Async operation (DELETE/scan trigger) 202 Sync DELETE 204 Validation error 400 Missing permission (RBAC) 403 RLS isolation / not found 404
Commands cd api && uv run pytest -x --tb=short
cd api && uv run pytest -k "test_provider"
cd api && uv run pytest api/src/backend/api/tests/test_rbac.py
Resources Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Transcribe audio via OpenAI Audio Transcriptions API (Whisper).
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Set up and use 1Password CLI (op). Use when installing the CLI, enabling desktop app integration, signing in (single or multi-account), or reading/injecting/running secrets via op.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Transcribe audio via OpenAI Audio Transcriptions API (Whisper).
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Set up and use 1Password CLI (op). Use when installing the CLI, enabling desktop app integration, signing in (single or multi-account), or reading/injecting/running secrets via op.