feat(platform): close AI expense value loop

Add tenant-safe value, telemetry, connector, commercial, and production-readiness foundations.
This commit is contained in:
caoxiaozhu
2026-07-17 14:14:08 +08:00
parent 242d68c36f
commit 787bc3a481
507 changed files with 82072 additions and 6344 deletions

View File

@@ -0,0 +1,211 @@
from __future__ import annotations
import uuid
from datetime import UTC, datetime, timedelta
import pytest
from sqlalchemy import create_engine, func, select
from sqlalchemy.orm import Session, sessionmaker
from sqlalchemy.pool import StaticPool
import app.models # noqa: F401 - 注册完整 metadata
from app.db.base_class import Base
from app.models.financial_connector import (
FinancialConnectorConfig,
FinancialConnectorOperationalEvent,
)
from app.schemas.financial_connector import FinancialEventEnvelope
from app.services.financial_connector_auth import (
FinancialConnectorAuthenticator,
FinancialConnectorAuthError,
FinancialConnectorSecretResolver,
)
from app.services.financial_connector_observability import (
FinancialConnectorObservabilityService,
)
from app.services.financial_connector_operational_events import (
FinancialConnectorOperationalContext,
FinancialConnectorOperationalEventService,
operational_event_candidate,
)
@pytest.fixture()
def db() -> Session:
engine = create_engine(
"sqlite+pysqlite:///:memory:",
connect_args={"check_same_thread": False},
poolclass=StaticPool,
)
Base.metadata.create_all(engine)
factory = sessionmaker(bind=engine, expire_on_commit=False)
with factory() as session:
yield session
Base.metadata.drop_all(engine)
engine.dispose()
def test_operational_attempts_are_counted_but_candidate_retry_is_idempotent(
db: Session,
) -> None:
config = _config(db)
context = _context(config)
as_of = datetime(2026, 7, 17, 10, 2, tzinfo=UTC)
first = operational_event_candidate(
context,
event_type="replay",
reason_code="duplicate_external_event",
occurred_at=as_of - timedelta(minutes=2),
)
second = operational_event_candidate(
context,
event_type="replay",
reason_code="duplicate_external_event",
occurred_at=as_of - timedelta(minutes=1),
)
outside_window = operational_event_candidate(
context,
event_type="auth_failure",
reason_code="signature_invalid",
occurred_at=as_of - timedelta(hours=2),
)
service = FinancialConnectorOperationalEventService(db)
service.record(first)
service.record(first)
service.record(second)
service.record(outside_window)
db.commit()
assert first.idempotency_key != second.idempotency_key
assert db.scalar(select(func.count(FinancialConnectorOperationalEvent.id))) == 3
observed = FinancialConnectorObservabilityService(db, now=as_of).read_for_tenant(
config.tenant_id,
window_hours=1,
)
assert observed.window_started_at == as_of - timedelta(hours=1)
assert observed.as_of == as_of
assert observed.source_revision == "20260716_0022"
assert observed.summary.retry_count == 2
assert observed.summary.auth_failure_count == 0
assert observed.summary.latest_replay_at == second.occurred_at
def test_auth_failure_is_attributed_only_after_trusted_config_and_secret_resolution(
db: Session,
) -> None:
config = _config(db)
secret = "trusted-server-secret"
now_epoch = 1_800_000_000
envelope = FinancialEventEnvelope(
tenant_id=config.tenant_id,
external_event_id="raw-external-event-must-not-be-stored",
event_type="payment_settled",
occurred_at=datetime.fromtimestamp(now_epoch, UTC),
correlation_id="raw-correlation-must-not-be-stored",
payload={
"claim_id": str(uuid.uuid4()),
"claim_reference": "BX-RAW-MUST-NOT-BE-STORED",
"amount": "66.00",
"currency": "CNY",
"external_payment_reference": "RAW-PAYMENT-MUST-NOT-BE-STORED",
},
)
authenticator = FinancialConnectorAuthenticator(
db,
secrets=FinancialConnectorSecretResolver({config.secret_ref: secret}),
now_epoch=now_epoch,
)
with pytest.raises(FinancialConnectorAuthError) as forged_tenant:
authenticator.verify(
envelope,
tenant_header="tenant-forged",
provider_header=config.provider,
key_version_header=config.key_version,
timestamp_header=str(now_epoch),
signature_header="sha256=" + "0" * 64,
)
assert forged_tenant.value.operational_context is None
with pytest.raises(FinancialConnectorAuthError) as forged_provider:
authenticator.verify(
envelope,
tenant_header=config.tenant_id,
provider_header="provider-forged",
key_version_header=config.key_version,
timestamp_header=str(now_epoch),
signature_header="sha256=" + "0" * 64,
)
assert forged_provider.value.operational_context is None
with pytest.raises(FinancialConnectorAuthError) as trusted_failure:
authenticator.verify(
envelope,
tenant_header=config.tenant_id,
provider_header=config.provider,
key_version_header=config.key_version,
timestamp_header=str(now_epoch),
signature_header="sha256=" + "0" * 64,
)
context = trusted_failure.value.operational_context
assert context is not None
assert context.request_fingerprint.startswith("hmac-sha256:")
assert context.external_event_fingerprint.startswith("hmac-sha256:")
candidate = operational_event_candidate(
context,
event_type="auth_failure",
reason_code=trusted_failure.value.code,
occurred_at=datetime.fromtimestamp(now_epoch, UTC),
)
FinancialConnectorOperationalEventService(db).record(candidate)
db.commit()
row = db.scalar(select(FinancialConnectorOperationalEvent))
assert row is not None
stored = "|".join(
(
row.provider,
row.reason_code,
row.request_fingerprint,
row.external_event_fingerprint,
row.idempotency_key,
)
)
for raw_value in (
secret,
envelope.external_event_id,
envelope.correlation_id,
envelope.payload["claim_reference"],
envelope.payload["external_payment_reference"],
):
assert str(raw_value) not in stored
def _config(db: Session) -> FinancialConnectorConfig:
row = FinancialConnectorConfig(
id=str(uuid.uuid4()),
tenant_id="tenant-operational",
provider="operational-bank",
environment="production",
key_version="v1",
secret_ref="connector/operational-test",
allowed_event_types_json=["payment_settled"],
clock_skew_seconds=300,
status="active",
created_by="operational-test",
)
db.add(row)
db.flush()
return row
def _context(config: FinancialConnectorConfig) -> FinancialConnectorOperationalContext:
return FinancialConnectorOperationalContext(
tenant_id=config.tenant_id,
config_id=config.id,
provider=config.provider,
environment=config.environment,
request_fingerprint="hmac-sha256:" + "a" * 64,
external_event_fingerprint="hmac-sha256:" + "b" * 64,
)