"""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