"""Integration smoke for native vSphere REST + SOAP.""" from __future__ import annotations import os import pytest from httpx import ASGITransport, AsyncClient from app.config import Settings from app.main import create_app from app.vsphere.seed import seed_vsphere_inventory pytestmark = pytest.mark.integration @pytest.fixture async def client() -> AsyncClient: database_url = os.getenv("TEST_DATABASE_URL") or os.getenv("DATABASE_URL") if not database_url: pytest.skip("TEST_DATABASE_URL / DATABASE_URL required") settings = Settings( database_url=database_url, # type: ignore[arg-type] contract_snapshot=None, enable_pve_stub=False, ) app = create_app(settings=settings, worker_factories=()) async with app.router.lifespan_context(app): await seed_vsphere_inventory(app.state.database, force=True, profile="small") async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as http: yield http async def _session(client: AsyncClient) -> dict[str, str]: login = await client.post( "/api/session", auth=("administrator@vsphere.local", "VMware1!"), ) assert login.status_code == 201 return {"vmware-api-session-id": login.json()} async def test_rest_session_inventory_power_clone_tags(client: AsyncClient) -> None: headers = await _session(client) vms = await client.get("/api/vcenter/vm", headers=headers) assert vms.status_code == 200 assert len(vms.json()) >= 5 vm_id = next(item["vm"] for item in vms.json() if item["name"] == "app-01") power = await client.post( f"/api/vcenter/vm/{vm_id}/power", params={"action": "start"}, headers=headers, ) assert power.status_code == 200 assert str(power.json().get("task") or "").startswith("task-") snap = await client.post( f"/api/vcenter/vm/{vm_id}/snapshots", headers=headers, json={"name": "pre-update", "description": "lab"}, ) assert snap.status_code == 200 assert "snapshot" in snap.json() clone = await client.post( f"/api/vcenter/vm/{vm_id}/clone", headers=headers, json={"name": "app-01-clone"}, ) assert clone.status_code == 200 assert clone.json()["vm"].startswith("vm-") cat = await client.post( "/api/cis/tagging/category", headers=headers, json={"name": "Owner-api-test", "associable_types": ["VirtualMachine"]}, ) assert cat.status_code == 200, cat.text tag = await client.post( "/api/cis/tagging/tag", headers=headers, json={"category_id": cat.json(), "name": "team-a"}, ) assert tag.status_code == 200 libs = await client.get("/api/content/library", headers=headers) assert libs.status_code == 200 assert len(libs.json()) >= 1 versions = await client.get("/ui/api/versions") assert versions.status_code == 200 assert versions.json()["plane"] == "vsphere-rest" async def test_readonly_cannot_mutate(client: AsyncClient) -> None: login = await client.post( "/api/session", auth=("readonly@vsphere.local", "VMware1!"), ) assert login.status_code == 201 headers = {"vmware-api-session-id": login.json()} session = await client.get("/api/session", headers=headers) assert session.status_code == 200 assert session.content in (b"", b"null") or not session.text.strip() assert "ReadOnly" in (session.headers.get("x-vmware-session-roles") or "") vms = await client.get("/api/vcenter/vm", headers=headers) assert vms.status_code == 200 assert len(vms.json()) >= 5 power = await client.post( f"/api/vcenter/vm/{vms.json()[0]['vm']}/power", params={"action": "start"}, headers=headers, ) assert power.status_code == 403 async def test_soap_property_collector_and_wsdl(client: AsyncClient) -> None: content = await client.post( "/sdk", content=""" <_this type="ServiceInstance">ServiceInstance """, headers={"Content-Type": "text/xml"}, ) assert content.status_code == 200 assert "SessionManager" in content.text login = await client.post( "/sdk", content=""" <_this type="SessionManager">SessionManager administrator@vsphere.local VMware1! """, headers={"Content-Type": "text/xml"}, ) assert login.status_code == 200 cookie = login.headers.get("set-cookie") or "" assert "vmware_soap_session" in cookie updates = await client.post( "/sdk", content=""" <_this type="PropertyCollector">propertyCollector """, headers={"Content-Type": "text/xml", "Cookie": cookie.split(";")[0]}, ) assert updates.status_code == 200 assert "WaitForUpdatesExResponse" in updates.text assert "vm-101" in updates.text wsdl = await client.get("/sdk/vimService.wsdl") assert wsdl.status_code == 200 assert "VimService" in wsdl.text