Skip to content

Commit 57ac756

Browse files
committed
test: add tests in pkg/mcp package
Added tests with basic VM actions - start, stop, restart Signed-off-by: Karel Simon <ksimon@redhat.com>
1 parent 88c80b1 commit 57ac756

File tree

1 file changed

+302
-0
lines changed

1 file changed

+302
-0
lines changed

pkg/mcp/kubevirt_test.go

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,308 @@ func (s *KubevirtSuite) TestCreate() {
509509
})
510510
}
511511

512+
func (s *KubevirtSuite) TestStartVM() {
513+
// Create a test VM in Halted state
514+
dynamicClient := dynamic.NewForConfigOrDie(envTestRestConfig)
515+
vm := &unstructured.Unstructured{}
516+
vm.SetUnstructuredContent(map[string]interface{}{
517+
"apiVersion": "kubevirt.io/v1",
518+
"kind": "VirtualMachine",
519+
"metadata": map[string]interface{}{
520+
"name": "test-vm-start",
521+
"namespace": "default",
522+
},
523+
"spec": map[string]interface{}{
524+
"runStrategy": "Halted",
525+
},
526+
})
527+
_, err := dynamicClient.Resource(schema.GroupVersionResource{
528+
Group: "kubevirt.io",
529+
Version: "v1",
530+
Resource: "virtualmachines",
531+
}).Namespace("default").Create(s.T().Context(), vm, metav1.CreateOptions{})
532+
s.Require().NoError(err, "failed to create test VM")
533+
534+
s.Run("vm_start missing required params", func() {
535+
testCases := []string{"name", "namespace"}
536+
for _, param := range testCases {
537+
s.Run("missing "+param, func() {
538+
params := map[string]interface{}{
539+
"name": "test-vm-start",
540+
"namespace": "default",
541+
}
542+
delete(params, param)
543+
toolResult, err := s.CallTool("vm_start", params)
544+
s.Require().Nilf(err, "call tool failed %v", err)
545+
s.Truef(toolResult.IsError, "expected call tool to fail due to missing %s", param)
546+
s.Equal(toolResult.Content[0].(mcp.TextContent).Text, param+" parameter required")
547+
})
548+
}
549+
})
550+
551+
s.Run("vm_start on halted VM", func() {
552+
toolResult, err := s.CallTool("vm_start", map[string]interface{}{
553+
"name": "test-vm-start",
554+
"namespace": "default",
555+
})
556+
s.Run("no error", func() {
557+
s.Nilf(err, "call tool failed %v", err)
558+
s.Falsef(toolResult.IsError, "call tool failed")
559+
})
560+
var decodedResult []unstructured.Unstructured
561+
err = yaml.Unmarshal([]byte(toolResult.Content[0].(mcp.TextContent).Text), &decodedResult)
562+
s.Run("returns yaml content", func() {
563+
s.Nilf(err, "invalid tool result content %v", err)
564+
s.Truef(strings.HasPrefix(toolResult.Content[0].(mcp.TextContent).Text, "# VirtualMachine started successfully"),
565+
"Expected success message, got %v", toolResult.Content[0].(mcp.TextContent).Text)
566+
s.Require().Lenf(decodedResult, 1, "invalid resource count, expected 1, got %v", len(decodedResult))
567+
s.Equal("test-vm-start", decodedResult[0].GetName(), "invalid resource name")
568+
s.Equal("default", decodedResult[0].GetNamespace(), "invalid resource namespace")
569+
s.Equal("Always",
570+
decodedResult[0].Object["spec"].(map[string]interface{})["runStrategy"].(string),
571+
"expected runStrategy to be Always after start")
572+
})
573+
})
574+
575+
s.Run("vm_start on already running VM (idempotent)", func() {
576+
toolResult, err := s.CallTool("vm_start", map[string]interface{}{
577+
"name": "test-vm-start",
578+
"namespace": "default",
579+
})
580+
s.Run("no error", func() {
581+
s.Nilf(err, "call tool failed %v", err)
582+
s.Falsef(toolResult.IsError, "call tool failed")
583+
})
584+
var decodedResult []unstructured.Unstructured
585+
err = yaml.Unmarshal([]byte(toolResult.Content[0].(mcp.TextContent).Text), &decodedResult)
586+
s.Run("returns yaml content showing VM was already running", func() {
587+
s.Nilf(err, "invalid tool result content %v", err)
588+
expectedPrefix := fmt.Sprintf("# VirtualMachine '%s' in namespace '%s' is already running", "test-vm-start", "default")
589+
s.Truef(strings.HasPrefix(toolResult.Content[0].(mcp.TextContent).Text, expectedPrefix),
590+
"Expected already running message, got %v", toolResult.Content[0].(mcp.TextContent).Text)
591+
s.Require().Lenf(decodedResult, 1, "invalid resource count, expected 1, got %v", len(decodedResult))
592+
s.Equal("Always",
593+
decodedResult[0].Object["spec"].(map[string]interface{})["runStrategy"].(string),
594+
"expected runStrategy to remain Always")
595+
})
596+
})
597+
598+
s.Run("vm_start on non-existent VM", func() {
599+
toolResult, err := s.CallTool("vm_start", map[string]interface{}{
600+
"name": "non-existent-vm",
601+
"namespace": "default",
602+
})
603+
s.Nilf(err, "call tool failed %v", err)
604+
s.Truef(toolResult.IsError, "expected call tool to fail for non-existent VM")
605+
s.Truef(strings.Contains(toolResult.Content[0].(mcp.TextContent).Text, "failed to get VirtualMachine"),
606+
"Expected error message about VM not found, got %v", toolResult.Content[0].(mcp.TextContent).Text)
607+
})
608+
}
609+
610+
func (s *KubevirtSuite) TestStopVM() {
611+
// Create a test VM in Always (running) state
612+
dynamicClient := dynamic.NewForConfigOrDie(envTestRestConfig)
613+
vm := &unstructured.Unstructured{}
614+
vm.SetUnstructuredContent(map[string]interface{}{
615+
"apiVersion": "kubevirt.io/v1",
616+
"kind": "VirtualMachine",
617+
"metadata": map[string]interface{}{
618+
"name": "test-vm-stop",
619+
"namespace": "default",
620+
},
621+
"spec": map[string]interface{}{
622+
"runStrategy": "Always",
623+
},
624+
})
625+
_, err := dynamicClient.Resource(schema.GroupVersionResource{
626+
Group: "kubevirt.io",
627+
Version: "v1",
628+
Resource: "virtualmachines",
629+
}).Namespace("default").Create(s.T().Context(), vm, metav1.CreateOptions{})
630+
s.Require().NoError(err, "failed to create test VM")
631+
632+
s.Run("vm_stop missing required params", func() {
633+
testCases := []string{"name", "namespace"}
634+
for _, param := range testCases {
635+
s.Run("missing "+param, func() {
636+
params := map[string]interface{}{
637+
"name": "test-vm-stop",
638+
"namespace": "default",
639+
}
640+
delete(params, param)
641+
toolResult, err := s.CallTool("vm_stop", params)
642+
s.Require().Nilf(err, "call tool failed %v", err)
643+
s.Truef(toolResult.IsError, "expected call tool to fail due to missing %s", param)
644+
s.Equal(toolResult.Content[0].(mcp.TextContent).Text, param+" parameter required")
645+
})
646+
}
647+
})
648+
649+
s.Run("vm_stop on running VM", func() {
650+
toolResult, err := s.CallTool("vm_stop", map[string]interface{}{
651+
"name": "test-vm-stop",
652+
"namespace": "default",
653+
})
654+
s.Run("no error", func() {
655+
s.Nilf(err, "call tool failed %v", err)
656+
s.Falsef(toolResult.IsError, "call tool failed")
657+
})
658+
var decodedResult []unstructured.Unstructured
659+
err = yaml.Unmarshal([]byte(toolResult.Content[0].(mcp.TextContent).Text), &decodedResult)
660+
s.Run("returns yaml content", func() {
661+
s.Nilf(err, "invalid tool result content %v", err)
662+
s.Truef(strings.HasPrefix(toolResult.Content[0].(mcp.TextContent).Text, "# VirtualMachine stopped successfully"),
663+
"Expected success message, got %v", toolResult.Content[0].(mcp.TextContent).Text)
664+
s.Require().Lenf(decodedResult, 1, "invalid resource count, expected 1, got %v", len(decodedResult))
665+
s.Equal("test-vm-stop", decodedResult[0].GetName(), "invalid resource name")
666+
s.Equal("default", decodedResult[0].GetNamespace(), "invalid resource namespace")
667+
s.Equal("Halted",
668+
decodedResult[0].Object["spec"].(map[string]interface{})["runStrategy"].(string),
669+
"expected runStrategy to be Halted after stop")
670+
})
671+
})
672+
673+
s.Run("vm_stop on already stopped VM (idempotent)", func() {
674+
toolResult, err := s.CallTool("vm_stop", map[string]interface{}{
675+
"name": "test-vm-stop",
676+
"namespace": "default",
677+
})
678+
s.Run("no error", func() {
679+
s.Nilf(err, "call tool failed %v", err)
680+
s.Falsef(toolResult.IsError, "call tool failed")
681+
})
682+
var decodedResult []unstructured.Unstructured
683+
err = yaml.Unmarshal([]byte(toolResult.Content[0].(mcp.TextContent).Text), &decodedResult)
684+
s.Run("returns yaml content showing VM was already stopped", func() {
685+
s.Nilf(err, "invalid tool result content %v", err)
686+
expectedPrefix := fmt.Sprintf("# VirtualMachine '%s' in namespace '%s' is already stopped", "test-vm-stop", "default")
687+
s.Truef(strings.HasPrefix(toolResult.Content[0].(mcp.TextContent).Text, expectedPrefix),
688+
"Expected already stopped message, got %v", toolResult.Content[0].(mcp.TextContent).Text)
689+
s.Require().Lenf(decodedResult, 1, "invalid resource count, expected 1, got %v", len(decodedResult))
690+
s.Equal("Halted",
691+
decodedResult[0].Object["spec"].(map[string]interface{})["runStrategy"].(string),
692+
"expected runStrategy to remain Halted")
693+
})
694+
})
695+
696+
s.Run("vm_stop on non-existent VM", func() {
697+
toolResult, err := s.CallTool("vm_stop", map[string]interface{}{
698+
"name": "non-existent-vm",
699+
"namespace": "default",
700+
})
701+
s.Nilf(err, "call tool failed %v", err)
702+
s.Truef(toolResult.IsError, "expected call tool to fail for non-existent VM")
703+
s.Truef(strings.Contains(toolResult.Content[0].(mcp.TextContent).Text, "failed to get VirtualMachine"),
704+
"Expected error message about VM not found, got %v", toolResult.Content[0].(mcp.TextContent).Text)
705+
})
706+
}
707+
708+
func (s *KubevirtSuite) TestRestartVM() {
709+
// Create a test VM in Always (running) state
710+
dynamicClient := dynamic.NewForConfigOrDie(envTestRestConfig)
711+
vm := &unstructured.Unstructured{}
712+
vm.SetUnstructuredContent(map[string]interface{}{
713+
"apiVersion": "kubevirt.io/v1",
714+
"kind": "VirtualMachine",
715+
"metadata": map[string]interface{}{
716+
"name": "test-vm-restart",
717+
"namespace": "default",
718+
},
719+
"spec": map[string]interface{}{
720+
"runStrategy": "Always",
721+
},
722+
})
723+
_, err := dynamicClient.Resource(schema.GroupVersionResource{
724+
Group: "kubevirt.io",
725+
Version: "v1",
726+
Resource: "virtualmachines",
727+
}).Namespace("default").Create(s.T().Context(), vm, metav1.CreateOptions{})
728+
s.Require().NoError(err, "failed to create test VM")
729+
730+
s.Run("vm_restart missing required params", func() {
731+
testCases := []string{"name", "namespace"}
732+
for _, param := range testCases {
733+
s.Run("missing "+param, func() {
734+
params := map[string]interface{}{
735+
"name": "test-vm-restart",
736+
"namespace": "default",
737+
}
738+
delete(params, param)
739+
toolResult, err := s.CallTool("vm_restart", params)
740+
s.Require().Nilf(err, "call tool failed %v", err)
741+
s.Truef(toolResult.IsError, "expected call tool to fail due to missing %s", param)
742+
s.Equal(toolResult.Content[0].(mcp.TextContent).Text, param+" parameter required")
743+
})
744+
}
745+
})
746+
747+
s.Run("vm_restart on running VM", func() {
748+
toolResult, err := s.CallTool("vm_restart", map[string]interface{}{
749+
"name": "test-vm-restart",
750+
"namespace": "default",
751+
})
752+
s.Run("no error", func() {
753+
s.Nilf(err, "call tool failed %v", err)
754+
s.Falsef(toolResult.IsError, "call tool failed")
755+
})
756+
var decodedResult []unstructured.Unstructured
757+
err = yaml.Unmarshal([]byte(toolResult.Content[0].(mcp.TextContent).Text), &decodedResult)
758+
s.Run("returns yaml content", func() {
759+
s.Nilf(err, "invalid tool result content %v", err)
760+
s.Truef(strings.HasPrefix(toolResult.Content[0].(mcp.TextContent).Text, "# VirtualMachine restarted successfully"),
761+
"Expected success message, got %v", toolResult.Content[0].(mcp.TextContent).Text)
762+
s.Require().Lenf(decodedResult, 1, "invalid resource count, expected 1, got %v", len(decodedResult))
763+
s.Equal("test-vm-restart", decodedResult[0].GetName(), "invalid resource name")
764+
s.Equal("default", decodedResult[0].GetNamespace(), "invalid resource namespace")
765+
s.Equal("Always",
766+
decodedResult[0].Object["spec"].(map[string]interface{})["runStrategy"].(string),
767+
"expected runStrategy to be Always after restart")
768+
})
769+
})
770+
771+
s.Run("vm_restart on stopped VM", func() {
772+
// First stop the VM
773+
stopResult, err := s.CallTool("vm_stop", map[string]interface{}{
774+
"name": "test-vm-restart",
775+
"namespace": "default",
776+
})
777+
s.Require().Nilf(err, "failed to stop VM for test setup")
778+
s.Require().Falsef(stopResult.IsError, "failed to stop VM for test setup")
779+
780+
// Now restart it
781+
toolResult, err := s.CallTool("vm_restart", map[string]interface{}{
782+
"name": "test-vm-restart",
783+
"namespace": "default",
784+
})
785+
s.Run("no error", func() {
786+
s.Nilf(err, "call tool failed %v", err)
787+
s.Falsef(toolResult.IsError, "call tool failed")
788+
})
789+
var decodedResult []unstructured.Unstructured
790+
err = yaml.Unmarshal([]byte(toolResult.Content[0].(mcp.TextContent).Text), &decodedResult)
791+
s.Run("returns yaml content showing VM restarted from stopped state", func() {
792+
s.Nilf(err, "invalid tool result content %v", err)
793+
s.Truef(strings.HasPrefix(toolResult.Content[0].(mcp.TextContent).Text, "# VirtualMachine restarted successfully"),
794+
"Expected success message, got %v", toolResult.Content[0].(mcp.TextContent).Text)
795+
s.Require().Lenf(decodedResult, 1, "invalid resource count, expected 1, got %v", len(decodedResult))
796+
s.Equal("Always",
797+
decodedResult[0].Object["spec"].(map[string]interface{})["runStrategy"].(string),
798+
"expected runStrategy to be Always after restart from Halted")
799+
})
800+
})
801+
802+
s.Run("vm_restart on non-existent VM", func() {
803+
toolResult, err := s.CallTool("vm_restart", map[string]interface{}{
804+
"name": "non-existent-vm",
805+
"namespace": "default",
806+
})
807+
s.Nilf(err, "call tool failed %v", err)
808+
s.Truef(toolResult.IsError, "expected call tool to fail for non-existent VM")
809+
s.Truef(strings.Contains(toolResult.Content[0].(mcp.TextContent).Text, "failed to get VirtualMachine"),
810+
"Expected error message about VM not found, got %v", toolResult.Content[0].(mcp.TextContent).Text)
811+
})
812+
}
813+
512814
func TestKubevirt(t *testing.T) {
513815
suite.Run(t, new(KubevirtSuite))
514816
}

0 commit comments

Comments
 (0)