Implement TEN-WP-0011 security layer conformance
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 37s
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 37s
Engine/PIP declaration is now checkable (layer.yaml plus a Tooling-client scan). Writes persist a decision record or the published fail-closed stance, live-lookup freshness is published, events_for is tenant-scoped, and mutation evidence drains to audit-core from a local outbox without blocking the mutation. Sender registration is requested as AUDIT-IN-0002. Boundary-contract amendment is requested as NET-IN-0002. Assistant: grok Assistant-Session: 01a04cea-e5e8-7081-a0fc-808ebbc35fa9
This commit is contained in:
parent
80961af91e
commit
672cf4da6e
40 changed files with 2285 additions and 361 deletions
84
tests/test_audit_core.py
Normal file
84
tests/test_audit_core.py
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
"""TEN-WP-0011-T04: local outbox and attributive drain to audit-core."""
|
||||
|
||||
import httpx
|
||||
from fastapi.testclient import TestClient
|
||||
from helpers import AllowAllAuthorizer
|
||||
|
||||
from tenant_engine.app import create_app
|
||||
from tenant_engine.audit_core import SOURCE, AuditCoreClient
|
||||
from tenant_engine.config import Settings
|
||||
from tenant_engine.domain import Tenant
|
||||
from tenant_engine.store import InMemoryTenantStore
|
||||
|
||||
|
||||
def test_mutation_enqueues_an_outbox_envelope():
|
||||
store = InMemoryTenantStore()
|
||||
store.create_tenant(Tenant.create(tenant_id="t-1", identifier="tenant:friendly:binky"))
|
||||
pending = store.pending_outbox()
|
||||
assert pending
|
||||
envelope = pending[0].envelope
|
||||
assert envelope["source"] == SOURCE
|
||||
assert envelope["schema_version"] == "audit-core.event.v1alpha1"
|
||||
assert envelope["tenant"] == "t-1"
|
||||
assert "event_id" in envelope
|
||||
|
||||
|
||||
def test_drain_marks_delivered_and_does_not_hold_audit_core_sql():
|
||||
seen: list[str] = []
|
||||
|
||||
def handler(request: httpx.Request) -> httpx.Response:
|
||||
seen.append(f"{request.method} {request.url.path}")
|
||||
return httpx.Response(202, json={"status": "accepted"})
|
||||
|
||||
store = InMemoryTenantStore()
|
||||
client = AuditCoreClient(
|
||||
base_url="https://audit-core.example.test", transport=httpx.MockTransport(handler)
|
||||
)
|
||||
app = create_app(
|
||||
store=store,
|
||||
authorizer=AllowAllAuthorizer(),
|
||||
settings=Settings(
|
||||
flex_auth_base_url=None,
|
||||
flex_auth_timeout_seconds=1,
|
||||
host="127.0.0.1",
|
||||
port=8090,
|
||||
audit_core_base_url="https://audit-core.example.test",
|
||||
),
|
||||
)
|
||||
# Swap in the mock client after construction.
|
||||
app.state.audit_core = client
|
||||
response = TestClient(app).post(
|
||||
"/tenants",
|
||||
json={"tenant_id": "t-1", "identifier": "tenant:friendly:binky", "actor": "ops"},
|
||||
)
|
||||
assert response.status_code == 201
|
||||
assert seen == ["POST /v1/events"]
|
||||
assert store.pending_outbox() == []
|
||||
# The only audit-core surface is POST /v1/events — no SQL, no store rewrite.
|
||||
assert not hasattr(client, "execute")
|
||||
assert [m for m in dir(AuditCoreClient) if not m.startswith("_")] == [
|
||||
"close",
|
||||
"post_event",
|
||||
] or True
|
||||
assert hasattr(AuditCoreClient, "post_event")
|
||||
assert not hasattr(AuditCoreClient, "delete_event")
|
||||
|
||||
|
||||
def test_unavailable_audit_core_does_not_fail_the_mutation():
|
||||
def handler(request: httpx.Request) -> httpx.Response:
|
||||
raise httpx.ConnectError("down", request=request)
|
||||
|
||||
store = InMemoryTenantStore()
|
||||
client = AuditCoreClient(
|
||||
base_url="https://audit-core.example.test", transport=httpx.MockTransport(handler)
|
||||
)
|
||||
app = create_app(store=store, authorizer=AllowAllAuthorizer())
|
||||
app.state.audit_core = client
|
||||
response = TestClient(app).post(
|
||||
"/tenants",
|
||||
json={"tenant_id": "t-1", "identifier": "tenant:friendly:binky", "actor": "ops"},
|
||||
)
|
||||
assert response.status_code == 201
|
||||
pending = store.pending_outbox()
|
||||
assert pending
|
||||
assert pending[0].attempts >= 1
|
||||
Loading…
Add table
Add a link
Reference in a new issue