"""Tests: Drei neue Module — Security Framework, Interop, Ethics.

Prüft:
  - SecurityMetrics, IncidentTracker, SecurityTeam, PostureScorer
  - InteropProtocol, CapabilityRegistry, MessageRouter, FederationManager
  - BudgetManager, CostTracker, BiasDetector, FairnessAuditor, EthicsPolicy
"""

from __future__ import annotations

# ============================================================================
# 1. AI Agent Security Framework
# ============================================================================
from cognithor.security.framework import (
    IncidentCategory,
    IncidentSeverity,
    IncidentStatus,
    IncidentTracker,
    PostureScorer,
    SecurityMetrics,
    SecurityTeam,
    TeamMember,
    TeamRole,
)


class TestIncidentTracker:
    def test_create_incident(self) -> None:
        tracker = IncidentTracker()
        inc = tracker.create(
            "Prompt Injection", IncidentCategory.PROMPT_INJECTION, IncidentSeverity.HIGH
        )
        assert inc.incident_id == "INC-00001"
        assert inc.status == IncidentStatus.DETECTED

    def test_transition(self) -> None:
        tracker = IncidentTracker()
        inc = tracker.create("Test", IncidentCategory.PROMPT_INJECTION, IncidentSeverity.MEDIUM)
        tracker.transition(inc.incident_id, IncidentStatus.INVESTIGATING)
        assert tracker.get(inc.incident_id).status == IncidentStatus.INVESTIGATING

    def test_transition_to_resolved(self) -> None:
        tracker = IncidentTracker()
        inc = tracker.create("Test", IncidentCategory.DATA_EXFILTRATION, IncidentSeverity.CRITICAL)
        tracker.transition(inc.incident_id, IncidentStatus.RESOLVED)
        assert tracker.get(inc.incident_id).resolved_at != ""

    def test_assign(self) -> None:
        tracker = IncidentTracker()
        inc = tracker.create("Test", IncidentCategory.BIAS_VIOLATION, IncidentSeverity.LOW)
        assert tracker.assign(inc.incident_id, "Alice", "compliance_officer")
        assert tracker.get(inc.incident_id).assigned_to == "Alice"

    def test_open_incidents(self) -> None:
        tracker = IncidentTracker()
        tracker.create("Open", IncidentCategory.PROMPT_INJECTION, IncidentSeverity.HIGH)
        inc2 = tracker.create("Resolved", IncidentCategory.DENIAL_OF_SERVICE, IncidentSeverity.LOW)
        tracker.transition(inc2.incident_id, IncidentStatus.RESOLVED)
        assert len(tracker.open_incidents()) == 1

    def test_by_severity(self) -> None:
        tracker = IncidentTracker()
        tracker.create("A", IncidentCategory.PROMPT_INJECTION, IncidentSeverity.HIGH)
        tracker.create("B", IncidentCategory.PROMPT_INJECTION, IncidentSeverity.HIGH)
        tracker.create("C", IncidentCategory.PROMPT_INJECTION, IncidentSeverity.LOW)
        assert len(tracker.by_severity(IncidentSeverity.HIGH)) == 2

    def test_by_category(self) -> None:
        tracker = IncidentTracker()
        tracker.create("A", IncidentCategory.MEMORY_POISONING, IncidentSeverity.HIGH)
        tracker.create("B", IncidentCategory.PROMPT_INJECTION, IncidentSeverity.HIGH)
        assert len(tracker.by_category(IncidentCategory.MEMORY_POISONING)) == 1

    def test_stats(self) -> None:
        tracker = IncidentTracker()
        tracker.create("A", IncidentCategory.PROMPT_INJECTION, IncidentSeverity.HIGH)
        stats = tracker.stats()
        assert stats["total"] == 1
        assert "by_severity" in stats

    def test_incident_to_dict(self) -> None:
        tracker = IncidentTracker()
        inc = tracker.create("Test", IncidentCategory.CREDENTIAL_LEAK, IncidentSeverity.CRITICAL)
        d = inc.to_dict()
        assert d["category"] == "credential_leak"
        assert d["severity"] == "critical"

    def test_transition_nonexistent(self) -> None:
        tracker = IncidentTracker()
        assert tracker.transition("FAKE", IncidentStatus.RESOLVED) is None


class TestSecurityMetrics:
    def test_resolution_rate_empty(self) -> None:
        tracker = IncidentTracker()
        metrics = SecurityMetrics(tracker)
        assert metrics.resolution_rate() == 100.0

    def test_resolution_rate(self) -> None:
        tracker = IncidentTracker()
        inc1 = tracker.create("A", IncidentCategory.PROMPT_INJECTION, IncidentSeverity.HIGH)
        tracker.create("B", IncidentCategory.PROMPT_INJECTION, IncidentSeverity.HIGH)
        tracker.transition(inc1.incident_id, IncidentStatus.RESOLVED)
        metrics = SecurityMetrics(tracker)
        assert metrics.resolution_rate() == 50.0

    def test_severity_distribution(self) -> None:
        tracker = IncidentTracker()
        tracker.create("A", IncidentCategory.PROMPT_INJECTION, IncidentSeverity.HIGH)
        tracker.create("B", IncidentCategory.PROMPT_INJECTION, IncidentSeverity.LOW)
        metrics = SecurityMetrics(tracker)
        dist = metrics.severity_distribution()
        assert dist["high"] == 1
        assert dist["low"] == 1

    def test_to_dict(self) -> None:
        tracker = IncidentTracker()
        metrics = SecurityMetrics(tracker)
        d = metrics.to_dict()
        assert "mttd_seconds" in d
        assert "mttr_seconds" in d
        assert "resolution_rate" in d

    def test_mttd_no_data(self) -> None:
        tracker = IncidentTracker()
        metrics = SecurityMetrics(tracker)
        assert metrics.mttd() == 0.0

    def test_mttr_no_data(self) -> None:
        tracker = IncidentTracker()
        metrics = SecurityMetrics(tracker)
        assert metrics.mttr() == 0.0


class TestSecurityTeam:
    def test_add_member(self) -> None:
        team = SecurityTeam()
        team.add_member(TeamMember("m1", "Alice", TeamRole.SECURITY_ANALYST, on_call=True))
        assert team.member_count == 1

    def test_by_role(self) -> None:
        team = SecurityTeam()
        team.add_member(TeamMember("m1", "Alice", TeamRole.SECURITY_ANALYST))
        team.add_member(TeamMember("m2", "Bob", TeamRole.ML_ENGINEER))
        assert len(team.by_role(TeamRole.SECURITY_ANALYST)) == 1

    def test_on_call(self) -> None:
        team = SecurityTeam()
        team.add_member(TeamMember("m1", "Alice", TeamRole.SECURITY_ANALYST, on_call=True))
        team.add_member(TeamMember("m2", "Bob", TeamRole.ML_ENGINEER, on_call=False))
        assert len(team.on_call()) == 1

    def test_auto_assign(self) -> None:
        team = SecurityTeam()
        team.add_member(TeamMember("m1", "Alice", TeamRole.SECURITY_ANALYST, on_call=True))
        tracker = IncidentTracker()
        inc = tracker.create("Test", IncidentCategory.PROMPT_INJECTION, IncidentSeverity.HIGH)
        assigned = team.auto_assign(inc)
        assert assigned is not None
        assert assigned.name == "Alice"
        assert inc.assigned_to == "Alice"

    def test_auto_assign_prefers_on_call(self) -> None:
        team = SecurityTeam()
        team.add_member(TeamMember("m1", "Alice", TeamRole.SECURITY_ANALYST, on_call=False))
        team.add_member(TeamMember("m2", "Bob", TeamRole.SECURITY_ANALYST, on_call=True))
        tracker = IncidentTracker()
        inc = tracker.create("Test", IncidentCategory.DATA_EXFILTRATION, IncidentSeverity.HIGH)
        assigned = team.auto_assign(inc)
        assert assigned.name == "Bob"

    def test_remove_member(self) -> None:
        team = SecurityTeam()
        team.add_member(TeamMember("m1", "Alice", TeamRole.SECURITY_ANALYST))
        assert team.remove_member("m1")
        assert team.member_count == 0

    def test_stats(self) -> None:
        team = SecurityTeam()
        team.add_member(TeamMember("m1", "Alice", TeamRole.SECURITY_ANALYST, on_call=True))
        stats = team.stats()
        assert stats["total_members"] == 1
        assert stats["on_call"] == 1


class TestPostureScorer:
    def test_perfect_score(self) -> None:
        scorer = PostureScorer()
        result = scorer.calculate(
            resolution_rate=100,
            mttr_seconds=0,
            team_roles_filled=6,
            team_roles_total=6,
            pipeline_pass_rate=100,
            compliance_score=100,
        )
        assert result["posture_score"] == 100.0
        assert result["level"] == "excellent"

    def test_poor_score(self) -> None:
        scorer = PostureScorer()
        result = scorer.calculate(
            resolution_rate=20,
            mttr_seconds=5000,
            team_roles_filled=1,
            team_roles_total=6,
            pipeline_pass_rate=30,
            compliance_score=20,
        )
        assert result["posture_score"] < 50
        assert result["level"] in ("poor", "critical")

    def test_breakdown_present(self) -> None:
        scorer = PostureScorer()
        result = scorer.calculate()
        assert "breakdown" in result
        assert "weights" in result


# ============================================================================
# 2. Cross-Agent Interoperability
# ============================================================================

from cognithor.core.interop import (
    AgentCapability,
    AgentIdentity,
    CapabilityRegistry,
    CapabilityType,
    FederationManager,
    FederationStatus,
    InteropMessage,
    InteropProtocol,
    MessageRouter,
    MessageType,
)


class TestCapabilityRegistry:
    def test_register_and_find(self) -> None:
        reg = CapabilityRegistry()
        reg.register("agent-1", [AgentCapability(CapabilityType.CODE_GENERATION)])
        assert "agent-1" in reg.find_agents(CapabilityType.CODE_GENERATION)
        assert "agent-1" not in reg.find_agents(CapabilityType.WEB_SEARCH)

    def test_best_agent_for(self) -> None:
        reg = CapabilityRegistry()
        reg.register("slow", [AgentCapability(CapabilityType.TRANSLATION, avg_response_ms=500)])
        reg.register("fast", [AgentCapability(CapabilityType.TRANSLATION, avg_response_ms=100)])
        assert reg.best_agent_for(CapabilityType.TRANSLATION) == "fast"

    def test_best_agent_prefer_cheap(self) -> None:
        reg = CapabilityRegistry()
        reg.register(
            "expensive",
            [AgentCapability(CapabilityType.DATA_ANALYSIS, cost_per_call=1.0, avg_response_ms=50)],
        )
        reg.register(
            "cheap",
            [AgentCapability(CapabilityType.DATA_ANALYSIS, cost_per_call=0.1, avg_response_ms=200)],
        )
        assert reg.best_agent_for(CapabilityType.DATA_ANALYSIS, prefer_fast=False) == "cheap"

    def test_find_by_language(self) -> None:
        reg = CapabilityRegistry()
        reg.register("de-agent", [AgentCapability(CapabilityType.TRANSLATION, languages=["de"])])
        reg.register("en-agent", [AgentCapability(CapabilityType.TRANSLATION, languages=["en"])])
        assert "de-agent" in reg.find_by_language("de")
        assert "de-agent" not in reg.find_by_language("en")

    def test_unregister(self) -> None:
        reg = CapabilityRegistry()
        reg.register("a1", [AgentCapability(CapabilityType.EMAIL)])
        assert reg.unregister("a1")
        assert reg.agent_count == 0

    def test_stats(self) -> None:
        reg = CapabilityRegistry()
        reg.register(
            "a1", [AgentCapability(CapabilityType.EMAIL), AgentCapability(CapabilityType.CRM)]
        )
        stats = reg.stats()
        assert stats["registered_agents"] == 1
        assert stats["total_capabilities"] == 2


class TestMessageRouter:
    def test_send_to_handler(self) -> None:
        router = MessageRouter()
        received: list[InteropMessage] = []
        router.register_handler("agent-1", lambda m: received.append(m) or "ok")
        msg = router.create_message(MessageType.REQUEST, "local", "agent-1", {"data": "hello"})
        result = router.send(msg)
        assert result["delivered"] is True
        assert len(received) == 1

    def test_send_to_unknown(self) -> None:
        router = MessageRouter()
        msg = router.create_message(MessageType.REQUEST, "local", "unknown")
        result = router.send(msg)
        assert result["delivered"] is False

    def test_broadcast(self) -> None:
        router = MessageRouter()
        count = [0]
        router.register_broadcast_handler(lambda m: count.__setitem__(0, count[0] + 1))
        router.register_broadcast_handler(lambda m: count.__setitem__(0, count[0] + 1))
        msg = router.create_message(MessageType.BROADCAST, "local", "")
        result = router.send(msg)
        assert result["broadcast"] is True
        assert count[0] == 2

    def test_message_log(self) -> None:
        router = MessageRouter()
        router.register_handler("a1", lambda m: "ok")
        msg = router.create_message(MessageType.HEARTBEAT, "local", "a1")
        router.send(msg)
        assert router.message_count == 1

    def test_stats(self) -> None:
        router = MessageRouter()
        stats = router.stats()
        assert "total_messages" in stats


class TestFederationManager:
    def test_propose_and_accept(self) -> None:
        fm = FederationManager()
        link = fm.propose("local", "remote", "https://remote.jarvis.io")
        assert link.status == FederationStatus.PENDING
        fm.accept(link.link_id)
        assert fm.get_link(link.link_id).status == FederationStatus.ACTIVE

    def test_suspend(self) -> None:
        fm = FederationManager()
        link = fm.propose("local", "remote", "https://remote.jarvis.io")
        fm.accept(link.link_id)
        fm.suspend(link.link_id)
        assert fm.get_link(link.link_id).status == FederationStatus.SUSPENDED

    def test_revoke(self) -> None:
        fm = FederationManager()
        link = fm.propose("local", "remote", "https://remote.jarvis.io")
        fm.revoke(link.link_id)
        assert fm.get_link(link.link_id).status == FederationStatus.REVOKED

    def test_can_delegate(self) -> None:
        fm = FederationManager()
        link = fm.propose(
            "local", "remote", "https://remote.jarvis.io", [CapabilityType.CODE_GENERATION]
        )
        fm.accept(link.link_id)
        assert fm.can_delegate(link.link_id, CapabilityType.CODE_GENERATION)
        assert not fm.can_delegate(link.link_id, CapabilityType.CRM)

    def test_rate_limiting(self) -> None:
        fm = FederationManager()
        link = fm.propose("local", "remote", "https://remote.jarvis.io")
        fm.accept(link.link_id)
        link_obj = fm.get_link(link.link_id)
        link_obj.max_requests_per_hour = 2
        assert fm.can_delegate(link.link_id, CapabilityType.TASK_EXECUTION)
        assert fm.can_delegate(link.link_id, CapabilityType.TASK_EXECUTION)
        assert not fm.can_delegate(link.link_id, CapabilityType.TASK_EXECUTION)

    def test_active_links(self) -> None:
        fm = FederationManager()
        l1 = fm.propose("a", "b", "url1")
        fm.accept(l1.link_id)
        fm.propose("a", "c", "url2")  # pending
        assert len(fm.active_links()) == 1

    def test_stats(self) -> None:
        fm = FederationManager()
        fm.propose("a", "b", "url")
        stats = fm.stats()
        assert stats["total_links"] == 1
        assert stats["pending"] == 1


class TestInteropProtocol:
    def test_register_and_find(self) -> None:
        proto = InteropProtocol()
        proto.register_agent(
            AgentIdentity("a1", "Agent-1"),
            [AgentCapability(CapabilityType.CODE_GENERATION)],
        )
        assert proto.agent_count == 1
        assert proto.get_agent("a1") is not None

    def test_delegate_task(self) -> None:
        proto = InteropProtocol()
        result_holder: list[dict] = []
        proto.register_agent(
            AgentIdentity("a1", "Agent-1"),
            [AgentCapability(CapabilityType.CODE_GENERATION, avg_response_ms=100)],
            handler=lambda m: result_holder.append(m.payload) or "done",
        )
        result = proto.delegate_task(CapabilityType.CODE_GENERATION, {"task": "write code"})
        assert result["success"] is True
        assert len(result_holder) == 1

    def test_delegate_no_agent(self) -> None:
        proto = InteropProtocol()
        result = proto.delegate_task(CapabilityType.IMAGE_ANALYSIS, {"task": "analyze"})
        assert result["success"] is False

    def test_unregister(self) -> None:
        proto = InteropProtocol()
        proto.register_agent(AgentIdentity("a1", "Agent-1"))
        assert proto.unregister_agent("a1")
        assert proto.agent_count == 0

    def test_online_agents(self) -> None:
        proto = InteropProtocol()
        a1 = AgentIdentity("a1", "Agent-1", status="online")
        a2 = AgentIdentity("a2", "Agent-2", status="offline")
        proto.register_agent(a1)
        proto.register_agent(a2)
        assert len(proto.online_agents()) == 1

    def test_stats(self) -> None:
        proto = InteropProtocol()
        proto.register_agent(AgentIdentity("a1", "Agent-1"))
        stats = proto.stats()
        assert stats["registered_agents"] == 1
        assert "capabilities" in stats
        assert "federation" in stats


# ============================================================================
# 3. Ethics & Economic Governance
# ============================================================================

from cognithor.audit.ethics import (
    BiasCategory,
    BiasDetector,
    BudgetManager,
    CostTracker,
    EconomicGovernor,
    EthicsPolicy,
    EthicsViolationType,
    FairnessAuditor,
)


class TestBudgetManager:
    def test_set_and_get_limit(self) -> None:
        bm = BudgetManager()
        limit = bm.set_limit("agent-1", daily=100.0, monthly=2000.0)
        assert limit.daily_limit == 100.0
        assert bm.get_limit("agent-1") is not None

    def test_can_spend_ok(self) -> None:
        bm = BudgetManager()
        bm.set_limit("agent-1", daily=50.0, per_request=10.0)
        result = bm.can_spend("agent-1", 5.0)
        assert result["allowed"] is True

    def test_can_spend_over_per_request(self) -> None:
        bm = BudgetManager()
        bm.set_limit("agent-1", per_request=2.0)
        result = bm.can_spend("agent-1", 5.0)
        assert result["allowed"] is False
        assert "Single request" in result["reason"]

    def test_can_spend_over_daily(self) -> None:
        bm = BudgetManager()
        bm.set_limit("agent-1", daily=10.0, per_request=100.0)
        bm.record_spend("agent-1", 9.0)
        result = bm.can_spend("agent-1", 5.0)
        assert result["allowed"] is False
        assert "Daily budget" in result["reason"] or "Tagesbudget" in result["reason"]

    def test_can_spend_no_limit(self) -> None:
        bm = BudgetManager()
        result = bm.can_spend("unknown", 999.0)
        assert result["allowed"] is True

    def test_reset_daily(self) -> None:
        bm = BudgetManager()
        bm.set_limit("agent-1", daily=50.0)
        bm.record_spend("agent-1", 30.0)
        bm.reset_daily("agent-1")
        assert bm.get_limit("agent-1").spent_today == 0.0

    def test_over_budget(self) -> None:
        bm = BudgetManager()
        bm.set_limit("agent-1", daily=10.0)
        bm.record_spend("agent-1", 9.5)
        assert len(bm.over_budget()) == 1

    def test_stats(self) -> None:
        bm = BudgetManager()
        bm.set_limit("agent-1")
        stats = bm.stats()
        assert stats["total_entities"] == 1


class TestCostTracker:
    def test_track_cost(self) -> None:
        tracker = CostTracker()
        entry = tracker.track("agent-1", "gpt-4o", 1000, 500)
        assert entry.cost_eur > 0
        assert tracker.entry_count == 1

    def test_local_model_free(self) -> None:
        tracker = CostTracker()
        entry = tracker.track("agent-1", "llama-3.1-70b", 5000, 2000)
        assert entry.cost_eur == 0.0

    def test_cost_by_agent(self) -> None:
        tracker = CostTracker()
        tracker.track("a1", "gpt-4o", 1000, 500)
        tracker.track("a2", "gpt-4o", 1000, 500)
        by_agent = tracker.cost_by_agent()
        assert "a1" in by_agent
        assert "a2" in by_agent

    def test_cost_by_model(self) -> None:
        tracker = CostTracker()
        tracker.track("a1", "gpt-4o", 1000, 500)
        tracker.track("a1", "claude-3-haiku", 1000, 500)
        by_model = tracker.cost_by_model()
        assert "gpt-4o" in by_model
        assert "claude-3-haiku" in by_model

    def test_budget_integration(self) -> None:
        bm = BudgetManager()
        bm.set_limit("agent-1", daily=1.0)
        tracker = CostTracker(bm)
        tracker.track("agent-1", "gpt-4o", 1000, 500)
        assert bm.get_limit("agent-1").spent_today > 0

    def test_stats(self) -> None:
        tracker = CostTracker()
        tracker.track("a1", "gpt-4o", 100, 50)
        stats = tracker.stats()
        assert stats["total_entries"] == 1
        assert stats["total_cost_eur"] > 0


class TestBiasDetector:
    def test_no_bias(self) -> None:
        detector = BiasDetector()
        findings = detector.check("Der Himmel ist blau.")
        assert len(findings) == 0

    def test_gender_bias(self) -> None:
        detector = BiasDetector()
        findings = detector.check("Sie ist emotional und typisch Frau")
        assert len(findings) >= 1
        assert findings[0].category == BiasCategory.GENDER

    def test_age_bias(self) -> None:
        detector = BiasDetector()
        findings = detector.check("Er ist zu alt für diesen Job")
        assert len(findings) >= 1
        assert findings[0].category == BiasCategory.AGE

    def test_stats(self) -> None:
        detector = BiasDetector()
        detector.check("Sie ist hysterisch")
        stats = detector.stats()
        assert stats["total_findings"] >= 1


class TestFairnessAuditor:
    def test_equal_latency(self) -> None:
        auditor = FairnessAuditor()
        result = auditor.audit_response_times({"group_a": [100, 110], "group_b": [105, 115]})
        assert result.passed is True
        assert result.score > 80

    def test_unequal_latency(self) -> None:
        auditor = FairnessAuditor()
        result = auditor.audit_response_times({"group_a": [100], "group_b": [500]})
        assert result.score < 50

    def test_error_rate_parity(self) -> None:
        auditor = FairnessAuditor()
        result = auditor.audit_error_rates({"group_a": (1, 100), "group_b": (2, 100)})
        assert result.passed is True

    def test_unequal_error_rates(self) -> None:
        auditor = FairnessAuditor()
        result = auditor.audit_error_rates({"group_a": (1, 100), "group_b": (50, 100)})
        assert result.passed is False

    def test_allocation_fairness(self) -> None:
        auditor = FairnessAuditor()
        result = auditor.audit_allocation({"a": 100, "b": 95, "c": 105})
        assert result.passed is True

    def test_pass_rate(self) -> None:
        auditor = FairnessAuditor()
        auditor.audit_response_times({"a": [100], "b": [100]})
        assert auditor.pass_rate() == 100.0

    def test_stats(self) -> None:
        auditor = FairnessAuditor()
        auditor.audit_response_times({"a": [100], "b": [100]})
        stats = auditor.stats()
        assert stats["total_audits"] == 1


class TestEthicsPolicy:
    def test_bias_within_limit(self) -> None:
        policy = EthicsPolicy(max_bias_findings_per_day=10)
        assert policy.check_bias(5) is None

    def test_bias_over_limit(self) -> None:
        policy = EthicsPolicy(max_bias_findings_per_day=5)
        violation = policy.check_bias(10)
        assert violation is not None
        assert violation.violation_type == EthicsViolationType.BIAS

    def test_fairness_ok(self) -> None:
        policy = EthicsPolicy(min_fairness_score=80.0)
        assert policy.check_fairness(90.0) is None

    def test_fairness_fail(self) -> None:
        policy = EthicsPolicy(min_fairness_score=80.0)
        violation = policy.check_fairness(40.0)
        assert violation is not None
        assert violation.severity == "high"

    def test_stats(self) -> None:
        policy = EthicsPolicy()
        policy.check_bias(100)
        stats = policy.stats()
        assert stats["total_violations"] == 1
        assert "config" in stats


class TestEconomicGovernor:
    def test_pre_flight_ok(self) -> None:
        gov = EconomicGovernor()
        gov.budget.set_limit("agent-1", daily=100.0, per_request=50.0)
        result = gov.pre_flight_check("agent-1", 10.0)
        assert result["approved"] is True

    def test_pre_flight_over_budget(self) -> None:
        gov = EconomicGovernor()
        gov.budget.set_limit("agent-1", daily=5.0, per_request=2.0)
        result = gov.pre_flight_check("agent-1", 10.0)
        assert result["approved"] is False

    def test_stats(self) -> None:
        gov = EconomicGovernor()
        stats = gov.stats()
        assert "budget" in stats
        assert "costs" in stats
        assert "bias" in stats
        assert "fairness" in stats
        assert "ethics" in stats
