parents (D12, 4/9 houses, Sun/Moon) and children (D7, 5th house, Jupiter, PK) are plan/card domains with Chinese labels and the brief's aliases. They send the family route contract to the engine and are rejected as a stored session theme, so neither Python nor the chat_sessions.theme CHECK changes. Methodology reports no strict checklist for them; must-use layers follow the plan domain. Tests with 原值/新值/原因 where assertions changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
114 lines
4.6 KiB
Python
114 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
||
"""Focused tests for the canonical consultation-domain registry."""
|
||
|
||
from __future__ import annotations
|
||
|
||
import pytest
|
||
|
||
from scripts.consultation_domain_registry import (
|
||
CANONICAL_DOMAINS,
|
||
DEFAULT_THEMES,
|
||
DOMAIN_ALIASES,
|
||
normalize_domain,
|
||
normalize_themes,
|
||
)
|
||
|
||
|
||
def test_canonical_domain_registry_is_complete_and_ordered() -> None:
|
||
assert CANONICAL_DOMAINS == (
|
||
"career",
|
||
"marriage",
|
||
"wealth",
|
||
"health",
|
||
"education",
|
||
"migration",
|
||
"family",
|
||
"annual",
|
||
"timing",
|
||
"general",
|
||
)
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
("value", "expected"),
|
||
[
|
||
("relationship", "marriage"),
|
||
("finance", "wealth"),
|
||
("money", "wealth"),
|
||
("health-pressure", "health"),
|
||
("health_pressure", "health"),
|
||
("home", "migration"),
|
||
("relocation", "migration"),
|
||
("varshaphala", "annual"),
|
||
],
|
||
)
|
||
def test_normalize_domain_accepts_required_aliases(value: str, expected: str) -> None:
|
||
assert normalize_domain(value) == expected
|
||
|
||
|
||
def test_normalize_themes_accepts_canonical_and_aliases_deduplicated_in_input_order() -> None:
|
||
assert normalize_themes(
|
||
["timing", "relationship", "marriage", "money", "wealth", "general", "health_pressure"]
|
||
) == ["timing", "marriage", "wealth", "general", "health"]
|
||
|
||
|
||
def test_normalize_themes_keeps_product_defaults() -> None:
|
||
assert normalize_themes(None) == list(DEFAULT_THEMES)
|
||
assert normalize_themes("all") == list(DEFAULT_THEMES)
|
||
assert normalize_themes([]) == list(DEFAULT_THEMES)
|
||
|
||
|
||
@pytest.mark.parametrize("value", ["spirituality", "灵性", "unknown-domain"])
|
||
def test_normalize_themes_rejects_unknown_domains_instead_of_falling_back(value: str) -> None:
|
||
with pytest.raises(ValueError, match="Unknown theme"):
|
||
normalize_themes(["career", value])
|
||
|
||
|
||
def test_typescript_registry_matches_python_canonical_domains_and_aliases() -> None:
|
||
import json
|
||
import re
|
||
from pathlib import Path
|
||
|
||
source = (Path(__file__).resolve().parents[1] / "frontend/src/lib/consultation-domain-registry.ts").read_text()
|
||
# 原值:TS 领域 id == Python CANONICAL_DOMAINS,TS 别名表 == DOMAIN_ALIASES。
|
||
# 新值:TS 的引擎领域(consultationEngineDomainIds)== CANONICAL_DOMAINS;TS 另有两个
|
||
# 只作用于数据卡的子领域 parents / children,引擎侧一律按 family 计算
|
||
# (strictWorkflowRoute / workflowThemes 都是 family)。别名比较按「引擎领域」进行:
|
||
# Python 认识的每个别名,在 TS 里落到的领域的引擎路由必须等于 Python 的归一结果;
|
||
# TS 独有的别名(父母、孩子等)只能落在 parents / children。
|
||
# 原因:TASK-consult-evidence-card-20260927 D2,家庭拆父母 / 子女,但不改 Python 引擎。
|
||
engine_match = re.search(r"consultationEngineDomainIds = \[([\s\S]*?)\] as const", source)
|
||
assert engine_match is not None
|
||
assert tuple(re.findall(r'"([^"\n]+)"', engine_match.group(1))) == CANONICAL_DOMAINS
|
||
|
||
canonical_match = re.search(r"consultationDomainIds = \[([\s\S]*?)\] as const", source)
|
||
assert canonical_match is not None
|
||
typescript_domains = tuple(re.findall(r'"([^"\n]+)"', canonical_match.group(1)))
|
||
card_only = ("parents", "children")
|
||
assert tuple(domain for domain in typescript_domains if domain not in card_only) == CANONICAL_DOMAINS
|
||
assert set(typescript_domains) - set(CANONICAL_DOMAINS) == set(card_only)
|
||
|
||
definitions = re.findall(
|
||
r'\{ id: "([a-z]+)"[^\n]*?aliases: (\[[^\]]*\]), strictWorkflowRoute: "([a-z]+)", workflowThemes: (\[[^\]]*\])',
|
||
source,
|
||
)
|
||
assert tuple(domain for domain, _aliases, _route, _themes in definitions) == typescript_domains
|
||
engine_route: dict[str, str] = {}
|
||
typescript_aliases: dict[str, str] = {}
|
||
for domain, raw_aliases, route, raw_themes in definitions:
|
||
engine_route[domain] = route
|
||
if domain in card_only:
|
||
assert route == "family" and json.loads(raw_themes) == ["family"], domain
|
||
else:
|
||
assert route == domain and json.loads(raw_themes) == [domain], domain
|
||
for alias in json.loads(raw_aliases):
|
||
if alias != domain:
|
||
typescript_aliases[alias] = domain
|
||
for alias, python_domain in DOMAIN_ALIASES.items():
|
||
assert alias in typescript_aliases or alias in typescript_domains, alias
|
||
ts_domain = typescript_aliases.get(alias, alias)
|
||
assert engine_route[ts_domain] == python_domain, alias
|
||
for alias, ts_domain in typescript_aliases.items():
|
||
if alias not in DOMAIN_ALIASES:
|
||
assert ts_domain in card_only, alias
|