from __future__ import annotations import uuid import pytest from sqlalchemy import create_engine from sqlalchemy.orm import Session, sessionmaker from sqlalchemy.pool import StaticPool from app.core.agent_enums import ( AgentAssetContentType, AgentAssetDomain, AgentAssetStatus, AgentAssetType, AgentName, AgentReviewStatus, AgentRunSource, AgentRunStatus, ) from app.db.base import Base from app.schemas.agent_asset import ( AgentAssetCreate, AgentAssetReviewCreate, AgentAssetVersionCreate, ) from app.services.agent_assets import AgentAssetService from app.services.agent_runs import AgentRunService from app.services.audit import AuditLogService def build_session() -> Session: engine = create_engine( "sqlite+pysqlite:///:memory:", connect_args={"check_same_thread": False}, poolclass=StaticPool, ) Base.metadata.create_all(bind=engine) session_factory = sessionmaker(bind=engine, autoflush=False, autocommit=False) return session_factory() def test_agent_asset_service_seeds_assets_and_enforces_review_before_activation() -> None: with build_session() as db: service = AgentAssetService(db) rules = service.list_assets(asset_type=AgentAssetType.RULE.value) assert len(rules) >= 3 pending_rule = next(item for item in rules if item.status == AgentAssetStatus.REVIEW.value) with pytest.raises(PermissionError): service.activate_asset(pending_rule.id, actor="pytest") def test_agent_asset_service_seeds_all_foundation_asset_types() -> None: with build_session() as db: service = AgentAssetService(db) assert len(service.list_assets(asset_type=AgentAssetType.RULE.value)) >= 3 assert len(service.list_assets(asset_type=AgentAssetType.SKILL.value)) >= 2 assert len(service.list_assets(asset_type=AgentAssetType.MCP.value)) >= 2 assert len(service.list_assets(asset_type=AgentAssetType.TASK.value)) >= 3 def test_agent_asset_service_can_activate_rule_after_review() -> None: with build_session() as db: service = AgentAssetService(db) created = service.create_asset( AgentAssetCreate( asset_type=AgentAssetType.RULE, code=f"rule.test.{uuid.uuid4().hex[:8]}", name="测试规则", description="用于测试审核和上线流程。", domain=AgentAssetDomain.EXPENSE, scenario_json=["expense", "risk_check"], owner="pytest", reviewer="reviewer", status=AgentAssetStatus.DRAFT, config_json={"enabled": False}, ), actor="pytest", ) service.create_version( created.id, AgentAssetVersionCreate( version="v1.0.0", content="# 测试规则\n\n- 仅用于测试。", content_type=AgentAssetContentType.MARKDOWN, change_note="初始化版本", created_by="pytest", ), actor="pytest", ) service.create_review( created.id, AgentAssetReviewCreate( version="v1.0.0", reviewer="reviewer", review_status=AgentReviewStatus.APPROVED, review_note="可以上线", ), actor="reviewer", ) activated = service.activate_asset(created.id, actor="reviewer") assert activated.status == AgentAssetStatus.ACTIVE.value assert activated.current_version == "v1.0.0" assert activated.latest_review is not None assert activated.latest_review.review_status == AgentReviewStatus.APPROVED.value def test_agent_asset_service_returns_recent_versions_for_rule_detail() -> None: with build_session() as db: service = AgentAssetService(db) rule = next( item for item in service.list_assets(asset_type=AgentAssetType.RULE.value) if item.code == "rule.expense.duplicate_expense_check" ) detail = service.get_asset(rule.id) assert detail is not None assert detail.current_version == "v1.1.0" assert detail.current_version_content_type == AgentAssetContentType.MARKDOWN.value assert isinstance(detail.current_version_content, str) assert len(detail.recent_versions) >= 2 assert any(item.is_current for item in detail.recent_versions) assert {item.version for item in detail.recent_versions} >= {"v1.0.0", "v1.1.0"} def test_agent_run_service_lists_seeded_trace_data() -> None: with build_session() as db: service = AgentRunService(db) runs = service.list_runs() assert len(runs) >= 3 assert any(item.tool_calls for item in runs) assert any(item.semantic_parse is not None for item in runs) def test_agent_run_service_creates_run_and_persists_error_message() -> None: with build_session() as db: service = AgentRunService(db) created = service.create_run( agent=AgentName.ORCHESTRATOR.value, source=AgentRunSource.SYSTEM_EVENT.value, status=AgentRunStatus.FAILED.value, error_message="simulated failure", result_summary="failed to route request", ) fetched = service.get_run(created.run_id) assert fetched is not None assert fetched.run_id.startswith("run_") assert fetched.status == AgentRunStatus.FAILED.value assert fetched.error_message == "simulated failure" assert fetched.result_summary == "failed to route request" def test_agent_asset_creation_writes_audit_log() -> None: with build_session() as db: service = AgentAssetService(db) created = service.create_asset( AgentAssetCreate( asset_type=AgentAssetType.SKILL, code=f"skill.test.{uuid.uuid4().hex[:8]}", name="测试技能", description="用于测试审计日志写入。", domain=AgentAssetDomain.KNOWLEDGE, scenario_json=["knowledge", "query"], owner="pytest", reviewer="reviewer", status=AgentAssetStatus.DRAFT, config_json={"enabled": True}, ), actor="pytest", ) logs = AuditLogService(db).list_logs(resource_id=created.id) assert any(item.action == "create_agent_asset" for item in logs)