deer-flow/backend/tests/test_channel_state_encoding.py
huyengiang101086-spec 1ad8147947
fix(channels): pin JSON state encoding across platforms (#7228)
* fix(channels): pin JSON state encoding across platforms

* test(channels): check encoding pins at each state I/O call

---------

Co-authored-by: huyengiang101086-spec <270684046+huyengiang101086-spec@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-21 18:16:22 +08:00

60 lines
2.8 KiB
Python

"""Pin the cross-platform byte contract for IM channel JSON state writers."""
import ast
import json
from pathlib import Path
import pytest
CHANNELS = Path(__file__).parents[1] / "app" / "channels"
def _state_io_calls(filename: str, class_name: str, method_name: str, call_name: str) -> list[ast.Call]:
module = ast.parse((CHANNELS / filename).read_text(encoding="utf-8"))
owner = next(node for node in module.body if isinstance(node, ast.ClassDef) and node.name == class_name)
method = next(node for node in owner.body if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and node.name == method_name)
calls = [node for node in ast.walk(method) if isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute) and node.func.attr == call_name]
assert calls, f"{filename}:{class_name}.{method_name} has no {call_name} call"
return calls
@pytest.mark.parametrize(
("filename", "class_name", "method_name", "call_name"),
[
("store.py", "JsonChannelStore", "_save", "NamedTemporaryFile"),
("discord.py", "DiscordChannel", "_persist_thread_mappings", "write_text"),
("runtime_config_store.py", "ChannelRuntimeConfigStore", "_save", "NamedTemporaryFile"),
("buzz_seen_events.py", "BuzzSeenEventStore", "_write_snapshot", "NamedTemporaryFile"),
("wechat.py", "WechatChannel", "_save_state", "write_text"),
("wechat.py", "WechatChannel", "_save_auth_state", "NamedTemporaryFile"),
],
)
def test_json_channel_state_writers_pin_utf8_and_lf(filename: str, class_name: str, method_name: str, call_name: str) -> None:
for call in _state_io_calls(filename, class_name, method_name, call_name):
keywords = {keyword.arg: keyword.value for keyword in call.keywords}
for name, expected in (("encoding", "utf-8"), ("newline", "")):
value = keywords.get(name)
assert isinstance(value, ast.Constant) and value.value == expected, f"{filename}:{call.lineno} must pin {name}={expected!r}"
def test_json_channel_store_writes_utf8_lf_bytes(tmp_path: Path) -> None:
from app.channels.store import JsonChannelStore
path = tmp_path / "channels" / "store.json"
store = JsonChannelStore(path)
store._set_sync("微信:资源", "thread-讨论", "用户")
text = path.read_bytes().decode("utf-8")
assert "\r" not in text
assert "\\u5fae\\u4fe1" in text
entry = json.loads(text)["微信:资源"]
assert entry["thread_id"] == "thread-讨论"
assert entry["user_id"] == "用户"
def test_discord_state_read_is_explicitly_utf8() -> None:
for call in _state_io_calls("discord.py", "DiscordChannel", "_load_active_threads", "read_text"):
keywords = {keyword.arg: keyword.value for keyword in call.keywords}
encoding = keywords.get("encoding")
assert isinstance(encoding, ast.Constant) and encoding.value == "utf-8"