Files
chongming-core/test/test_inline_config_align.py
T

164 lines
6.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
inline 插件 [workers.config] 对齐(网关侧兜底)单测 —— WorkerManager.align_inline_config。
网关侧在创建 inline 副本之前,按「配置优先来源模式」(根 config.toml 的
[cache].launch_worker_count)对齐插件配置:cache 模式取共享缓存里的段,
toml 模式以 config/workers/<type>/config.toml 为准并覆盖缓存。目的是让网关与
worker 包版本解耦——worker 包仍是旧版时,旧版 on_init 只拿到 toml 展平配置,
会出现“前端显示缓存值、实际跑 toml”。
用 dict 作缓存桩,验证两种模式下插件拿到的配置与缓存/文件两侧的取值一致。
"""
import os
import sys
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(REPO, "gateway", "src"))
os.chdir(REPO) # 网关模块导入期读根 config.toml / 日志配置
from chongming.app.core.worker_manager import _CONFIGS_KEY, WorkerManager # noqa: E402
PASS = 0
FAIL = 0
WT = "aibox_sophon_adapter"
# _discover_plugins 展平出来的形态(toml 值)
FLAT = {"type": WT, "count": 1, "data_dir": "./data", "mediamtx_addr": "rtsp://toml:8554"}
# 该插件 config.toml 的 [workers.config] 原始段
SECTION = {"data_dir": "./data", "mediamtx_addr": "rtsp://toml:8554", "heartbeat": {"timeout": 15}}
# 前端下发后缓存里的段(改过 mediamtx_addr 与 heartbeat)
CACHED = {"data_dir": "./data", "mediamtx_addr": "rtsp://cache:9999", "heartbeat": {"timeout": 30}}
def report(name, cond, detail=""):
global PASS, FAIL
if cond:
PASS += 1
print(f"✅ {name}" + (f" — {detail}" if detail else ""))
else:
FAIL += 1
print(f"❌ {name}" + (f" — {detail}" if detail else ""))
def manager(cache, mode):
m = WorkerManager.__new__(WorkerManager) # 免会话构造:仅用到 cache + config_mode
m.cache = cache
m._launch_worker_count = mode
return m
def run() -> int:
key = _CONFIGS_KEY.format(type=WT)
# ── 1. cache 模式:缓存有段 → 插件拿到缓存值(本次修复目标)──────────
cache = {key: dict(CACHED)}
got = manager(cache, "cache").align_inline_config(WT, dict(FLAT), dict(SECTION))
report(
"cache 模式:插件配置取缓存值(覆盖 toml 同名键)",
got.get("mediamtx_addr") == "rtsp://cache:9999"
and got.get("heartbeat") == {"timeout": 30},
f"mediamtx_addr={got.get('mediamtx_addr')}",
)
report("cache 模式:不覆盖已存在的缓存段", cache[key] == CACHED, f"cache={cache[key]}")
# ── 2. cache 模式:缓存缺失 → 用 toml 段引导并写回 ──────────────────
cache2 = {}
got2 = manager(cache2, "cache").align_inline_config(WT, dict(FLAT), dict(SECTION))
report(
"cache 模式:缓存缺失用 config.toml 引导并写回",
cache2.get(key) == SECTION and got2.get("mediamtx_addr") == "rtsp://toml:8554",
f"cache={cache2.get(key)}",
)
# ── 3. toml 模式:以文件为准并覆盖缓存(前端随之显示文件值)─────────
cache3 = {key: dict(CACHED)}
got3 = manager(cache3, "toml").align_inline_config(WT, dict(FLAT), dict(SECTION))
report(
"toml 模式:插件配置为 toml 值",
got3.get("mediamtx_addr") == "rtsp://toml:8554",
f"mediamtx_addr={got3.get('mediamtx_addr')}",
)
report(
"toml 模式:启动时用 toml 覆盖缓存(前端与运行值一致)",
cache3.get(key) == SECTION,
f"cache={cache3.get(key)}",
)
# ── 4. 边界:无缓存后端 / 无段(旧 config/workers/config.toml 兼容路径)──
cache4 = {key: dict(CACHED)}
m4 = manager(cache4, "cache")
m4.cache = None
report(
"无缓存后端:原样返回(经典 toml 行为)",
m4.align_inline_config(WT, dict(FLAT), dict(SECTION)) == FLAT
and cache4[key] == CACHED,
)
cache5 = {}
got5 = manager(cache5, "toml").align_inline_config(WT, dict(FLAT), None)
report("无 [workers.config] 段(旧写法):不写缓存、原样返回", got5 == FLAT and not cache5)
# ── 5. 与 SDK 侧一致:缓存键必须相同;SDK 为本地版本时才比对解析结果 ──
# 注意:venv 里的 chongming-worker 可能是索引里的发布版(uv sync 会把可编辑安装
# 换回锁文件版本),新版才有 resolve_startup_config / 正确的 toml 分支语义。
sys.path.insert(0, os.path.join(REPO, "utils", "worker", "src"))
import chongming_worker.runtime as r # noqa: E402
report(
"缓存键与 SDK 一致(两端必须读写同一键)",
r._configs_cache_key(WT) == _CONFIGS_KEY.format(type=WT),
f"sdk={r._configs_cache_key(WT)}",
)
local_src = os.path.join(REPO, "utils", "worker", "src") in os.path.abspath(r.__file__)
if not local_src:
print(f"⚠️ 跳过解析结果比对:当前 SDK 为 {r.__file__}(非本地源码,缺本次改动)")
else:
c = {r._configs_cache_key(WT): dict(CACHED)}
sdk = r.resolve_runtime_config(WT, dict(FLAT), c, "cache")
report(
"cache 模式:网关侧兜底与 SDK 解析结果一致",
sdk.get("mediamtx_addr") == CACHED["mediamtx_addr"],
f"sdk={sdk.get('mediamtx_addr')}",
)
# ── 6. 端到端:网关对齐 + 插件初始化(部署组合:网关新、worker 包为发布版)──
# 该断言即线上现象的复现与验证:插件 on_init 拿到的必须是网关对齐后的缓存值,
# 与 venv 里 chongming-worker 是否为含本次改动的版本无关。
import asyncio
from chongming_worker.plugin import Plugin # noqa: E402
seen = {}
class DemoPlugin(Plugin):
type = WT
auto_bind = False
async def _bootstrap(self): # 隔离数据库/对象存储引导,只看配置来源
return None
async def on_init(self):
seen["on_init"] = self.config.get("mediamtx_addr")
def _create_workers_impl(self, session, config, count=1, worker_manager=None):
seen["impl"] = (config or {}).get("mediamtx_addr")
return []
aligned = manager({key: dict(CACHED)}, "cache").align_inline_config(
WT, dict(FLAT), dict(SECTION)
)
asyncio.run(DemoPlugin().create_workers(None, aligned, 1))
report(
"端到端:网关对齐后插件 on_init 读到缓存值(不依赖 worker 包版本)",
seen.get("on_init") == CACHED["mediamtx_addr"] == seen.get("impl"),
f"on_init={seen.get('on_init')}, impl={seen.get('impl')}, "
f"sdk={os.path.abspath(__import__('chongming_worker').__file__)}",
)
print("\n" + "=" * 56)
print(f"测试结果: ✅ {PASS} 通过 ❌ {FAIL} 失败")
return 0 if FAIL == 0 else 1
if __name__ == "__main__":
sys.exit(run())