github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/aagent/watchers/execwatcher/exec_test.go (about) 1 // Copyright (c) 2020-2022, R.I. Pienaar and the Choria Project contributors 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 5 package execwatcher 6 7 import ( 8 "encoding/json" 9 "testing" 10 "time" 11 12 "github.com/choria-io/go-choria/aagent/model" 13 "github.com/golang/mock/gomock" 14 . "github.com/onsi/ginkgo/v2" 15 . "github.com/onsi/gomega" 16 ) 17 18 func Test(t *testing.T) { 19 RegisterFailHandler(Fail) 20 RunSpecs(t, "AAgent/Watchers/ExecWatcher") 21 } 22 23 var _ = Describe("ExecWatcher", func() { 24 var ( 25 mockctl *gomock.Controller 26 mockMachine *model.MockMachine 27 watch *Watcher 28 now time.Time 29 ) 30 31 BeforeEach(func() { 32 mockctl = gomock.NewController(GinkgoT()) 33 mockMachine = model.NewMockMachine(mockctl) 34 35 mockMachine.EXPECT().Name().Return("exec").AnyTimes() 36 mockMachine.EXPECT().Identity().Return("ginkgo").AnyTimes() 37 mockMachine.EXPECT().InstanceID().Return("1234567890").AnyTimes() 38 mockMachine.EXPECT().Version().Return("1.0.0").AnyTimes() 39 mockMachine.EXPECT().TimeStampSeconds().Return(now.Unix()).AnyTimes() 40 mockMachine.EXPECT().Debugf(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() 41 mockMachine.EXPECT().Infof(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() 42 43 now = time.Unix(1606924953, 0) 44 45 wi, err := New(mockMachine, "ginkgo", []string{"always"}, "fail", "success", "2m", time.Second, map[string]any{ 46 "command": "foo", 47 }) 48 Expect(err).ToNot(HaveOccurred()) 49 watch = wi.(*Watcher) 50 }) 51 52 AfterEach(func() { 53 mockctl.Finish() 54 }) 55 56 Describe("processTemplate", func() { 57 It("Should handle absent data correctly", func() { 58 mockMachine.EXPECT().Facts().Return([]byte(`{"location":"lon"}`)).AnyTimes() 59 mockMachine.EXPECT().Data().Return(map[string]any{"x": 1}).AnyTimes() 60 61 res, err := watch.ProcessTemplate(`{{lookup "facts.foo" "default"}}`) 62 Expect(err).ToNot(HaveOccurred()) 63 Expect(res).To(Equal("default")) 64 }) 65 66 It("Should handle present data correctly", func() { 67 mockMachine.EXPECT().Facts().Return([]byte(`{"location":"lon", "i":1}`)) 68 mockMachine.EXPECT().Data().Return(map[string]any{"x": 1}) 69 70 res, err := watch.ProcessTemplate(`{{lookup "facts.location" "default"| ToUpper }}: {{ lookup "i" 1 }} ({{ lookup "data.x" 2 }})`) 71 Expect(err).ToNot(HaveOccurred()) 72 Expect(res).To(Equal("LON: 1 (1)")) 73 }) 74 }) 75 76 Describe("setProperties", func() { 77 It("Should parse valid properties", func() { 78 prop := map[string]any{ 79 "command": "cmd", 80 "timeout": "1.5s", 81 "environment": []string{"key1=val1", "key2=val2"}, 82 "suppress_success_announce": "true", 83 } 84 Expect(watch.setProperties(prop)).ToNot(HaveOccurred()) 85 Expect(watch.properties.Command).To(Equal("cmd")) 86 Expect(watch.properties.Timeout).To(Equal(1500 * time.Millisecond)) 87 Expect(watch.properties.Environment).To(Equal([]string{"key1=val1", "key2=val2"})) 88 Expect(watch.properties.SuppressSuccessAnnounce).To(BeTrue()) 89 }) 90 91 It("Should handle errors", func() { 92 watch.properties = nil 93 err := watch.setProperties(map[string]any{}) 94 Expect(err).To(MatchError("command is required")) 95 }) 96 97 It("Should enforce 1 second intervals", func() { 98 err := watch.setProperties(map[string]any{ 99 "command": "cmd", 100 "timeout": "0", 101 }) 102 Expect(err).ToNot(HaveOccurred()) 103 Expect(watch.properties.Command).To(Equal("cmd")) 104 Expect(watch.properties.Timeout).To(Equal(time.Second)) 105 }) 106 107 It("Should fail for invalid combinations", func() { 108 err := watch.setProperties(map[string]any{ 109 "disown": true, 110 "parse_as_data": true, 111 }) 112 Expect(err).To(MatchError("cannot parse output as data while disowning child processes")) 113 }) 114 }) 115 116 Describe("CurrentState", func() { 117 It("Should be a valid state", func() { 118 watch.properties.Command = "/bin/sh" 119 watch.previous = Success 120 watch.previousRunTime = time.Second 121 122 cs := watch.CurrentState() 123 csj, err := cs.(*StateNotification).JSON() 124 Expect(err).ToNot(HaveOccurred()) 125 126 event := map[string]any{} 127 err = json.Unmarshal(csj, &event) 128 Expect(err).ToNot(HaveOccurred()) 129 delete(event, "id") 130 131 Expect(event).To(Equal(map[string]any{ 132 "time": "2020-12-02T16:02:33Z", 133 "type": "io.choria.machine.watcher.exec.v1.state", 134 "subject": "ginkgo", 135 "specversion": "1.0", 136 "source": "io.choria.machine", 137 "datacontenttype": "application/json", 138 "data": map[string]any{ 139 "command": "/bin/sh", 140 "previous_outcome": "success", 141 "previous_run_time": float64(time.Second), 142 "id": "1234567890", 143 "identity": "ginkgo", 144 "machine": "exec", 145 "name": "ginkgo", 146 "protocol": "io.choria.machine.watcher.exec.v1.state", 147 "type": "exec", 148 "version": "1.0.0", 149 "timestamp": float64(now.Unix()), 150 }, 151 })) 152 }) 153 }) 154 })