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