Why Use This
This skill provides specialized capabilities for prowler-cloud's codebase.
Use Cases
- Developing new features in the prowler-cloud repository
- Refactoring existing code to follow prowler-cloud standards
- Understanding and working with prowler-cloud's codebase structure
Skill Snapshot
Auto scan of skill assets. Informational only.
Valid SKILL.md
Checks against SKILL.md specification
Source & Community
Updated At Jan 10, 2026, 09:54 AM
Skill Stats
SKILL.md 123 Lines
Total Files 1
Total Size 0 B
License Apache-2.0
---
name: prowler-test-api
description: >
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).
license: Apache-2.0
metadata:
author: prowler-cloud
version: "1.0"
scope: [root, api]
auto_invoke:
- "Writing Prowler API tests"
- "Testing RLS tenant isolation"
allowed-tools: Read, Edit, Write, Glob, Grep, Bash, WebFetch, WebSearch, Task
---
## Critical Rules
- ALWAYS use `response.json()["data"]` not `response.data`
- ALWAYS use `content_type = "application/vnd.api+json"` in requests
- ALWAYS test cross-tenant isolation with `other_tenant_provider` fixture
- NEVER skip RLS isolation tests when adding new endpoints
---
## 1. JSON:API Format (Critical)
```python
content_type = "application/vnd.api+json"
payload = {
"data": {
"type": "providers", # Plural, kebab-case
"id": str(resource.id), # Required for PATCH
"attributes": {"alias": "updated"},
}
}
response.json()["data"]["attributes"]["alias"]
```
---
## 2. RLS Isolation Tests
```python
def test_cross_tenant_access_denied(self, authenticated_client, other_tenant_provider):
"""User cannot see resources from other tenants."""
response = authenticated_client.get(
reverse("provider-detail", args=[other_tenant_provider.id])
)
assert response.status_code == status.HTTP_404_NOT_FOUND
```
---
## 3. RBAC Tests
```python
def test_unlimited_visibility_sees_all(self, authenticated_client_admin, providers_fixture):
response = authenticated_client_admin.get(reverse("provider-list"))
assert len(response.json()["data"]) == len(providers_fixture)
def test_limited_visibility_sees_only_assigned(self, authenticated_client_limited):
# User with unlimited_visibility=False sees only providers in their provider_groups
pass
def test_permission_required(self, authenticated_client_readonly):
response = authenticated_client_readonly.post(reverse("provider-list"), ...)
assert response.status_code == status.HTTP_403_FORBIDDEN
```
---
## 4. Managers (objects vs all_objects)
```python
def test_objects_excludes_deleted(self):
deleted_provider = Provider.objects.create(..., is_deleted=True)
assert deleted_provider not in Provider.objects.all()
assert deleted_provider in Provider.all_objects.all()
```
---
## 5. Celery Task Tests
```python
@patch("tasks.tasks.perform_prowler_scan")
def test_task_success(self, mock_scan):
mock_scan.return_value = {"findings_count": 100}
result = perform_scan_task(tenant_id="...", scan_id="...", provider_id="...")
assert result["findings_count"] == 100
```
---
## 6. Key Fixtures
| Fixture | Description |
|---------|-------------|
| `create_test_user` | Session user (dev@prowler.com) |
| `tenants_fixture` | 3 tenants (2 with membership, 1 isolated) |
| `providers_fixture` | Providers in tenant 1 |
| `other_tenant_provider` | Provider in isolated tenant (RLS tests) |
| `authenticated_client` | Client with JWT for tenant 1 |
---
## Commands
```bash
cd api && poetry run pytest -x --tb=short
cd api && poetry run pytest -k "test_provider"
cd api && poetry run pytest -k "TestRBAC"
```
---
## Resources
- **Documentation**: See [references/test-api-docs.md](references/test-api-docs.md) for local file paths and documentation