litellm/tests/unit/proxy/management_endpoints/test_compliance_endpoints.py
devin-ai-integration[bot] 73072b8643
test(proxy): move management_endpoints, management_helpers and guardrails tests into tests/unit/proxy (#44003)
* test(proxy): move auth, hooks, policy_engine and client tests into tests/unit/proxy

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(proxy): stub HIBP through respx by disabling the aiohttp transport

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(proxy): share the httpx transport fixture across proxy unit tests

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(proxy): restore proxy globals without a missing-value sentinel

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(proxy): package moved dirs and stub the login breach check at the HTTP boundary

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(proxy): isolate the mcp server manager per test

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(proxy): move management_endpoints, management_helpers and guardrails tests into tests/unit/proxy

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(proxy): reuse the shared httpx transport fixture in moved proxy tests

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(proxy): stub outbound HTTP and package moved test dirs

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(proxy): restore the config server hostname in the mcp resolution test

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(proxy): pin the completion tokenizer model in the straiker screening test

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: yuneng <yuneng@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-01 10:52:03 -07:00

625 lines
25 KiB
Python

"""
Unit tests for compliance check endpoints (EU AI Act and GDPR).
"""
import pytest
from litellm.proxy.compliance_checks import ComplianceChecker
from litellm.types.proxy.compliance_endpoints import ComplianceCheckRequest
# ---------------------------------------------------------------------------
# EU AI Act — Non-compliant cases (Task #3)
# ---------------------------------------------------------------------------
class TestEuAiActNonCompliant:
"""Requests that should NOT be EU AI Act compliant."""
def test_no_guardrails_applied(self):
"""Request with no guardrail information at all."""
data = ComplianceCheckRequest(
request_id="req-001",
user_id="user-1",
model="gpt-4",
timestamp="2026-02-17T00:00:00Z",
guardrail_information=None,
)
checks = ComplianceChecker(data).check_eu_ai_act()
results = {c.check_name: c.passed for c in checks}
assert results["Guardrails applied"] is False
assert results["Content screened before LLM"] is False
assert results["Audit record complete"] is False
def test_empty_guardrails_list(self):
"""Request with an empty guardrail list."""
data = ComplianceCheckRequest(
request_id="req-002",
user_id="user-1",
model="gpt-4",
timestamp="2026-02-17T00:00:00Z",
guardrail_information=[],
)
checks = ComplianceChecker(data).check_eu_ai_act()
results = {c.check_name: c.passed for c in checks}
assert results["Guardrails applied"] is False
assert results["Content screened before LLM"] is False
assert results["Audit record complete"] is False
def test_no_prohibited_practices_screening(self):
"""Guardrails exist but only post-call (no pre-call screening)."""
data = ComplianceCheckRequest(
request_id="req-003",
user_id="user-1",
model="gpt-4",
timestamp="2026-02-17T00:00:00Z",
guardrail_information=[
{
"guardrail_name": "content_filter",
"guardrail_mode": "post_call",
"guardrail_status": "success",
}
],
)
checks = ComplianceChecker(data).check_eu_ai_act()
results = {c.check_name: c.passed for c in checks}
assert results["Guardrails applied"] is True
assert results["Content screened before LLM"] is False
def test_incomplete_audit_missing_user_id(self):
"""Audit record missing user_id."""
data = ComplianceCheckRequest(
request_id="req-004",
user_id=None,
model="gpt-4",
timestamp="2026-02-17T00:00:00Z",
guardrail_information=[
{
"guardrail_name": "prohibited_practices",
"guardrail_status": "success",
}
],
)
checks = ComplianceChecker(data).check_eu_ai_act()
results = {c.check_name: c.passed for c in checks}
assert results["Audit record complete"] is False
def test_incomplete_audit_missing_model(self):
"""Audit record missing model."""
data = ComplianceCheckRequest(
request_id="req-005",
user_id="user-1",
model=None,
timestamp="2026-02-17T00:00:00Z",
guardrail_information=[
{
"guardrail_name": "prohibited_practices",
"guardrail_status": "success",
}
],
)
checks = ComplianceChecker(data).check_eu_ai_act()
results = {c.check_name: c.passed for c in checks}
assert results["Audit record complete"] is False
def test_incomplete_audit_missing_timestamp(self):
"""Audit record missing timestamp."""
data = ComplianceCheckRequest(
request_id="req-006",
user_id="user-1",
model="gpt-4",
timestamp=None,
guardrail_information=[
{
"guardrail_name": "prohibited_practices",
"guardrail_status": "success",
}
],
)
checks = ComplianceChecker(data).check_eu_ai_act()
results = {c.check_name: c.passed for c in checks}
assert results["Audit record complete"] is False
def test_incomplete_audit_missing_guardrails(self):
"""Audit record has user/model/timestamp but no guardrails."""
data = ComplianceCheckRequest(
request_id="req-007",
user_id="user-1",
model="gpt-4",
timestamp="2026-02-17T00:00:00Z",
guardrail_information=[],
)
checks = ComplianceChecker(data).check_eu_ai_act()
results = {c.check_name: c.passed for c in checks}
assert results["Audit record complete"] is False
# ---------------------------------------------------------------------------
# GDPR — Non-compliant cases (Task #3)
# ---------------------------------------------------------------------------
class TestGdprNonCompliant:
"""Requests that should NOT be GDPR compliant."""
def test_no_pii_detection(self):
"""Guardrails exist but only post-call (no pre-call data protection)."""
data = ComplianceCheckRequest(
request_id="req-101",
user_id="user-1",
model="gpt-4",
timestamp="2026-02-17T00:00:00Z",
guardrail_information=[
{
"guardrail_name": "content_filter",
"guardrail_mode": "post_call",
"guardrail_status": "success",
}
],
)
checks = ComplianceChecker(data).check_gdpr()
results = {c.check_name: c.passed for c in checks}
assert results["Data protection applied"] is False
assert results["Sensitive data protected"] is False
def test_empty_guardrails(self):
"""Empty guardrail list — no PII scan."""
data = ComplianceCheckRequest(
request_id="req-102",
user_id="user-1",
model="gpt-4",
timestamp="2026-02-17T00:00:00Z",
guardrail_information=[],
)
checks = ComplianceChecker(data).check_gdpr()
results = {c.check_name: c.passed for c in checks}
assert results["Data protection applied"] is False
assert results["Audit record complete"] is False
def test_pii_sent_in_plaintext(self):
"""PII detection ran but status indicates PII was passed through."""
data = ComplianceCheckRequest(
request_id="req-103",
user_id="user-1",
model="gpt-4",
timestamp="2026-02-17T00:00:00Z",
guardrail_information=[
{
"guardrail_name": "pii_detection",
"guardrail_status": "pii_detected_not_blocked",
}
],
)
checks = ComplianceChecker(data).check_gdpr()
results = {c.check_name: c.passed for c in checks}
assert results["Data protection applied"] is True
assert results["Sensitive data protected"] is False
def test_gdpr_audit_missing_user_id(self):
"""GDPR audit missing user_id."""
data = ComplianceCheckRequest(
request_id="req-104",
user_id=None,
model="gpt-4",
timestamp="2026-02-17T00:00:00Z",
guardrail_information=[
{
"guardrail_name": "pii_detection",
"guardrail_status": "success",
}
],
)
checks = ComplianceChecker(data).check_gdpr()
results = {c.check_name: c.passed for c in checks}
assert results["Audit record complete"] is False
def test_gdpr_audit_missing_model(self):
"""GDPR audit missing model."""
data = ComplianceCheckRequest(
request_id="req-105",
user_id="user-1",
model=None,
timestamp="2026-02-17T00:00:00Z",
guardrail_information=[
{
"guardrail_name": "pii_detection",
"guardrail_status": "success",
}
],
)
checks = ComplianceChecker(data).check_gdpr()
results = {c.check_name: c.passed for c in checks}
assert results["Audit record complete"] is False
def test_no_guardrails_at_all(self):
"""None guardrail_information."""
data = ComplianceCheckRequest(
request_id="req-106",
user_id="user-1",
model="gpt-4",
timestamp="2026-02-17T00:00:00Z",
guardrail_information=None,
)
checks = ComplianceChecker(data).check_gdpr()
results = {c.check_name: c.passed for c in checks}
assert results["Data protection applied"] is False
assert results["Audit record complete"] is False
# ---------------------------------------------------------------------------
# EU AI Act — Compliant cases (Task #4)
# ---------------------------------------------------------------------------
class TestEuAiActCompliant:
"""Requests that SHOULD be EU AI Act compliant."""
def test_fully_compliant(self):
"""All checks pass: guardrails, prohibited_practices, full audit."""
data = ComplianceCheckRequest(
request_id="req-201",
user_id="user-1",
model="gpt-4",
timestamp="2026-02-17T00:00:00Z",
guardrail_information=[
{
"guardrail_name": "content_filter",
"guardrail_status": "success",
},
{
"guardrail_name": "prohibited_practices",
"guardrail_status": "success",
},
],
)
checks = ComplianceChecker(data).check_eu_ai_act()
results = {c.check_name: c.passed for c in checks}
assert results["Guardrails applied"] is True
assert results["Content screened before LLM"] is True
assert results["Audit record complete"] is True
assert all(c.passed for c in checks)
def test_compliant_with_multiple_guardrails(self):
"""Multiple guardrails including prohibited_practices."""
data = ComplianceCheckRequest(
request_id="req-202",
user_id="user-2",
model="claude-3",
timestamp="2026-02-17T12:00:00Z",
guardrail_information=[
{
"guardrail_name": "pii_detection",
"guardrail_status": "success",
},
{
"guardrail_name": "prohibited_practices",
"guardrail_status": "success",
},
{
"guardrail_name": "content_filter",
"guardrail_status": "success",
},
],
)
checks = ComplianceChecker(data).check_eu_ai_act()
assert all(c.passed for c in checks)
# ---------------------------------------------------------------------------
# GDPR — Compliant cases (Task #4)
# ---------------------------------------------------------------------------
class TestGdprCompliant:
"""Requests that SHOULD be GDPR compliant."""
def test_fully_compliant_pii_no_issues(self):
"""PII scan ran, found nothing (status=success), full audit."""
data = ComplianceCheckRequest(
request_id="req-301",
user_id="user-1",
model="gpt-4",
timestamp="2026-02-17T00:00:00Z",
guardrail_information=[
{
"guardrail_name": "pii_detection",
"guardrail_status": "success",
}
],
)
checks = ComplianceChecker(data).check_gdpr()
results = {c.check_name: c.passed for c in checks}
assert results["Data protection applied"] is True
assert results["Sensitive data protected"] is True
assert results["Audit record complete"] is True
assert all(c.passed for c in checks)
def test_compliant_pii_masked(self):
"""PII detected and masked (guardrail_intervened) — still compliant."""
data = ComplianceCheckRequest(
request_id="req-302",
user_id="user-1",
model="gpt-4",
timestamp="2026-02-17T00:00:00Z",
guardrail_information=[
{
"guardrail_name": "pii_detection",
"guardrail_status": "guardrail_intervened",
}
],
)
checks = ComplianceChecker(data).check_gdpr()
results = {c.check_name: c.passed for c in checks}
assert results["Data protection applied"] is True
assert results["Sensitive data protected"] is True
assert results["Audit record complete"] is True
assert all(c.passed for c in checks)
def test_compliant_with_other_guardrails(self):
"""PII detection plus other guardrails — still compliant."""
data = ComplianceCheckRequest(
request_id="req-303",
user_id="user-2",
model="claude-3",
timestamp="2026-02-17T12:00:00Z",
guardrail_information=[
{
"guardrail_name": "content_filter",
"guardrail_status": "success",
},
{
"guardrail_name": "pii_detection",
"guardrail_status": "success",
},
{
"guardrail_name": "prohibited_practices",
"guardrail_status": "success",
},
],
)
checks = ComplianceChecker(data).check_gdpr()
assert all(c.passed for c in checks)
class TestModeMatching:
"""Direct coverage of ComplianceChecker._mode_matches for every shape.
LitellmParams.mode is Union[str, List[str], Mode], so a spend-log
guardrail_mode can be None / str / list / tuple / dict. A prior
implementation compared `g_mode == mode`, which silently failed for the
non-str shapes and reported NON-COMPLIANT for every multi-mode guardrail.
A match now reports a mode satisfied only when every configured branch
runs in that mode: fails safe, no false-COMPLIANT.
"""
@pytest.mark.parametrize(
"g_mode, mode, expected",
[
(None, "pre_call", True),
(None, "post_call", False),
(None, "during_call", False),
("pre_call", "pre_call", True),
("post_call", "pre_call", False),
("during_call", "during_call", True),
# list/tuple: only guaranteed when every listed mode equals `mode`
(["pre_call"], "pre_call", True),
(["pre_call", "pre_call"], "pre_call", True),
(["pre_call", "post_call"], "pre_call", False),
(["pre_call", "post_call"], "post_call", False),
([], "pre_call", False),
(("during_call",), "during_call", True),
(("pre_call", "post_call"), "pre_call", False),
# dict: default only
({"default": "pre_call"}, "pre_call", True),
({"default": "pre_call"}, "post_call", False),
({"default": ["pre_call", "post_call"]}, "post_call", False),
({"default": ["pre_call"]}, "pre_call", True),
# dict with tags: every branch must run in mode
({"default": "pre_call", "tags": {"a": "pre_call"}}, "pre_call", True),
({"default": "pre_call", "tags": {"a": ["pre_call"]}}, "pre_call", True),
({"default": "pre_call", "tags": {"a": ["pre_call", "post_call"]}}, "pre_call", False),
({"default": "pre_call", "tags": {"eu": "post_call"}}, "pre_call", False),
({"default": "pre_call", "tags": {"eu": "post_call"}}, "post_call", False),
({"default": "pre_call", "tags": {"eu": ["during_call"]}}, "during_call", False),
({"default": ["pre_call", "post_call"], "tags": {"a": "pre_call"}}, "post_call", False),
# Missing default: untagged routing is unknown, nothing guaranteed
({"tags": {"x": "post_call"}}, "pre_call", False),
({"tags": {"x": "post_call"}}, "post_call", False),
({}, "pre_call", False),
({}, "post_call", False),
({"default": 123}, "pre_call", False),
# Unknown top-level shapes never match
(5, "pre_call", False),
(object(), "pre_call", False),
],
)
def test_mode_matches(self, g_mode, mode, expected):
assert ComplianceChecker._mode_matches(g_mode, mode) is expected
def test_list_mode_guardrail_not_misclassified(self):
"""A guardrail configured with mode ["pre_call", "post_call"] is logged
with the raw list when the writer cannot infer the concrete hook that
ran (e.g. apply_guardrail invocations). The spend log records "this
guardrail could have run at either hook", not "which hook fired this
request". Counting it for both would let a request that only fired
post_call pass a pre_call compliance check. It counts for neither."""
data = ComplianceCheckRequest(
request_id="req-mode-1",
user_id="user-1",
model="gpt-4",
timestamp="2026-02-17T00:00:00Z",
guardrail_information=[
{
"guardrail_name": "pii_masking",
"guardrail_mode": ["pre_call", "post_call"],
"guardrail_status": "success",
}
],
)
checker = ComplianceChecker(data)
assert len(checker._get_guardrails_by_mode("pre_call")) == 0
assert len(checker._get_guardrails_by_mode("post_call")) == 0
results = {c.check_name: c.passed for c in checker.check_eu_ai_act()}
assert results["Content screened before LLM"] is False
def test_list_mode_single_value_counts(self):
"""A single-entry list ["pre_call"] runs pre_call unconditionally, so it
counts for pre_call and no other mode."""
data = ComplianceCheckRequest(
request_id="req-mode-1b",
user_id="user-1",
model="gpt-4",
timestamp="2026-02-17T00:00:00Z",
guardrail_information=[
{
"guardrail_name": "pii_masking",
"guardrail_mode": ["pre_call"],
"guardrail_status": "success",
}
],
)
checker = ComplianceChecker(data)
assert len(checker._get_guardrails_by_mode("pre_call")) == 1
assert len(checker._get_guardrails_by_mode("post_call")) == 0
def test_dict_tag_routed_guardrail_not_misclassified(self):
"""A tag-routed guardrail (default=pre_call, a post_call tag) is not
guaranteed to run in either mode, so it counts for neither."""
data = ComplianceCheckRequest(
request_id="req-mode-2",
user_id="user-1",
model="gpt-4",
timestamp="2026-02-17T12:00:00Z",
guardrail_information=[
{
"guardrail_name": "pii_masking",
"guardrail_mode": {"default": "pre_call", "tags": {"eu": "post_call"}},
"guardrail_status": "success",
}
],
)
checker = ComplianceChecker(data)
assert len(checker._get_guardrails_by_mode("pre_call")) == 0
assert len(checker._get_guardrails_by_mode("post_call")) == 0
results = {c.check_name: c.passed for c in checker.check_eu_ai_act()}
assert results["Content screened before LLM"] is False
def test_dict_all_branches_pre_call_counts(self):
"""When default and every tag override all run pre_call, the guardrail is
guaranteed pre_call regardless of routing, so it counts for pre_call."""
data = ComplianceCheckRequest(
request_id="req-mode-4",
user_id="user-1",
model="gpt-4",
timestamp="2026-02-17T12:00:00Z",
guardrail_information=[
{
"guardrail_name": "pii_masking",
"guardrail_mode": {"default": "pre_call", "tags": {"eu": "pre_call"}},
"guardrail_status": "success",
}
],
)
checker = ComplianceChecker(data)
assert len(checker._get_guardrails_by_mode("pre_call")) == 1
def test_none_mode_defaults_to_pre_call(self):
"""A guardrail logged without a mode counts as pre_call only."""
data = ComplianceCheckRequest(
request_id="req-mode-3",
user_id="user-1",
model="gpt-4",
timestamp="2026-02-17T12:00:00Z",
guardrail_information=[{"guardrail_name": "pii_masking", "guardrail_status": "success"}],
)
checker = ComplianceChecker(data)
assert len(checker._get_guardrails_by_mode("pre_call")) == 1
assert len(checker._get_guardrails_by_mode("post_call")) == 0
def test_never_reports_false_compliant(self):
"""The core invariant: a match reports `mode` satisfied only when every
configured branch runs in that mode. So True can never claim a hook the
guardrail may not have actually executed. The only allowed error
direction is under-reporting."""
def _branch_modes(value):
if isinstance(value, str):
return {value}
if isinstance(value, (list, tuple)):
return {v for v in value if isinstance(v, str)}
return set()
def _guaranteed_modes(g_mode):
"""Modes every branch of ``g_mode`` runs in."""
if isinstance(g_mode, str):
return {g_mode}
if isinstance(g_mode, (list, tuple)):
sets = [_branch_modes(m) for m in g_mode]
return set.intersection(*sets) if sets else set()
if isinstance(g_mode, dict):
default = g_mode.get("default")
if default is None:
return set()
branches = [default, *(g_mode.get("tags") or {}).values()]
sets = [_branch_modes(b) for b in branches]
return set.intersection(*sets) if sets else set()
return set()
shapes = [
None,
"pre_call",
"post_call",
["pre_call"],
["pre_call", "post_call"],
[],
{"default": "pre_call"},
{"default": ["pre_call", "post_call"]},
{"default": "pre_call", "tags": {"a": "pre_call"}},
{"default": "pre_call", "tags": {"a": "post_call"}},
{"default": ["pre_call", "post_call"], "tags": {"a": "pre_call"}},
{"tags": {"a": "post_call"}},
{},
{"default": 123},
5,
]
for g_mode in shapes:
for mode in ("pre_call", "post_call", "during_call"):
matched = ComplianceChecker._mode_matches(g_mode, mode)
if g_mode is None:
assert matched is (mode == "pre_call"), (g_mode, mode)
continue
if matched:
assert mode in _guaranteed_modes(g_mode), (g_mode, mode)
class TestNotRunGuardrails:
"""LIT-6314 logs a not_run entry for a guardrail that message scoping left nothing to scan."""
def test_not_run_alone_never_evidences_compliance(self):
data = ComplianceCheckRequest(
request_id="req-601",
user_id="user-1",
model="gpt-4",
timestamp="2026-02-17T00:00:00Z",
guardrail_information=[
{"guardrail_name": "pii_detection", "guardrail_status": "not_run", "guardrail_mode": "pre_call"},
],
)
results = {c.check_name: c.passed for c in ComplianceChecker(data).check_eu_ai_act()}
assert results["Guardrails applied"] is False
assert results["Content screened before LLM"] is False
assert results["Audit record complete"] is False
def test_not_run_sibling_does_not_fail_a_passing_request(self):
data = ComplianceCheckRequest(
request_id="req-602",
user_id="user-1",
model="gpt-4",
timestamp="2026-02-17T00:00:00Z",
pii_detected=True,
guardrail_information=[
{"guardrail_name": "pii_detection", "guardrail_status": "success", "guardrail_mode": "pre_call"},
{"guardrail_name": "system_only", "guardrail_status": "not_run", "guardrail_mode": "pre_call"},
],
)
results = {c.check_name: c.passed for c in ComplianceChecker(data).check_gdpr()}
assert results["Sensitive data protected"] is True