feat: 完成权益到期与续期闭环
This commit is contained in:
@@ -1,16 +1,16 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime
|
||||
from datetime import UTC, datetime, timedelta
|
||||
|
||||
import pytest
|
||||
from fastapi import HTTPException
|
||||
from sqlalchemy import create_engine, event
|
||||
from sqlalchemy import create_engine, event, func, select
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.pool import StaticPool
|
||||
|
||||
from app.models import Base
|
||||
from app.models.chat import ChatMessage, ChatSession, TopicSession
|
||||
from app.models.entitlement import EntitlementPlan
|
||||
from app.models.entitlement import EntitlementPlan, UserEntitlement, UserEntitlementLog
|
||||
from app.models.user import User
|
||||
from app.services.chat_service import ChatService
|
||||
from app.services.entitlement_service import EntitlementService, entitlement_dict
|
||||
@@ -113,6 +113,112 @@ def test_assign_user_plan_replaces_previous_active_plan():
|
||||
assert view.monthly_topic_remaining == 86
|
||||
|
||||
|
||||
def test_expired_entitlement_falls_back_and_keeps_previous_plan_context():
|
||||
with _db() as db:
|
||||
user, _session = _seed_user_session(db)
|
||||
db.add_all(
|
||||
[
|
||||
EntitlementPlan(id=10, name="基础版", plan_type="basic", monthly_topic_limit=30, status=1, sort_order=10),
|
||||
EntitlementPlan(id=20, name="深度陪伴版", plan_type="deep", monthly_topic_limit=90, status=1, sort_order=20),
|
||||
UserEntitlement(
|
||||
id=100,
|
||||
user_id=user.id,
|
||||
plan_id=20,
|
||||
status="active",
|
||||
effective_at=_now() - timedelta(days=40),
|
||||
expired_at=_now() - timedelta(days=1),
|
||||
),
|
||||
]
|
||||
)
|
||||
db.commit()
|
||||
|
||||
view = EntitlementService.active_entitlement(db, user)
|
||||
expired_count = EntitlementService.expire_due_entitlements(db)
|
||||
db.commit()
|
||||
|
||||
assert view.plan_id == 10
|
||||
assert view.lifecycle_status == "expired_fallback"
|
||||
assert view.previous_plan_name == "深度陪伴版"
|
||||
assert view.previous_expired_at is not None
|
||||
assert expired_count == 1
|
||||
assert db.get(UserEntitlement, 100).status == "expired"
|
||||
assert db.scalar(select(UserEntitlementLog).where(UserEntitlementLog.action == "expire")) is not None
|
||||
|
||||
|
||||
def test_renew_user_plan_extends_from_current_expiry_and_is_idempotent():
|
||||
with _db() as db:
|
||||
user, _session = _seed_user_session(db)
|
||||
current_expiry = _now() + timedelta(days=5)
|
||||
db.add(EntitlementPlan(id=20, name="深度陪伴版", plan_type="deep", monthly_topic_limit=90, status=1))
|
||||
db.add(
|
||||
UserEntitlement(
|
||||
id=100,
|
||||
user_id=user.id,
|
||||
plan_id=20,
|
||||
status="active",
|
||||
effective_at=_now() - timedelta(days=20),
|
||||
expired_at=current_expiry,
|
||||
)
|
||||
)
|
||||
db.commit()
|
||||
|
||||
renewed = EntitlementService.renew_user_plan(
|
||||
db,
|
||||
user=user,
|
||||
extension_days=30,
|
||||
request_key="renew-test-key:1",
|
||||
operated_by=99,
|
||||
remark="测试续期",
|
||||
)
|
||||
db.commit()
|
||||
duplicate = EntitlementService.renew_user_plan(
|
||||
db,
|
||||
user=user,
|
||||
extension_days=30,
|
||||
request_key="renew-test-key:1",
|
||||
operated_by=99,
|
||||
remark="重复请求",
|
||||
)
|
||||
db.commit()
|
||||
|
||||
assert renewed.id == duplicate.id
|
||||
assert renewed.expired_at == current_expiry + timedelta(days=30)
|
||||
assert db.get(UserEntitlement, 100).status == "replaced"
|
||||
assert db.scalar(select(func.count(UserEntitlementLog.id)).where(UserEntitlementLog.action == "renew")) == 1
|
||||
|
||||
|
||||
def test_renew_expired_user_plan_restores_same_plan_from_now():
|
||||
with _db() as db:
|
||||
user, _session = _seed_user_session(db)
|
||||
db.add(EntitlementPlan(id=20, name="深度陪伴版", plan_type="deep", monthly_topic_limit=90, status=1))
|
||||
db.add(
|
||||
UserEntitlement(
|
||||
id=100,
|
||||
user_id=user.id,
|
||||
plan_id=20,
|
||||
status="expired",
|
||||
effective_at=_now() - timedelta(days=40),
|
||||
expired_at=_now() - timedelta(days=1),
|
||||
)
|
||||
)
|
||||
db.commit()
|
||||
|
||||
renewed = EntitlementService.renew_user_plan(
|
||||
db,
|
||||
user=user,
|
||||
extension_days=30,
|
||||
request_key="restore-test-key:1",
|
||||
operated_by=99,
|
||||
)
|
||||
db.commit()
|
||||
view = EntitlementService.active_entitlement(db, user)
|
||||
|
||||
assert renewed.expired_at is not None
|
||||
assert timedelta(days=29) < renewed.expired_at - _now() <= timedelta(days=30)
|
||||
assert view.plan_id == 20
|
||||
assert view.source == "assigned"
|
||||
|
||||
|
||||
def test_monthly_topic_quota_blocks_new_topic_but_allows_existing_topic():
|
||||
with _db() as db:
|
||||
user, session = _seed_user_session(db)
|
||||
|
||||
Reference in New Issue
Block a user