Enforce trading authorization and suitability checks

This commit is contained in:
张胜宇
2026-09-13 19:24:59 +08:00
parent b9aafce120
commit e3c316aafb
7 changed files with 149 additions and 28 deletions
+27 -1
View File
@@ -25,7 +25,8 @@ from app.api.schemas.trading import (
HoldingListResponse,
PortfolioSummary,
)
from app.core.errors import FundQuoteUnavailableError
from app.core.contracts import RequestContext
from app.core.errors import FundQuoteUnavailableError, SuitabilityMismatchError
from app.service.trade_service import TradeService, _FeeRule
# ---------------------------------------------------------------------------
@@ -206,3 +207,28 @@ async def test_quote_freshness_only_blocks_trade_path() -> None:
assert snapshot.price == Decimal("4.5000")
with pytest.raises(FundQuoteUnavailableError, match="行情已过期"):
await service._fetch_quote(product, enforce_freshness=True) # type: ignore[arg-type]
@pytest.mark.asyncio
async def test_trade_suitability_uses_request_context_and_denies_mismatch() -> None:
evaluator = SimpleNamespace(
evaluate=AsyncMock(
return_value=SimpleNamespace(allowed=False, reason_code="RISK_LEVEL_MISMATCH")
)
)
service = TradeService(session=None, suitability_evaluator=evaluator) # type: ignore[arg-type]
product = SimpleNamespace(
product_code="510500",
risk_level="R4",
risk_disclosure_required=1,
second_confirmation_required=0,
)
context = RequestContext(
user_id="9001", trace_id="trade-test", roles=("customer",), customer_ids=("9001",)
)
with pytest.raises(SuitabilityMismatchError, match="RISK_LEVEL_MISMATCH"):
await service._check_suitability(9001, product, context) # type: ignore[arg-type]
evaluator.evaluate.assert_awaited_once()
assert evaluator.evaluate.await_args.kwargs["context"] is context