Initial commit: add Gitea PyPI publish workflow

This commit is contained in:
2026-07-06 11:02:19 -05:00
commit b0d2873cbc
40 changed files with 5831 additions and 0 deletions

242
tests/test_compute.py Normal file
View File

@@ -0,0 +1,242 @@
"""Tests for the Proxmox compute client."""
from __future__ import annotations
import json
import pytest
import respx
from httpx import Response
from conduit_client import AsyncConduitClient, ConduitClient
from conduit_client.models.compute import (
CloneVmRequest,
ComputeTicketRequest,
CreateContainerRequest,
CreateSnapshotRequest,
CreateVmRequest,
MigrateVmRequest,
NodeTasksQuery,
StorageQuery,
UploadStorageRequest,
)
@respx.mock
def test_auth_ticket(api_key: str) -> None:
route = respx.post("https://api.example.com/compute/auth/ticket").mock(
return_value=Response(
200,
json={
"ticket": "tck",
"CSRFPreventionToken": "csrf",
"username": "root",
},
)
)
with ConduitClient(api_key, base_url="https://api.example.com") as client:
result = client.compute.auth_ticket(
ComputeTicketRequest(username="root", password="pw")
)
assert result.ticket == "tck"
assert result.csrfpreventiontoken == "csrf"
assert json.loads(route.calls[0].request.content) == {
"username": "root",
"password": "pw",
}
@respx.mock
def test_list_resources_and_nodes(api_key: str) -> None:
respx.get("https://api.example.com/compute/resources").mock(
return_value=Response(200, json=[{"id": "qemu/100"}])
)
respx.get("https://api.example.com/compute/resources").mock(
return_value=Response(200, json=[{"id": "qemu/100"}])
)
respx.get("https://api.example.com/compute/cluster/status").mock(
return_value=Response(200, json={"quorate": True})
)
respx.get("https://api.example.com/compute/nodes").mock(
return_value=Response(200, json=[{"node": "pve"}])
)
with ConduitClient(api_key, base_url="https://api.example.com") as client:
assert client.compute.list_resources() == [{"id": "qemu/100"}]
assert client.compute.list_resources(type_="vm") == [{"id": "qemu/100"}]
assert client.compute.cluster_status() == {"quorate": True}
assert client.compute.list_nodes() == [{"node": "pve"}]
@respx.mock
def test_node_status_and_tasks(api_key: str) -> None:
respx.get("https://api.example.com/compute/nodes/pve/status").mock(
return_value=Response(200, json={"cpu": 0.1})
)
route = respx.get("https://api.example.com/compute/nodes/pve/tasks").mock(
return_value=Response(200, json=[])
)
with ConduitClient(api_key, base_url="https://api.example.com") as client:
assert client.compute.get_node_status("pve") == {"cpu": 0.1}
assert client.compute.list_node_tasks("pve", NodeTasksQuery(limit=10)) == []
assert route.calls[0].request.url.params["limit"] == "10"
@respx.mock
def test_vm_lifecycle(api_key: str) -> None:
respx.get("https://api.example.com/compute/vms").mock(
return_value=Response(200, json=[{"vmid": 100}])
)
create_route = respx.post("https://api.example.com/compute/vms").mock(
return_value=Response(200, json={"task": "UPID:abc"})
)
respx.get("https://api.example.com/compute/vms/100").mock(
return_value=Response(200, json={"vmid": 100, "name": "vm1"})
)
patch_route = respx.patch("https://api.example.com/compute/vms/100").mock(
return_value=Response(200, json={"task": "UPID:patch"})
)
respx.delete("https://api.example.com/compute/vms/100").mock(
return_value=Response(204)
)
respx.post("https://api.example.com/compute/vms/100/start").mock(
return_value=Response(200, json={"task": "UPID:start"})
)
respx.post("https://api.example.com/compute/vms/100/stop").mock(
return_value=Response(200, json={"task": "UPID:stop"})
)
respx.post("https://api.example.com/compute/vms/100/shutdown").mock(
return_value=Response(200, json={"task": "UPID:shutdown"})
)
respx.post("https://api.example.com/compute/vms/100/reboot").mock(
return_value=Response(200, json={"task": "UPID:reboot"})
)
with ConduitClient(api_key, base_url="https://api.example.com") as client:
assert client.compute.list_vms() == [{"vmid": 100}]
result = client.compute.create_vm(
CreateVmRequest(node="pve", vmid=100, cores=2, memory=1024)
)
assert result.task == "UPID:abc"
assert client.compute.get_vm(100)["name"] == "vm1"
assert client.compute.patch_vm(100, {"memory": 2048}).task == "UPID:patch"
client.compute.delete_vm(100, confirm=100)
assert client.compute.start_vm(100).task == "UPID:start"
assert client.compute.stop_vm(100).task == "UPID:stop"
assert client.compute.shutdown_vm(100).task == "UPID:shutdown"
assert client.compute.reboot_vm(100).task == "UPID:reboot"
body = json.loads(create_route.calls[0].request.content)
assert body["node"] == "pve"
assert body["cores"] == 2
assert json.loads(patch_route.calls[0].request.content) == {"memory": 2048}
@respx.mock
def test_vm_clone_migrate_snapshots_agent(api_key: str) -> None:
respx.post("https://api.example.com/compute/vms/100/clone").mock(
return_value=Response(200, json={"task": "UPID:clone"})
)
respx.post("https://api.example.com/compute/vms/100/migrate").mock(
return_value=Response(200, json={"task": "UPID:migrate"})
)
respx.get("https://api.example.com/compute/vms/100/snapshots").mock(
return_value=Response(200, json=[{"name": "snap1"}])
)
respx.post("https://api.example.com/compute/vms/100/snapshots").mock(
return_value=Response(200, json={"task": "UPID:snap"})
)
respx.post("https://api.example.com/compute/vms/100/agent/exec").mock(
return_value=Response(200, json={"out-data": "ok"})
)
with ConduitClient(api_key, base_url="https://api.example.com") as client:
assert client.compute.clone_vm(100, CloneVmRequest(newid=200)).task == "UPID:clone"
assert client.compute.migrate_vm(
100, MigrateVmRequest(target="pve2", online=True)
).task == "UPID:migrate"
assert client.compute.list_snapshots(100) == [{"name": "snap1"}]
assert client.compute.create_snapshot(
100, CreateSnapshotRequest(snapname="snap2")
).task == "UPID:snap"
assert client.compute.agent_action(100, "exec", {"command": "ls"}) == {"out-data": "ok"}
@respx.mock
def test_containers(api_key: str) -> None:
create_route = respx.post("https://api.example.com/compute/containers").mock(
return_value=Response(200, json={"task": "UPID:ct"})
)
respx.post("https://api.example.com/compute/containers/200/start").mock(
return_value=Response(200, json={"task": "UPID:start"})
)
respx.post("https://api.example.com/compute/containers/200/stop").mock(
return_value=Response(200, json={"task": "UPID:stop"})
)
with ConduitClient(api_key, base_url="https://api.example.com") as client:
result = client.compute.create_container(
CreateContainerRequest(node="pve", ostemplate="local:vztmpl/debian.tar.gz", vmid=200)
)
assert result.task == "UPID:ct"
assert client.compute.start_container(200).task == "UPID:start"
assert client.compute.stop_container(200).task == "UPID:stop"
body = json.loads(create_route.calls[0].request.content)
assert body["node"] == "pve"
assert body["ostemplate"] == "local:vztmpl/debian.tar.gz"
@respx.mock
def test_storage(api_key: str) -> None:
respx.get("https://api.example.com/compute/storage").mock(
return_value=Response(200, json=[{"storage": "local"}])
)
respx.get("https://api.example.com/compute/storage/local/content").mock(
return_value=Response(200, json=[{"volid": "local:iso/img.iso"}])
)
upload_route = respx.post("https://api.example.com/compute/storage/local/upload").mock(
return_value=Response(200, json={"task": "UPID:upload"})
)
with ConduitClient(api_key, base_url="https://api.example.com") as client:
assert client.compute.list_storage(StorageQuery(node="pve")) == [{"storage": "local"}]
assert client.compute.list_storage_content("local", StorageQuery(node="pve")) == [
{"volid": "local:iso/img.iso"}
]
result = client.compute.upload_to_storage(
"local",
UploadStorageRequest(content="abc", filename="img.iso"),
node="pve",
)
assert result.task == "UPID:upload"
assert upload_route.calls[0].request.url.params["node"] == "pve"
@respx.mock
def test_backup_jobs_and_misc(api_key: str) -> None:
respx.get("https://api.example.com/compute/backups/jobs").mock(
return_value=Response(200, json=[])
)
respx.post("https://api.example.com/compute/backups/jobs").mock(
return_value=Response(200, json={"task": "UPID:backup"})
)
respx.get("https://api.example.com/compute/ha/resources").mock(
return_value=Response(200, json=[])
)
respx.get("https://api.example.com/compute/access/users").mock(
return_value=Response(200, json=[])
)
respx.get("https://api.example.com/compute/access/acl").mock(
return_value=Response(200, json=[])
)
with ConduitClient(api_key, base_url="https://api.example.com") as client:
assert client.compute.list_backup_jobs() == []
assert client.compute.create_backup_job({"id": "job1"}).task == "UPID:backup"
assert client.compute.list_ha_resources() == []
assert client.compute.list_access_users() == []
assert client.compute.list_access_acl() == []
@pytest.mark.asyncio
@respx.mock
async def test_async_start_vm(api_key: str) -> None:
respx.post("https://api.example.com/compute/vms/100/start").mock(
return_value=Response(200, json={"task": "UPID:start"})
)
async with AsyncConduitClient(api_key, base_url="https://api.example.com") as client:
result = await client.compute.start_vm(100)
assert result.task == "UPID:start"