Initial commit: InternalAuditInterprise
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
"""线索闭环 + 系统自审计集成测试(需 PostgreSQL)。
|
||||
|
||||
覆盖 R7/R17/R18/R19:线索生成与分级、状态流转、底稿、审计哈希链、线索不可删。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.exc import InternalError, ProgrammingError
|
||||
|
||||
from app.audit import service as audit
|
||||
from app.clues import service as clue_svc
|
||||
from app.clues.models import ClueStatus, ConfidenceTier
|
||||
|
||||
|
||||
def _new_clue(session, score=0.9):
|
||||
return clue_svc.create_clue(
|
||||
session,
|
||||
title="疑似政企拆单",
|
||||
risk_domain="收入",
|
||||
scenario_code="R8",
|
||||
score=score,
|
||||
rationale="8 个客户金额集中在审批阈值边缘,且法人关联同一实控人",
|
||||
evidence={"contracts": 8, "threshold": 1000000},
|
||||
amount_involved=4800000,
|
||||
actor="system",
|
||||
)
|
||||
|
||||
|
||||
def test_score_to_confidence_tier():
|
||||
assert clue_svc.score_to_tier(0.9) == ConfidenceTier.HIGH
|
||||
assert clue_svc.score_to_tier(0.6) == ConfidenceTier.MEDIUM
|
||||
assert clue_svc.score_to_tier(0.2) == ConfidenceTier.LOW
|
||||
|
||||
|
||||
def test_clue_full_lifecycle(session):
|
||||
clue = _new_clue(session)
|
||||
assert clue.confidence == ConfidenceTier.HIGH
|
||||
assert clue.status == ClueStatus.NEW
|
||||
|
||||
clue_svc.assign(session, clue, assignee="auditor_zhang", actor="manager_li")
|
||||
assert clue.status == ClueStatus.ASSIGNED
|
||||
assert clue.assignee == "auditor_zhang"
|
||||
|
||||
paper = clue_svc.adjudicate(session, clue, confirmed=True, actor="auditor_zhang", note="属实,移交")
|
||||
assert clue.status == ClueStatus.CONFIRMED
|
||||
assert clue.feedback == "confirmed"
|
||||
assert paper.conclusion == "confirmed"
|
||||
assert paper.snapshot["score"] == 0.9
|
||||
|
||||
# 继续闭环:确认 -> 移交 -> 销项
|
||||
clue_svc.transition(session, clue, ClueStatus.TRANSFERRED, actor="manager_li")
|
||||
clue_svc.transition(session, clue, ClueStatus.CLOSED, actor="manager_li")
|
||||
assert clue.status == ClueStatus.CLOSED
|
||||
|
||||
|
||||
def test_illegal_transition_rejected(session):
|
||||
clue = _new_clue(session)
|
||||
with pytest.raises(clue_svc.IllegalTransitionError):
|
||||
# NEW 不能直接到 CLOSED
|
||||
clue_svc.transition(session, clue, ClueStatus.CLOSED, actor="x")
|
||||
|
||||
|
||||
def test_audit_hash_chain_integrity(session):
|
||||
_new_clue(session)
|
||||
clue = _new_clue(session)
|
||||
clue_svc.assign(session, clue, "auditor_zhang", "manager_li")
|
||||
ok, broken = audit.verify_chain(session)
|
||||
assert ok is True
|
||||
assert broken is None
|
||||
|
||||
|
||||
def test_clue_cannot_be_deleted(session):
|
||||
"""R19:数据库触发器禁止物理删除线索。"""
|
||||
clue = _new_clue(session)
|
||||
session.flush()
|
||||
with pytest.raises((InternalError, ProgrammingError)):
|
||||
session.execute(text("DELETE FROM clue WHERE id = :i"), {"i": clue.id})
|
||||
session.flush()
|
||||
|
||||
|
||||
def test_list_clues_filters(session):
|
||||
_new_clue(session, score=0.9)
|
||||
_new_clue(session, score=0.3)
|
||||
highs = clue_svc.list_clues(session, confidence=ConfidenceTier.HIGH)
|
||||
assert all(c.confidence == ConfidenceTier.HIGH for c in highs)
|
||||
Reference in New Issue
Block a user