@@ -199,6 +199,186 @@ func (s *NodesSuite) TestNodesLog() {
199199	}
200200}
201201
202+ func  (s  * NodesSuite ) TestNodesLogWithArrayQuery () {
203+ 	s .mockServer .Handle (http .HandlerFunc (func (w  http.ResponseWriter , req  * http.Request ) {
204+ 		// Get Node response 
205+ 		if  req .URL .Path  ==  "/api/v1/nodes/existing-node"  {
206+ 			w .Header ().Set ("Content-Type" , "application/json" )
207+ 			w .WriteHeader (http .StatusOK )
208+ 			_ , _  =  w .Write ([]byte (`{ 
209+ 				"apiVersion": "v1", 
210+ 				"kind": "Node", 
211+ 				"metadata": { 
212+ 					"name": "existing-node" 
213+ 				} 
214+ 			}` ))
215+ 			return 
216+ 		}
217+ 		// Get Proxy Logs 
218+ 		if  req .URL .Path  ==  "/api/v1/nodes/existing-node/proxy/logs"  {
219+ 			w .Header ().Set ("Content-Type" , "text/plain" )
220+ 			query  :=  req .URL .Query ().Get ("query" )
221+ 			var  logContent  string 
222+ 			switch  query  {
223+ 			case  "/kubelet.log" :
224+ 				logContent  =  "Kubelet log line 1\n Kubelet log line 2\n " 
225+ 			case  "/kube-proxy.log" :
226+ 				logContent  =  "Kube-proxy log line 1\n Kube-proxy log line 2\n " 
227+ 			case  "/empty.log" :
228+ 				logContent  =  "" 
229+ 			default :
230+ 				w .WriteHeader (http .StatusNotFound )
231+ 				return 
232+ 			}
233+ 			w .WriteHeader (http .StatusOK )
234+ 			_ , _  =  w .Write ([]byte (logContent ))
235+ 			return 
236+ 		}
237+ 		w .WriteHeader (http .StatusNotFound )
238+ 	}))
239+ 	s .InitMcpClient ()
240+ 
241+ 	s .Run ("nodes_log(name=existing-node, query=[/kubelet.log, /kube-proxy.log])" , func () {
242+ 		toolResult , err  :=  s .CallTool ("nodes_log" , map [string ]interface {}{
243+ 			"name" :  "existing-node" ,
244+ 			"query" : []interface {}{"/kubelet.log" , "/kube-proxy.log" },
245+ 		})
246+ 		s .Require ().NotNil (toolResult , "toolResult should not be nil" )
247+ 		s .Run ("no error" , func () {
248+ 			s .Falsef (toolResult .IsError , "call tool should succeed" )
249+ 			s .Nilf (err , "call tool should not return error object" )
250+ 		})
251+ 		s .Run ("returns logs from both files with separators" , func () {
252+ 			result  :=  toolResult .Content [0 ].(mcp.TextContent ).Text 
253+ 			s .Contains (result , "=== Logs for query: /kubelet.log ===" , "should contain kubelet log header" )
254+ 			s .Contains (result , "Kubelet log line 1\n Kubelet log line 2\n " , "should contain kubelet log content" )
255+ 			s .Contains (result , "=== Logs for query: /kube-proxy.log ===" , "should contain kube-proxy log header" )
256+ 			s .Contains (result , "Kube-proxy log line 1\n Kube-proxy log line 2\n " , "should contain kube-proxy log content" )
257+ 		})
258+ 	})
259+ 
260+ 	s .Run ("nodes_log(name=existing-node, query=[])" , func () {
261+ 		toolResult , err  :=  s .CallTool ("nodes_log" , map [string ]interface {}{
262+ 			"name" :  "existing-node" ,
263+ 			"query" : []interface {}{},
264+ 		})
265+ 		s .Require ().NotNil (toolResult , "toolResult should not be nil" )
266+ 		s .Run ("has error" , func () {
267+ 			s .Truef (toolResult .IsError , "call tool should fail" )
268+ 			s .Nilf (err , "call tool should not return error object" )
269+ 		})
270+ 		s .Run ("describes empty array" , func () {
271+ 			expectedMessage  :=  "failed to get node log, query array cannot be empty" 
272+ 			s .Equalf (expectedMessage , toolResult .Content [0 ].(mcp.TextContent ).Text ,
273+ 				"expected descriptive error '%s', got %v" , expectedMessage , toolResult .Content [0 ].(mcp.TextContent ).Text )
274+ 		})
275+ 	})
276+ 
277+ 	s .Run ("nodes_log(name=existing-node, query=[''])" , func () {
278+ 		toolResult , err  :=  s .CallTool ("nodes_log" , map [string ]interface {}{
279+ 			"name" :  "existing-node" ,
280+ 			"query" : []interface {}{"" },
281+ 		})
282+ 		s .Require ().NotNil (toolResult , "toolResult should not be nil" )
283+ 		s .Run ("has error" , func () {
284+ 			s .Truef (toolResult .IsError , "call tool should fail" )
285+ 			s .Nilf (err , "call tool should not return error object" )
286+ 		})
287+ 		s .Run ("describes empty string in array" , func () {
288+ 			expectedMessage  :=  "failed to get node log, query array element 0 cannot be empty" 
289+ 			s .Equalf (expectedMessage , toolResult .Content [0 ].(mcp.TextContent ).Text ,
290+ 				"expected descriptive error '%s', got %v" , expectedMessage , toolResult .Content [0 ].(mcp.TextContent ).Text )
291+ 		})
292+ 	})
293+ 
294+ 	s .Run ("nodes_log(name=existing-node, query=[123])" , func () {
295+ 		toolResult , err  :=  s .CallTool ("nodes_log" , map [string ]interface {}{
296+ 			"name" :  "existing-node" ,
297+ 			"query" : []interface {}{123 },
298+ 		})
299+ 		s .Require ().NotNil (toolResult , "toolResult should not be nil" )
300+ 		s .Run ("has error" , func () {
301+ 			s .Truef (toolResult .IsError , "call tool should fail" )
302+ 			s .Nilf (err , "call tool should not return error object" )
303+ 		})
304+ 		s .Run ("describes non-string element in array" , func () {
305+ 			expectedMessage  :=  "failed to get node log, query array element 0 is not a string" 
306+ 			s .Equalf (expectedMessage , toolResult .Content [0 ].(mcp.TextContent ).Text ,
307+ 				"expected descriptive error '%s', got %v" , expectedMessage , toolResult .Content [0 ].(mcp.TextContent ).Text )
308+ 		})
309+ 	})
310+ 
311+ 	s .Run ("nodes_log(name=existing-node, query=[/kubelet.log, /empty.log])" , func () {
312+ 		toolResult , err  :=  s .CallTool ("nodes_log" , map [string ]interface {}{
313+ 			"name" :  "existing-node" ,
314+ 			"query" : []interface {}{"/kubelet.log" , "/empty.log" },
315+ 		})
316+ 		s .Require ().NotNil (toolResult , "toolResult should not be nil" )
317+ 		s .Run ("no error" , func () {
318+ 			s .Falsef (toolResult .IsError , "call tool should succeed" )
319+ 			s .Nilf (err , "call tool should not return error object" )
320+ 		})
321+ 		s .Run ("handles empty log in array" , func () {
322+ 			result  :=  toolResult .Content [0 ].(mcp.TextContent ).Text 
323+ 			s .Contains (result , "=== Logs for query: /kubelet.log ===" , "should contain kubelet log header" )
324+ 			s .Contains (result , "Kubelet log line 1\n Kubelet log line 2\n " , "should contain kubelet log content" )
325+ 			s .Contains (result , "=== Logs for query: /empty.log ===" , "should contain empty log header" )
326+ 			s .Contains (result , "The node existing-node has not logged any message yet for query '/empty.log' or the log file is empty" , "should contain empty log message" )
327+ 		})
328+ 	})
329+ 
330+ 	s .Run ("nodes_log(name=existing-node, query=[/missing.log])" , func () {
331+ 		toolResult , err  :=  s .CallTool ("nodes_log" , map [string ]interface {}{
332+ 			"name" :  "existing-node" ,
333+ 			"query" : []interface {}{"/missing.log" },
334+ 		})
335+ 		s .Require ().NotNil (toolResult , "toolResult should not be nil" )
336+ 		s .Run ("has error" , func () {
337+ 			s .Truef (toolResult .IsError , "call tool should fail" )
338+ 			s .Nilf (err , "call tool should not return error object" )
339+ 		})
340+ 		s .Run ("describes missing log file" , func () {
341+ 			expectedMessage  :=  "failed to get node log for existing-node: failed to get node logs: the server could not find the requested resource" 
342+ 			s .Equalf (expectedMessage , toolResult .Content [0 ].(mcp.TextContent ).Text ,
343+ 				"expected descriptive error '%s', got %v" , expectedMessage , toolResult .Content [0 ].(mcp.TextContent ).Text )
344+ 		})
345+ 	})
346+ 
347+ 	s .Run ("nodes_log(name=existing-node, query=[/missing1.log, /missing2.log])" , func () {
348+ 		toolResult , err  :=  s .CallTool ("nodes_log" , map [string ]interface {}{
349+ 			"name" :  "existing-node" ,
350+ 			"query" : []interface {}{"/missing1.log" , "/missing2.log" },
351+ 		})
352+ 		s .Require ().NotNil (toolResult , "toolResult should not be nil" )
353+ 		s .Run ("has error" , func () {
354+ 			s .Truef (toolResult .IsError , "call tool should fail" )
355+ 			s .Nilf (err , "call tool should not return error object" )
356+ 		})
357+ 		s .Run ("describes missing log file with query when multiple queries" , func () {
358+ 			expectedMessage  :=  "failed to get node log for existing-node (query: /missing1.log): failed to get node logs: the server could not find the requested resource" 
359+ 			s .Equalf (expectedMessage , toolResult .Content [0 ].(mcp.TextContent ).Text ,
360+ 				"expected descriptive error '%s', got %v" , expectedMessage , toolResult .Content [0 ].(mcp.TextContent ).Text )
361+ 		})
362+ 	})
363+ 
364+ 	s .Run ("nodes_log(name=existing-node, query='')" , func () {
365+ 		toolResult , err  :=  s .CallTool ("nodes_log" , map [string ]interface {}{
366+ 			"name" :  "existing-node" ,
367+ 			"query" : "" ,
368+ 		})
369+ 		s .Require ().NotNil (toolResult , "toolResult should not be nil" )
370+ 		s .Run ("has error" , func () {
371+ 			s .Truef (toolResult .IsError , "call tool should fail" )
372+ 			s .Nilf (err , "call tool should not return error object" )
373+ 		})
374+ 		s .Run ("describes empty string query" , func () {
375+ 			expectedMessage  :=  "failed to get node log, query cannot be empty" 
376+ 			s .Equalf (expectedMessage , toolResult .Content [0 ].(mcp.TextContent ).Text ,
377+ 				"expected descriptive error '%s', got %v" , expectedMessage , toolResult .Content [0 ].(mcp.TextContent ).Text )
378+ 		})
379+ 	})
380+ }
381+ 
202382func  (s  * NodesSuite ) TestNodesLogDenied () {
203383	s .Require ().NoError (toml .Unmarshal ([]byte (` 
204384		denied_resources = [ { version = "v1", kind = "Node" } ] 
0 commit comments