""" Copyright 2024-2026 ReplyPier Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ import pytest from fastapi.testclient import TestClient from fastapi import FastAPI from app.models.user import User from app.models.agent import Agent, AgentType from app.models.role import Role from app.models.permission import Permission, role_permissions from app.models.organization import Organization from app.models.workflow import Workflow, WorkflowStatus from uuid import uuid4 from app.api import agent as agent_router from app.core.auth import get_current_user, require_permissions, get_unified_auth from app.database import get_db # Create a test FastAPI app app = FastAPI() app.include_router( agent_router.router, prefix="/api/agents", tags=["agent"] ) @pytest.fixture def test_permissions(db) -> list[Permission]: """Create test permissions""" permissions = [] for name in ["manage_agents", "view_all"]: perm = Permission( name=name, description=f"Test permission for {name}" ) db.add(perm) permissions.append(perm) db.commit() for p in permissions: db.refresh(p) return permissions @pytest.fixture def test_role(db, test_organization_id, test_permissions) -> Role: """Create a test role with required permissions""" role = Role( name="Test Role", description="Test Role Description", organization_id=test_organization_id, is_default=True ) db.add(role) db.commit() # Associate permissions with role using the role_permissions table for perm in test_permissions: db.execute( role_permissions.insert().values( role_id=role.id, permission_id=perm.id ) ) db.commit() db.refresh(role) return role @pytest.fixture def test_user(db, test_organization_id, test_role) -> User: """Create a test user with required permissions""" user = User( id=uuid4(), email="test@example.com", hashed_password="hashed_password", is_active=True, organization_id=test_organization_id, full_name="Test User", role_id=test_role.id ) db.add(user) db.commit() db.refresh(user) return user @pytest.fixture def test_agent(db, test_user) -> Agent: """Create a test agent""" agent = Agent( id=uuid4(), name="Test Agent", display_name="Test Display Name", description="Test Description", agent_type=AgentType.CUSTOMER_SUPPORT, instructions=["Test instruction"], # Using the correct field name is_active=True, organization_id=test_user.organization_id, transfer_to_human=True, use_workflow=False ) db.add(agent) db.commit() db.refresh(agent) return agent @pytest.fixture def test_workflow(db, test_user, test_agent) -> Workflow: """Create a test workflow""" workflow = Workflow( id=uuid4(), name="Test Workflow", description="Test workflow description", status=WorkflowStatus.PUBLISHED, version=1, is_template=False, default_language="en", canvas_data={}, settings={}, organization_id=test_user.organization_id, agent_id=test_agent.id, created_by=test_user.id ) db.add(workflow) db.commit() db.refresh(workflow) return workflow @pytest.fixture def client(db, test_user) -> TestClient: """Create test client with mocked dependencies""" async def override_get_current_user(): return test_user async def override_require_permissions(*args, **kwargs): return test_user async def override_get_unified_auth(): # Authentication only — see the note in tests/api/test_agent.py. db.refresh(test_user) db.refresh(test_user.role) return { "auth_type": "jwt", "organization_id": test_user.organization_id, # Keep as UUID "user_id": test_user.id, "current_user": test_user } def override_get_db(): try: yield db finally: pass app.dependency_overrides[get_current_user] = override_get_current_user app.dependency_overrides[require_permissions] = override_require_permissions app.dependency_overrides[get_unified_auth] = override_get_unified_auth app.dependency_overrides[get_db] = override_get_db return TestClient(app) class TestAgentWorkflowAPI: def test_create_agent_with_workflow_disabled(self, client, test_user): """Test creating an agent with workflow disabled""" agent_data = { "name": "New Agent", "display_name": "New Agent Display", "description": "New agent description", "agent_type": "customer_support", "instructions": ["New agent instruction"], "is_active": True, "transfer_to_human": True, "use_workflow": False } response = client.post("/api/agents", json=agent_data) assert response.status_code == 201 data = response.json() assert data["name"] == agent_data["name"] assert data["use_workflow"] is False assert data["active_workflow_id"] is None def test_update_agent_enable_workflow(self, client, test_agent, test_workflow): """Test updating an agent to enable workflow""" update_data = { "use_workflow": True, "active_workflow_id": str(test_workflow.id) } response = client.put(f"/api/agents/{test_agent.id}", json=update_data) assert response.status_code == 200 data = response.json() assert data["use_workflow"] is True assert data["active_workflow_id"] == str(test_workflow.id) def test_update_agent_disable_workflow(self, client, test_agent, test_workflow): """Test updating an agent to disable workflow""" # First enable workflow test_agent.use_workflow = True test_agent.active_workflow_id = test_workflow.id update_data = { "use_workflow": False, "active_workflow_id": None } response = client.put(f"/api/agents/{test_agent.id}", json=update_data) assert response.status_code == 200 data = response.json() assert data["use_workflow"] is False assert data["active_workflow_id"] is None def test_update_agent_workflow_without_id(self, client, test_agent): """Test updating an agent to use workflow without providing workflow ID""" update_data = { "use_workflow": True # Missing active_workflow_id } response = client.put(f"/api/agents/{test_agent.id}", json=update_data) # This should either fail validation or default to None # The exact behavior depends on your API implementation assert response.status_code in [200, 422] def test_get_agent_with_workflow(self, client, test_agent, test_workflow): """Test getting an agent that has workflow enabled""" # Set up agent with workflow test_agent.use_workflow = True test_agent.active_workflow_id = test_workflow.id response = client.get(f"/api/agents/{test_agent.id}") assert response.status_code == 200 data = response.json() assert data["use_workflow"] is True assert data["active_workflow_id"] == str(test_workflow.id) def test_list_agents_with_workflow(self, client, test_agent, test_workflow): """Test listing agents, including those with workflows""" # Set up agent with workflow test_agent.use_workflow = True test_agent.active_workflow_id = test_workflow.id response = client.get("/api/agents/list") assert response.status_code == 200 data = response.json() assert len(data) >= 1 # Find our test agent in the response agent_data = next((agent for agent in data if agent["id"] == str(test_agent.id)), None) assert agent_data is not None assert agent_data["use_workflow"] is True assert agent_data["active_workflow_id"] == str(test_workflow.id)