from __future__ import annotations from ariadne.services import cluster_state_fetchers as fetchers from ariadne.services import cluster_state_flux_events as flux_events from ariadne.services import cluster_state_nodes as nodes from ariadne.services import cluster_state_pods as pods from ariadne.services import cluster_state_workloads as workloads def test_node_summary_inventory_and_hardware_usage() -> None: payload = { "items": [ { "metadata": { "name": "titan-1", "labels": {"kubernetes.io/arch": "arm64", "hardware": "rpi5"}, "creationTimestamp": "2026-01-01T00:00:00Z", }, "spec": {"unschedulable": True, "taints": [{"key": "dedicated", "effect": "NoSchedule"}]}, "status": { "conditions": [ {"type": "Ready", "status": "True"}, {"type": "DiskPressure", "status": "True", "reason": "Full", "message": "disk full"}, ], "capacity": {"cpu": "4", "memory": "8Gi", "pods": "110"}, "allocatable": {"cpu": "3", "memory": "7Gi"}, "nodeInfo": {"architecture": "arm64"}, "addresses": [{"type": "InternalIP", "address": "10.0.0.1"}], }, }, {"metadata": {"name": "titan-2", "labels": {}}, "status": {"conditions": []}}, ] } summary = nodes._summarize_nodes(payload) assert summary["total"] == 2 assert summary["not_ready"] == 1 details = nodes._node_details(payload) assert details[0]["pressure"]["DiskPressure"] is True assert details[0]["taints"][0]["key"] == "dedicated" assert nodes._node_labels({"node-role.kubernetes.io/control-plane": "", "other": "skip"}) assert nodes._node_addresses({"addresses": [{"type": "Hostname", "address": "titan-1"}]}) == {"Hostname": "titan-1"} assert nodes._node_capacity({"cpu": "4", "unknown": "skip"}) == {"cpu": "4"} assert nodes._node_pressure_conditions([{"type": "PIDPressure", "status": "True"}])["PIDPressure"] is True assert nodes._node_roles({"node-role.kubernetes.io/control-plane": ""}) == ["control-plane"] assert nodes._node_is_worker({"node-role.kubernetes.io/control-plane": ""}) is False assert nodes._hardware_hint({"jetson": "true"}, {"architecture": "arm64"}) == "jetson" assert nodes._condition_status([], "Ready") == (None, "", "") assert nodes._age_hours("not-a-date") is None assert nodes._node_age_stats(details)["oldest"] assert nodes._node_flagged(details, "unschedulable") == ["titan-1"] assert nodes._summarize_inventory(details)["unschedulable_nodes"] == ["titan-1"] assert nodes._hardware_groups(details)[0]["hardware"] assert nodes._pressure_summary(nodes._summarize_inventory(details))["total"] == 1 usage = nodes._node_usage_by_hardware([{"node": "titan-1", "cpu": 80.0, "load_index": 0.5}], details) assert usage[0]["hardware"] == "rpi5" def test_flux_and_event_summaries() -> None: flux_payload = { "items": [ { "metadata": {"name": "apps", "namespace": "flux-system"}, "spec": {"suspend": False}, "status": {"conditions": [{"type": "Ready", "status": "True"}]}, }, { "metadata": {"name": "broken", "namespace": "flux-system"}, "spec": {"suspend": True}, "status": {"conditions": [{"type": "Ready", "status": "False", "reason": "Bad", "message": "no"}]}, }, ] } assert flux_events._summarize_kustomizations(flux_payload)["not_ready"] == 1 assert flux_events._namespace_allowed("apps") is True assert flux_events._namespace_allowed("kube-system") is False assert flux_events._event_sort_key("bad") == 0.0 events = flux_events._summarize_events( { "items": [ { "metadata": {"namespace": "apps"}, "type": "Warning", "reason": "BackOff", "message": "retry", "count": 2, "lastTimestamp": "2026-01-01T00:00:00Z", "involvedObject": {"kind": "Pod", "name": "api"}, }, {"metadata": {"namespace": "kube-system"}, "type": "Warning", "reason": "Ignored"}, {"metadata": {"namespace": "apps"}, "type": "Normal"}, ] } ) assert events["warnings_total"] == 1 assert events["warnings_top_reason"]["reason"] == "BackOff" def test_pod_summaries_and_issue_detection() -> None: payload = { "items": [ { "metadata": { "name": "api-1", "namespace": "apps", "labels": {"app": "api"}, "creationTimestamp": "2026-01-01T00:00:00Z", }, "spec": {"nodeName": "titan-1"}, "status": { "phase": "Pending", "reason": "Unschedulable", "containerStatuses": [ {"restartCount": 2, "state": {"waiting": {"reason": "CrashLoopBackOff"}}} ], }, }, { "metadata": { "name": "worker-1", "namespace": "apps", "ownerReferences": [{"name": "worker", "kind": "ReplicaSet"}], }, "spec": {"nodeName": "titan-2"}, "status": {"phase": "Running"}, }, ] } assert pods._workload_from_labels({"app": "api"}) == ("api", "label:app") assert pods._owner_reference({"ownerReferences": [{"name": "rs", "kind": "ReplicaSet"}]}) == ("rs", "owner:ReplicaSet") assert pods._pod_workload({"labels": {}, "ownerReferences": [{"name": "rs"}]})[0] == "rs" assert pods._summarize_workloads(payload)[0]["workload"] == "api" assert pods._summarize_namespace_pods(payload)[0]["pods_total"] == 2 assert pods._summarize_namespace_nodes(payload)[0]["primary_node"] node_pods = pods._summarize_node_pods(payload) assert pods._node_pods_top(node_pods)[0]["node"] == "titan-1" issues = pods._summarize_pod_issues(payload) assert issues["counts"]["Pending"] == 1 assert issues["waiting_reasons"]["CrashLoopBackOff"] == 1 def test_workload_job_longhorn_and_fetch_summaries(monkeypatch) -> None: jobs = workloads._summarize_jobs( { "items": [ { "metadata": {"name": "backup", "namespace": "apps", "creationTimestamp": "2026-01-01T00:00:00Z"}, "status": {"failed": 1, "succeeded": 0, "active": 1}, } ] } ) assert jobs["totals"]["failed"] == 1 deployments = workloads._summarize_deployments( {"items": [{"metadata": {"name": "api", "namespace": "apps"}, "spec": {"replicas": 2}, "status": {"readyReplicas": 1}}]} ) statefulsets = workloads._summarize_statefulsets( {"items": [{"metadata": {"name": "db", "namespace": "apps"}, "spec": {"replicas": 2}, "status": {"readyReplicas": 1}}]} ) daemonsets = workloads._summarize_daemonsets( {"items": [{"metadata": {"name": "agent", "namespace": "apps"}, "status": {"desiredNumberScheduled": 2, "numberReady": 1}}]} ) health = workloads._summarize_workload_health(deployments, statefulsets, daemonsets) assert health["deployments"]["not_ready"] == 1 longhorn = workloads._summarize_longhorn_volumes( { "items": [ { "metadata": {"name": "pvc-data"}, "spec": {"size": "1Gi"}, "status": {"state": "attached", "robustness": "degraded", "actualSize": "500Mi"}, } ] } ) assert longhorn["degraded_count"] == 1 def fake_get_json(path: str): if path.endswith("/nodes"): return {"items": []} if path.startswith("/api/v1/pods"): return {"items": []} if path.startswith("/apis/batch"): return {"items": []} if "longhorn" in path: return {"items": []} if "deployments" in path or "statefulsets" in path or "daemonsets" in path: return {"items": []} if path.startswith("/api/v1/events"): return {"items": []} return {"items": []} monkeypatch.setattr(fetchers, "_get_json", fake_get_json) errors: list[str] = [] assert fetchers._fetch_nodes(errors)[0]["total"] == 0 assert fetchers._fetch_flux(errors)["not_ready"] == 0 assert fetchers._fetch_pods(errors)[0] == [] assert fetchers._fetch_jobs(errors)["totals"]["total"] == 0 assert fetchers._fetch_longhorn(errors) == {} assert fetchers._fetch_workload_health(errors)["deployments"]["total"] == 0 assert fetchers._fetch_events(errors)["warnings_total"] == 0 assert errors == [] monkeypatch.setattr(fetchers, "_get_json", lambda _path: (_ for _ in ()).throw(RuntimeError("boom"))) errors = [] assert fetchers._fetch_jobs(errors) == {} assert errors == ["jobs: boom"]