prowler-test-api
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCritical Rules
核心规则
- ALWAYS use not
response.json()["data"]response.data - ALWAYS use for PATCH/PUT requests
content_type = "application/vnd.api+json" - ALWAYS use for POST requests
format="vnd.api+json" - 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 AND
.delay()for async task testsTask.objects.get
- 始终使用 而非
response.json()["data"]response.data - 对PATCH/PUT请求始终使用
content_type = "application/vnd.api+json" - 对POST请求始终使用
format="vnd.api+json" - 始终测试跨租户隔离 - RLS返回404,而非403
- 添加新端点时绝不要跳过RLS隔离测试
- 测试中绝不要使用看起来真实的API密钥(TruffleHog会标记它们)
- 对异步任务测试始终同时模拟 和
.delay()Task.objects.get
1. Fixture Dependency Chain
1. 测试装置依赖链
create_test_user (session) ─► tenants_fixture (function) ─► authenticated_client
│
└─► providers_fixture ─► scans_fixture ─► findings_fixturecreate_test_user (session) ─► tenants_fixture (function) ─► authenticated_client
│
└─► providers_fixture ─► scans_fixture ─► findings_fixtureKey Fixtures
关键测试装置
| Fixture | Description |
|---|---|
| Session user ( |
| 3 tenants: [0],[1] have membership, [2] isolated |
| JWT client for tenant[0] |
| 9 providers in tenant[0] |
| 2 Celery tasks with TaskResult |
| 测试装置 | 说明 |
|---|---|
| 会话用户(dev@prowler.com) |
| 3个租户:[0]、[1]有成员关系,[2]为隔离租户 |
| 租户[0]的JWT客户端 |
| 租户[0]中的9个提供商 |
| 2个带TaskResult的Celery任务 |
RBAC Fixtures
RBAC测试装置
| Fixture | Permissions |
|---|---|
| All permissions (admin) |
| Membership but NO roles |
| All permissions = False |
| 测试装置 | 权限 |
|---|---|
| 所有权限(管理员) |
| 有成员关系但无角色 |
| 所有权限均为False |
2. JSON:API Requests
2. JSON:API请求
POST (Create)
POST(创建)
python
response = client.post(
reverse("provider-list"),
data={"data": {"type": "providers", "attributes": {...}}},
format="vnd.api+json", # NOT content_type!
)python
response = client.post(
reverse("provider-list"),
data={"data": {"type": "providers", "attributes": {...}}},
format="vnd.api+json", # NOT content_type!
)PATCH (Update)
PATCH(更新)
python
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!
)python
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
读取响应
python
data = response.json()["data"]
attrs = data["attributes"]
errors = response.json()["errors"] # For 400 responsespython
data = response.json()["data"]
attrs = data["attributes"]
errors = response.json()["errors"] # 针对400响应3. RLS Isolation (Cross-Tenant)
3. RLS隔离(跨租户)
RLS returns 404, NOT 403 - the resource is invisible, not forbidden.
python
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!RLS返回404,而非403 - 资源是不可见的,而非被禁止访问。
python
def test_cross_tenant_access_denied(self, authenticated_client, tenants_fixture):
other_tenant = tenants_fixture[2] # 隔离租户
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
4. Celery任务测试
Testing Strategies
测试策略
| Strategy | Use For |
|---|---|
Mock | Testing views that trigger tasks |
| Synchronous task logic testing |
Mock | Testing Canvas orchestration |
Mock | Testing |
Mock | Testing Beat scheduled tasks |
| 策略 | 适用场景 |
|---|---|
模拟 | 测试触发任务的视图 |
| 同步任务逻辑测试 |
模拟 | 测试Canvas编排 |
模拟 | 测试 |
模拟 | 测试Beat定时任务 |
Why NOT task_always_eager
task_always_eager为什么不使用 task_always_eager
task_always_eager| Problem | Impact |
|---|---|
| No task serialization | Misses argument type errors |
| No broker interaction | Hides connection issues |
| Different execution context | |
Instead, use: for sync execution, mocking for isolation.
task.apply()Full examples: See assets/api_test.py for,TestCeleryTaskLogic,TestCeleryCanvas,TestSetTenantDecorator.TestBeatScheduling
| 问题 | 影响 |
|---|---|
| 无任务序列化 | 遗漏参数类型错误 |
| 无代理交互 | 隐藏连接问题 |
| 执行上下文不同 | |
替代方案: 使用 进行同步执行,使用模拟实现隔离。
task.apply()完整示例: 查看 assets/api_test.py 中的、TestCeleryTaskLogic、TestCeleryCanvas、TestSetTenantDecorator。TestBeatScheduling
5. Fake Secrets (TruffleHog)
5. 虚假密钥(TruffleHog)
python
undefinedpython
undefinedBAD - TruffleHog flags these:
错误示例 - TruffleHog会标记这些:
api_key = "sk-test1234567890T3BlbkFJtest1234567890"
api_key = "sk-test1234567890T3BlbkFJtest1234567890"
GOOD - obviously fake:
正确示例 - 明显是虚假的:
api_key = "sk-fake-test-key-for-unit-testing-only"
---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 |
| 场景 | 状态码 |
|---|---|
| GET请求成功 | 200 |
| POST请求成功 | 201 |
| 异步操作(删除/扫描触发) | 202 |
| 同步删除 | 204 |
| 验证错误 | 400 |
| 缺少权限(RBAC) | 403 |
| RLS隔离 / 资源不存在 | 404 |
Commands
命令
bash
cd api && poetry run pytest -x --tb=short
cd api && poetry run pytest -k "test_provider"
cd api && poetry run pytest api/src/backend/api/tests/test_rbac.pybash
cd api && poetry run pytest -x --tb=short
cd api && poetry run pytest -k "test_provider"
cd api && poetry run pytest api/src/backend/api/tests/test_rbac.pyResources
资源
- Full Examples: See assets/api_test.py for complete test patterns
- Fixture Reference: See references/test-api-docs.md
- Fixture Source:
api/src/backend/conftest.py
- 完整示例:查看 assets/api_test.py 获取完整测试模式
- 测试装置参考:查看 references/test-api-docs.md
- 测试装置源码:
api/src/backend/conftest.py