import sys import types import pytest from services.intelligence.agents.core_agent_framework import TaskProposal # Stub deep onboarding import chain before importing today_workflow_service. module_names = [ "api", "api.content_planning", "api.content_planning.services", "api.content_planning.services.content_strategy", "api.content_planning.services.content_strategy.onboarding", "api.content_planning.services.content_strategy.onboarding.data_integration", ] for name in module_names: if name not in sys.modules: sys.modules[name] = types.ModuleType(name) class _StubOnboardingDataIntegrationService: def get_integrated_data_sync(self, user_id, db): return {} sys.modules[ "api.content_planning.services.content_strategy.onboarding.data_integration" ].OnboardingDataIntegrationService = _StubOnboardingDataIntegrationService from services import today_workflow_service as workflow class _NoopActivityService: def __init__(self, db, user_id): self.db = db self.user_id = user_id class _NoopMemoryService: def __init__(self, user_id, db): self.user_id = user_id self.db = db class _StaticAgent: def __init__(self, proposals): self._proposals = proposals async def propose_daily_tasks(self, _grounding): return self._proposals class _FakeOrchestrationService: def __init__(self, orchestrator): self._orchestrator = orchestrator async def get_or_create_orchestrator(self, _user_id): return self._orchestrator @pytest.mark.asyncio async def test_generate_agent_enhanced_plan_includes_strategy_agent_proposals(monkeypatch): strategy_proposal = TaskProposal( title="Define weekly content themes", description="Create this week's strategic themes for planned posts.", pillar_id="plan", priority="high", estimated_time=20, source_agent="StrategyArchitectAgent", reasoning="Strategy-driven planning", action_type="navigate", action_url="/content-planning-dashboard", ) orchestrator = type( "Orchestrator", (), {"agents": {"strategy": _StaticAgent([strategy_proposal])}}, )() monkeypatch.setattr(workflow, "build_grounding_context", lambda db, user_id, date: {"date": date}) monkeypatch.setattr(workflow, "AgentActivityService", _NoopActivityService) monkeypatch.setattr(workflow, "TaskMemoryService", _NoopMemoryService) monkeypatch.setattr(workflow, "orchestration_service", _FakeOrchestrationService(orchestrator)) # Mock DB session class MockDB: pass result = await workflow.generate_agent_enhanced_plan(db=MockDB(), user_id="u1", date="2026-01-01") # Assertions # Note: result["tasks"] might contain fallback tasks if pillar coverage guardrail is active # We just check if our strategy proposal is present strategy_tasks = [t for t in result["tasks"] if t["metadata"].get("source_agent") == "StrategyArchitectAgent"] assert len(strategy_tasks) >= 1 assert strategy_tasks[0]["pillarId"] == "plan" assert strategy_tasks[0]["title"] == "Define weekly content themes" @pytest.mark.asyncio async def test_generate_agent_enhanced_plan_dedupes_plan_tasks_with_priority_and_tiebreak(monkeypatch): title = "Build next week content plan" content_medium = TaskProposal( title=title, description="Draft a medium-priority weekly plan.", pillar_id="plan", priority="medium", estimated_time=15, source_agent="ZetaAgent", reasoning="baseline", action_type="navigate", action_url="/", context_data={} ) strategy_high = TaskProposal( title=title, description="Draft a high-priority strategic plan.", pillar_id="plan", priority="high", estimated_time=20, source_agent="StrategyArchitectAgent", reasoning="urgent update", action_type="navigate", action_url="/", context_data={} ) competitor_high = TaskProposal( title=title, description="Alternative high-priority plan.", pillar_id="plan", priority="high", estimated_time=25, source_agent="BetaAgent", reasoning="same priority tie", action_type="navigate", action_url="/", context_data={} ) class MockOrchestrator: agents = { "content": _StaticAgent([content_medium]), "strategy": _StaticAgent([strategy_high]), "competitor": _StaticAgent([competitor_high]), } orchestrator = MockOrchestrator() monkeypatch.setattr(workflow, "build_grounding_context", lambda db, user_id, date: {"date": date}) monkeypatch.setattr(workflow, "AgentActivityService", _NoopActivityService) monkeypatch.setattr(workflow, "TaskMemoryService", _NoopMemoryService) monkeypatch.setattr(workflow, "orchestration_service", _FakeOrchestrationService(orchestrator)) class MockDB: pass result = await workflow.generate_agent_enhanced_plan(db=MockDB(), user_id="u1", date="2026-01-01") # Filter for our specific test title to ignore backfilled tasks target_tasks = [t for t in result["tasks"] if t["title"] == title] assert len(target_tasks) == 1 task = target_tasks[0] assert task["title"] == title assert task["priority"] == "high" # The tie-breaking logic in today_workflow_service iterates through agent_tasks. # The order depends on the dict iteration order of unique_map. # We just want to ensure it picked one of the high priority ones, not the medium one. assert task["metadata"]["source_agent"] in ["StrategyArchitectAgent", "BetaAgent"]