```
feat: 添加飞书用户模块和订阅功能支持 - 新增feishu_users模块用于处理飞书用户身份验证和权限管理 - 新增subscriptions模块用于处理订阅相关功能 - 新增personalization模块用于个性化服务 - 在alembic迁移配置中注册新的模型模块 - 在API路由器中添加feishu_users和subscriptions路由 - 实现事件调度服务的改进,包括错误处理和状态更新优化 - 添加飞书命令处理的权限检查机制 - 实现飞书应用票据事件处理 - 改进审计日志记录功能 ```
This commit is contained in:
167
tests/test_personalization_cleanup.py
Normal file
167
tests/test_personalization_cleanup.py
Normal file
@@ -0,0 +1,167 @@
|
||||
from collections.abc import Iterator
|
||||
from datetime import timedelta
|
||||
|
||||
import pytest
|
||||
from fastapi import FastAPI
|
||||
from sqlalchemy import create_engine, func, select
|
||||
from sqlalchemy.orm import Session, sessionmaker
|
||||
from sqlalchemy.pool import StaticPool
|
||||
|
||||
from app.application.scheduling import create_scheduler
|
||||
from app.core.config import get_settings
|
||||
from app.core.database import Base
|
||||
from app.core.utils.time import utc_now
|
||||
from app.modules.feishu_users.models import FeishuUser
|
||||
from app.modules.personalization.models import AIConversation, AIConversationMessage
|
||||
from app.modules.personalization.services import ConversationService
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _reset_settings() -> Iterator[None]:
|
||||
get_settings.cache_clear()
|
||||
yield
|
||||
get_settings.cache_clear()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def session_factory() -> Iterator[sessionmaker[Session]]:
|
||||
engine = create_engine(
|
||||
"sqlite://",
|
||||
connect_args={"check_same_thread": False},
|
||||
poolclass=StaticPool,
|
||||
)
|
||||
Base.metadata.create_all(engine)
|
||||
factory = sessionmaker(bind=engine, expire_on_commit=False)
|
||||
try:
|
||||
yield factory
|
||||
finally:
|
||||
engine.dispose()
|
||||
|
||||
|
||||
def test_global_cleanup_removes_inactive_user_history(
|
||||
session_factory: sessionmaker[Session],
|
||||
) -> None:
|
||||
with session_factory() as db:
|
||||
expired = _conversation(db, "expired")
|
||||
mixed = _conversation(db, "mixed")
|
||||
fresh = _conversation(db, "fresh")
|
||||
old_time = utc_now() - timedelta(days=31)
|
||||
fresh_time = utc_now() - timedelta(days=29)
|
||||
db.add_all(
|
||||
[
|
||||
AIConversationMessage(
|
||||
conversation_id=expired.id,
|
||||
role="user",
|
||||
content="expired question",
|
||||
created_at=old_time,
|
||||
),
|
||||
AIConversationMessage(
|
||||
conversation_id=expired.id,
|
||||
role="assistant",
|
||||
content="expired answer",
|
||||
created_at=old_time,
|
||||
),
|
||||
AIConversationMessage(
|
||||
conversation_id=mixed.id,
|
||||
role="user",
|
||||
content="old mixed question",
|
||||
created_at=old_time,
|
||||
),
|
||||
AIConversationMessage(
|
||||
conversation_id=mixed.id,
|
||||
role="assistant",
|
||||
content="fresh mixed answer",
|
||||
created_at=fresh_time,
|
||||
),
|
||||
AIConversationMessage(
|
||||
conversation_id=fresh.id,
|
||||
role="user",
|
||||
content="fresh question",
|
||||
created_at=fresh_time,
|
||||
),
|
||||
]
|
||||
)
|
||||
db.commit()
|
||||
|
||||
deleted = ConversationService(db).cleanup_expired_globally()
|
||||
|
||||
assert deleted == {
|
||||
"conversation_messages": 3,
|
||||
"conversations": 1,
|
||||
}
|
||||
assert db.get(AIConversation, expired.id) is None
|
||||
assert db.get(AIConversation, mixed.id) is not None
|
||||
assert db.get(AIConversation, fresh.id) is not None
|
||||
assert db.scalar(
|
||||
select(func.count()).select_from(AIConversationMessage)
|
||||
) == 2
|
||||
|
||||
with session_factory() as verification_db:
|
||||
assert verification_db.get(AIConversation, expired.id) is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("features_enabled", "job_expected"),
|
||||
[(False, False), (True, True)],
|
||||
)
|
||||
def test_scheduler_wires_one_global_retention_job(
|
||||
session_factory: sessionmaker[Session],
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
features_enabled: bool,
|
||||
job_expected: bool,
|
||||
) -> None:
|
||||
monkeypatch.setenv(
|
||||
"FEISHU_USER_FEATURES_ENABLED",
|
||||
str(features_enabled).lower(),
|
||||
)
|
||||
monkeypatch.setattr("app.core.database.SessionLocal", session_factory)
|
||||
get_settings.cache_clear()
|
||||
app = FastAPI()
|
||||
scheduler = create_scheduler(app)
|
||||
|
||||
job = scheduler.get_job("personalization_retention_cleanup")
|
||||
|
||||
assert (job is not None) is job_expected
|
||||
if job is None:
|
||||
return
|
||||
assert job.trigger.interval == timedelta(minutes=1)
|
||||
|
||||
with session_factory() as db:
|
||||
expired = _conversation(db, "scheduled")
|
||||
db.add(
|
||||
AIConversationMessage(
|
||||
conversation_id=expired.id,
|
||||
role="user",
|
||||
content="expired scheduled message",
|
||||
created_at=utc_now() - timedelta(days=31),
|
||||
)
|
||||
)
|
||||
db.commit()
|
||||
expired_id = expired.id
|
||||
|
||||
job.func()
|
||||
|
||||
assert app.state.last_personalization_retention_cleanup == {
|
||||
"conversation_messages": 1,
|
||||
"conversations": 1,
|
||||
}
|
||||
with session_factory() as db:
|
||||
assert db.get(AIConversation, expired_id) is None
|
||||
|
||||
|
||||
def _conversation(db: Session, suffix: str) -> AIConversation:
|
||||
owner = FeishuUser(
|
||||
code=f"FSU-{suffix}",
|
||||
tenant_key=f"tenant-{suffix}",
|
||||
open_id=f"open-{suffix}",
|
||||
)
|
||||
db.add(owner)
|
||||
db.flush()
|
||||
conversation = AIConversation(
|
||||
owner_id=owner.id,
|
||||
chat_type="private",
|
||||
chat_key=f"chat-{suffix}",
|
||||
)
|
||||
db.add(conversation)
|
||||
db.flush()
|
||||
return conversation
|
||||
Reference in New Issue
Block a user