feat(cloud-api): run lifecycle workers

This commit is contained in:
2026-07-12 18:26:41 +08:00
parent bd261e4992
commit 937a4646e1
3 changed files with 136 additions and 6 deletions
+64 -3
View File
@@ -1,5 +1,7 @@
from __future__ import annotations
import time
import pytest
from fastapi.testclient import TestClient
@@ -20,8 +22,20 @@ def test_create_app_returns_independent_cloud_application() -> None:
def test_cloud_application_owns_database_lifecycle() -> None:
events: list[str] = []
class FakeRepository:
def health_check(self) -> None:
events.append("healthy")
def list_queued_tasks(self) -> list[object]:
events.append("scheduled")
return []
def reap_expired_leases(self, *, now, max_attempts: int) -> list[str]:
events.append("reaped")
return []
class FakeDatabase:
repository = object()
repository = FakeRepository()
def close(self) -> None:
events.append("closed")
@@ -37,9 +51,9 @@ def test_cloud_application_owns_database_lifecycle() -> None:
assert (
app.state.cloud_services.pool.store is app.state.cloud_services.repository
)
assert events == []
assert "healthy" in events
assert events == ["closed"]
assert events[-1] == "closed"
def test_repository_proxy_is_available_only_during_lifespan() -> None:
@@ -47,6 +61,12 @@ def test_repository_proxy_is_available_only_during_lifespan() -> None:
def health_check(self) -> None:
return None
def list_queued_tasks(self) -> list[object]:
return []
def reap_expired_leases(self, *, now, max_attempts: int) -> list[str]:
return []
class FakeDatabase:
repository = FakeRepository()
@@ -65,6 +85,47 @@ def test_repository_proxy_is_available_only_during_lifespan() -> None:
app.state.cloud_services.repository.health_check()
def test_lifespan_runs_scheduler_and_reaper_until_shutdown() -> None:
events: list[str] = []
class FakeRepository:
def health_check(self) -> None:
events.append("healthy")
def list_queued_tasks(self) -> list[object]:
events.append("scheduled")
return []
def reap_expired_leases(self, *, now, max_attempts: int) -> list[str]:
events.append(f"reaped:{max_attempts}")
return []
class FakeDatabase:
repository = FakeRepository()
def close(self) -> None:
events.append("closed")
app = create_app(
config=CloudControlConfig(
database_url="sqlite:///:memory:",
scheduler_interval_seconds=0.01,
lease_reaper_interval_seconds=0.01,
max_task_attempts=4,
),
database_factory=lambda _config: FakeDatabase(), # type: ignore[arg-type,return-value]
)
with TestClient(app):
time.sleep(0.04)
assert events.count("scheduled") >= 2
assert events.count("reaped:4") >= 2
assert all(not task.done() for task in app.state.worker_tasks)
assert all(task.done() for task in app.state.worker_tasks)
assert events[-1] == "closed"
def test_production_app_rejects_missing_credentials() -> None:
with pytest.raises(CloudConfigurationError, match="credential"):
create_app(