Files
micro_servers/tests/blackbox/test_captcha.py
T
2026-07-28 09:39:37 +08:00

148 lines
5.4 KiB
Python

"""
captcha 黑盒用户模拟测试 — 获取验证码 → 校验 → 一次性消费。
用户旅程:
1. 请求 /captcha/image 获取验证码图片 + token
2. 使用正确答案校验 → 应通过
3. 使用错误答案校验 → 应拒绝
4. 同一 token 重复校验 → 应拒绝 (一次性)
5. 缺少 token/answer → 应报错
6. 不存在的 token → 应拒绝
7. 健康检查
"""
import json
import string
from helpers import (
http_get, http_post, http_request,
assert_eq, assert_status, assert_true, assert_json_key, assert_contains,
skip, Counter, check_service,
)
from config import get_url
CAPTCHA_URL = get_url("captcha")
def run(url: str = None):
"""captcha 用户模拟测试入口。返回 (passed, failed, skipped)。"""
cu = url or CAPTCHA_URL
c = Counter()
print(f"\n--- captcha 验证码获取与校验流程 ---\n")
# ------------------------------------------------------------------
# 0. 检查服务可用性
# ------------------------------------------------------------------
if not check_service(cu)[0]:
print(" captcha 服务不可达,跳过所有测试")
c.add_skip()
return c.passed, c.failed, c.skipped
# ------------------------------------------------------------------
# 场景 1: 获取验证码图片
# ------------------------------------------------------------------
status, body, headers = http_get(f"{cu}/captcha/image")
assert_eq(status, 200, "获取验证码图片 (200)", c)
captcha_token = headers.get("X-Captcha-Token", headers.get("x-captcha-token", ""))
assert_true(
len(captcha_token) == 32,
"获取验证码-Token 32位hex", c,
f"token={captcha_token[:10]}...",
)
# 验证返回的是图片
content_type = headers.get("Content-Type", headers.get("content-type", ""))
assert_contains(content_type, "image", "获取验证码-图片类型", c)
# ------------------------------------------------------------------
# 场景 2: 缺少参数 — token 和 answer 都缺
# ------------------------------------------------------------------
status, body, _ = http_post(f"{cu}/captcha/verify", {})
data = json.loads(body) if body else {}
assert_true(
not data.get("valid", True),
"校验-缺少参数-拒绝", c,
)
# ------------------------------------------------------------------
# 场景 3: 不存在的 token
# ------------------------------------------------------------------
status, body, _ = http_post(f"{cu}/captcha/verify", {
"token": "0" * 32,
"answer": "abcde",
})
data = json.loads(body) if body else {}
assert_true(
not data.get("valid", True),
"校验-不存在token-拒绝", c,
)
# ------------------------------------------------------------------
# 场景 4: 错误答案
# ------------------------------------------------------------------
if captcha_token:
status, body, _ = http_post(f"{cu}/captcha/verify", {
"token": captcha_token,
"answer": "ZZZZZ",
})
data = json.loads(body) if body else {}
assert_true(
not data.get("valid", True),
"校验-错误答案-拒绝", c,
)
# ------------------------------------------------------------------
# 场景 5: 验证码一次性消费 — 拿新 token 分别测正确答案和二次使用
# ------------------------------------------------------------------
# 重新获取一个 captcha(上一步的 token 已被消费)
status2, body2, headers2 = http_get(f"{cu}/captcha/image")
assert_eq(status2, 200, "获取第二个验证码 (200)", c)
# 没法从图片自动识别文字,但我们可以测试一次性消费的行为
# 先用错误答案消费 token
token2 = headers2.get("X-Captcha-Token", headers.get("x-captcha-token", ""))
if token2:
# 第一次:错误答案
status_a, body_a, _ = http_post(f"{cu}/captcha/verify", {
"token": token2,
"answer": "WRONG",
})
data_a = json.loads(body_a) if body_a else {}
assert_true(
not data_a.get("valid", True),
"一次性消费-第一次用完", c,
)
# 第二次:同一 token 再次使用
status_b, body_b, _ = http_post(f"{cu}/captcha/verify", {
"token": token2,
"answer": "WRONG",
})
data_b = json.loads(body_b) if body_b else {}
assert_true(
not data_b.get("valid", True),
"一次性消费-重复使用拒绝", c,
)
# ------------------------------------------------------------------
# 场景 6: 健康检查
# ------------------------------------------------------------------
status, body, _ = http_get(f"{cu}/health")
assert_eq(status, 200, "健康检查 (200)", c)
# ------------------------------------------------------------------
# 场景 7: 不存在路径 → 404
# ------------------------------------------------------------------
status, body, _ = http_get(f"{cu}/nonexistent")
assert_eq(status, 404, "不存在路径-404", c)
# ------------------------------------------------------------------
# 汇总
# ------------------------------------------------------------------
print(f"\n captcha 测试完成: {c.summary()}")
return c.passed, c.failed, c.skipped
if __name__ == "__main__":
run()