feat(platform): close AI expense value loop
Add tenant-safe value, telemetry, connector, commercial, and production-readiness foundations.
This commit is contained in:
90
server/tests/test_commercial_rollover_scheduler.py
Normal file
90
server/tests/test_commercial_rollover_scheduler.py
Normal file
@@ -0,0 +1,90 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime
|
||||
from decimal import Decimal
|
||||
|
||||
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.commercial_billing import CommercialBillingPeriod
|
||||
from app.schemas.commercial import CommercialPlanCreate, CommercialSubscriptionCreate
|
||||
from app.services.commercial_admin import CommercialAdminService
|
||||
from app.services.commercial_rollover_scheduler import CommercialRolloverScheduler
|
||||
|
||||
|
||||
def test_scheduler_rolls_due_subscription_once_and_respects_leader_lease() -> None:
|
||||
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)
|
||||
due = datetime(2026, 2, 1, tzinfo=UTC)
|
||||
with factory() as db:
|
||||
subscription_id = _seed_due_subscription(db)
|
||||
db.commit()
|
||||
|
||||
scheduler = CommercialRolloverScheduler(session_factory=factory)
|
||||
first = scheduler._run_once(as_of=due)
|
||||
replay = scheduler._run_once(as_of=due)
|
||||
|
||||
assert first["scanned"] == 1
|
||||
assert first["rolled_over"] == 1
|
||||
assert first["periods_created"] == 1
|
||||
assert replay["scanned"] == 0
|
||||
with factory() as db:
|
||||
assert (
|
||||
db.scalar(
|
||||
select(func.count())
|
||||
.select_from(CommercialBillingPeriod)
|
||||
.where(CommercialBillingPeriod.subscription_id == subscription_id)
|
||||
)
|
||||
== 2
|
||||
)
|
||||
|
||||
scheduler._try_acquire_lease = lambda _db: False # type: ignore[method-assign]
|
||||
skipped = scheduler._run_once(as_of=due)
|
||||
assert skipped["leader_skipped"] == 1
|
||||
engine.dispose()
|
||||
|
||||
|
||||
def _seed_due_subscription(db: Session) -> str:
|
||||
start = datetime(2026, 1, 1, tzinfo=UTC)
|
||||
due = datetime(2026, 2, 1, tzinfo=UTC)
|
||||
admin = CommercialAdminService(db)
|
||||
plan = admin.create_plan(
|
||||
"tenant-scheduler",
|
||||
CommercialPlanCreate(
|
||||
plan_code="scheduler",
|
||||
name="调度器测试套餐",
|
||||
pricing_model="subscription",
|
||||
billing_interval="monthly",
|
||||
currency="CNY",
|
||||
base_fee=Decimal("100"),
|
||||
effective_from=start,
|
||||
),
|
||||
actor_id="scheduler-test",
|
||||
)
|
||||
admin.activate_plan(
|
||||
"tenant-scheduler",
|
||||
plan.id,
|
||||
expected_version=plan.version,
|
||||
)
|
||||
subscription = admin.create_subscription(
|
||||
"tenant-scheduler",
|
||||
CommercialSubscriptionCreate(
|
||||
subscription_key="scheduler-subscription",
|
||||
plan_id=plan.id,
|
||||
starts_at=start,
|
||||
current_period_start=start,
|
||||
current_period_end=due,
|
||||
seats=1,
|
||||
auto_renew=True,
|
||||
),
|
||||
actor_id="scheduler-test",
|
||||
)
|
||||
return subscription.id
|
||||
Reference in New Issue
Block a user