164 lines
6.4 KiB
Python
164 lines
6.4 KiB
Python
"""Tests for the domain compliance timeline with real data."""
|
|
|
|
from fastapi.testclient import TestClient
|
|
|
|
from app.services.report_store import ReportStore
|
|
|
|
|
|
def _add_report_to_store(
|
|
domain, report_id, begin_ts, end_ts, total, passed, failed, org_name="test.org"
|
|
):
|
|
"""Helper to add a report to the ReportStore with integer timestamps."""
|
|
store = ReportStore.get_instance()
|
|
store.add_report(
|
|
{
|
|
"domain": domain,
|
|
"report_id": report_id,
|
|
"org_name": org_name,
|
|
"begin_date": begin_ts,
|
|
"end_date": end_ts,
|
|
"begin_timestamp": begin_ts,
|
|
"end_timestamp": end_ts,
|
|
"policy": "none",
|
|
"records": [],
|
|
"summary": {
|
|
"total_count": total,
|
|
"passed_count": passed,
|
|
"failed_count": failed,
|
|
},
|
|
}
|
|
)
|
|
|
|
|
|
class TestComplianceTimeline:
|
|
"""Tests that the compliance timeline returns real data instead of mock."""
|
|
|
|
def test_timeline_has_entries_after_upload(self, client: TestClient):
|
|
"""When a domain has reports, the timeline should have entries."""
|
|
_add_report_to_store("example.com", "rpt-001", 1597449600, 1597535999, 10, 8, 2)
|
|
|
|
response = client.get("/api/v1/domains/example.com/reports?limit=10")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
timeline = data["compliance_timeline"]
|
|
assert len(timeline) >= 1
|
|
assert timeline[0]["total"] == 10
|
|
assert timeline[0]["volume"] == 10
|
|
assert timeline[0]["passed"] == 8
|
|
assert timeline[0]["failed"] == 2
|
|
assert timeline[0]["failure_rate"] == 20.0
|
|
|
|
def test_timeline_uses_real_dates(self, client: TestClient):
|
|
"""Timeline dates should come from actual report begin_dates."""
|
|
_add_report_to_store("example.com", "rpt-001", 1597449600, 1597535999, 10, 8, 2)
|
|
|
|
response = client.get("/api/v1/domains/example.com/reports?limit=10")
|
|
data = response.json()
|
|
timeline = data["compliance_timeline"]
|
|
|
|
# The begin_date=1597449600 is 2020-08-15
|
|
dates = [point["date"] for point in timeline]
|
|
assert "2020-08-15" in dates
|
|
|
|
def test_timeline_compliance_rate_is_deterministic(self, client: TestClient):
|
|
"""Compliance rate should be deterministic, not random."""
|
|
_add_report_to_store("example.com", "rpt-001", 1597449600, 1597535999, 10, 8, 2)
|
|
|
|
resp1 = client.get("/api/v1/domains/example.com/reports?limit=10")
|
|
resp2 = client.get("/api/v1/domains/example.com/reports?limit=10")
|
|
|
|
timeline1 = resp1.json()["compliance_timeline"]
|
|
timeline2 = resp2.json()["compliance_timeline"]
|
|
assert timeline1 == timeline2
|
|
|
|
def test_timeline_empty_for_domain_with_no_valid_dates(self, client: TestClient):
|
|
"""A domain with begin_date=0 should have an empty timeline."""
|
|
store = ReportStore.get_instance()
|
|
store.add_report(
|
|
{
|
|
"domain": "empty-timeline.com",
|
|
"report_id": "rpt-empty",
|
|
"org_name": "test",
|
|
"begin_date": 0,
|
|
"end_date": 0,
|
|
"policy": "none",
|
|
"records": [],
|
|
"summary": {"total_count": 0, "passed_count": 0, "failed_count": 0},
|
|
}
|
|
)
|
|
|
|
response = client.get("/api/v1/domains/empty-timeline.com/reports?limit=10")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["compliance_timeline"] == []
|
|
|
|
def test_timeline_handles_iso_string_dates(self, client: TestClient):
|
|
"""Timeline should handle reports with ISO-format string dates."""
|
|
from app.api.api_v1.endpoints.domains import _build_compliance_timeline
|
|
|
|
store = ReportStore.get_instance()
|
|
store.add_report(
|
|
{
|
|
"domain": "isodate.com",
|
|
"report_id": "rpt-iso",
|
|
"org_name": "test",
|
|
"begin_date": "2020-08-15T00:00:00",
|
|
"end_date": "2020-08-15T23:59:59",
|
|
"policy": "none",
|
|
"records": [],
|
|
"summary": {"total_count": 10, "passed_count": 9, "failed_count": 1},
|
|
}
|
|
)
|
|
|
|
timeline = _build_compliance_timeline(store, "isodate.com")
|
|
assert len(timeline) == 1
|
|
assert timeline[0].date == "2020-08-15"
|
|
assert timeline[0].total == 10
|
|
assert timeline[0].passed == 9
|
|
assert timeline[0].failed == 1
|
|
assert timeline[0].compliance_rate == 90.0
|
|
assert timeline[0].failure_rate == 10.0
|
|
|
|
|
|
class TestBuildComplianceTimelineMultipleReports:
|
|
"""Test timeline aggregation with multiple reports."""
|
|
|
|
def test_multiple_reports_same_day(self, client: TestClient):
|
|
"""Multiple reports on the same day should be aggregated."""
|
|
_add_report_to_store("multi.com", "rpt-1", 1597449600, 1597535999, 10, 8, 2)
|
|
_add_report_to_store("multi.com", "rpt-2", 1597449600, 1597535999, 10, 6, 4)
|
|
|
|
response = client.get("/api/v1/domains/multi.com/reports?limit=10")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
timeline = data["compliance_timeline"]
|
|
|
|
assert len(timeline) == 1
|
|
assert timeline[0]["date"] == "2020-08-15"
|
|
assert timeline[0]["total"] == 20
|
|
assert timeline[0]["passed"] == 14
|
|
assert timeline[0]["failed"] == 6
|
|
# Aggregated: 14 passed out of 20 total = 70%
|
|
assert timeline[0]["compliance_rate"] == 70.0
|
|
assert timeline[0]["failure_rate"] == 30.0
|
|
|
|
def test_reports_on_different_days(self, client: TestClient):
|
|
"""Reports on different days should produce separate timeline points."""
|
|
_add_report_to_store("days.com", "rpt-d1", 1597449600, 1597535999, 10, 10, 0)
|
|
_add_report_to_store("days.com", "rpt-d2", 1597536000, 1597622399, 10, 5, 5)
|
|
|
|
response = client.get("/api/v1/domains/days.com/reports?limit=10")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
timeline = data["compliance_timeline"]
|
|
|
|
assert len(timeline) == 2
|
|
# Sorted by date
|
|
assert timeline[0]["date"] == "2020-08-15"
|
|
assert timeline[0]["total"] == 10
|
|
assert timeline[0]["compliance_rate"] == 100.0
|
|
assert timeline[1]["date"] == "2020-08-16"
|
|
assert timeline[1]["total"] == 10
|
|
assert timeline[1]["compliance_rate"] == 50.0
|
|
assert timeline[1]["failure_rate"] == 50.0
|