From 46ef0c59c3020566c9c4c103f6a87bc323224716 Mon Sep 17 00:00:00 2001 From: haochen <3033940900@qq.com> Date: Thu, 24 Sep 2026 15:11:49 +0800 Subject: [PATCH] fix(channels): persist runtime configuration as UTF-8 (#5803) Co-authored-by: Zhangchenhao-111 <285911435+Zhangchenhao-111@users.noreply.github.com> --- backend/app/channels/runtime_config_store.py | 1 + .../test_channel_runtime_config_encoding.py | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 backend/tests/test_channel_runtime_config_encoding.py diff --git a/backend/app/channels/runtime_config_store.py b/backend/app/channels/runtime_config_store.py index 2c6e599b3f..ec7b810bfb 100644 --- a/backend/app/channels/runtime_config_store.py +++ b/backend/app/channels/runtime_config_store.py @@ -46,6 +46,7 @@ class ChannelRuntimeConfigStore: def _save(self) -> None: fd = tempfile.NamedTemporaryFile( mode="w", + encoding="utf-8", dir=self._path.parent, suffix=".tmp", delete=False, diff --git a/backend/tests/test_channel_runtime_config_encoding.py b/backend/tests/test_channel_runtime_config_encoding.py new file mode 100644 index 0000000000..a628a79803 --- /dev/null +++ b/backend/tests/test_channel_runtime_config_encoding.py @@ -0,0 +1,35 @@ +"""Runtime channel configuration must round-trip independently of host locale.""" + +import json + +import pytest + +from app.channels import runtime_config_store + + +@pytest.mark.parametrize( + ("locale_encoding", "relay_url"), + [ + ("cp1252", "wss://relay.example/caf\u00e9"), + ("gbk", "wss://relay.example/\u804a\u5929"), + ("cp1252", "wss://relay.example/\u804a\u5929"), + ], +) +def test_runtime_config_round_trips_non_ascii_with_legacy_locale(tmp_path, monkeypatch, locale_encoding, relay_url): + named_temporary_file = runtime_config_store.tempfile.NamedTemporaryFile + + def locale_temporary_file(*args, **kwargs): + # Simulate a legacy host default only when the writer omits encoding. + kwargs.setdefault("encoding", locale_encoding) + return named_temporary_file(*args, **kwargs) + + monkeypatch.setattr(runtime_config_store.tempfile, "NamedTemporaryFile", locale_temporary_file) + path = tmp_path / "channels" / "runtime-config.json" + config = {"enabled": True, "relay_url": relay_url} + store = runtime_config_store.ChannelRuntimeConfigStore(path) + + store.set_provider_config("buzz", config) + + assert json.loads(path.read_text(encoding="utf-8")) == {"buzz": config} + reloaded = runtime_config_store.ChannelRuntimeConfigStore(path) + assert reloaded.get_provider_config("buzz") == config