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

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

200 lines
7.3 KiB
Python

from datetime import date, timedelta
import pytest
from fastapi import HTTPException
from sqlalchemy import create_engine, func, select
from sqlalchemy.orm import sessionmaker
from app.application.events import EventDispatchService
from app.core.database import Base
from app.core.utils.time import utc_now
from app.modules.ai_memory.constants import (
AIMemoryPayloadKey,
AIMemoryStatus,
)
from app.modules.ai_memory.models import AIMemoryEntry
from app.modules.ai_memory.service import AIMemoryService
from app.modules.business.constants import StatusValue
from app.modules.business.models import FundAccount, PerformanceMetric, Project
from app.modules.events.constants import (
EventAggregateType,
EventSource,
EventStatus,
EventType,
)
from app.modules.events.models import DomainEvent
from app.modules.events.services import EventService
from app.modules.reports.constants import EnterpriseAnalyticsKey, MetricKey
from app.modules.reports.services import ReportService
from app.modules.workflows.constants import WorkflowStatus, WorkflowType
from app.modules.workflows.models import WorkflowAction, WorkflowInstance
def _session_factory():
engine = create_engine("sqlite://")
Base.metadata.create_all(engine)
return engine, sessionmaker(bind=engine, expire_on_commit=False)
def test_workflow_actions_are_idempotent_per_source_event() -> None:
engine, factory = _session_factory()
try:
with factory() as db:
event = EventService(db).emit(
event_type=EventType.ENTERPRISE_ANALYTICS_GENERATED,
source=EventSource.ANALYTICS,
aggregate_type=EventAggregateType.ENTERPRISE_ANALYTICS,
aggregate_id="snapshot-1",
idempotency_key="analytics:snapshot-1",
)
first = EventDispatchService(db).dispatch_event(
event.event_id,
worker_id="pytest-first",
)
assert first.status == EventStatus.PROCESSED
first.status = EventStatus.PENDING
first.processed_at = None
first.next_attempt_at = utc_now()
db.commit()
second = EventDispatchService(db).dispatch_event(
event.event_id,
worker_id="pytest-second",
)
assert second.status == EventStatus.PROCESSED
assert db.scalar(select(func.count()).select_from(WorkflowInstance)) == 1
assert db.scalar(select(func.count()).select_from(WorkflowAction)) == 1
action = db.execute(select(WorkflowAction)).scalar_one()
assert action.source_event_id == event.event_id
finally:
engine.dispose()
def test_ai_memory_has_no_unrelated_fallback_and_archives_expired_entries() -> None:
engine, factory = _session_factory()
try:
with factory() as db:
service = AIMemoryService(db)
first = service.auto_write(
prompt="Remember concise operational summaries",
context={
AIMemoryPayloadKey.SCOPE: "project",
AIMemoryPayloadKey.SUBJECT: "P-MEM-IDEMPOTENT",
},
answer="Use concise bullet summaries for project updates.",
)
duplicate = service.auto_write(
prompt="Remember concise operational summaries",
context={
AIMemoryPayloadKey.SCOPE: "project",
AIMemoryPayloadKey.SUBJECT: "P-MEM-IDEMPOTENT",
},
answer="Use concise bullet summaries for project updates.",
)
assert first is not None
assert duplicate is not None
assert duplicate.code == first.code
assert db.scalar(select(func.count()).select_from(AIMemoryEntry)) == 1
assert (
service.recall(
query="no-such-memory-token",
scope="project",
subject="P-MEM-IDEMPOTENT",
)
== []
)
first.expires_at = utc_now() - timedelta(seconds=1)
db.commit()
assert (
service.list_entries(
scope="project",
subject="P-MEM-IDEMPOTENT",
status_filter=AIMemoryStatus.ACTIVE,
)
== []
)
db.refresh(first)
assert first.status == AIMemoryStatus.ARCHIVED
finally:
engine.dispose()
def test_enterprise_analytics_validates_scope_and_reuses_identical_snapshot() -> None:
engine, factory = _session_factory()
try:
with factory() as db:
db.add_all(
[
Project(
code="P-SCOPED",
name="Scoped project",
owner="owner-a",
status=StatusValue.RUNNING_CN,
),
PerformanceMetric(
code="PERF-GLOBAL",
name="Global metric",
weight=10,
auto_score=80,
confirmed_score=75,
status=StatusValue.REVIEWED,
),
FundAccount(
code="FUND-GLOBAL",
name="Global account",
current_balance=1000,
),
]
)
db.commit()
service = ReportService(db)
with pytest.raises(HTTPException) as exc_info:
service.enterprise_analytics(
period_start=date(2030, 2, 2),
period_end=date(2030, 2, 1),
)
assert exc_info.value.status_code == 422
first = service.enterprise_analytics(project_code="P-SCOPED")
second = service.enterprise_analytics(project_code="P-SCOPED")
assert first[EnterpriseAnalyticsKey.CODE] == second[EnterpriseAnalyticsKey.CODE]
assert first[EnterpriseAnalyticsKey.PERFORMANCE][MetricKey.TOTAL] == 0
assert (
first[EnterpriseAnalyticsKey.FINANCE][
MetricKey.CURRENT_BALANCE_TOTAL
]
== 0
)
events = list(
db.execute(
select(DomainEvent).where(
DomainEvent.event_type
== EventType.ENTERPRISE_ANALYTICS_GENERATED
)
).scalars()
)
assert len(events) == 1
assert events[0].aggregate_id == first[EnterpriseAnalyticsKey.CODE]
dispatched = EventDispatchService(db).dispatch_event(
events[0].event_id,
worker_id="pytest-enterprise",
)
assert dispatched.status == EventStatus.PROCESSED
workflow = db.execute(
select(WorkflowInstance).where(
WorkflowInstance.workflow_type
== WorkflowType.ENTERPRISE_ANALYTICS
)
).scalar_one()
assert workflow.status == WorkflowStatus.COMPLETED
assert workflow.aggregate_id == first[EnterpriseAnalyticsKey.CODE]
finally:
engine.dispose()