github.com/netdata/go.d.plugin@v0.58.1/agent/functions/manager_test.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package functions 4 5 import ( 6 "context" 7 "sort" 8 "strings" 9 "testing" 10 "time" 11 12 "github.com/stretchr/testify/assert" 13 ) 14 15 func TestNewManager(t *testing.T) { 16 mgr := NewManager() 17 18 assert.NotNilf(t, mgr.Input, "Input") 19 assert.NotNilf(t, mgr.FunctionRegistry, "FunctionRegistry") 20 } 21 22 func TestManager_Register(t *testing.T) { 23 type testInputFn struct { 24 name string 25 invalid bool 26 } 27 tests := map[string]struct { 28 input []testInputFn 29 expected []string 30 }{ 31 "valid registration": { 32 input: []testInputFn{ 33 {name: "fn1"}, 34 {name: "fn2"}, 35 }, 36 expected: []string{"fn1", "fn2"}, 37 }, 38 "registration with duplicates": { 39 input: []testInputFn{ 40 {name: "fn1"}, 41 {name: "fn2"}, 42 {name: "fn1"}, 43 }, 44 expected: []string{"fn1", "fn2"}, 45 }, 46 "registration with nil functions": { 47 input: []testInputFn{ 48 {name: "fn1"}, 49 {name: "fn2", invalid: true}, 50 }, 51 expected: []string{"fn1"}, 52 }, 53 } 54 55 for name, test := range tests { 56 t.Run(name, func(t *testing.T) { 57 mgr := NewManager() 58 59 for _, v := range test.input { 60 if v.invalid { 61 mgr.Register(v.name, nil) 62 } else { 63 mgr.Register(v.name, func(Function) {}) 64 } 65 } 66 67 var got []string 68 for name := range mgr.FunctionRegistry { 69 got = append(got, name) 70 } 71 sort.Strings(got) 72 sort.Strings(test.expected) 73 74 assert.Equal(t, test.expected, got) 75 }) 76 } 77 } 78 79 func TestManager_Run(t *testing.T) { 80 tests := map[string]struct { 81 register []string 82 input string 83 expected []Function 84 }{ 85 "valid function: single": { 86 register: []string{"fn1"}, 87 input: ` 88 FUNCTION UID 1 "fn1 arg1 arg2" 89 `, 90 expected: []Function{ 91 { 92 key: "FUNCTION", 93 UID: "UID", 94 Timeout: time.Second, 95 Name: "fn1", 96 Args: []string{"arg1", "arg2"}, 97 Payload: nil, 98 }, 99 }, 100 }, 101 "valid function: multiple": { 102 register: []string{"fn1", "fn2"}, 103 input: ` 104 FUNCTION UID 1 "fn1 arg1 arg2" 105 FUNCTION UID 1 "fn2 arg1 arg2" 106 `, 107 expected: []Function{ 108 { 109 key: "FUNCTION", 110 UID: "UID", 111 Timeout: time.Second, 112 Name: "fn1", 113 Args: []string{"arg1", "arg2"}, 114 Payload: nil, 115 }, 116 { 117 key: "FUNCTION", 118 UID: "UID", 119 Timeout: time.Second, 120 Name: "fn2", 121 Args: []string{"arg1", "arg2"}, 122 Payload: nil, 123 }, 124 }, 125 }, 126 "valid function: single with payload": { 127 register: []string{"fn1", "fn2"}, 128 input: ` 129 FUNCTION_PAYLOAD UID 1 "fn1 arg1 arg2" 130 payload line1 131 payload line2 132 FUNCTION_PAYLOAD_END 133 `, 134 expected: []Function{ 135 { 136 key: "FUNCTION_PAYLOAD", 137 UID: "UID", 138 Timeout: time.Second, 139 Name: "fn1", 140 Args: []string{"arg1", "arg2"}, 141 Payload: []byte("payload line1\npayload line2"), 142 }, 143 }, 144 }, 145 "valid function: multiple with payload": { 146 register: []string{"fn1", "fn2"}, 147 input: ` 148 FUNCTION_PAYLOAD UID 1 "fn1 arg1 arg2" 149 payload line1 150 payload line2 151 FUNCTION_PAYLOAD_END 152 153 FUNCTION_PAYLOAD UID 1 "fn2 arg1 arg2" 154 payload line3 155 payload line4 156 FUNCTION_PAYLOAD_END 157 `, 158 expected: []Function{ 159 { 160 key: "FUNCTION_PAYLOAD", 161 UID: "UID", 162 Timeout: time.Second, 163 Name: "fn1", 164 Args: []string{"arg1", "arg2"}, 165 Payload: []byte("payload line1\npayload line2"), 166 }, 167 { 168 key: "FUNCTION_PAYLOAD", 169 UID: "UID", 170 Timeout: time.Second, 171 Name: "fn2", 172 Args: []string{"arg1", "arg2"}, 173 Payload: []byte("payload line3\npayload line4"), 174 }, 175 }, 176 }, 177 "valid function: multiple with and without payload": { 178 register: []string{"fn1", "fn2", "fn3", "fn4"}, 179 input: ` 180 FUNCTION_PAYLOAD UID 1 "fn1 arg1 arg2" 181 payload line1 182 payload line2 183 FUNCTION_PAYLOAD_END 184 185 FUNCTION UID 1 "fn2 arg1 arg2" 186 FUNCTION UID 1 "fn3 arg1 arg2" 187 188 FUNCTION_PAYLOAD UID 1 "fn4 arg1 arg2" 189 payload line3 190 payload line4 191 FUNCTION_PAYLOAD_END 192 `, 193 expected: []Function{ 194 { 195 key: "FUNCTION_PAYLOAD", 196 UID: "UID", 197 Timeout: time.Second, 198 Name: "fn1", 199 Args: []string{"arg1", "arg2"}, 200 Payload: []byte("payload line1\npayload line2"), 201 }, 202 { 203 key: "FUNCTION", 204 UID: "UID", 205 Timeout: time.Second, 206 Name: "fn2", 207 Args: []string{"arg1", "arg2"}, 208 Payload: nil, 209 }, 210 { 211 key: "FUNCTION", 212 UID: "UID", 213 Timeout: time.Second, 214 Name: "fn3", 215 Args: []string{"arg1", "arg2"}, 216 Payload: nil, 217 }, 218 { 219 key: "FUNCTION_PAYLOAD", 220 UID: "UID", 221 Timeout: time.Second, 222 Name: "fn4", 223 Args: []string{"arg1", "arg2"}, 224 Payload: []byte("payload line3\npayload line4"), 225 }, 226 }, 227 }, 228 } 229 230 for name, test := range tests { 231 t.Run(name, func(t *testing.T) { 232 mgr := NewManager() 233 234 mgr.Input = strings.NewReader(test.input) 235 236 mock := &mockFunctionExecutor{} 237 for _, v := range test.register { 238 mgr.Register(v, mock.execute) 239 } 240 241 testTime := time.Second * 5 242 ctx, cancel := context.WithTimeout(context.Background(), testTime) 243 defer cancel() 244 245 done := make(chan struct{}) 246 247 go func() { defer close(done); mgr.Run(ctx) }() 248 249 timeout := testTime + time.Second*2 250 tk := time.NewTimer(timeout) 251 defer tk.Stop() 252 253 select { 254 case <-done: 255 assert.Equal(t, test.expected, mock.executed) 256 case <-tk.C: 257 t.Errorf("timed out after %s", timeout) 258 } 259 }) 260 } 261 } 262 263 type mockFunctionExecutor struct { 264 executed []Function 265 } 266 267 func (m *mockFunctionExecutor) execute(fn Function) { 268 m.executed = append(m.executed, fn) 269 }