"""Native vSphere console catalog tests.""" from app.vsphere.contracts.catalog import ( _param_index, list_vsphere_majors, vsphere_catalog_payload, vsphere_method_payload, ) from app.vsphere.rest.coverage import catalog_entries, is_implemented def test_list_vsphere_majors() -> None: payload = list_vsphere_majors(runtime_version="8.0.2") series = {item["series"] for item in payload["majors"]} assert series == { "vSphere 7.0", "vSphere 7.0 U3", "vSphere 8.0", "vSphere 8.0 U2", } assert payload["plane"] == "vsphere-rest" def test_vsphere_catalog_marks_implemented_methods() -> None: payload = vsphere_catalog_payload(9) assert payload["source_version"] == "8.0.2" assert payload["method_count"] == len(catalog_entries()) assert is_implemented("GET", "/api/vcenter/vm") assert is_implemented("POST", "/api/cis/tagging/category") assert vsphere_catalog_payload(6)["method_count"] < payload["method_count"] # Methods for the same path must be merged (GET+POST+DELETE on one path entry). vm_paths = [ p for cat in payload["categories"] for p in cat["paths"] if p["path"] == "/api/vcenter/vm" ] assert len(vm_paths) == 1 assert {m["verb"] for m in vm_paths[0]["methods"]} >= {"GET", "POST"} def test_vsphere_method_payload_extracts_path_fields() -> None: payload = vsphere_method_payload( major=9, path="/api/vcenter/vm/{vm}", verb="GET", runtime_version="8.0.2", ) assert payload["implemented"] is True assert len(payload["path_fields"]) == 1 assert payload["path_fields"][0]["name"] == "vm" assert payload["resolved_path"] == "/api/vcenter/vm/vm-101" def test_param_index_loaded_from_openapi() -> None: _param_index.cache_clear() methods = _param_index() assert "GET /api/vcenter/vm" in methods assert "POST /api/vcenter/vm" in methods assert len(methods) > 500 def test_vsphere_method_payload_uses_openapi_query_filters() -> None: payload = vsphere_method_payload( major=9, path="/api/vcenter/vm", verb="GET", runtime_version="8.0.2", ) assert payload["param_source"] == "openapi" query_names = {field["name"] for field in payload["query_fields"]} assert query_names >= { "vms", "names", "folders", "datacenters", "hosts", "clusters", "resource_pools", "power_states", } # GET has no JSON body in the real Automation API. assert payload["body_example"] == {} def test_vsphere_method_payload_uses_openapi_create_spec() -> None: payload = vsphere_method_payload( major=9, path="/api/vcenter/vm", verb="POST", runtime_version="8.0.2", ) assert payload["param_source"] == "openapi" assert payload["body_example"]["guest_os"] == "OTHER_GUEST_64" assert payload["body_example"]["name"] == "web-01" assert isinstance(payload["body_example"]["placement"], dict) assert payload["body_example"]["placement"]["host"] == "host-11" assert payload["body_example"]["cpu"]["count"] == 2 assert payload["body_example"]["memory"]["size_mib"] == 2048 body_names = {field["name"] for field in payload["body_fields"]} assert "guest_os" in body_names assert "name" in body_names assert "placement.host" in body_names assert "placement.folder" in body_names assert "cpu.count" in body_names assert "memory.size_mib" in body_names # Nested object keys themselves are not PARAM rows — only scalar leaves. assert "placement" not in body_names assert "cpu" not in body_names # Create must not inherit ?action=clone pollution. assert not any(field["name"] == "action" for field in payload["query_fields"]) def test_vsphere_method_payload_power_action_query() -> None: payload = vsphere_method_payload( major=9, path="/api/vcenter/vm/{vm}/power", verb="POST", runtime_version="8.0.2", ) assert payload["param_source"] == "openapi" action = next(field for field in payload["query_fields"] if field["name"] == "action") assert action["optional"] is False assert set(action["enum"]) >= {"start", "stop", "reset", "suspend"} # Params drawer merges query into body_fields for editing. assert any(field["name"] == "action" for field in payload["body_fields"])