dotb_ai-platform/tests/test_app_credential_application.py
hungtran24 2268b6a77a feat: replace tenant auth with app-scoped scoring access
Add app credentials, account-scoped idempotency and per-app terminal notifications. Consolidate the unreleased migration baseline, preserve rule descriptions as AI context, and regenerate provider contracts with regression coverage.
2026-10-02 10:56:58 +07:00

118 lines
4.3 KiB
Python

from dataclasses import replace
from datetime import UTC, datetime
import pytest
from ai_platform.platform.application.app_credentials import (
AppCredentialConflict,
AppCredentialNotFound,
AppCredentialRecord,
AppCredentialUseCases,
AppFeatureForbidden,
)
from ai_platform.platform.security import digest
class Repository:
def __init__(self):
self.record = AppCredentialRecord(
"omni-local", True, True, datetime.now(UTC), datetime.now(UTC)
)
self.verifier = digest("initial-key")
self.events = []
self.error: Exception | None = None
async def list(self):
return [self.record]
async def active_app_for_verifier(self, verifier):
return self.record if self.record.active and verifier == self.verifier else None
async def create(self, app_id, verifier, lead_scoring, actor):
if self.error:
raise self.error
self.record = replace(self.record, app_id=app_id, lead_scoring=lead_scoring)
self.verifier = verifier
self.events.append(("create", actor))
async def rotate(self, app_id, verifier, actor):
if self.error:
raise self.error
self.verifier = verifier
self.record = replace(self.record, active=True)
self.events.append(("rotate", actor))
async def revoke(self, app_id, actor):
if self.error:
raise self.error
self.record = replace(self.record, active=False)
self.events.append(("revoke", actor))
return self.record
class Transaction:
def __init__(self):
self.commits = 0
self.rollbacks = 0
self.error: Exception | None = None
async def commit(self):
self.commits += 1
if self.error:
raise self.error
async def rollback(self):
self.rollbacks += 1
async def test_lifecycle_preserves_identity_and_stores_only_verifier():
repo, tx = Repository(), Transaction()
keys = iter(["created-key", "rotated-key", "reactivated-key"])
cases = AppCredentialUseCases(repo, tx.commit, tx.rollback, lambda: next(keys))
assert await cases.create("omni-local", "operator") == "created-key"
assert repo.verifier == digest("created-key")
assert await cases.authenticate("created-key") == "omni-local"
assert await cases.rotate("omni-local", "operator") == "rotated-key"
assert await cases.authenticate("created-key") is None
assert await cases.authenticate("rotated-key") == "omni-local"
assert not (await cases.revoke("omni-local", "operator")).active
assert await cases.authenticate("rotated-key") is None
await cases.rotate("omni-local", "operator")
assert await cases.authenticate("reactivated-key") == "omni-local"
assert tx.commits == 4 and tx.rollbacks == 0
assert repo.events == [(name, "operator") for name in ("create", "rotate", "revoke", "rotate")]
async def test_valid_app_without_feature_permission_is_forbidden():
repo, tx = Repository(), Transaction()
repo.record = replace(repo.record, lead_scoring=False)
cases = AppCredentialUseCases(repo, tx.commit, tx.rollback, lambda: "key")
with pytest.raises(AppFeatureForbidden):
await cases.authenticate("initial-key")
assert await cases.authenticate("incorrect-key") is None
await cases.rotate("omni-local", "operator")
assert repo.record.lead_scoring is False
assert await cases.list_apps() == [repo.record]
@pytest.mark.parametrize("operation", ["create", "rotate", "revoke"])
@pytest.mark.parametrize(
"error", [AppCredentialConflict(), AppCredentialNotFound(), RuntimeError("storage")]
)
async def test_repository_error_rolls_back(operation, error):
repo, tx = Repository(), Transaction()
repo.error = error
cases = AppCredentialUseCases(repo, tx.commit, tx.rollback, lambda: "key")
with pytest.raises(type(error)):
await getattr(cases, operation)("omni-local", "operator")
assert tx.commits == 0 and tx.rollbacks == 1
async def test_failed_commit_rolls_back():
repo, tx = Repository(), Transaction()
tx.error = RuntimeError("commit failed")
cases = AppCredentialUseCases(repo, tx.commit, tx.rollback, lambda: "key")
with pytest.raises(RuntimeError, match="commit failed"):
await cases.rotate("omni-local", "operator")
assert tx.commits == 1 and tx.rollbacks == 1