Files
inecs f8d3cbdd59 Initial commit: VMware vSphere API simulator scaffold.
Add the FastAPI app, PostgreSQL migrations, Docker/Helm packaging, API
contracts, docs, client examples, and the unit/integration/compatibility
test suite for local client and tooling labs without a real vCenter.
2026-07-18 04:42:11 +03:00

156 lines
5.3 KiB
Python

"""Persistent LXC semantic handler tests."""
import uuid
from datetime import UTC, datetime
from typing import Any
import pytest
from fastapi import Request
from app.api.errors import ApiError
from app.api.registry import HandlerRegistry
from app.handlers.lxc import register_lxc_handlers
class LxcPool:
def __init__(self) -> None:
self.resource_exists = False
self.missing = False
self.running = False
self.commands: list[str] = []
self.resource_id = uuid.uuid4()
async def fetchval(self, sql: str, *args: object) -> bool | int:
del args
if "pg_backend_pid" in sql:
return 123
if "extract(epoch" in sql:
return 1_700_000_000
if "FROM nodes" in sql:
return True
if "FROM resources" in sql:
return self.resource_exists
if "FROM snapshots" in sql:
return False
raise AssertionError(sql)
async def fetch(self, sql: str, *args: object) -> list[dict[str, object]]:
del args
if "FROM resources" in sql and "kind='lxc'" in sql:
return [{"vmid": 200, "state": '{"status":"stopped","name":"service"}'}]
assert "FROM snapshots" in sql
return [
{
"name": "baseline",
"parent_name": None,
"description": "stable",
"created_at": datetime(2026, 1, 1, tzinfo=UTC),
}
]
async def fetchrow(self, sql: str, *args: object) -> dict[str, object] | None:
del args
if self.missing:
return None
if "SELECT r.id, r.version" in sql:
return {
"id": self.resource_id,
"version": 1,
"state": '{"name":"old","status":"stopped"}',
"config": '{"name":"old"}',
}
if "SELECT r.id, r.state, c.config" in sql:
return {"id": self.resource_id, "state": '{"status":"stopped"}', "config": "{}"}
if "SELECT r.id, r.state FROM resources" in sql:
status = "running" if self.running else "stopped"
return {"id": self.resource_id, "state": f'{{"status":"{status}"}}'}
if "SELECT s.* FROM snapshots" in sql:
return {
"id": uuid.uuid4(),
"name": "baseline",
"parent_name": None,
"description": "stable",
"created_at": datetime(2026, 1, 1, tzinfo=UTC),
"state": "{}",
}
raise AssertionError(sql)
async def execute(self, sql: str, *args: object) -> str:
del sql, args
self.commands.append("execute")
return "UPDATE 1"
class FakeDatabase:
def __init__(self, pool: LxcPool) -> None:
self.pool = pool
class FakeTaskRepository:
def __init__(self, pool: LxcPool) -> None:
self.pool = pool
self.created: list[dict[str, Any]] = []
async def create(self, **kwargs: Any) -> Any:
self.created.append(kwargs)
return type(
"Task", (), {"upid": "UPID:pve1:00000001:00000001:1700000000:pctcreate:201:root@pam:"}
)()
def _request(pool: LxcPool) -> Request:
app = type("App", (), {"state": type("State", (), {"database": FakeDatabase(pool)})()})()
request = Request({"type": "http", "headers": [], "method": "POST", "path": "/"})
request.scope["app"] = app
request.state.principal = "root@pam"
return request
@pytest.fixture
def registry() -> HandlerRegistry:
handler_registry = HandlerRegistry()
register_lxc_handlers(handler_registry)
return handler_registry
async def test_lxc_list_returns_seeded_containers(registry: HandlerRegistry) -> None:
pool = LxcPool()
handler = registry.get("/nodes/{node}/lxc", "GET")
assert handler is not None
result = await handler(_request(pool), {"values": {"node": "pve1"}})
assert result == [{"vmid": 200, "status": "stopped", "name": "service"}]
async def test_lxc_create_rejects_duplicate_vmid(registry: HandlerRegistry) -> None:
pool = LxcPool()
pool.resource_exists = True
handler = registry.get("/nodes/{node}/lxc", "POST")
assert handler is not None
with pytest.raises(ApiError, match="VMID already exists"):
await handler(
_request(pool),
{"values": {"node": "pve1", "vmid": 201, "hostname": "app"}},
)
async def test_lxc_delete_requires_stopped_container(registry: HandlerRegistry) -> None:
pool = LxcPool()
pool.running = True
handler = registry.get("/nodes/{node}/lxc/{vmid}", "DELETE")
assert handler is not None
with pytest.raises(ApiError, match="cannot delete a running container"):
await handler(_request(pool), {"values": {"node": "pve1", "vmid": "200"}})
async def test_lxc_start_creates_task(
monkeypatch: pytest.MonkeyPatch, registry: HandlerRegistry
) -> None:
pool = LxcPool()
repository = FakeTaskRepository(pool)
monkeypatch.setattr("app.handlers.lxc.TaskRepository", lambda _pool: repository)
handler = registry.get("/nodes/{node}/lxc/{vmid}/status/start", "POST")
assert handler is not None
upid = await handler(_request(pool), {"values": {"node": "pve1", "vmid": "200"}})
assert upid.startswith("UPID:")
assert repository.created[0]["task_type"] == "lxc-start"