Download backend/tests/unit/test_advanced_explainability.py from yusufcalisir/Collaborative-Fraud-Intelligence-Simulator: direct link, hf CLI and curl.
- Browser
- Download file 5.75 kB
-
https://huggingface.co/spaces/yusufcalisir/Collaborative-Fraud-Intelligence-Simulator/resolve/main/backend/tests/unit/test_advanced_explainability.py
- Command line
-
hf download hf://spaces/yusufcalisir/Collaborative-Fraud-Intelligence-Simulator/backend/tests/unit/test_advanced_explainability.py
-
curl -L -o test_advanced_explainability.py https://huggingface.co/spaces/yusufcalisir/Collaborative-Fraud-Intelligence-Simulator/resolve/main/backend/tests/unit/test_advanced_explainability.py
5.75 kB
| """Unit tests for Advanced AI Explainability Portal (Counterfactuals, Decision Replay, GNNExplainer).""" | |
| from __future__ import annotations | |
| from app.application.services.explainability_service import ExplainabilityService | |
| from app.domain.enums import AlertSeverity, AlertStatus | |
| from app.domain.investigation_entities import Alert | |
| def _make_sample_alert(alert_id: str = "alt_1001", risk_score: float = 750.0) -> Alert: | |
| return Alert( | |
| id=alert_id, | |
| bank_id="bank_a", | |
| transaction_id="tx_9988", | |
| risk_score=risk_score, | |
| severity=AlertSeverity.HIGH, | |
| status=AlertStatus.NEW, | |
| reason_codes=["HIGH-AMT", "GEO-RISK", "VEL-001"], | |
| confidence=0.92, | |
| involved_entity_ids=["cust_a1"], | |
| top_features=[{"feature": "transaction_amount", "contribution": 0.42}], | |
| risk_factors=["Transaction amount exceeds threshold"], | |
| historical_evidence=["Prior alert 2 days ago"], | |
| model_confidence=0.92, | |
| ) | |
| class TestCounterfactualEngine: | |
| """Verify counterfactual remediation path generation.""" | |
| def test_generate_counterfactuals_clears_alert(self): | |
| svc = ExplainabilityService() | |
| alert = _make_sample_alert(risk_score=750.0) | |
| cf = svc.generate_counterfactuals(alert, target_score=350.0) | |
| assert cf.alert_id == alert.id | |
| assert cf.original_score == 750.0 | |
| assert cf.remediated_score <= 350.0 | |
| assert cf.is_cleared is True | |
| assert len(cf.changes) >= 1 | |
| assert "CLEARED" in cf.summary_text | |
| def test_counterfactual_remediates_high_amount_and_geo(self): | |
| svc = ExplainabilityService() | |
| alert = _make_sample_alert(risk_score=850.0) | |
| cf = svc.generate_counterfactuals(alert, target_score=300.0) | |
| features_changed = [c.feature for c in cf.changes] | |
| assert "transaction_amount" in features_changed or "country_code" in features_changed | |
| def test_counterfactual_executes_real_risk_engine_search(self): | |
| """Verifies that counterfactual remediated_score is computed by real RiskScoringEngine and strictly clears target threshold.""" | |
| from app.application.services.risk_engine import RiskScoringEngine | |
| svc = ExplainabilityService() | |
| engine = RiskScoringEngine() | |
| alert = _make_sample_alert(risk_score=850.0) | |
| target_threshold = 350.0 | |
| result = svc.generate_counterfactuals(alert, target_score=target_threshold, risk_engine=engine) | |
| assert result.original_score == 850.0 | |
| assert result.remediated_score <= target_threshold | |
| assert result.is_cleared is True | |
| assert len(result.changes) >= 1 | |
| assert isinstance(result.remediated_score, float) | |
| def test_counterfactual_honest_failure_when_unreachable(self): | |
| """Verifies that when target threshold is deliberately unreachable within the search budget (e.g. 10.0), | |
| the search reports failure honestly rather than forcing a fake cleared status. | |
| """ | |
| from app.application.services.risk_engine import RiskScoringEngine | |
| svc = ExplainabilityService() | |
| engine = RiskScoringEngine() | |
| alert = _make_sample_alert(risk_score=850.0) | |
| unreachable_target = 10.0 | |
| result = svc.generate_counterfactuals(alert, target_score=unreachable_target, risk_engine=engine) | |
| # Confirm search reports failure honestly | |
| assert result.original_score == 850.0 | |
| assert result.is_cleared is False | |
| assert result.remediated_score <= result.original_score | |
| assert result.remediated_score > unreachable_target | |
| assert "did not reach" in result.summary_text | |
| class TestDecisionReplayAudit: | |
| """Verify deterministic decision replay inference audit.""" | |
| def test_replay_inference_audit_reproduces_score(self): | |
| svc = ExplainabilityService() | |
| alert = _make_sample_alert(risk_score=680.0) | |
| audit = svc.replay_inference_audit(alert) | |
| assert audit.alert_id == alert.id | |
| assert audit.model_version == "v1.4.2-champion" | |
| assert audit.model_auc > 0.90 | |
| assert len(audit.policy_rules_evaluated) == 9 | |
| assert audit.reconstructed_risk_score > 0 | |
| assert audit.audit_matched is not None | |
| class TestGNNExplainer: | |
| """Verify GNNExplainer graph attribution over entity neighborhood.""" | |
| def test_explain_gnn_embedding_calculates_edge_contributions(self): | |
| from app.application.services.graph_engine import GraphEngine | |
| from app.domain.enums import EntityType, RelationshipType | |
| from app.domain.investigation_entities import Entity, Relationship | |
| ge = GraphEngine() | |
| ge.register_entity( | |
| Entity(id="cust_a1", entity_type=EntityType.CUSTOMER, privacy_id="priv_a1", bank_id="bank_a") | |
| ) | |
| ge.register_entity( | |
| Entity(id="dev_x99", entity_type=EntityType.DEVICE, privacy_id="priv_x99", bank_id="bank_a") | |
| ) | |
| ge.add_relationship( | |
| Relationship( | |
| source_entity_id="cust_a1", | |
| target_entity_id="dev_x99", | |
| relationship_type=RelationshipType.SHARES_DEVICE, | |
| ) | |
| ) | |
| svc = ExplainabilityService() | |
| gnn_exp = svc.explain_gnn_embedding("cust_a1") | |
| assert gnn_exp.node_id == "cust_a1" | |
| assert gnn_exp.subgraph_nodes_count >= 1 | |
| assert len(gnn_exp.top_contributing_edges) >= 1 | |
| assert "Primary GNN Driver" in gnn_exp.primary_driver_text | |
| assert gnn_exp.top_contributing_edges[0].target == "dev_x99" | |
| # Verify sum of contribution percentages equals 100% | |
| total_pct = sum(e.contribution_percentage for e in gnn_exp.top_contributing_edges) | |
| assert abs(total_pct - 100.0) < 1.0 | |