关于 prowler-test-api
prowler-test-api 是供 Prowler API 贡献者使用的测试技能,提供 JSON:API 请求、租户隔离、测试夹具和 Celery 测试规范。
主要功能
- 01JSON:API 请求与响应模式
- 02租户隔离测试指引
- 03可复用测试夹具参考
- 04Celery 任务测试策略
- 05API 测试命令
适用场景
- 01测试 API 端点变更
- 02验证跨租户访问隔离
- 03测试异步视图与 Celery 任务
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ prowler-cloud/ prowler --skill prowler-test-api安装后在 Claude Code 中输入/prowler-test-api确认已加载。
附带文件assets/api_test.pyreferences/test-api-docs.md
详细介绍
关键规则
- 始终使用
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 依赖链
create_test_user (session) ─► tenants_fixture (function) ─► authenticated_client
│
└─► aws_provider ─► scans_fixture ─► findings_fixture
关键 Fixture
| Fixture | 说明 |
|---|---|
create_test_user | 会话用户([email protected]) |
tenants_fixture | 3 个租户:[0]、[1] 有成员关系,[2] 为隔离租户 |
authenticated_client | 使用 tenant[0] 的 JWT 的 Django 测试客户端 |
authenticated_client_for_tenant_factory | 创建一个使用指定用户和租户的 JWT 的 Django 测试客户端 |
provider_factory | 创建一个具有特定于提供方的默认值且已验证的提供方 |
aws_provider | tenant[0] 中的 1 个 AWS 提供方 |
aws_provider_pair | tenant[0] 中的 2 个 AWS 提供方 |
all_provider_types_fixture | 每种受支持的提供方类型各有 1 个提供方 |
tasks_fixture | 2 个带有 TaskResult 的 Celery 任务 |
RBAC Fixture
| Fixture | 权限 |
|---|---|
authenticated_client_rbac | 所有权限(管理员) |
authenticated_client_rbac_noroles | 有成员关系,但没有角色 |
authenticated_client_no_permissions_rbac | 所有权限均为 False |
普通视图行为测试使用 authenticated_client。它使用开销较低的 JWT,
同时仍会经过真实的请求身份验证流程。只有当测试专门针对令牌获取/刷新、
无效令牌、过期令牌、通过令牌切换租户、API 密钥或未通过身份验证时的 401
行为时,才使用序列化器生成的 JWT 或 API 密钥客户端。若测试需要为其他用户
或租户创建开销较低的 JWT 客户端,请使用
authenticated_client_for_tenant_factory。
2. JSON:API 请求
POST(创建)
response = client.post(
reverse("provider-list"),
data={"data": {"type": "providers", "attributes": {...}}},
format="vnd.api+json", # NOT content_type!
)
PATCH(更新)
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!
)
读取响应
data = response.json()["data"]
attrs = data["attributes"]
errors = response.json()["errors"] # For 400 responses
3. RLS 隔离(跨租户)
RLS 返回 404,而不是 403——该资源不可见,而非禁止访问。
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 任务测试
测试策略
| 策略 | 适用场景 |
|---|---|
模拟 .delay() + Task.objects.get | 测试会触发任务的视图 |
task.apply() | 同步测试任务逻辑 |
模拟 chain/group | 测试 Canvas 编排 |
模拟 connection | 测试 @set_tenant 装饰器 |
模拟 apply_async | 测试 Beat 定时任务 |
为什么不使用 task_always_eager
| 问题 | 影响 |
|---|---|
| 不进行任务序列化 | 无法发现参数类型错误 |
| 不与代理交互 | 会掩盖连接问题 |
| 执行上下文不同 | self.request 的行为会有所不同 |
请改用: 使用 task.apply() 进行同步执行,通过模拟实现隔离。
完整示例: 请参见 assets/api_test.py 中的
TestCeleryTaskLogic、TestCeleryCanvas、TestSetTenantDecorator和TestBeatScheduling。
5. 伪造密钥(TruffleHog)
# BAD - TruffleHog flags these:
api_key = "sk-test1234567890T3BlbkFJtest1234567890"
# GOOD - obviously fake:
api_key = "sk-fake-test-key-for-unit-testing-only"
6. 响应状态码
| 场景 | 代码 |
|---|---|
| GET 成功 | 200 |
| POST 成功 | 201 |
| 异步操作(DELETE/触发扫描) | 202 |
| 同步 DELETE | 204 |
| 验证错误 | 400 |
| 缺少权限(RBAC) | 403 |
| RLS 隔离/未找到 | 404 |
命令
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
资源
- 完整示例:请参见 assets/api_test.py 中的完整测试模式
- Fixture 参考:请参见 references/test-api-docs.md
- Fixture 源文件:
api/src/backend/conftest.py
评论
支持 Markdown