spike 闸门(任务书 §5.2)结果为红,按 §6.3 退回 A 方案。 spike:115 个闭包方法整体移入 ConsultationComputeMixin 后, tests/test_api_server_security.py 一字不改跑出 1 failed / 128 passed。 test_chart_async_job_executes_in_background 挂在 monkeypatch.setattr(jyotish_api_server, '_write_async_job_record', ...): 调用方法随 mixin 搬走后从新模块 globals 解析,补丁落在旧模块绑定上不生效。 已实证把同一 fake 打到 mixin 模块即恢复原行为,故为落点问题而非搬坏。 循环 import 不是障碍(移动集不引用 JyotishAPIHandler)。 退回 A 的实际交付:新建 scripts/offline_compute_mixins.py, 收 BadRequest、3 个模块级助手,以及 RequestParamMixin / VedastroEvidenceMixin / SynastryMixin 共 7 个方法(300 行), JyotishAPIHandler 通过继承保留全部方法,HTTP 侧零变化。 consultation_workflow_service.build_runtime_evidence_helpers 与 local_accuracy_report 改为直接实例化 mixin,不再伪造 handler。 11 个搬走的定义经 SHA-256 逐个比对与搬走前字节级相同; jyotish_api_server.py 的 diff 为 11 行插入 / 378 行删除,无重排。 scripts 侧 __new__ 4 → 2(剩 2 处都在咨询工作流链上); 类方法 225 → 218;行数 11,291 → 10,924。 合同测试收紧基线并新增 scripts 侧专门断言与反向 import 断言; 两次反向验证(加回 __new__ / 加类方法)均正确变红。 新增 tests/test_offline_compute_mixins.py 覆盖此前零覆盖的 MCP 路径, 并加入 CORE_PYTEST_TARGETS。 tests/test_api_server_security.py 一字未改,129 passed。 快速门 pytest 段 798 passed / 1 skipped / 0 failed。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JUei7K13cYxLHE3Axe4A45
88 lines
3.1 KiB
Python
88 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Offline compute mixins: the handler-free path used by MCP and report scripts.
|
|
|
|
TASK-api-server-backdoor-close-20260916. These assertions pin the two production
|
|
call sites that no longer forge a ``JyotishAPIHandler`` without its constructor,
|
|
and pin that the HTTP handler keeps the moved methods through inheritance.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import ast
|
|
import os
|
|
import sys
|
|
|
|
SCRIPTS = os.path.join(os.path.dirname(__file__), '..', 'scripts')
|
|
if SCRIPTS not in sys.path:
|
|
sys.path.insert(0, SCRIPTS)
|
|
|
|
from ashtakoot import calculate_ashtakoot # noqa: E402
|
|
from jyotish_api_server import JyotishAPIHandler # noqa: E402
|
|
from offline_compute_mixins import ( # noqa: E402
|
|
RequestParamMixin,
|
|
SynastryMixin,
|
|
VedastroEvidenceMixin,
|
|
)
|
|
|
|
_CHART = {
|
|
'success': True,
|
|
'ascendant': {'sign': 'Aries', 'sign_idx': 0, 'degree': 12.0, 'lon': 12.0},
|
|
'planets': {
|
|
'Sun': {'sign': 'Leo', 'lon': 130.0},
|
|
'Moon': {'sign': 'Cancer', 'lon': 100.0},
|
|
},
|
|
}
|
|
|
|
|
|
def test_synastry_mixin_needs_no_http_handler() -> None:
|
|
"""local_accuracy_report.py's path: score a pair without a handler."""
|
|
api = SynastryMixin()._compute_synastry({'male_moon': 0, 'female_moon': 60})
|
|
direct = calculate_ashtakoot(0, 60)
|
|
assert api['total_score'] == direct['total_score']
|
|
assert api['male_details'] == direct['male_details']
|
|
assert api['female_details'] == direct['female_details']
|
|
|
|
|
|
def test_runtime_evidence_helpers_run_without_forging_a_handler() -> None:
|
|
"""mcp_server.py:4751 -> consultation_workflow_service.build_runtime_evidence_helpers."""
|
|
from consultation_workflow_service import build_runtime_evidence_helpers
|
|
|
|
helpers = build_runtime_evidence_helpers(_CHART)
|
|
assert set(helpers) == {
|
|
'vedastro_official',
|
|
'vedastro_archive_manifest',
|
|
'interpretation_coverage',
|
|
}
|
|
assert isinstance(helpers['vedastro_official'], dict)
|
|
assert isinstance(helpers['vedastro_archive_manifest'], dict)
|
|
assert isinstance(helpers['interpretation_coverage'], dict)
|
|
|
|
|
|
def test_consultation_workflow_service_no_longer_forges_for_evidence() -> None:
|
|
source = os.path.join(SCRIPTS, 'consultation_workflow_service.py')
|
|
tree = ast.parse(open(source, encoding='utf-8').read())
|
|
fn = next(
|
|
n for n in tree.body
|
|
if isinstance(n, ast.FunctionDef) and n.name == 'build_runtime_evidence_helpers'
|
|
)
|
|
body = ast.dump(fn)
|
|
assert 'JyotishAPIHandler' not in body
|
|
assert 'VedastroEvidenceMixin' in body
|
|
|
|
|
|
def test_http_handler_still_inherits_the_moved_methods() -> None:
|
|
"""HTTP-visible behaviour must be unchanged: same methods, now via MRO."""
|
|
for name in (
|
|
'_get_float',
|
|
'_check_range',
|
|
'_normalize_degree',
|
|
'_compute_synastry',
|
|
'_high_rigor_vedastro_official_summary',
|
|
'_compute_vedastro_gateway_archives',
|
|
'_interpretation_source_runtime_coverage',
|
|
):
|
|
assert hasattr(JyotishAPIHandler, name), name
|
|
assert issubclass(JyotishAPIHandler, VedastroEvidenceMixin)
|
|
assert issubclass(JyotishAPIHandler, SynastryMixin)
|
|
assert issubclass(SynastryMixin, RequestParamMixin)
|