"""人设卡治理层纯函数测试 galgame_card 包 __init__ 会触发 NoneBot 运行时初始化(require orm), 无法裸导入,故用 importlib 按文件路径直接加载 processor 模块 (与 test_rate_limit.py 同款做法)。 """ import importlib.util from pathlib import Path import pytest _MODULE_PATH = ( Path(__file__).resolve().parents[1] / "hexi" / "plugins" / "nonebot_plugin_galgame_card" / "processor.py" ) def _load_processor(): spec = importlib.util.spec_from_file_location("persona_processor", _MODULE_PATH) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module @pytest.fixture(scope="module") def processor(): return _load_processor() COMMAND_STARTS = {"/", ""} def test_filter_content_normal(processor): assert processor.filter_content("草,又被加班干碎了", COMMAND_STARTS) == "草,又被加班干碎了" def test_filter_content_command(processor): assert processor.filter_content("/打卡", COMMAND_STARTS) is None def test_filter_content_command_empty_prefix_not_fatal(processor): # command_start 里空串不应导致全量误杀 assert processor.filter_content("正常说话", {""}) == "正常说话" assert processor.filter_content("/打卡", {""}) == "/打卡" def test_desensitize_clear_patterns(processor): assert processor.desensitize("我电话是13800138000") == "我电话是[手机号]" assert processor.desensitize("手机号138-0013-8000") == "手机号[手机号]" # 带分隔符变体 assert processor.desensitize("身份证110101199003070012") == "身份证[身份证]" assert processor.desensitize("卡号6222021234567890") == "卡号[银行卡]" assert processor.desensitize("联系我 a@b.com") == "联系我 [邮箱]" assert processor.desensitize("服务器192.168.1.1") == "服务器[IP]" assert processor.desensitize("加我 wxid_abc123xyz") == "加我 [微信号]" assert processor.desensitize("车牌川A12345") == "车牌[车牌]" assert processor.desensitize("定位30.5,104.0") == "定位[坐标]" def test_desensitize_key_value(processor): # 定位式:关键词 + 值,只替换值、保留关键词 assert processor.desensitize("密码是 xyz789") == "密码是 [密码]" assert processor.desensitize("账号 abc123") == "账号 [账号]" assert processor.desensitize("账号是 1122334455") == "账号是 [账号]" assert processor.desensitize("激活码 ABCDE-12345") == "激活码 [兑换码]" assert processor.desensitize("验证码是 123456") == "验证码是 [验证码]" assert processor.desensitize("月薪 25000") == "月薪 [收入]" assert processor.desensitize("VX: xiaoming123") == "VX: [微信号]" def test_desensitize_fallback(processor): # 兜底:裸长数字串 → [账号],字母数字混合 → [密码] assert processor.desensitize("1122334455") == "[账号]" assert processor.desensitize("abc1234") == "[密码]" def test_desensitize_context_hint(processor): # 语境强化:附近出现"验证码"关键词时,6-8 位数字 → [验证码] assert processor.desensitize("123456", context_text="发下验证码") == "[验证码]" assert processor.desensitize("验证码是 123456") == "验证码是 [验证码]" # 无关键词时 6-8 位数字走兜底 → [账号] assert processor.desensitize("123456") == "[账号]" def test_desensitize_keeps_normal_text(processor): # 正常文本不受影响(短数字、中文、英文单词) assert processor.desensitize("今晚八点开黑,打了2800分") == "今晚八点开黑,打了2800分" assert processor.desensitize("有一说一确实wwww") == "有一说一确实wwww" assert processor.desensitize("房间码12345") == "房间码12345" assert processor.desensitize("体重120斤") == "体重120斤" def test_filter_content_url_flood(processor): text = "看看这个 http://a.com http://b.com http://c.com" assert processor.filter_content(text, COMMAND_STARTS) is None def test_filter_content_truncate(processor): long_text = "长" * 500 assert len(processor.filter_content(long_text, COMMAND_STARTS)) == processor.MAX_CONTENT_LEN def test_is_command(processor): assert processor.is_command("/查询", {"/"}) assert not processor.is_command("你好", {"/"}) def test_truncate(processor): assert processor.truncate("abc", 2) == "ab" assert processor.truncate("abc", 10) == "abc" def test_image_hash_from_file(processor): HASH32 = "0123456789abcdef0123456789abcdef" # 优先 file 里的 32 位 hash assert processor.image_hash_from_file(f"{HASH32}.image") == HASH32 # 回退 url 文件名里的 hash assert ( processor.image_hash_from_file("", f"https://example.com/pics/{HASH32}.png") == HASH32 ) # 都没有则截断文件名 assert processor.image_hash_from_file("somefile.png") == "somefile.png" def test_build_content(processor): assert processor.build_content("") is None assert processor.build_content("草") == "草" assert processor.build_content("", 2) == "[图片×2]" assert processor.build_content("", 0, 3) == "[表情×3]" assert processor.build_content("", 2, 3) == "[图片×2][表情×3]" assert processor.build_content("草", 1) == "草 [图片×1]"