@@ -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+
512814func TestKubevirt (t * testing.T ) {
513815 suite .Run (t , new (KubevirtSuite ))
514816}
0 commit comments