Files
sc 9d55c237ed feat(preferences): review_push_mode 端点与前端开关配套
- settings: GET 返回 review_push_mode, PUT review-push 支持 mode 入参
- market_recap: push 移入 SaveReportRequest body(显式外发)
- Review 页: 推送触发方式 auto/manual 开关
- 门控测试: 手动/定时两条路径的 manual/auto 推送断言
2026-09-06 16:20:42 +08:00

136 lines
4.5 KiB
Python

"""复盘推送触发方式 review_push_mode 测试 — auto/manual 白名单与默认值 + 推送门控。"""
from __future__ import annotations
import asyncio
import pytest
from app.services import preferences
@pytest.fixture(autouse=True)
def _isolated(tmp_path, monkeypatch):
path = tmp_path / "preferences.json"
monkeypatch.setattr(preferences, "_path", lambda: path)
preferences._invalidate_cache()
yield path
preferences._invalidate_cache()
def test_review_push_mode_defaults_to_manual():
assert preferences.get_review_push_mode() == "manual"
def test_set_and_get_review_push_mode():
assert preferences.set_review_push_mode("auto") == "auto"
assert preferences.get_review_push_mode() == "auto"
assert preferences.set_review_push_mode("manual") == "manual"
assert preferences.get_review_push_mode() == "manual"
def test_set_review_push_mode_rejects_invalid_value():
assert preferences.set_review_push_mode("bogus") == "manual"
assert preferences.get_review_push_mode() == "manual"
# ── 推送门控 ────────────────────────────────────────────────────────
# 门控语义:
# manual: 定时复盘只归档不推送; 手动保存需显式 push=True 才推
# auto: 归档即推(与旧逻辑一致)
def test_save_report_manual_requires_explicit_push(monkeypatch):
from app.api import market_recap
from app.jobs import daily_pipeline
pushed: list[dict] = []
monkeypatch.setattr(
"app.services.market_recap_reports.save_report",
lambda d: {"id": "r1"},
)
monkeypatch.setattr(
daily_pipeline,
"_maybe_push_review",
lambda content, meta: pushed.append(meta),
)
preferences.set_review_push_mode("manual")
# 默认 push=False: manual 模式下只归档, 不外发
market_recap.save_report(None, market_recap.SaveReportRequest(as_of="2026-07-18", content="正文"))
assert pushed == []
# 显式 push=True: manual 模式下外发
market_recap.save_report(None, market_recap.SaveReportRequest(as_of="2026-07-18", content="正文", push=True))
assert pushed == [{"as_of": "2026-07-18", "emotion_label": ""}]
def test_save_report_auto_pushes_without_flag(monkeypatch):
from app.api import market_recap
from app.jobs import daily_pipeline
pushed: list[dict] = []
monkeypatch.setattr(
"app.services.market_recap_reports.save_report",
lambda d: {"id": "r1"},
)
monkeypatch.setattr(
daily_pipeline,
"_maybe_push_review",
lambda content, meta: pushed.append(meta),
)
preferences.set_review_push_mode("auto")
# auto 模式: 无需 push 标志即外发
market_recap.save_report(None, market_recap.SaveReportRequest(as_of="2026-07-18", content="正文"))
assert pushed == [{"as_of": "2026-07-18", "emotion_label": ""}]
def _patch_scheduled_review(monkeypatch, pushed: list, archived: list):
"""装配定时复盘的依赖: 有 AI key、流式产出固定内容、捕获归档与推送调用。"""
from app.jobs import daily_pipeline
async def _fake_stream(*a, **k):
return "正文", {"as_of": "2026-07-18", "emotion_label": "中性"}
monkeypatch.setattr("app.secrets_store.get_ai_key", lambda: "sk-test")
monkeypatch.setattr(daily_pipeline, "_stream_review_with_retry", _fake_stream)
monkeypatch.setattr(
"app.services.market_recap_reports.save_report",
lambda d: archived.append(d) or {"id": "r1"},
)
monkeypatch.setattr(
daily_pipeline,
"_maybe_push_review",
lambda content, meta: pushed.append(meta),
)
def test_scheduled_review_manual_archives_without_push(monkeypatch):
from app.jobs import daily_pipeline
pushed: list = []
archived: list = []
_patch_scheduled_review(monkeypatch, pushed, archived)
preferences.set_review_push_mode("manual")
asyncio.run(daily_pipeline._run_scheduled_review(None))
# manual 模式: 归档发生, 但不外发
assert len(archived) == 1
assert pushed == []
def test_scheduled_review_auto_pushes(monkeypatch):
from app.jobs import daily_pipeline
pushed: list = []
archived: list = []
_patch_scheduled_review(monkeypatch, pushed, archived)
preferences.set_review_push_mode("auto")
asyncio.run(daily_pipeline._run_scheduled_review(None))
# auto 模式: 归档并外发
assert len(archived) == 1
assert pushed == [{"as_of": "2026-07-18", "emotion_label": "中性"}]