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 })