Files
company-ai-platform/tests/test_feishu_personalization_migration.py
JiuContinent d7db84571d ```
feat: 添加飞书用户模块和订阅功能支持

- 新增feishu_users模块用于处理飞书用户身份验证和权限管理
- 新增subscriptions模块用于处理订阅相关功能
- 新增personalization模块用于个性化服务
- 在alembic迁移配置中注册新的模型模块
- 在API路由器中添加feishu_users和subscriptions路由
- 实现事件调度服务的改进,包括错误处理和状态更新优化
- 添加飞书命令处理的权限检查机制
- 实现飞书应用票据事件处理
- 改进审计日志记录功能
```
2026-07-27 08:02:17 +08:00

194 lines
6.7 KiB
Python

from datetime import datetime
from pathlib import Path
import pytest
from alembic import command
from alembic.config import Config
from sqlalchemy import create_engine, inspect, text
from app.core.config import get_settings
def test_personalization_migration_preserves_and_classifies_legacy_data(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
database_path = tmp_path / "personalization-migration.db"
database_url = f"sqlite:///{database_path.as_posix()}"
monkeypatch.setenv("DATABASE_URL", database_url)
get_settings.cache_clear()
config = Config("alembic.ini")
engine = create_engine(database_url)
timestamp = datetime(2026, 7, 26, 8, 0)
try:
command.upgrade(config, "202607260002")
with engine.begin() as connection:
connection.execute(
text(
"""
INSERT INTO ai_memory_entries (
code,
fingerprint,
scope,
subject,
content,
source,
importance,
status,
actor,
created_at,
updated_at
)
VALUES (
:code,
:fingerprint,
'global',
:subject,
:content,
:source,
1,
'active',
'legacy-user',
:created_at,
:updated_at
)
"""
),
[
{
"code": "legacy-rule",
"fingerprint": "legacy-rule-fingerprint",
"subject": "rule",
"content": "Use concise Chinese.",
"source": "user_rule",
"created_at": timestamp,
"updated_at": timestamp,
},
{
"code": "legacy-auto-memory",
"fingerprint": "legacy-auto-fingerprint",
"subject": "preference",
"content": "Prefers market summaries.",
"source": "auto",
"created_at": timestamp,
"updated_at": timestamp,
},
{
"code": "legacy-hermes-memory",
"fingerprint": "legacy-hermes-fingerprint",
"subject": "preference",
"content": "Prefers short answers.",
"source": "hermes",
"created_at": timestamp,
"updated_at": timestamp,
},
],
)
connection.execute(
text(
"""
INSERT INTO market_watchlists (
actor,
symbol,
enabled,
created_at,
updated_at
)
VALUES (
'legacy-user',
'600000.SH',
1,
:created_at,
:updated_at
)
"""
),
{"created_at": timestamp, "updated_at": timestamp},
)
command.upgrade(config, "202607260005")
inspector = inspect(engine)
expected_tables = {
"feishu_app_tickets",
"feishu_admin_bootstrap_tombstones",
"feishu_users",
"user_preferences",
"ai_conversations",
"ai_conversation_messages",
"personal_data_erasure_requests",
"push_subscriptions",
"push_deliveries",
}
assert expected_tables <= set(inspector.get_table_names())
tombstone_columns = {
column["name"]
for column in inspector.get_columns(
"feishu_admin_bootstrap_tombstones"
)
}
assert tombstone_columns == {"id", "identity_hash", "created_at"}
memory_columns = {
column["name"] for column in inspector.get_columns("ai_memory_entries")
}
assert {"owner_id", "kind"} <= memory_columns
memory_indexes = {
index["name"]: index for index in inspector.get_indexes("ai_memory_entries")
}
assert memory_indexes["ix_ai_memory_entries_fingerprint"]["unique"] == 0
assert {
"ix_ai_memory_entries_kind",
"ix_ai_memory_entries_owner_id",
} <= set(memory_indexes)
memory_constraints = {
constraint["name"]
for constraint in inspector.get_unique_constraints("ai_memory_entries")
}
assert "uq_ai_memory_owner_fingerprint" in memory_constraints
memory_foreign_keys = {
foreign_key["name"]
for foreign_key in inspector.get_foreign_keys("ai_memory_entries")
}
assert "fk_ai_memory_entries_owner_id" in memory_foreign_keys
with engine.connect() as connection:
memories = {
row.code: row
for row in connection.execute(
text(
"""
SELECT code, owner_id, kind, source, status
FROM ai_memory_entries
ORDER BY code
"""
)
)
}
watchlist_owner_id = connection.scalar(
text(
"""
SELECT owner_id
FROM market_watchlists
WHERE actor = 'legacy-user' AND symbol = '600000.SH'
"""
)
)
legacy_rule = memories["legacy-rule"]
assert legacy_rule.owner_id is None
assert legacy_rule.kind == "company_rule"
assert legacy_rule.source == "legacy_company"
assert legacy_rule.status == "active"
for code in ("legacy-auto-memory", "legacy-hermes-memory"):
assert memories[code].owner_id is None
assert memories[code].kind == "memory"
assert memories[code].status == "archived"
assert watchlist_owner_id is None
command.check(config)
finally:
engine.dispose()
get_settings.cache_clear()