签入版本

This commit is contained in:
2026-09-12 17:39:13 +08:00
parent 25c8b039ac
commit 3233d221b0
871 changed files with 103088 additions and 10561 deletions
+86
View File
@@ -0,0 +1,86 @@
"""utils/captcha.py 验证码校验:原子消费(get→del→compare)杜绝重放与无限试错。
fakeredis 不支持 Lua eval,这里用 Python 复刻 Lua 语义(原子 get→del→compare,
无论对错都消费验证码)作为 fake,验证业务层行为;另断言生产代码确实调用
_CAPTCHA_VERIFY_LUA 脚本。
"""
import pytest
import fakeredis.aioredis
@pytest.fixture
def _captcha_redis(monkeypatch):
from core import redis_client as rc
redis = fakeredis.aioredis.FakeRedis(decode_responses=True)
async def fake_eval(script, numkeys, key, user_input):
# 复刻 _CAPTCHA_VERIFY_LUA 语义:原子 get→del→compare,无论对错都消费
stored = await redis.get(key)
if not stored:
return 0
await redis.delete(key)
if str(stored).upper().strip() == str(user_input).upper().strip():
return 1
return 0
monkeypatch.setattr(redis, 'eval', fake_eval)
monkeypatch.setattr(rc, 'get_redis', lambda: redis)
return redis
@pytest.mark.asyncio
async def test_verify_captcha_correct_then_consumed(_captcha_redis):
from utils.captcha import verify_captcha
await _captcha_redis.set('k1', 'Ab3X')
# 大小写不敏感比较通过
assert await verify_captcha('k1', 'ab3x') is True
# 已消费:同一验证码无法再次使用(防重放)
assert await verify_captcha('k1', 'Ab3X') is False
@pytest.mark.asyncio
async def test_verify_captcha_wrong_input_burns_captcha(_captcha_redis):
from utils.captcha import verify_captcha
await _captcha_redis.set('k2', 'Ab3X')
# 错误输入 → False,且验证码被消费(失败即失效,防无限试错)
assert await verify_captcha('k2', 'wrong') is False
# 随后即使用正确输入也无法通过
assert await verify_captcha('k2', 'Ab3X') is False
assert await _captcha_redis.get('k2') is None
@pytest.mark.asyncio
async def test_verify_captcha_missing_key(_captcha_redis):
from utils.captcha import verify_captcha
assert await verify_captcha('missing', 'any') is False
@pytest.mark.asyncio
async def test_verify_captcha_empty_input(_captcha_redis):
from utils.captcha import verify_captcha
assert await verify_captcha('k', '') is False
assert await verify_captcha('', 'x') is False
@pytest.mark.asyncio
async def test_verify_captcha_uses_atomic_lua_script(_captcha_redis, monkeypatch):
from utils.captcha import verify_captcha, _CAPTCHA_VERIFY_LUA
await _captcha_redis.set('k1', 'Ab3X')
calls = []
original = _captcha_redis.eval
async def spy_eval(script, numkeys, key, user_input):
calls.append((script, numkeys))
return await original(script, numkeys, key, user_input)
monkeypatch.setattr(_captcha_redis, 'eval', spy_eval)
assert await verify_captcha('k1', 'Ab3X') is True
# 生产代码调用原子 Lua 脚本(numkeys=1)
assert calls and calls[0] == (_CAPTCHA_VERIFY_LUA, 1)