import pytest from app.services.providers import proxmox from app.services.providers.base import ProviderConnection from app.services.providers.proxmox import ProxmoxProvider class FakeResponse: def __init__(self, data: object) -> None: self._data = data def json(self) -> dict: return {"data": self._data} def raise_for_status(self) -> None: return None class FakeAsyncClient: deleted_urls: list[str] = [] posted_payloads: list[dict] = [] put_urls: list[str] = [] put_payloads: list[dict] = [] def __init__(self, **_: object) -> None: return None async def __aenter__(self) -> "FakeAsyncClient": return self async def __aexit__(self, *_: object) -> None: return None async def get(self, url: str, **__: object) -> FakeResponse: if url.endswith("/config"): return FakeResponse({"net0": "virtio=AA:BB:CC:DD:EE:FF,bridge=vmbr0,firewall=0", "name": "web"}) if url.endswith("/cluster/firewall/options"): return FakeResponse({"enable": 1}) if url.endswith("/firewall/options"): return FakeResponse({"enable": 1}) return FakeResponse( [ {"pos": 0, "comment": "manual rule"}, {"pos": 1, "comment": "NexaFabric policy=policy-1 version=1 rule=1 target=web"}, ] ) async def delete(self, url: str, **__: object) -> FakeResponse: self.deleted_urls.append(url) return FakeResponse(None) async def put(self, url: str, data: dict, **__: object) -> FakeResponse: self.put_urls.append(url) self.put_payloads.append(data) return FakeResponse(None) async def post(self, _: str, data: dict, **__: object) -> FakeResponse: self.posted_payloads.append(data) return FakeResponse({"pos": 1}) @pytest.mark.asyncio async def test_apply_rules_replaces_only_marked_nexafabric_rules(monkeypatch: pytest.MonkeyPatch) -> None: FakeAsyncClient.deleted_urls = [] FakeAsyncClient.posted_payloads = [] FakeAsyncClient.put_urls = [] FakeAsyncClient.put_payloads = [] monkeypatch.setattr(proxmox.httpx, "AsyncClient", FakeAsyncClient) result = await ProxmoxProvider().apply_rules( ProviderConnection(api_url="https://pve.example:8006", token="user@pve!token=secret", read_only=False), [ { "policy_id": "policy-1", "audit_only": False, "provider_target": {"node": "pve1", "kind": "qemu", "vmid": "100"}, "provider_rule": { "type": "in", "action": "ACCEPT", "enable": 1, "proto": "tcp", "dport": "443", "comment": "NexaFabric policy=policy-1 version=2 rule=1 target=web", }, } ], ) assert result["applied"] is True assert result["rules_deleted"] == 1 assert FakeAsyncClient.deleted_urls == ["https://pve.example:8006/api2/json/nodes/pve1/qemu/100/firewall/rules/1"] assert FakeAsyncClient.put_urls == [ "https://pve.example:8006/api2/json/nodes/pve1/qemu/100/firewall/options", "https://pve.example:8006/api2/json/nodes/pve1/qemu/100/config", ] assert FakeAsyncClient.put_payloads == [ {"enable": 1}, {"net0": "virtio=AA:BB:CC:DD:EE:FF,bridge=vmbr0,firewall=1"}, ] assert FakeAsyncClient.posted_payloads == [ { "type": "in", "action": "ACCEPT", "enable": 1, "proto": "tcp", "dport": "443", "comment": "NexaFabric policy=policy-1 version=2 rule=1 target=web", } ] @pytest.mark.asyncio async def test_delete_policy_rules_removes_marked_rules(monkeypatch: pytest.MonkeyPatch) -> None: FakeAsyncClient.deleted_urls = [] FakeAsyncClient.posted_payloads = [] FakeAsyncClient.put_urls = [] FakeAsyncClient.put_payloads = [] monkeypatch.setattr(proxmox.httpx, "AsyncClient", FakeAsyncClient) result = await ProxmoxProvider().delete_policy_rules( ProviderConnection(api_url="https://pve.example:8006", token="user@pve!token=secret", read_only=False), [{"node": "pve1", "kind": "qemu", "vmid": "100"}], "policy-1", ) assert result["applied"] is True assert result["rules_deleted"] == 1 assert FakeAsyncClient.deleted_urls == ["https://pve.example:8006/api2/json/nodes/pve1/qemu/100/firewall/rules/1"] assert FakeAsyncClient.posted_payloads == []