Files
ai-kefu-admin/tests/test_config_merge.py
T
浅念 8a16a2959b test: 钉住 deep_merge 的 None 替换与列表替换语义 + 只读约定注释
- characterization:显式 None 整体替换且兄弟键保留默认(Task 9 回退契约依赖);列表整体替换不与默认拼接
- DEFAULT_CONFIG 与 TenantConfig.data/Site.brand 补只读约定注释
- platform 包依赖章程改明确:defaults/tenant_config 为叶子模块供 engine 反向消费,registry 组装层可 import app/engine

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-12 16:59:31 +08:00

88 lines
3.4 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.
"""出厂默认与快照深合并(E4 配置演进)——Task 3 TDD 用例。
覆盖:
- deep_merge 三性质:缺省补齐 / 不动入参 / 兄弟键保留
- TenantConfig.from_snapshot 的站点模型:覆盖生效、未覆盖项继承租户级 brand、
无 sites 时空元组
"""
from app.platform.defaults import DEFAULT_CONFIG, deep_merge
from app.platform.tenant_config import Site, TenantConfig
# ---------- deep_merge 三性质 ----------
def test_missing_keys_filled_from_defaults():
snapshot = {"brand": {"title": "X"}} # 快照缺一大堆键
merged = deep_merge(DEFAULT_CONFIG, snapshot)
assert merged["brand"]["title"] == "X" # 覆盖生效
assert "color" in merged["brand"] # 缺省补齐
assert "wall" in merged and "push" in merged # 顶层组补齐
def test_snapshot_not_mutated():
snapshot = {"brand": {"title": "X"}}
deep_merge(DEFAULT_CONFIG, snapshot)
assert snapshot == {"brand": {"title": "X"}}
def test_nested_override_keeps_sibling():
snapshot = {"email": {"sender_name": "【T】"}}
merged = deep_merge(DEFAULT_CONFIG, snapshot)
assert merged["email"]["sender_name"] == "【T】"
assert "notify_emails" in merged["email"] # 兄弟键保留默认
# ---------- TenantConfig / Site 站点模型 ----------
def test_site_overrides_and_inherits_tenant_brand():
"""站点覆盖项生效;未覆盖项继承租户级 brand(而非出厂默认)。"""
tenant = TenantConfig.from_snapshot(
slug="t1",
name="Tenant 1",
snapshot={"brand": {"title": "T Co", "welcome": "hi from tenant"}},
sites=[
{"slug": "s1", "name": "Site 1", "domains": ["a.com"],
"brand": {"color": "#ff0000"}},
],
)
site = tenant.sites[0]
assert isinstance(site, Site)
assert site.slug == "s1"
assert site.name == "Site 1"
assert site.domains == ("a.com",)
assert site.brand["color"] == "#ff0000" # 站点覆盖生效
assert site.brand["title"] == "T Co" # 继承租户级 brand
assert site.brand["welcome"] == "hi from tenant"
# 站点 brand 是独立副本,不与租户 data 共享可变对象
assert site.brand is not tenant.data["brand"]
def test_tenant_without_sites():
"""无 sites 时 sites 为空元组,data 正常深合并。"""
tenant = TenantConfig.from_snapshot(
slug="t2", name="Tenant 2", snapshot={"ai": {"model": "glm-4-plus"}},
)
assert tenant.sites == ()
assert tenant.status == "active"
assert tenant.data["ai"]["model"] == "glm-4-plus" # 覆盖生效
assert tenant.data["push"]["enabled"] is False # 缺省补齐
# ---------- characterization:None / 列表整体替换语义 ----------
def test_explicit_none_replaces_and_keeps_sibling():
"""显式 None 整体替换该键;兄弟键保留默认。
Task 9 的回退契约依赖此语义:快照里显式写 null 表示"清空/未设置",
不能被解读为"回落默认值"。
"""
merged = deep_merge(DEFAULT_CONFIG, {"ai": {"api_key": None}})
assert merged["ai"]["api_key"] is None # 显式 None 生效
assert merged["ai"]["model"] == "glm-4-flash" # 兄弟键保留默认
def test_list_replaced_wholesale():
"""列表整体替换,不与默认值拼接。"""
merged = deep_merge(DEFAULT_CONFIG, {"email": {"notify_emails": ["x@y.z"]}})
assert merged["email"]["notify_emails"] == ["x@y.z"]