Expand lab seed tiers, OpenAPI PARAMS, and console DATA/Params UX.
This commit is contained in:
@@ -26,7 +26,7 @@ async def client() -> AsyncClient:
|
||||
)
|
||||
app = create_app(settings=settings, worker_factories=())
|
||||
async with app.router.lifespan_context(app):
|
||||
await seed_vsphere_inventory(app.state.database, force=True, profile="demo-cluster")
|
||||
await seed_vsphere_inventory(app.state.database, force=True, profile="big")
|
||||
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as http:
|
||||
yield http
|
||||
|
||||
@@ -42,7 +42,7 @@ async def test_demo_cluster_api_state_and_inventory(client: AsyncClient) -> None
|
||||
|
||||
vm_list = await client.get("/api/vcenter/vm", headers=headers)
|
||||
assert vm_list.status_code == 200
|
||||
assert len(vm_list.json()) >= 1000
|
||||
assert len(vm_list.json()) >= 2000
|
||||
|
||||
hosts = await client.get("/api/vcenter/host", headers=headers)
|
||||
assert hosts.status_code == 200
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
"""Deep-handler wire realism: seed non-empty, mutations, tasks, 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_deep_inventory_lists_and_details(client: AsyncClient) -> None:
|
||||
headers = await _session(client)
|
||||
hosts = await client.get("/api/vcenter/host", headers=headers)
|
||||
assert hosts.status_code == 200
|
||||
assert len(hosts.json()) >= 3
|
||||
host_id = hosts.json()[0]["host"]
|
||||
host = await client.get(f"/api/vcenter/host/{host_id}", headers=headers)
|
||||
assert host.status_code == 200
|
||||
assert host.json()["name"]
|
||||
|
||||
stores = await client.get("/api/vcenter/datastore", headers=headers)
|
||||
assert stores.status_code == 200
|
||||
ds_id = stores.json()[0]["datastore"]
|
||||
detail = await client.get(f"/api/vcenter/datastore/{ds_id}", headers=headers)
|
||||
assert detail.status_code == 200
|
||||
files = await client.get(f"/api/vcenter/datastore/{ds_id}/files", headers=headers)
|
||||
assert files.status_code == 200
|
||||
assert len(files.json()) >= 1
|
||||
|
||||
|
||||
async def test_content_library_deep_get_not_stub(client: AsyncClient) -> None:
|
||||
headers = await _session(client)
|
||||
libs = await client.get("/api/content/library", headers=headers)
|
||||
assert libs.status_code == 200
|
||||
assert "lib-local-1" in libs.json()
|
||||
|
||||
local = await client.get("/api/content/local-library", headers=headers)
|
||||
assert local.status_code == 200
|
||||
assert "lib-local-1" in local.json()
|
||||
|
||||
info = await client.get("/api/content/library/lib-local-1", headers=headers)
|
||||
assert info.status_code == 200
|
||||
body = info.json()
|
||||
assert body["id"] == "lib-local-1"
|
||||
assert body["name"] == "Local Content"
|
||||
assert body["type"] == "LOCAL"
|
||||
assert "path" not in body # stub placeholder must not leak
|
||||
|
||||
local_info = await client.get("/api/content/local-library/lib-local-1", headers=headers)
|
||||
assert local_info.status_code == 200
|
||||
assert local_info.json()["id"] == "lib-local-1"
|
||||
|
||||
items = await client.get(
|
||||
"/api/content/library/item",
|
||||
params={"library_id": "lib-local-1"},
|
||||
headers=headers,
|
||||
)
|
||||
assert items.status_code == 200
|
||||
assert "item-ubuntu" in items.json()
|
||||
|
||||
item = await client.get("/api/content/library/item/item-ubuntu", headers=headers)
|
||||
assert item.status_code == 200
|
||||
assert item.json()["name"] == "ubuntu-22.04"
|
||||
assert item.json()["library_id"] == "lib-local-1"
|
||||
|
||||
|
||||
async def test_power_task_poll_and_soap_consistency(client: AsyncClient) -> None:
|
||||
headers = await _session(client)
|
||||
power = await client.post(
|
||||
"/api/vcenter/vm/vm-104/power",
|
||||
params={"action": "start"},
|
||||
headers=headers,
|
||||
)
|
||||
assert power.status_code == 200
|
||||
task_id = power.json()["task"]
|
||||
assert task_id.startswith("task-")
|
||||
|
||||
task = await client.get(f"/api/cis/tasks/{task_id}", headers=headers)
|
||||
assert task.status_code == 200
|
||||
payload = task.json()
|
||||
assert payload["status"] == "SUCCEEDED"
|
||||
assert payload["state"] == "SUCCEEDED"
|
||||
assert isinstance(payload["description"], dict)
|
||||
assert payload["description"]["default_message"]
|
||||
assert isinstance(payload["progress"], dict)
|
||||
assert payload["progress"]["completed"] == 100
|
||||
assert payload["result"]["vm"] == "vm-104"
|
||||
|
||||
listed = await client.post(
|
||||
"/api/cis/tasks",
|
||||
params={"action": "list"},
|
||||
headers=headers,
|
||||
json={"filter_spec": {"tasks": [task_id]}},
|
||||
)
|
||||
assert listed.status_code == 200
|
||||
assert task_id in listed.json()
|
||||
|
||||
state = await client.get("/api/vcenter/vm/vm-104/power", headers=headers)
|
||||
assert state.json()["state"] == "POWERED_ON"
|
||||
|
||||
soap = await client.post(
|
||||
"/sdk",
|
||||
content="""<?xml version="1.0"?>
|
||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<soapenv:Body>
|
||||
<Login xmlns="urn:vim25">
|
||||
<_this type="SessionManager">SessionManager</_this>
|
||||
<userName>administrator@vsphere.local</userName>
|
||||
<password>VMware1!</password>
|
||||
</Login>
|
||||
</soapenv:Body>
|
||||
</soapenv:Envelope>""",
|
||||
headers={"Content-Type": "text/xml"},
|
||||
)
|
||||
assert soap.status_code == 200
|
||||
cookie = (soap.headers.get("set-cookie") or "").split(";")[0]
|
||||
power_soap = await client.post(
|
||||
"/sdk",
|
||||
content="""<?xml version="1.0"?>
|
||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<soapenv:Body>
|
||||
<PowerOffVM_Task xmlns="urn:vim25">
|
||||
<_this type="VirtualMachine">vm-104</_this>
|
||||
</PowerOffVM_Task>
|
||||
</soapenv:Body>
|
||||
</soapenv:Envelope>""",
|
||||
headers={"Content-Type": "text/xml", "Cookie": cookie},
|
||||
)
|
||||
assert power_soap.status_code == 200
|
||||
assert "task-" in power_soap.text
|
||||
assert 'type="Task"' in power_soap.text
|
||||
|
||||
|
||||
async def test_tagging_association_query_action(client: AsyncClient) -> None:
|
||||
headers = await _session(client)
|
||||
tags = await client.get("/api/cis/tagging/tag", headers=headers)
|
||||
assert tags.status_code == 200
|
||||
tag_id = tags.json()[0]
|
||||
attach = await client.post(
|
||||
"/api/cis/tagging/tag-association",
|
||||
params={"action": "attach"},
|
||||
headers=headers,
|
||||
json={"tag_id": tag_id, "object_id": {"type": "VirtualMachine", "id": "vm-103"}},
|
||||
)
|
||||
assert attach.status_code == 204
|
||||
listed = await client.post(
|
||||
"/api/cis/tagging/tag-association",
|
||||
params={"action": "list-attached-tags"},
|
||||
headers=headers,
|
||||
json={"object_id": {"type": "VirtualMachine", "id": "vm-103"}},
|
||||
)
|
||||
assert listed.status_code == 200
|
||||
assert tag_id in listed.json()
|
||||
|
||||
|
||||
async def test_folder_create_and_authz_permissions(client: AsyncClient) -> None:
|
||||
headers = await _session(client)
|
||||
folder = await client.post(
|
||||
"/api/vcenter/folder",
|
||||
headers=headers,
|
||||
json={"name": "deep-repro-folder", "type": "VIRTUAL_MACHINE", "parent": "group-v23"},
|
||||
)
|
||||
assert folder.status_code == 200
|
||||
assert folder.json().startswith("group-")
|
||||
|
||||
roles = await client.get("/api/vcenter/authorization/roles", headers=headers)
|
||||
assert roles.status_code == 200
|
||||
assert any(r["role"] == "Administrator" for r in roles.json())
|
||||
perms = await client.get("/api/vcenter/authorization/permissions", headers=headers)
|
||||
assert perms.status_code == 200
|
||||
assert len(perms.json()) >= 1
|
||||
|
||||
|
||||
async def test_ovf_deploy_accepts_official_target_fields(client: AsyncClient) -> None:
|
||||
headers = await _session(client)
|
||||
deploy = await client.post(
|
||||
"/api/vcenter/ovf/library-item/item-ubuntu",
|
||||
headers=headers,
|
||||
json={
|
||||
"target": {
|
||||
"resource_pool_id": "resgroup-22",
|
||||
"folder_id": "group-v23",
|
||||
"host_id": "host-11",
|
||||
"datastore_id": "datastore-31",
|
||||
},
|
||||
"deployment_spec": {"name": "ovf-deep-repro", "accept_all_EULA": True},
|
||||
},
|
||||
)
|
||||
assert deploy.status_code == 200
|
||||
body = deploy.json()
|
||||
assert body["resource_id"]["type"] == "VirtualMachine"
|
||||
assert body["resource_id"]["id"].startswith("vm-")
|
||||
assert body["task"].startswith("task-")
|
||||
vm = await client.get(f"/api/vcenter/vm/{body['resource_id']['id']}", headers=headers)
|
||||
assert vm.status_code == 200
|
||||
assert vm.json()["name"] == "ovf-deep-repro"
|
||||
Reference in New Issue
Block a user