github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/aagent/machine/machine_test.go (about)

     1  // Copyright (c) 2019-2021, R.I. Pienaar and the Choria Project contributors
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package machine
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"os"
    11  	"sync"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/choria-io/go-choria/aagent/plugin"
    16  	"github.com/choria-io/go-choria/aagent/watchers"
    17  	"github.com/ghodss/yaml"
    18  	"github.com/sirupsen/logrus"
    19  
    20  	"github.com/golang/mock/gomock"
    21  	. "github.com/onsi/ginkgo/v2"
    22  	. "github.com/onsi/gomega"
    23  )
    24  
    25  func TestMachine(t *testing.T) {
    26  	RegisterFailHandler(Fail)
    27  	RunSpecs(t, "Aagent/Machine")
    28  }
    29  
    30  var _ = Describe("Aagent/Machine", func() {
    31  	var (
    32  		mockctl *gomock.Controller
    33  		service *MockNotificationService
    34  		manager *MockWatcherManager
    35  		machine *Machine
    36  		log     *logrus.Entry
    37  		err     error
    38  	)
    39  
    40  	BeforeEach(func() {
    41  		logger := logrus.New()
    42  		logger.SetOutput(GinkgoWriter)
    43  		log = logrus.NewEntry(logger)
    44  
    45  		mockctl = gomock.NewController(GinkgoT())
    46  		service = NewMockNotificationService(mockctl)
    47  		manager = NewMockWatcherManager(mockctl)
    48  		machine = &Machine{
    49  			notifiers:   []NotificationService{},
    50  			manager:     manager,
    51  			MachineName: "ginkgo",
    52  		}
    53  	})
    54  
    55  	AfterEach(func() {
    56  		mockctl.Finish()
    57  	})
    58  
    59  	Describe("FromYAML", func() {
    60  		It("Should configure the manager", func() {
    61  			manager.EXPECT().SetMachine(gomock.AssignableToTypeOf(&Machine{})).Return(fmt.Errorf("set machine error"))
    62  			machine, err = FromYAML("testdata/empty.yaml", manager)
    63  			Expect(err).To(MatchError("could not register with manager: set machine error"))
    64  		})
    65  
    66  		It("Should setup the machine", func() {
    67  			manager.EXPECT().SetMachine(gomock.AssignableToTypeOf(&Machine{}))
    68  			machine, err = FromYAML("testdata/empty.yaml", manager)
    69  			Expect(err).To(MatchError("validation failed: a machine name is required"))
    70  		})
    71  
    72  		It("Should load good machines", func() {
    73  			manager.EXPECT().SetMachine(gomock.AssignableToTypeOf(&Machine{}))
    74  			machine, err = FromYAML("testdata/machine.yaml", manager)
    75  			Expect(err).ToNot(HaveOccurred())
    76  			Expect(machine.Name()).To(Equal("TestMachine"))
    77  		})
    78  	})
    79  
    80  	Describe("FromPlugin", func() {
    81  		var (
    82  			mplug *plugin.MachinePlugin
    83  		)
    84  		BeforeEach(func() {
    85  			myaml, err := os.ReadFile("testdata/machine.yaml")
    86  			Expect(err).ToNot(HaveOccurred())
    87  
    88  			Expect(yaml.Unmarshal(myaml, machine)).ToNot(HaveOccurred())
    89  			mplug = plugin.NewMachinePlugin("TestMachine", machine)
    90  		})
    91  
    92  		It("Should configure the manager and handle errors", func() {
    93  			manager.EXPECT().SetMachine(gomock.Any()).DoAndReturn(func(m *Machine) error {
    94  				Expect(m.MachineName).To(Equal("TestMachine"))
    95  				Expect(m.manager).To(Equal(manager))
    96  
    97  				return fmt.Errorf("set machine error")
    98  			})
    99  
   100  			machine, err = FromPlugin(mplug, manager, log)
   101  			Expect(err).To(MatchError("could not register with manager: set machine error"))
   102  		})
   103  
   104  		It("Should setup the machine", func() {
   105  			manager.EXPECT().SetMachine(gomock.AssignableToTypeOf(machine))
   106  			machine.MachineName = ""
   107  			machine, err = FromPlugin(mplug, manager, log)
   108  			Expect(err).To(MatchError("validation failed: a machine name is required"))
   109  		})
   110  
   111  		It("Should load good machines", func() {
   112  			manager.EXPECT().SetMachine(gomock.AssignableToTypeOf(machine))
   113  			machine, err = FromPlugin(mplug, manager, log)
   114  			Expect(err).ToNot(HaveOccurred())
   115  			Expect(machine.Name()).To(Equal("TestMachine"))
   116  			Expect(machine.manager).To(Equal(manager))
   117  		})
   118  	})
   119  
   120  	Describe("Machine", func() {
   121  		BeforeEach(func() {
   122  			manager.EXPECT().SetMachine(gomock.AssignableToTypeOf(&Machine{}))
   123  			machine, err = FromYAML("testdata/machine.yaml", manager)
   124  		})
   125  
   126  		Describe("Watchers", func() {
   127  			It("Should return the machine watchers", func() {
   128  				watchers := machine.Watchers()
   129  				Expect(watchers).To(Equal(machine.WatcherDefs))
   130  			})
   131  		})
   132  
   133  		Describe("Name", func() {
   134  			It("Should return the name", func() {
   135  				Expect(machine.Name()).To(Equal("TestMachine"))
   136  			})
   137  		})
   138  	})
   139  
   140  	Describe("Validate", func() {
   141  		It("Should check common problems", func() {
   142  			machine.MachineName = ""
   143  			Expect(machine.Validate()).To(MatchError("a machine name is required"))
   144  
   145  			machine.MachineName = "ginkgo"
   146  			machine.MachineVersion = ""
   147  			Expect(machine.Validate()).To(MatchError("a machine version is required"))
   148  
   149  			machine.MachineVersion = "1.2.3"
   150  			machine.InitialState = ""
   151  			Expect(machine.Validate()).To(MatchError("an initial state is required"))
   152  
   153  			machine.InitialState = "unknown"
   154  			Expect(machine.Validate()).To(MatchError("no transitions defined"))
   155  
   156  			machine.Transitions = []*Transition{{}}
   157  			Expect(machine.Validate()).To(MatchError("no watchers defined"))
   158  
   159  			machine.WatcherDefs = []*watchers.WatcherDef{{}}
   160  			Expect(machine.Validate()).ToNot(HaveOccurred())
   161  		})
   162  	})
   163  
   164  	Describe("Start", func() {
   165  		It("Should start the machine using the manager", func() {
   166  			wg := &sync.WaitGroup{}
   167  			ctx, cancel := context.WithCancel(context.Background())
   168  			defer cancel()
   169  
   170  			manager.EXPECT().SetMachine(gomock.AssignableToTypeOf(&Machine{}))
   171  			machine, err = FromYAML("testdata/machine.yaml", manager)
   172  			Expect(err).ToNot(HaveOccurred())
   173  			manager.EXPECT().Run(gomock.AssignableToTypeOf(ctx), wg)
   174  
   175  			machine.SplayStart = 0
   176  
   177  			<-machine.Start(ctx, wg)
   178  			Expect(machine.startTime.IsZero()).To(BeFalse())
   179  		})
   180  	})
   181  
   182  	Describe("Stop", func() {
   183  		It("Should not panic when nil", func() {
   184  			machine.Stop()
   185  		})
   186  
   187  		It("Should stop a running machine", func() {
   188  			machine.ctx, machine.cancel = context.WithCancel(context.Background())
   189  			machine.startTime = time.Now()
   190  
   191  			machine.Stop()
   192  			Expect(machine.startTime.IsZero()).To(BeTrue())
   193  			Expect(machine.ctx.Err()).To(HaveOccurred())
   194  		})
   195  	})
   196  
   197  	Describe("State", func() {
   198  		It("Should return the current state", func() {
   199  			manager.EXPECT().SetMachine(gomock.AssignableToTypeOf(&Machine{}))
   200  			machine, err = FromYAML("testdata/machine.yaml", manager)
   201  			Expect(err).ToNot(HaveOccurred())
   202  			machine.ctx = context.Background()
   203  			Expect(machine.State()).To(Equal("unknown"))
   204  		})
   205  	})
   206  
   207  	Describe("Transition", func() {
   208  		It("Should initiate the event", func() {
   209  			manager.EXPECT().SetMachine(gomock.AssignableToTypeOf(&Machine{}))
   210  			machine, err = FromYAML("testdata/machine.yaml", manager)
   211  			Expect(err).ToNot(HaveOccurred())
   212  			machine.ctx = context.Background()
   213  			machine.Transition("fire_1")
   214  			Expect(machine.State()).To(Equal("one"))
   215  			machine.RegisterNotifier(service)
   216  			service.EXPECT().Warnf(machine, "machine", "Could not fire '%s' event while in %s", "fire_10", "one")
   217  			machine.Transition("fire_10")
   218  			Expect(machine.State()).To(Equal("one"))
   219  		})
   220  	})
   221  })