kubectl-mcp-server 1.15.0__py3-none-any.whl → 1.16.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tests/test_browser.py CHANGED
@@ -501,11 +501,11 @@ class TestServerIntegration:
501
501
  tools = asyncio.run(server.server.list_tools())
502
502
  tool_names = [t.name for t in tools]
503
503
 
504
- # Should have browser tools (131 + 26 = 157)
504
+ # Should have browser tools (224 + 26 = 250)
505
505
  assert "browser_open" in tool_names
506
506
  assert "browser_screenshot" in tool_names
507
507
  assert "browser_connect_cdp" in tool_names # v0.7 tool
508
- assert len(tools) == 157, f"Expected 157 tools (131 + 26), got {len(tools)}"
508
+ assert len(tools) == 250, f"Expected 250 tools (224 + 26), got {len(tools)}"
509
509
 
510
510
 
511
511
  import asyncio
@@ -0,0 +1,331 @@
1
+ """
2
+ Unit tests for ecosystem tools (GitOps, Cert-Manager, Policy, Backup).
3
+
4
+ This module tests the CRD detector and all ecosystem toolsets.
5
+ """
6
+
7
+ import pytest
8
+ from unittest.mock import patch, MagicMock
9
+ import subprocess
10
+
11
+
12
+ class TestCRDDetector:
13
+ """Tests for the CRD auto-discovery framework."""
14
+
15
+ @pytest.mark.unit
16
+ def test_crd_detector_imports(self):
17
+ """Test that CRD detector module can be imported."""
18
+ from kubectl_mcp_tool.crd_detector import (
19
+ CRD_GROUPS,
20
+ detect_crds,
21
+ crd_exists,
22
+ get_enabled_toolsets,
23
+ get_crd_status_summary,
24
+ FeatureNotInstalledError,
25
+ require_crd,
26
+ require_any_crd,
27
+ )
28
+ assert CRD_GROUPS is not None
29
+ assert callable(detect_crds)
30
+ assert callable(crd_exists)
31
+ assert callable(get_enabled_toolsets)
32
+ assert callable(get_crd_status_summary)
33
+ assert issubclass(FeatureNotInstalledError, Exception)
34
+ assert callable(require_crd)
35
+ assert callable(require_any_crd)
36
+
37
+ @pytest.mark.unit
38
+ def test_crd_groups_structure(self):
39
+ """Test that CRD_GROUPS has expected structure."""
40
+ from kubectl_mcp_tool.crd_detector import CRD_GROUPS
41
+
42
+ expected_groups = ["flux", "argocd", "certmanager", "kyverno", "gatekeeper", "velero"]
43
+ for group in expected_groups:
44
+ assert group in CRD_GROUPS, f"Expected group '{group}' in CRD_GROUPS"
45
+ assert isinstance(CRD_GROUPS[group], list), f"CRD_GROUPS['{group}'] should be a list"
46
+ assert len(CRD_GROUPS[group]) > 0, f"CRD_GROUPS['{group}'] should not be empty"
47
+
48
+ @pytest.mark.unit
49
+ def test_detect_crds_with_mocked_kubectl(self):
50
+ """Test CRD detection with mocked kubectl."""
51
+ from kubectl_mcp_tool.crd_detector import detect_crds, _crd_cache
52
+
53
+ # Clear cache before test
54
+ _crd_cache.clear()
55
+
56
+ mock_output = """NAME CREATED AT
57
+ applications.argoproj.io 2024-01-01T00:00:00Z
58
+ certificates.cert-manager.io 2024-01-01T00:00:00Z
59
+ """
60
+ with patch("subprocess.run") as mock_run:
61
+ mock_run.return_value = MagicMock(
62
+ returncode=0,
63
+ stdout=mock_output
64
+ )
65
+ result = detect_crds(force_refresh=True)
66
+
67
+ assert isinstance(result, dict)
68
+ assert "argocd" in result
69
+ assert "certmanager" in result
70
+
71
+ @pytest.mark.unit
72
+ def test_detect_crds_handles_kubectl_failure(self):
73
+ """Test CRD detection handles kubectl failure gracefully."""
74
+ from kubectl_mcp_tool.crd_detector import detect_crds, _crd_cache
75
+
76
+ _crd_cache.clear()
77
+
78
+ with patch("subprocess.run") as mock_run:
79
+ mock_run.side_effect = subprocess.SubprocessError("Command failed")
80
+ result = detect_crds(force_refresh=True)
81
+
82
+ assert isinstance(result, dict)
83
+ # All groups should be False when kubectl fails
84
+ for value in result.values():
85
+ assert value is False
86
+
87
+ @pytest.mark.unit
88
+ def test_feature_not_installed_error(self):
89
+ """Test FeatureNotInstalledError exception."""
90
+ from kubectl_mcp_tool.crd_detector import FeatureNotInstalledError
91
+
92
+ error = FeatureNotInstalledError("velero", ["backups.velero.io"])
93
+ assert "velero" in str(error)
94
+ assert "backups.velero.io" in str(error)
95
+ assert error.toolset == "velero"
96
+ assert "backups.velero.io" in error.required_crds
97
+
98
+ @pytest.mark.unit
99
+ def test_get_crd_status_summary(self):
100
+ """Test CRD status summary generation."""
101
+ from kubectl_mcp_tool.crd_detector import get_crd_status_summary, _crd_cache
102
+
103
+ _crd_cache.clear()
104
+
105
+ with patch("subprocess.run") as mock_run:
106
+ mock_run.return_value = MagicMock(
107
+ returncode=0,
108
+ stdout="applications.argoproj.io 2024-01-01T00:00:00Z\n"
109
+ )
110
+ summary = get_crd_status_summary()
111
+
112
+ # Summary returns a dict with crd_groups and enabled_toolsets
113
+ assert isinstance(summary, dict)
114
+ assert "crd_groups" in summary
115
+ assert "enabled_toolsets" in summary
116
+
117
+
118
+ class TestGitOpsTools:
119
+ """Tests for GitOps toolset (Flux and ArgoCD)."""
120
+
121
+ @pytest.mark.unit
122
+ def test_gitops_tools_import(self):
123
+ """Test that GitOps tools can be imported."""
124
+ from kubectl_mcp_tool.tools.gitops import register_gitops_tools
125
+ assert callable(register_gitops_tools)
126
+
127
+ @pytest.mark.unit
128
+ def test_gitops_tools_register(self, mock_all_kubernetes_apis):
129
+ """Test that GitOps tools register correctly."""
130
+ from kubectl_mcp_tool.mcp_server import MCPServer
131
+ import asyncio
132
+
133
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
134
+ server = MCPServer(name="test")
135
+
136
+ async def get_tools():
137
+ return await server.server.list_tools()
138
+
139
+ tools = asyncio.run(get_tools())
140
+ tool_names = {t.name for t in tools}
141
+
142
+ gitops_tools = [
143
+ "gitops_apps_list_tool", "gitops_app_get_tool", "gitops_app_sync_tool",
144
+ "gitops_app_status_tool", "gitops_sources_list_tool", "gitops_source_get_tool",
145
+ "gitops_detect_engine_tool"
146
+ ]
147
+ for tool in gitops_tools:
148
+ assert tool in tool_names, f"GitOps tool '{tool}' not registered"
149
+
150
+ @pytest.mark.unit
151
+ def test_gitops_non_destructive_mode(self, mock_all_kubernetes_apis):
152
+ """Test that GitOps sync is blocked in non-destructive mode."""
153
+ from kubectl_mcp_tool.mcp_server import MCPServer
154
+
155
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
156
+ server = MCPServer(name="test", non_destructive=True)
157
+
158
+ # Server should initialize with non_destructive=True
159
+ assert server.non_destructive is True
160
+
161
+
162
+ class TestCertManagerTools:
163
+ """Tests for Cert-Manager toolset."""
164
+
165
+ @pytest.mark.unit
166
+ def test_certs_tools_import(self):
167
+ """Test that Cert-Manager tools can be imported."""
168
+ from kubectl_mcp_tool.tools.certs import register_certs_tools
169
+ assert callable(register_certs_tools)
170
+
171
+ @pytest.mark.unit
172
+ def test_certs_tools_register(self, mock_all_kubernetes_apis):
173
+ """Test that Cert-Manager tools register correctly."""
174
+ from kubectl_mcp_tool.mcp_server import MCPServer
175
+ import asyncio
176
+
177
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
178
+ server = MCPServer(name="test")
179
+
180
+ async def get_tools():
181
+ return await server.server.list_tools()
182
+
183
+ tools = asyncio.run(get_tools())
184
+ tool_names = {t.name for t in tools}
185
+
186
+ certs_tools = [
187
+ "certs_list_tool", "certs_get_tool", "certs_issuers_list_tool", "certs_issuer_get_tool",
188
+ "certs_renew_tool", "certs_status_explain_tool", "certs_challenges_list_tool",
189
+ "certs_requests_list_tool", "certs_detect_tool"
190
+ ]
191
+ for tool in certs_tools:
192
+ assert tool in tool_names, f"Cert-Manager tool '{tool}' not registered"
193
+
194
+
195
+ class TestPolicyTools:
196
+ """Tests for Policy toolset (Kyverno and Gatekeeper)."""
197
+
198
+ @pytest.mark.unit
199
+ def test_policy_tools_import(self):
200
+ """Test that Policy tools can be imported."""
201
+ from kubectl_mcp_tool.tools.policy import register_policy_tools
202
+ assert callable(register_policy_tools)
203
+
204
+ @pytest.mark.unit
205
+ def test_policy_tools_register(self, mock_all_kubernetes_apis):
206
+ """Test that Policy tools register correctly."""
207
+ from kubectl_mcp_tool.mcp_server import MCPServer
208
+ import asyncio
209
+
210
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
211
+ server = MCPServer(name="test")
212
+
213
+ async def get_tools():
214
+ return await server.server.list_tools()
215
+
216
+ tools = asyncio.run(get_tools())
217
+ tool_names = {t.name for t in tools}
218
+
219
+ policy_tools = [
220
+ "policy_list_tool", "policy_get_tool", "policy_violations_list_tool",
221
+ "policy_explain_denial_tool", "policy_audit_tool", "policy_detect_tool"
222
+ ]
223
+ for tool in policy_tools:
224
+ assert tool in tool_names, f"Policy tool '{tool}' not registered"
225
+
226
+
227
+ class TestBackupTools:
228
+ """Tests for Backup toolset (Velero)."""
229
+
230
+ @pytest.mark.unit
231
+ def test_backup_tools_import(self):
232
+ """Test that Backup tools can be imported."""
233
+ from kubectl_mcp_tool.tools.backup import register_backup_tools
234
+ assert callable(register_backup_tools)
235
+
236
+ @pytest.mark.unit
237
+ def test_backup_tools_register(self, mock_all_kubernetes_apis):
238
+ """Test that Backup tools register correctly."""
239
+ from kubectl_mcp_tool.mcp_server import MCPServer
240
+ import asyncio
241
+
242
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
243
+ server = MCPServer(name="test")
244
+
245
+ async def get_tools():
246
+ return await server.server.list_tools()
247
+
248
+ tools = asyncio.run(get_tools())
249
+ tool_names = {t.name for t in tools}
250
+
251
+ backup_tools = [
252
+ "backup_list_tool", "backup_get_tool", "backup_create_tool", "backup_delete_tool",
253
+ "restore_list_tool", "restore_create_tool", "restore_get_tool",
254
+ "backup_locations_list_tool", "backup_schedules_list_tool",
255
+ "backup_schedule_create_tool", "backup_detect_tool"
256
+ ]
257
+ for tool in backup_tools:
258
+ assert tool in tool_names, f"Backup tool '{tool}' not registered"
259
+
260
+ @pytest.mark.unit
261
+ def test_backup_non_destructive_mode(self, mock_all_kubernetes_apis):
262
+ """Test that backup operations are blocked in non-destructive mode."""
263
+ from kubectl_mcp_tool.mcp_server import MCPServer
264
+
265
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
266
+ server = MCPServer(name="test", non_destructive=True)
267
+
268
+ # Server should initialize with non_destructive=True
269
+ assert server.non_destructive is True
270
+
271
+
272
+ class TestEcosystemToolsIntegration:
273
+ """Integration tests for ecosystem tools."""
274
+
275
+ @pytest.mark.unit
276
+ def test_all_ecosystem_tools_have_descriptions(self, mock_all_kubernetes_apis):
277
+ """Test that all ecosystem tools have descriptions."""
278
+ from kubectl_mcp_tool.mcp_server import MCPServer
279
+ import asyncio
280
+
281
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
282
+ server = MCPServer(name="test")
283
+
284
+ async def get_tools():
285
+ return await server.server.list_tools()
286
+
287
+ tools = asyncio.run(get_tools())
288
+
289
+ ecosystem_prefixes = ["gitops_", "certs_", "policy_", "backup_", "restore_"]
290
+ ecosystem_tools = [t for t in tools if any(t.name.startswith(p) for p in ecosystem_prefixes)]
291
+
292
+ tools_without_description = [
293
+ t.name for t in ecosystem_tools
294
+ if not t.description or len(t.description.strip()) == 0
295
+ ]
296
+ assert not tools_without_description, f"Ecosystem tools without descriptions: {tools_without_description}"
297
+
298
+ @pytest.mark.unit
299
+ def test_ecosystem_tool_count(self, mock_all_kubernetes_apis):
300
+ """Test that correct number of ecosystem tools are registered."""
301
+ from kubectl_mcp_tool.mcp_server import MCPServer
302
+ import asyncio
303
+
304
+ with patch("kubectl_mcp_tool.mcp_server.MCPServer._check_dependencies", return_value=True):
305
+ server = MCPServer(name="test")
306
+
307
+ async def get_tools():
308
+ return await server.server.list_tools()
309
+
310
+ tools = asyncio.run(get_tools())
311
+
312
+ # Filter ecosystem tools, but exclude backup_resource which is in operations.py
313
+ ecosystem_tool_names = [
314
+ "gitops_apps_list_tool", "gitops_app_get_tool", "gitops_app_sync_tool",
315
+ "gitops_app_status_tool", "gitops_sources_list_tool", "gitops_source_get_tool",
316
+ "gitops_detect_engine_tool",
317
+ "certs_list_tool", "certs_get_tool", "certs_issuers_list_tool", "certs_issuer_get_tool",
318
+ "certs_renew_tool", "certs_status_explain_tool", "certs_challenges_list_tool",
319
+ "certs_requests_list_tool", "certs_detect_tool",
320
+ "policy_list_tool", "policy_get_tool", "policy_violations_list_tool",
321
+ "policy_explain_denial_tool", "policy_audit_tool", "policy_detect_tool",
322
+ "backup_list_tool", "backup_get_tool", "backup_create_tool", "backup_delete_tool",
323
+ "restore_list_tool", "restore_create_tool", "restore_get_tool",
324
+ "backup_locations_list_tool", "backup_schedules_list_tool",
325
+ "backup_schedule_create_tool", "backup_detect_tool"
326
+ ]
327
+ tool_names = {t.name for t in tools}
328
+ ecosystem_tools = [name for name in ecosystem_tool_names if name in tool_names]
329
+
330
+ # 7 GitOps + 9 Certs + 6 Policy + 11 Backup = 33 ecosystem tools
331
+ assert len(ecosystem_tools) == 33, f"Expected 33 ecosystem tools, got {len(ecosystem_tools)}"
tests/test_tools.py CHANGED
@@ -1,8 +1,8 @@
1
1
  """
2
2
  Unit tests for all MCP tools in kubectl-mcp-server.
3
3
 
4
- This module contains comprehensive tests for all 125 Kubernetes tools
5
- provided by the MCP server (131 total with UI tools).
4
+ This module contains comprehensive tests for all Kubernetes tools
5
+ provided by the MCP server (224 total with ecosystem tools).
6
6
  """
7
7
 
8
8
  import pytest
@@ -12,7 +12,7 @@ from unittest.mock import patch, MagicMock
12
12
  from datetime import datetime
13
13
 
14
14
 
15
- # Complete list of all 131 tools that must be registered (125 core + 6 UI)
15
+ # Complete list of all 224 tools that must be registered (125 core + 6 UI + 93 ecosystem)
16
16
  EXPECTED_TOOLS = [
17
17
  # Pods (pods.py)
18
18
  "get_pods", "get_logs", "get_pod_events", "check_pod_health", "exec_in_pod",
@@ -65,15 +65,55 @@ EXPECTED_TOOLS = [
65
65
  # UI tools (ui.py) - 6 tools for MCP-UI interactive dashboards
66
66
  "show_pod_logs_ui", "show_pods_dashboard_ui", "show_resource_yaml_ui",
67
67
  "show_cluster_overview_ui", "show_events_timeline_ui", "render_k8s_dashboard_screenshot",
68
+ # GitOps tools (gitops.py) - 7 tools for Flux and ArgoCD
69
+ "gitops_apps_list_tool", "gitops_app_get_tool", "gitops_app_sync_tool", "gitops_app_status_tool",
70
+ "gitops_sources_list_tool", "gitops_source_get_tool", "gitops_detect_engine_tool",
71
+ # Cert-Manager tools (certs.py) - 9 tools
72
+ "certs_list_tool", "certs_get_tool", "certs_issuers_list_tool", "certs_issuer_get_tool",
73
+ "certs_renew_tool", "certs_status_explain_tool", "certs_challenges_list_tool",
74
+ "certs_requests_list_tool", "certs_detect_tool",
75
+ # Policy tools (policy.py) - 6 tools for Kyverno and Gatekeeper
76
+ "policy_list_tool", "policy_get_tool", "policy_violations_list_tool", "policy_explain_denial_tool",
77
+ "policy_audit_tool", "policy_detect_tool",
78
+ # Backup tools (backup.py) - 11 tools for Velero
79
+ "backup_list_tool", "backup_get_tool", "backup_create_tool", "backup_delete_tool",
80
+ "restore_list_tool", "restore_create_tool", "restore_get_tool", "backup_locations_list_tool",
81
+ "backup_schedules_list_tool", "backup_schedule_create_tool", "backup_detect_tool",
82
+ # KEDA tools (keda.py) - 7 tools for autoscaling
83
+ "keda_scaledobjects_list_tool", "keda_scaledobject_get_tool", "keda_scaledjobs_list_tool",
84
+ "keda_triggerauths_list_tool", "keda_triggerauth_get_tool", "keda_hpa_list_tool", "keda_detect_tool",
85
+ # Cilium tools (cilium.py) - 8 tools for network observability
86
+ "cilium_policies_list_tool", "cilium_policy_get_tool", "cilium_endpoints_list_tool",
87
+ "cilium_identities_list_tool", "cilium_nodes_list_tool", "cilium_status_tool",
88
+ "hubble_flows_query_tool", "cilium_detect_tool",
89
+ # Rollouts tools (rollouts.py) - 11 tools for progressive delivery
90
+ "rollouts_list_tool", "rollout_get_tool", "rollout_status_tool", "rollout_promote_tool",
91
+ "rollout_abort_tool", "rollout_retry_tool", "rollout_restart_tool", "analysis_runs_list_tool",
92
+ "flagger_canaries_list_tool", "flagger_canary_get_tool", "rollouts_detect_tool",
93
+ # Cluster API tools (capi.py) - 11 tools for cluster lifecycle
94
+ "capi_clusters_list_tool", "capi_cluster_get_tool", "capi_machines_list_tool",
95
+ "capi_machine_get_tool", "capi_machinedeployments_list_tool", "capi_machinedeployment_scale_tool",
96
+ "capi_machinesets_list_tool", "capi_machinehealthchecks_list_tool", "capi_clusterclasses_list_tool",
97
+ "capi_cluster_kubeconfig_tool", "capi_detect_tool",
98
+ # KubeVirt tools (kubevirt.py) - 13 tools for VM management
99
+ "kubevirt_vms_list_tool", "kubevirt_vm_get_tool", "kubevirt_vmis_list_tool",
100
+ "kubevirt_vm_start_tool", "kubevirt_vm_stop_tool", "kubevirt_vm_restart_tool",
101
+ "kubevirt_vm_pause_tool", "kubevirt_vm_unpause_tool", "kubevirt_vm_migrate_tool",
102
+ "kubevirt_datasources_list_tool", "kubevirt_instancetypes_list_tool",
103
+ "kubevirt_datavolumes_list_tool", "kubevirt_detect_tool",
104
+ # Istio/Kiali tools (kiali.py) - 10 tools for service mesh
105
+ "istio_virtualservices_list_tool", "istio_virtualservice_get_tool", "istio_destinationrules_list_tool",
106
+ "istio_gateways_list_tool", "istio_peerauthentications_list_tool", "istio_authorizationpolicies_list_tool",
107
+ "istio_proxy_status_tool", "istio_analyze_tool", "istio_sidecar_status_tool", "istio_detect_tool",
68
108
  ]
69
109
 
70
110
 
71
111
  class TestAllToolsRegistered:
72
- """Comprehensive tests to verify all 131 tools are registered (125 core + 6 UI)."""
112
+ """Comprehensive tests to verify all 224 tools are registered (125 core + 6 UI + 93 ecosystem)."""
73
113
 
74
114
  @pytest.mark.unit
75
- def test_all_127_tools_registered(self):
76
- """Verify all 131 expected tools are registered (excluding optional browser tools)."""
115
+ def test_all_164_tools_registered(self):
116
+ """Verify all 224 expected tools are registered (excluding optional browser tools)."""
77
117
  import os
78
118
  from kubectl_mcp_tool.mcp_server import MCPServer
79
119
 
@@ -94,8 +134,8 @@ class TestAllToolsRegistered:
94
134
  tools = asyncio.run(get_tools())
95
135
  tool_names = {t.name for t in tools}
96
136
 
97
- # Verify count (131 tools = 125 core + 6 UI, browser tools disabled)
98
- assert len(tools) == 131, f"Expected 131 tools, got {len(tools)}"
137
+ # Verify count (224 tools = 125 core + 6 UI + 93 ecosystem, browser tools disabled)
138
+ assert len(tools) == 224, f"Expected 224 tools, got {len(tools)}"
99
139
 
100
140
  # Check for missing tools
101
141
  missing_tools = set(EXPECTED_TOOLS) - tool_names
@@ -120,6 +160,16 @@ class TestAllToolsRegistered:
120
160
  register_operations_tools,
121
161
  register_diagnostics_tools,
122
162
  register_cost_tools,
163
+ register_gitops_tools,
164
+ register_certs_tools,
165
+ register_policy_tools,
166
+ register_backup_tools,
167
+ register_keda_tools,
168
+ register_cilium_tools,
169
+ register_rollouts_tools,
170
+ register_capi_tools,
171
+ register_kubevirt_tools,
172
+ register_istio_tools,
123
173
  )
124
174
  # All imports should succeed
125
175
  assert callable(register_helm_tools)
@@ -133,6 +183,18 @@ class TestAllToolsRegistered:
133
183
  assert callable(register_operations_tools)
134
184
  assert callable(register_diagnostics_tools)
135
185
  assert callable(register_cost_tools)
186
+ # Ecosystem tools
187
+ assert callable(register_gitops_tools)
188
+ assert callable(register_certs_tools)
189
+ assert callable(register_policy_tools)
190
+ assert callable(register_backup_tools)
191
+ # Advanced ecosystem tools
192
+ assert callable(register_keda_tools)
193
+ assert callable(register_cilium_tools)
194
+ assert callable(register_rollouts_tools)
195
+ assert callable(register_capi_tools)
196
+ assert callable(register_kubevirt_tools)
197
+ assert callable(register_istio_tools)
136
198
 
137
199
  @pytest.mark.unit
138
200
  def test_all_tools_have_descriptions(self):