Implement AUDIT-WP-0006 honest operational custody.
Postgres now reports custody_class=operational with a cited 30-day recoverable window. Join ITC-CAP operations.audit at D4, publish the interface card, and overlay user-engine tenants [*] from Git so an ExternalSecret refresh cannot shrink it.
This commit is contained in:
parent
0a3d05ff1c
commit
ded432a63f
25 changed files with 832 additions and 94 deletions
|
|
@ -4,7 +4,11 @@ import json
|
|||
import pytest
|
||||
|
||||
from audit_core.ingestion import IngestionApplication
|
||||
from audit_core.interface import BackendUnavailableError, RetentionPolicy
|
||||
from audit_core.interface import (
|
||||
BackendUnavailableError,
|
||||
RetentionPolicy,
|
||||
custody_class_satisfies,
|
||||
)
|
||||
from audit_core.mock_file_backend import MockFileAuditBackend
|
||||
from audit_core.sqlite_backend import SQLiteAuditBackend
|
||||
|
||||
|
|
@ -408,10 +412,71 @@ def test_required_custody_class_refuses_a_development_backend(tmp_path):
|
|||
"""Losing AUDIT_CORE_DATABASE_URL must fail to start, not silently
|
||||
downgrade custody to the development store."""
|
||||
backend = SQLiteAuditBackend(str(tmp_path / "dev.db"))
|
||||
with pytest.raises(ValueError, match="does not meet the required"):
|
||||
IngestionApplication(backend, "opaque", require_custody_class="operational")
|
||||
with pytest.raises(ValueError, match="does not meet the required"):
|
||||
IngestionApplication(backend, "opaque", require_custody_class="archive")
|
||||
|
||||
|
||||
def test_operational_and_archive_alias_for_one_deploy():
|
||||
"""A mixed rollout must start: new backend + old require, and the reverse."""
|
||||
|
||||
class _Operational(_BrokenBackend):
|
||||
@property
|
||||
def retention_policy(self):
|
||||
return RetentionPolicy(
|
||||
custody_class="operational",
|
||||
retention_days=None,
|
||||
immutable=True,
|
||||
tamper_evidence=False,
|
||||
durable=True,
|
||||
recoverable_days=30,
|
||||
recoverable_source="cited",
|
||||
recoverable_basis="measured",
|
||||
)
|
||||
|
||||
IngestionApplication(_Operational(), "opaque", require_custody_class="archive")
|
||||
IngestionApplication(_Operational(), "opaque", require_custody_class="operational")
|
||||
|
||||
|
||||
def test_custody_class_alias_is_not_development():
|
||||
assert custody_class_satisfies("operational", "archive")
|
||||
assert custody_class_satisfies("archive", "operational")
|
||||
assert not custody_class_satisfies("development", "operational")
|
||||
assert not custody_class_satisfies("development", "archive")
|
||||
assert custody_class_satisfies("development", "development")
|
||||
|
||||
|
||||
def test_readiness_reports_recovery_fields_for_operational_backend():
|
||||
class _Operational(_BrokenBackend):
|
||||
@property
|
||||
def retention_policy(self):
|
||||
return RetentionPolicy(
|
||||
custody_class="operational",
|
||||
retention_days=None,
|
||||
immutable=True,
|
||||
tamper_evidence=False,
|
||||
durable=True,
|
||||
recoverable_days=30,
|
||||
recoverable_source="resource-control/data/capability/platform-audit-storage.json",
|
||||
recoverable_basis="measured",
|
||||
)
|
||||
|
||||
def health(self):
|
||||
return None
|
||||
|
||||
status, body = invoke(
|
||||
IngestionApplication(_Operational(), "opaque"),
|
||||
None, path="/readyz", method="GET", body=b"",
|
||||
)
|
||||
assert status.startswith("200")
|
||||
assert body["custody_class"] == "operational"
|
||||
assert body["durable"] is True
|
||||
assert body["recoverable_days"] == 30
|
||||
assert body["recoverable_basis"] == "measured"
|
||||
assert "platform-audit-storage" in body["recoverable_source"]
|
||||
|
||||
|
||||
def test_counters_track_each_outcome(tmp_path):
|
||||
app, _ = bound_app(tmp_path, may_read=True)
|
||||
invoke(app, event()) # accepted
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue