Files
group_xinghuo_jinrong/tests/test_sprint3_kyc_session.py
T
zhanghongyu_0626 f856ab4aa6 fix(tests): Resolve pytest session binding issues and update test cases
- Updated test files to import `AgentSessionLocal` from `advisor_db` instead of directly, preventing session binding to the real database during tests.
- Fixed 6 test cases to use the new login token utility, ensuring consistency across authentication methods.
- Adjusted customer risk codes in `test_convert_confirm.py` to reflect changes in customer classification (C3 to C4).
- Verified that changes resulted in zero database pollution during test runs, maintaining integrity of the testing environment.
- Documented findings and updates in the relevant test logs and memory files, ensuring clarity on the current state of tests and defects.
2026-09-13 23:21:34 +08:00

207 lines
7.1 KiB
Python

from datetime import datetime, timedelta, timezone
from uuid import uuid4
import pytest
from fastapi.testclient import TestClient
from sqlalchemy import inspect
import app.api.kyc as kyc_api
from app import advisor_db
from app.main import app
from app.model.entities import AgentSession
from app.model.entities_advisor import KycSession
from app.model.advisor_schemas import AuthContext, KycSessionCreate
from app.repository.kyc_session_repository import KycSessionRepository
from app.service.kyc_session_service import KycSessionService
from app.utils.exceptions import AppError
client = TestClient(app)
class FakeCoreRepository:
def get_customer_l0(self, customer_id: str) -> dict | None:
if customer_id == "CUST-KYC-001":
return {
"customer_id": customer_id,
"display_name": "客户·KYC测试",
"age": 32,
}
return None
class AllowOwnership:
def assert_customer_access(self, auth: AuthContext, customer_id: str) -> None:
assert auth.advisor_id == "ADV-KYC-001"
assert customer_id == "CUST-KYC-001"
def advisor_context() -> AuthContext:
return AuthContext(
user_id="advisor_test",
display_name="测试顾问",
roles=["advisor"],
permissions=["kyc:create", "kyc:chat"],
trace_id="trace-kyc-session-unit",
advisor_id="ADV-KYC-001",
)
def session_service() -> KycSessionService:
return KycSessionService(
repository=KycSessionRepository(),
core_repository=FakeCoreRepository(),
ownership_service=AllowOwnership(),
)
def test_kyc_session_migration_and_creation_associate_agent_session():
inspector = inspect(advisor_db.agent_engine)
assert "kyc_session" in inspector.get_table_names()
columns = {column["name"] for column in inspector.get_columns("kyc_session")}
assert {
"session_id",
"trace_id",
"advisor_id",
"customer_id",
"session_type",
"status",
"current_node",
"collected_fields",
"missing_fields",
"progress_pct",
"dialog_turns",
"started_at",
"completed_at",
"duration_seconds",
}.issubset(columns)
service = session_service()
result = service.create_session(
KycSessionCreate(customer_id="CUST-KYC-001", session_type="new_customer"),
auth=advisor_context(),
trace_id=f"trace-kyc-create-{uuid4().hex}",
)
assert result.status == "in_progress"
assert result.current_node == "basic_info"
assert result.collected_fields == {}
assert result.progress_pct == 0
assert result.suggested_question
assert "年龄" in result.suggested_question
with advisor_db.AgentSessionLocal() as session:
agent_session = (
session.query(AgentSession)
.filter(AgentSession.session_id == result.session_id)
.one()
)
assert agent_session.customer_id == "CUST-KYC-001"
assert agent_session.agent_type == "advisor"
assert agent_session.metadata_["kyc_session_id"] == result.session_id
def test_kyc_session_can_resume_and_complete():
service = session_service()
auth = advisor_context()
created = service.create_session(
KycSessionCreate(customer_id="CUST-KYC-001", session_type="periodic_review"),
auth=auth,
trace_id=f"trace-kyc-resume-{uuid4().hex}",
)
resumed = service.get_session(created.session_id, auth=auth)
assert resumed.session_id == created.session_id
assert resumed.status == "in_progress"
assert resumed.current_node == "basic_info"
completed = service.complete_session(
created.session_id,
auth=auth,
trace_id=f"trace-kyc-complete-{uuid4().hex}",
)
assert completed.status == "completed"
assert completed.current_node == "profile_generation"
assert completed.completed_at is not None
assert completed.duration_seconds is not None
def test_expired_in_progress_session_is_archived_as_abandoned():
service = session_service()
auth = advisor_context()
created = service.create_session(
KycSessionCreate(customer_id="CUST-KYC-001", session_type="deep_kyc"),
auth=auth,
trace_id=f"trace-kyc-expire-create-{uuid4().hex}",
)
old_time = datetime.now(timezone.utc).replace(tzinfo=None) - timedelta(hours=5)
with advisor_db.AgentSessionLocal() as session:
row = session.query(KycSession).filter(KycSession.session_id == created.session_id).one()
row.started_at = old_time
row.updated_at = old_time
session.commit()
expired = service.expire_stale_sessions(
now=datetime.now(timezone.utc),
ttl_hours=4,
trace_id=f"trace-kyc-expire-{uuid4().hex}",
)
assert expired == 1
archived = service.get_session(created.session_id, auth=auth)
assert archived.status == "abandoned"
assert archived.completed_at is None
def test_completed_session_cannot_be_completed_again():
service = session_service()
auth = advisor_context()
created = service.create_session(
KycSessionCreate(customer_id="CUST-KYC-001", session_type="new_customer"),
auth=auth,
trace_id=f"trace-kyc-repeat-{uuid4().hex}",
)
service.complete_session(
created.session_id,
auth=auth,
trace_id=f"trace-kyc-complete-first-{uuid4().hex}",
)
with pytest.raises(AppError) as exc_info:
service.complete_session(
created.session_id,
auth=auth,
trace_id=f"trace-kyc-complete-second-{uuid4().hex}",
)
assert exc_info.value.code == "40901"
assert exc_info.value.status_code == 409
def test_kyc_session_api_creates_and_resumes_session(monkeypatch):
monkeypatch.setattr(kyc_api, "kyc_session_service", KycSessionService())
login = client.post("/api/auth/login", json={"actor_id": "STAFF-10086", "token_type": "staff"})
token = login.json()["data"]["access_token"]
trace_id = f"trace-kyc-api-{uuid4().hex}"
created = client.post(
"/api/advisor-agent/kyc/sessions",
json={
"customer_id": "CUST-1001",
"session_type": "new_customer",
"customer_display_name": "客户·王**",
},
headers={"Authorization": f"Bearer {token}", "X-Trace-Id": trace_id},
)
assert created.status_code == 200
assert created.json()["data"]["status"] == "in_progress"
assert created.json()["data"]["current_node"] == "basic_info"
assert created.json()["trace_id"] == trace_id
session_id = created.json()["data"]["session_id"]
resumed = client.get(
f"/api/advisor-agent/kyc/sessions/{session_id}",
headers={"Authorization": f"Bearer {token}", "X-Trace-Id": trace_id},
)
assert resumed.status_code == 200
assert resumed.json()["data"]["session_id"] == session_id
assert resumed.json()["data"]["suggested_question"]
def test_kyc_session_api_requires_authentication():
response = client.post(
"/api/advisor-agent/kyc/sessions",
json={"customer_id": "CUST-1001", "session_type": "new_customer"},
)
assert response.status_code == 401