github.com/Jeffail/benthos/v3@v3.65.0/internal/docs/config_test.go (about) 1 package docs_test 2 3 import ( 4 "fmt" 5 "testing" 6 7 "github.com/Jeffail/benthos/v3/internal/docs" 8 "github.com/stretchr/testify/assert" 9 "github.com/stretchr/testify/require" 10 "gopkg.in/yaml.v3" 11 ) 12 13 func TestInference(t *testing.T) { 14 docsProv := docs.NewMappedDocsProvider() 15 for _, t := range docs.Types() { 16 docsProv.RegisterDocs(docs.ComponentSpec{ 17 Name: fmt.Sprintf("testfoo%v", string(t)), 18 Type: t, 19 }) 20 docsProv.RegisterDocs(docs.ComponentSpec{ 21 Name: fmt.Sprintf("testbar%v", string(t)), 22 Type: t, 23 }) 24 } 25 26 type testCase struct { 27 inputType docs.Type 28 inputConf interface{} 29 inputDefault string 30 31 res string 32 err string 33 } 34 35 tests := []testCase{ 36 { 37 inputType: docs.TypeInput, 38 inputConf: map[string]interface{}{ 39 "processors": "yep", 40 }, 41 inputDefault: "testfooinput", 42 res: "testfooinput", 43 }, 44 { 45 inputType: docs.TypeOutput, 46 inputConf: map[string]interface{}{ 47 "foo": "yep", 48 "bar": "yep", 49 "processors": "yep", 50 }, 51 err: "unable to infer output type, candidates were: [bar foo]", 52 }, 53 { 54 inputType: docs.TypeInput, 55 inputConf: map[string]interface{}{ 56 "foo": "yep", 57 "bar": "yep", 58 "processors": "yep", 59 }, 60 err: "unable to infer input type, candidates were: [bar foo]", 61 }, 62 { 63 inputType: docs.TypeTracer, 64 inputConf: map[string]interface{}{ 65 "testbartracer": "baz", 66 "testbarbuffer": "baz", 67 }, 68 res: "testbartracer", 69 }, 70 { 71 inputType: docs.TypeRateLimit, 72 inputConf: map[string]interface{}{ 73 "testbarrate_limit": "baz", 74 "testbarbuffer": "baz", 75 }, 76 res: "testbarrate_limit", 77 }, 78 { 79 inputType: docs.TypeProcessor, 80 inputConf: map[string]interface{}{ 81 "testbarprocessor": "baz", 82 "testbarbuffer": "baz", 83 }, 84 res: "testbarprocessor", 85 }, 86 { 87 inputType: docs.TypeOutput, 88 inputConf: map[string]interface{}{ 89 "testbaroutput": "baz", 90 "testbarbuffer": "baz", 91 }, 92 res: "testbaroutput", 93 }, 94 { 95 inputType: docs.TypeMetrics, 96 inputConf: map[string]interface{}{ 97 "testfoometrics": "baz", 98 "testbarbuffer": "baz", 99 }, 100 res: "testfoometrics", 101 }, 102 { 103 inputType: docs.TypeInput, 104 inputConf: map[string]interface{}{ 105 "testfooinput": "baz", 106 "testbarbuffer": "baz", 107 }, 108 res: "testfooinput", 109 }, 110 { 111 inputType: docs.TypeCache, 112 inputConf: map[string]interface{}{ 113 "testfoocache": "baz", 114 "testbarbuffer": "baz", 115 }, 116 res: "testfoocache", 117 }, 118 { 119 inputType: docs.TypeBuffer, 120 inputConf: map[string]interface{}{ 121 "testfoobuffer": "baz", 122 "testbarbuffer": "baz", 123 }, 124 err: "unable to infer buffer type, multiple candidates 'testbarbuffer' and 'testfoobuffer'", 125 }, 126 { 127 inputType: docs.TypeBuffer, 128 inputConf: map[string]interface{}{ 129 "testfoobuffer": "baz", 130 }, 131 res: "testfoobuffer", 132 }, 133 { 134 inputType: docs.TypeBuffer, 135 inputConf: map[string]interface{}{ 136 "type": "testfoobuffer", 137 "foobar": "baz", 138 }, 139 res: "testfoobuffer", 140 }, 141 { 142 inputType: docs.TypeBuffer, 143 inputConf: map[string]interface{}{ 144 "type": "notreal", 145 "foobar": "baz", 146 }, 147 err: "buffer type 'notreal' was not recognised", 148 }, 149 { 150 inputType: docs.TypeBuffer, 151 err: "invalid config value <nil>, expected object", 152 }, 153 } 154 155 for i, test := range tests { 156 res, spec, err := docs.GetInferenceCandidate(docsProv, test.inputType, test.inputDefault, test.inputConf) 157 if len(test.err) > 0 { 158 assert.EqualError(t, err, test.err, "test: %v", i) 159 } else { 160 assert.Equal(t, test.res, spec.Name) 161 assert.Equal(t, test.inputType, spec.Type) 162 assert.NoError(t, err) 163 assert.Equal(t, test.res, res, "test: %v", i) 164 } 165 166 var node yaml.Node 167 require.NoError(t, node.Encode(test.inputConf)) 168 res, spec, err = docs.GetInferenceCandidateFromYAML(docsProv, test.inputType, test.inputDefault, &node) 169 if len(test.err) > 0 { 170 assert.Error(t, err) 171 } else { 172 assert.Equal(t, test.res, spec.Name) 173 assert.Equal(t, test.inputType, spec.Type) 174 assert.NoError(t, err) 175 assert.Equal(t, test.res, res, "test: %v", i) 176 } 177 } 178 } 179 180 func TestSanitation(t *testing.T) { 181 for _, t := range docs.Types() { 182 docs.RegisterDocs(docs.ComponentSpec{ 183 Name: fmt.Sprintf("testsanitfoo%v", string(t)), 184 Type: t, 185 Config: docs.FieldComponent().WithChildren( 186 docs.FieldCommon("foo1", ""), 187 docs.FieldAdvanced("foo2", ""), 188 docs.FieldCommon("foo3", "").HasType(docs.FieldTypeProcessor), 189 docs.FieldAdvanced("foo4", "").Array().HasType(docs.FieldTypeProcessor), 190 docs.FieldCommon("foo5", "").Map().HasType(docs.FieldTypeProcessor), 191 docs.FieldDeprecated("foo6"), 192 ), 193 }) 194 docs.RegisterDocs(docs.ComponentSpec{ 195 Name: fmt.Sprintf("testsanitbar%v", string(t)), 196 Type: t, 197 Config: docs.FieldComponent().Array().WithChildren( 198 docs.FieldCommon("bar1", ""), 199 docs.FieldAdvanced("bar2", ""), 200 docs.FieldCommon("bar3", "").HasType(docs.FieldTypeProcessor), 201 ), 202 }) 203 docs.RegisterDocs(docs.ComponentSpec{ 204 Name: fmt.Sprintf("testsanitbaz%v", string(t)), 205 Type: t, 206 Config: docs.FieldComponent().Map().WithChildren( 207 docs.FieldCommon("baz1", ""), 208 docs.FieldAdvanced("baz2", ""), 209 docs.FieldCommon("baz3", "").HasType(docs.FieldTypeProcessor), 210 ), 211 }) 212 } 213 214 type testCase struct { 215 name string 216 inputType docs.Type 217 inputConf interface{} 218 inputFilter func(f docs.FieldSpec) bool 219 220 res interface{} 221 err string 222 } 223 224 tests := []testCase{ 225 { 226 name: "input with processors", 227 inputType: docs.TypeInput, 228 inputConf: map[string]interface{}{ 229 "testsanitfooinput": map[string]interface{}{ 230 "foo1": "simple field", 231 "foo2": "advanced field", 232 "foo6": "deprecated field", 233 }, 234 "someotherinput": map[string]interface{}{ 235 "ignore": "me please", 236 }, 237 "processors": []interface{}{ 238 map[string]interface{}{ 239 "testsanitbarprocessor": map[string]interface{}{ 240 "bar1": "bar value", 241 "bar5": "undocumented field", 242 }, 243 "someotherprocessor": map[string]interface{}{ 244 "ignore": "me please", 245 }, 246 }, 247 }, 248 }, 249 res: map[string]interface{}{ 250 "testsanitfooinput": map[string]interface{}{ 251 "foo1": "simple field", 252 "foo2": "advanced field", 253 "foo6": "deprecated field", 254 }, 255 "processors": []interface{}{ 256 map[string]interface{}{ 257 "testsanitbarprocessor": map[string]interface{}{ 258 "bar1": "bar value", 259 "bar5": "undocumented field", 260 }, 261 }, 262 }, 263 }, 264 }, 265 { 266 name: "output array with nested map processor", 267 inputType: docs.TypeOutput, 268 inputConf: map[string]interface{}{ 269 "testsanitbaroutput": []interface{}{ 270 map[string]interface{}{ 271 "bar1": "simple field", 272 "bar3": map[string]interface{}{ 273 "testsanitbazprocessor": map[string]interface{}{ 274 "customkey1": map[string]interface{}{ 275 "baz1": "simple field", 276 }, 277 }, 278 "someotherprocessor": map[string]interface{}{ 279 "ignore": "me please", 280 }, 281 }, 282 }, 283 map[string]interface{}{ 284 "bar2": "advanced field", 285 }, 286 }, 287 }, 288 res: map[string]interface{}{ 289 "testsanitbaroutput": []interface{}{ 290 map[string]interface{}{ 291 "bar1": "simple field", 292 "bar3": map[string]interface{}{ 293 "testsanitbazprocessor": map[string]interface{}{ 294 "customkey1": map[string]interface{}{ 295 "baz1": "simple field", 296 }, 297 }, 298 }, 299 }, 300 map[string]interface{}{ 301 "bar2": "advanced field", 302 }, 303 }, 304 }, 305 }, 306 { 307 name: "metrics map with nested map processor", 308 inputType: docs.TypeMetrics, 309 inputConf: map[string]interface{}{ 310 "testsanitbazmetrics": map[string]interface{}{ 311 "customkey1": map[string]interface{}{ 312 "baz1": "simple field", 313 "baz3": map[string]interface{}{ 314 "testsanitbazprocessor": map[string]interface{}{ 315 "customkey1": map[string]interface{}{ 316 "baz1": "simple field", 317 }, 318 }, 319 "someotherprocessor": map[string]interface{}{ 320 "ignore": "me please", 321 }, 322 }, 323 }, 324 "customkey2": map[string]interface{}{ 325 "baz2": "advanced field", 326 }, 327 }, 328 }, 329 res: map[string]interface{}{ 330 "testsanitbazmetrics": map[string]interface{}{ 331 "customkey1": map[string]interface{}{ 332 "baz1": "simple field", 333 "baz3": map[string]interface{}{ 334 "testsanitbazprocessor": map[string]interface{}{ 335 "customkey1": map[string]interface{}{ 336 "baz1": "simple field", 337 }, 338 }, 339 }, 340 }, 341 "customkey2": map[string]interface{}{ 342 "baz2": "advanced field", 343 }, 344 }, 345 }, 346 }, 347 { 348 name: "ratelimit with array field processor", 349 inputType: docs.TypeRateLimit, 350 inputConf: map[string]interface{}{ 351 "testsanitfoorate_limit": map[string]interface{}{ 352 "foo1": "simple field", 353 "foo4": []interface{}{ 354 map[string]interface{}{ 355 "testsanitbazprocessor": map[string]interface{}{ 356 "customkey1": map[string]interface{}{ 357 "baz1": "simple field", 358 }, 359 }, 360 "someotherprocessor": map[string]interface{}{ 361 "ignore": "me please", 362 }, 363 }, 364 }, 365 }, 366 }, 367 res: map[string]interface{}{ 368 "testsanitfoorate_limit": map[string]interface{}{ 369 "foo1": "simple field", 370 "foo4": []interface{}{ 371 map[string]interface{}{ 372 "testsanitbazprocessor": map[string]interface{}{ 373 "customkey1": map[string]interface{}{ 374 "baz1": "simple field", 375 }, 376 }, 377 }, 378 }, 379 }, 380 }, 381 }, 382 { 383 name: "ratelimit with map field processor", 384 inputType: docs.TypeRateLimit, 385 inputConf: map[string]interface{}{ 386 "testsanitfoorate_limit": map[string]interface{}{ 387 "foo1": "simple field", 388 "foo5": map[string]interface{}{ 389 "customkey1": map[string]interface{}{ 390 "testsanitbazprocessor": map[string]interface{}{ 391 "customkey1": map[string]interface{}{ 392 "baz1": "simple field", 393 }, 394 }, 395 "someotherprocessor": map[string]interface{}{ 396 "ignore": "me please", 397 }, 398 }, 399 }, 400 }, 401 }, 402 res: map[string]interface{}{ 403 "testsanitfoorate_limit": map[string]interface{}{ 404 "foo1": "simple field", 405 "foo5": map[string]interface{}{ 406 "customkey1": map[string]interface{}{ 407 "testsanitbazprocessor": map[string]interface{}{ 408 "customkey1": map[string]interface{}{ 409 "baz1": "simple field", 410 }, 411 }, 412 }, 413 }, 414 }, 415 }, 416 }, 417 { 418 name: "input with processors no deprecated", 419 inputType: docs.TypeInput, 420 inputFilter: docs.ShouldDropDeprecated(true), 421 inputConf: map[string]interface{}{ 422 "testsanitfooinput": map[string]interface{}{ 423 "foo1": "simple field", 424 "foo2": "advanced field", 425 "foo6": "deprecated field", 426 }, 427 "someotherinput": map[string]interface{}{ 428 "ignore": "me please", 429 }, 430 "processors": []interface{}{ 431 map[string]interface{}{ 432 "testsanitfooprocessor": map[string]interface{}{ 433 "foo1": "simple field", 434 "foo2": "advanced field", 435 "foo6": "deprecated field", 436 }, 437 "someotherprocessor": map[string]interface{}{ 438 "ignore": "me please", 439 }, 440 }, 441 }, 442 }, 443 res: map[string]interface{}{ 444 "testsanitfooinput": map[string]interface{}{ 445 "foo1": "simple field", 446 "foo2": "advanced field", 447 }, 448 "processors": []interface{}{ 449 map[string]interface{}{ 450 "testsanitfooprocessor": map[string]interface{}{ 451 "foo1": "simple field", 452 "foo2": "advanced field", 453 }, 454 }, 455 }, 456 }, 457 }, 458 } 459 460 for _, test := range tests { 461 test := test 462 t.Run(test.name, func(t *testing.T) { 463 err := docs.SanitiseComponentConfig(test.inputType, test.inputConf, test.inputFilter) 464 if len(test.err) > 0 { 465 assert.EqualError(t, err, test.err) 466 } else { 467 assert.NoError(t, err) 468 assert.Equal(t, test.res, test.inputConf) 469 } 470 }) 471 } 472 }