from decimal import Decimal from app.algorithem.employee_behavior_profile import ( ProfileComponent, build_review_suggestions, calculate_review_priority_score, evaluate_weighted_profile, level_from_score, normalize_by_peer_percentiles, percentile, ) from app.algorithem.employee_behavior_profile_tags import build_profile_radar, build_profile_tags def test_peer_percentile_normalization_and_level_mapping() -> None: assert percentile([10, 20, 30, 40, 50], 90) == Decimal("46.0") assert normalize_by_peer_percentiles(35, 20, 50) == 50 assert normalize_by_peer_percentiles(10, 20, 50) == 0 assert level_from_score(82) == "escalation" def test_weighted_profile_uses_component_weights() -> None: result = evaluate_weighted_profile( "expense", [ ProfileComponent("frequency_score", "申请频次", 80, weight=Decimal("0.20")), ProfileComponent("amount_occupancy_score", "预算占用", 60, weight=Decimal("0.30")), ProfileComponent("peer_deviation_score", "同组偏离", 40, weight=Decimal("0.50")), ], ) assert result.profile_score == 54 assert result.profile_level == "watch" assert result.top_contributors(1)[0]["code"] == "peer_deviation_score" def test_review_priority_excludes_ai_usage_score() -> None: assert ( calculate_review_priority_score( expense_profile_score=80, process_quality_score=20, ) == 62 ) assert calculate_review_priority_score( expense_profile_score=80, process_quality_score=20, ) == calculate_review_priority_score( expense_profile_score=80, process_quality_score=20, ) def test_review_suggestions_generate_caps_without_auto_penalty() -> None: suggestions = build_review_suggestions( expense_profile_score=72, process_quality_score=65, requested_days=Decimal("5"), peer_days_p75=Decimal("3"), policy_limit=Decimal("800"), peer_unit_amount_p75=Decimal("600"), ) types = {item["type"] for item in suggestions} assert "review_travel_days" in types assert "review_entertainment_unit_amount" in types assert any(item["recommended_upper"] == "3" for item in suggestions) def test_profile_tags_and_approval_radar_use_quantified_evidence() -> None: profiles = [ { "profile_type": "expense", "score": 82, "level": "escalation", "metrics": { "window_days": 90, "expense_type_scope": "travel", "peer_sample_size": 20, "amount_total": "128000", "amount_share": "0.34", "claim_count": 6, "current_claim_amount": "56000", "requested_days": "5", "peer_days_p75": "3", }, "top_contributors": [ {"code": "amount_occupancy_score", "score": 90}, {"code": "peer_deviation_score", "score": 88}, {"code": "current_claim_deviation_score", "score": 86}, {"code": "frequency_score", "score": 84}, ], }, { "profile_type": "process_quality", "score": 68, "level": "review", "metrics": { "peer_sample_size": 20, "return_count": 2, "missing_attachment_count": 3, "invoice_mismatch_count": 1, "missing_business_context_count": 2, }, "top_contributors": [ {"code": "return_count_score", "score": 70}, {"code": "missing_attachment_score", "score": 75}, {"code": "invoice_mismatch_score", "score": 60}, ], }, ] tags = build_profile_tags(profiles, scene="approval") tag_codes = {item["code"] for item in tags} assert {"expense_king", "large_amount_deviation", "return_frequent"} <= tag_codes assert all(item["evidence"] for item in tags) radar = build_profile_radar(profiles, tags, scene="approval") dimensions = {item["code"]: item for item in radar["dimensions"]} assert set(dimensions) == { "expense_intensity", "application_rhythm", "travel_entertainment", "material_completeness", "process_pressure", } assert dimensions["expense_intensity"]["score"] >= 70 assert "expense_king" in dimensions["expense_intensity"]["top_tags"] def test_profile_tags_include_ai_and_approval_traits_outside_approval_scene() -> None: profiles = [ { "profile_type": "ai_usage", "score": 72, "level": "review", "metrics": { "peer_sample_size": 15, "ai_run_count": 14, "tool_call_count": 10, "failed_tool_call_count": 3, "estimated_token_count": 22000, "token_count_mode": "estimated_token_count", }, "top_contributors": [ {"code": "ai_call_count_score", "score": 75}, {"code": "token_cost_score", "score": 70}, {"code": "failed_ai_call_score", "score": 80}, ], }, { "profile_type": "approval", "score": 64, "level": "review", "metrics": { "peer_sample_size": 12, "approval_record_count": 6, "direct_approve_ratio": "0.5", "return_count": 3, "sla_overdue_rate": "0.4", }, "top_contributors": [ {"code": "system_advice_override_score", "score": 70}, ], }, ] tags = build_profile_tags(profiles, scene="operations") tag_codes = {item["code"] for item in tags} assert {"ai_heavy", "token_high", "ai_failure_cluster", "cautious_reviewer"} <= tag_codes radar = build_profile_radar(profiles, tags, scene="operations") assert len(radar["dimensions"]) == 8 assert any( item["code"] == "ai_collaboration" and item["score"] > 0 for item in radar["dimensions"] )