"""Tests for the Chatwoot playbook module.""" import uuid import pytest import pytest_asyncio from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.models.task import Task, TaskStatus from app.models.tenant import Tenant from app.playbooks.chatwoot import ( CHATWOOT_ENV_PATH, CHATWOOT_STACK_DIR, CompositeStep, build_chatwoot_setup_steps, create_chatwoot_setup_task, ) class TestBuildChatwootSetupSteps: """Tests for the build_chatwoot_setup_steps function.""" def test_returns_two_steps(self): """Verify that build_chatwoot_setup_steps returns exactly 2 steps.""" steps = build_chatwoot_setup_steps(domain="support.example.com") assert len(steps) == 2 assert all(isinstance(step, CompositeStep) for step in steps) def test_env_update_payload(self): """Verify the ENV_UPDATE step has the correct payload structure.""" domain = "support.example.com" steps = build_chatwoot_setup_steps(domain=domain) env_step = steps[0] assert env_step.type == "ENV_UPDATE" assert env_step.payload["path"] == CHATWOOT_ENV_PATH assert env_step.payload["updates"]["FRONTEND_URL"] == f"https://{domain}" assert env_step.payload["updates"]["BACKEND_URL"] == f"https://{domain}" def test_docker_reload_payload(self): """Verify the DOCKER_RELOAD step has the correct payload structure.""" steps = build_chatwoot_setup_steps(domain="support.example.com") docker_step = steps[1] assert docker_step.type == "DOCKER_RELOAD" assert docker_step.payload["compose_dir"] == CHATWOOT_STACK_DIR assert docker_step.payload["pull"] is True def test_domain_url_formatting(self): """Verify that domain URLs are properly formatted with https.""" domain = "chat.mycompany.io" steps = build_chatwoot_setup_steps(domain=domain) env_step = steps[0] assert env_step.payload["updates"]["FRONTEND_URL"] == "https://chat.mycompany.io" assert env_step.payload["updates"]["BACKEND_URL"] == "https://chat.mycompany.io" @pytest.mark.asyncio class TestCreateChatwootSetupTask: """Tests for the create_chatwoot_setup_task function.""" async def test_persists_composite_task(self, db: AsyncSession, test_tenant: Tenant): """Verify that create_chatwoot_setup_task persists a COMPOSITE task.""" task = await create_chatwoot_setup_task( db=db, tenant_id=test_tenant.id, agent_id=None, domain="support.example.com", ) assert task.id is not None assert task.tenant_id == test_tenant.id assert task.type == "COMPOSITE" assert task.status == TaskStatus.PENDING.value async def test_task_payload_contains_steps(self, db: AsyncSession, test_tenant: Tenant): """Verify that the task payload contains the steps array.""" task = await create_chatwoot_setup_task( db=db, tenant_id=test_tenant.id, agent_id=None, domain="support.example.com", ) assert "steps" in task.payload assert len(task.payload["steps"]) == 2 async def test_task_steps_structure(self, db: AsyncSession, test_tenant: Tenant): """Verify that the steps in the payload have the correct structure.""" task = await create_chatwoot_setup_task( db=db, tenant_id=test_tenant.id, agent_id=None, domain="support.example.com", ) steps = task.payload["steps"] # First step should be ENV_UPDATE assert steps[0]["type"] == "ENV_UPDATE" assert "path" in steps[0]["payload"] assert "updates" in steps[0]["payload"] # Second step should be DOCKER_RELOAD assert steps[1]["type"] == "DOCKER_RELOAD" assert "compose_dir" in steps[1]["payload"] assert steps[1]["payload"]["pull"] is True async def test_task_with_agent_id(self, db: AsyncSession, test_tenant: Tenant): """Verify that agent_id is properly assigned when provided.""" agent_id = uuid.uuid4() # Note: In a real scenario, the agent would need to exist in the DB # For this test, we're just verifying the task stores the agent_id task = await create_chatwoot_setup_task( db=db, tenant_id=test_tenant.id, agent_id=agent_id, domain="support.example.com", ) assert task.agent_id == agent_id async def test_task_persisted_to_database(self, db: AsyncSession, test_tenant: Tenant): """Verify the task is actually persisted and can be retrieved.""" task = await create_chatwoot_setup_task( db=db, tenant_id=test_tenant.id, agent_id=None, domain="support.example.com", ) # Query the task back from the database result = await db.execute(select(Task).where(Task.id == task.id)) retrieved_task = result.scalar_one_or_none() assert retrieved_task is not None assert retrieved_task.type == "COMPOSITE" assert retrieved_task.tenant_id == test_tenant.id