"""Tests for the Nextcloud 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.nextcloud import ( NEXTCLOUD_STACK_DIR, CompositeStep, build_nextcloud_initial_setup_step, build_nextcloud_set_domain_steps, create_nextcloud_initial_setup_task, create_nextcloud_set_domain_task, ) # ============================================================================= # Tests for Playwright Initial Setup # ============================================================================= class TestBuildNextcloudInitialSetupStep: """Tests for the build_nextcloud_initial_setup_step function.""" def test_returns_playwright_payload(self): """Verify that build_nextcloud_initial_setup_step returns correct structure.""" payload = build_nextcloud_initial_setup_step( base_url="https://cloud.example.com", admin_username="admin", admin_password="securepassword123", ) assert payload["scenario"] == "nextcloud.initial_setup" assert "inputs" in payload assert "timeout" in payload def test_inputs_contain_required_fields(self): """Verify that inputs contain all required fields.""" payload = build_nextcloud_initial_setup_step( base_url="https://cloud.example.com", admin_username="admin", admin_password="securepassword123", ) inputs = payload["inputs"] assert inputs["base_url"] == "https://cloud.example.com" assert inputs["admin_username"] == "admin" assert inputs["admin_password"] == "securepassword123" assert "allowed_domains" in inputs def test_allowed_domains_extracted_from_url(self): """Verify that allowed_domains is extracted from base_url.""" payload = build_nextcloud_initial_setup_step( base_url="https://cloud.example.com", admin_username="admin", admin_password="password", ) assert payload["inputs"]["allowed_domains"] == ["cloud.example.com"] def test_allowed_domains_with_port(self): """Verify that allowed_domains handles URLs with ports.""" payload = build_nextcloud_initial_setup_step( base_url="https://cloud.example.com:8443", admin_username="admin", admin_password="password", ) assert payload["inputs"]["allowed_domains"] == ["cloud.example.com:8443"] def test_timeout_is_set(self): """Verify that timeout is set in the payload.""" payload = build_nextcloud_initial_setup_step( base_url="https://cloud.example.com", admin_username="admin", admin_password="password", ) assert payload["timeout"] == 120 @pytest.mark.asyncio class TestCreateNextcloudInitialSetupTask: """Tests for the create_nextcloud_initial_setup_task function.""" async def test_persists_playwright_task(self, db: AsyncSession, test_tenant: Tenant): """Verify that create_nextcloud_initial_setup_task persists a PLAYWRIGHT task.""" agent_id = uuid.uuid4() task = await create_nextcloud_initial_setup_task( db=db, tenant_id=test_tenant.id, agent_id=agent_id, base_url="https://cloud.example.com", admin_username="admin", admin_password="securepassword123", ) assert task.id is not None assert task.tenant_id == test_tenant.id assert task.agent_id == agent_id assert task.type == "PLAYWRIGHT" assert task.status == TaskStatus.PENDING.value async def test_task_payload_contains_scenario( self, db: AsyncSession, test_tenant: Tenant ): """Verify that the task payload contains the scenario field.""" task = await create_nextcloud_initial_setup_task( db=db, tenant_id=test_tenant.id, agent_id=uuid.uuid4(), base_url="https://cloud.example.com", admin_username="admin", admin_password="password", ) assert task.payload["scenario"] == "nextcloud.initial_setup" async def test_task_payload_contains_inputs( self, db: AsyncSession, test_tenant: Tenant ): """Verify that the task payload contains the inputs field.""" task = await create_nextcloud_initial_setup_task( db=db, tenant_id=test_tenant.id, agent_id=uuid.uuid4(), base_url="https://cloud.example.com", admin_username="testadmin", admin_password="testpassword123", ) inputs = task.payload["inputs"] assert inputs["base_url"] == "https://cloud.example.com" assert inputs["admin_username"] == "testadmin" assert inputs["admin_password"] == "testpassword123" assert inputs["allowed_domains"] == ["cloud.example.com"] 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_nextcloud_initial_setup_task( db=db, tenant_id=test_tenant.id, agent_id=uuid.uuid4(), base_url="https://cloud.example.com", admin_username="admin", admin_password="password", ) # 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 == "PLAYWRIGHT" assert retrieved_task.tenant_id == test_tenant.id # ============================================================================= # Tests for Set Domain Playbook # ============================================================================= class TestBuildNextcloudSetDomainSteps: """Tests for the build_nextcloud_set_domain_steps function.""" def test_returns_two_steps(self): """Verify that build_nextcloud_set_domain_steps returns exactly 2 steps.""" steps = build_nextcloud_set_domain_steps( public_url="https://cloud.example.com", pull=False ) assert len(steps) == 2 assert all(isinstance(step, CompositeStep) for step in steps) def test_first_step_is_nextcloud_set_domain(self): """Verify the first step is NEXTCLOUD_SET_DOMAIN.""" steps = build_nextcloud_set_domain_steps( public_url="https://cloud.example.com", pull=False ) assert steps[0].type == "NEXTCLOUD_SET_DOMAIN" def test_nextcloud_set_domain_payload(self): """Verify NEXTCLOUD_SET_DOMAIN step has correct payload.""" public_url = "https://cloud.example.com" steps = build_nextcloud_set_domain_steps(public_url=public_url, pull=False) assert steps[0].payload["public_url"] == public_url def test_docker_reload_payload(self): """Verify the DOCKER_RELOAD step has the correct payload structure.""" steps = build_nextcloud_set_domain_steps( public_url="https://cloud.example.com", pull=False ) docker_step = steps[1] assert docker_step.type == "DOCKER_RELOAD" assert docker_step.payload["compose_dir"] == NEXTCLOUD_STACK_DIR assert docker_step.payload["pull"] is False def test_pull_flag_true(self): """Verify that pull=True is passed correctly.""" steps = build_nextcloud_set_domain_steps( public_url="https://cloud.example.com", pull=True ) docker_step = steps[1] assert docker_step.payload["pull"] is True def test_pull_flag_false(self): """Verify that pull=False is passed correctly.""" steps = build_nextcloud_set_domain_steps( public_url="https://cloud.example.com", pull=False ) docker_step = steps[1] assert docker_step.payload["pull"] is False @pytest.mark.asyncio class TestCreateNextcloudSetDomainTask: """Tests for the create_nextcloud_set_domain_task function.""" async def test_persists_composite_task(self, db: AsyncSession, test_tenant: Tenant): """Verify that create_nextcloud_set_domain_task persists a COMPOSITE task.""" agent_id = uuid.uuid4() task = await create_nextcloud_set_domain_task( db=db, tenant_id=test_tenant.id, agent_id=agent_id, public_url="https://cloud.example.com", pull=False, ) assert task.id is not None assert task.tenant_id == test_tenant.id assert task.agent_id == agent_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_nextcloud_set_domain_task( db=db, tenant_id=test_tenant.id, agent_id=uuid.uuid4(), public_url="https://cloud.example.com", pull=False, ) 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_nextcloud_set_domain_task( db=db, tenant_id=test_tenant.id, agent_id=uuid.uuid4(), public_url="https://cloud.example.com", pull=True, ) steps = task.payload["steps"] # First step should be NEXTCLOUD_SET_DOMAIN assert steps[0]["type"] == "NEXTCLOUD_SET_DOMAIN" assert steps[0]["payload"]["public_url"] == "https://cloud.example.com" # Second step should be DOCKER_RELOAD assert steps[1]["type"] == "DOCKER_RELOAD" assert steps[1]["payload"]["compose_dir"] == NEXTCLOUD_STACK_DIR assert steps[1]["payload"]["pull"] is True async def test_task_with_pull_false(self, db: AsyncSession, test_tenant: Tenant): """Verify that pull=False is correctly stored in the task payload.""" task = await create_nextcloud_set_domain_task( db=db, tenant_id=test_tenant.id, agent_id=uuid.uuid4(), public_url="https://cloud.example.com", pull=False, ) steps = task.payload["steps"] assert steps[1]["payload"]["pull"] is False 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_nextcloud_set_domain_task( db=db, tenant_id=test_tenant.id, agent_id=uuid.uuid4(), public_url="https://cloud.example.com", pull=False, ) # 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