"""C-04 亏损阈值提醒服务单测。""" from __future__ import annotations from decimal import Decimal import pytest from app.service import threshold_service as ts def test_parse_loss_threshold_pct(): assert ts.parse_loss_threshold_pct("亏10%就提醒我") == Decimal("10") assert ts.parse_loss_threshold_pct("亏损 12.5% 通知我") == Decimal("12.5") assert ts.parse_loss_threshold_pct("没有数字") is None def test_portfolio_pnl_pct_weighted(): rows = [ {"market_value": 60000, "pnl_pct": -12.0}, {"market_value": 40000, "pnl_pct": -8.0}, ] pnl = ts.portfolio_pnl_pct(rows) assert pnl is not None assert round(pnl, 2) == -10.4 def test_build_threshold_alert_when_breached(monkeypatch): logs: list[dict] = [] class FakeRepo: def list_enabled(self, cid): return [{ "id": 7, "scope_type": "portfolio", "loss_threshold_pct": Decimal("10"), }] def insert_notify_log(self, **kwargs): logs.append(kwargs) monkeypatch.setattr(ts, "ThresholdRepository", FakeRepo) holdings = [{"market_value": 100000, "pnl_pct": -15.0}] alert = ts.build_threshold_alert("CUST-1", holdings, trace_id="t1") assert alert and "阈值提醒" in alert assert logs and logs[0]["customer_id"] == "CUST-1" def test_build_threshold_alert_skips_when_within_threshold(monkeypatch): class FakeRepo: def list_enabled(self, cid): return [{"id": 1, "scope_type": "portfolio", "loss_threshold_pct": Decimal("20")}] def insert_notify_log(self, **kwargs): raise AssertionError("should not notify") monkeypatch.setattr(ts, "ThresholdRepository", FakeRepo) holdings = [{"market_value": 100000, "pnl_pct": -5.0}] assert ts.build_threshold_alert("CUST-1", holdings) is None