github.com/Jeffail/benthos/v3@v3.65.0/lib/processor/dedupe_test.go (about) 1 package processor 2 3 import ( 4 "errors" 5 "fmt" 6 "math/rand" 7 "net/http" 8 "reflect" 9 "testing" 10 "time" 11 12 "github.com/Jeffail/benthos/v3/lib/cache" 13 "github.com/Jeffail/benthos/v3/lib/log" 14 "github.com/Jeffail/benthos/v3/lib/message" 15 "github.com/Jeffail/benthos/v3/lib/metrics" 16 "github.com/Jeffail/benthos/v3/lib/response" 17 "github.com/Jeffail/benthos/v3/lib/types" 18 ) 19 20 func init() { 21 rand.Seed(time.Now().UnixNano()) 22 } 23 24 var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 25 26 type fakeMgr struct { 27 caches map[string]types.Cache 28 ratelimits map[string]types.RateLimit 29 } 30 31 func (f *fakeMgr) RegisterEndpoint(path, desc string, h http.HandlerFunc) { 32 } 33 func (f *fakeMgr) GetCache(name string) (types.Cache, error) { 34 if c, exists := f.caches[name]; exists { 35 return c, nil 36 } 37 return nil, types.ErrCacheNotFound 38 } 39 func (f *fakeMgr) GetCondition(name string) (types.Condition, error) { 40 return nil, types.ErrConditionNotFound 41 } 42 func (f *fakeMgr) GetRateLimit(name string) (types.RateLimit, error) { 43 if r, exists := f.ratelimits[name]; exists { 44 return r, nil 45 } 46 return nil, types.ErrRateLimitNotFound 47 } 48 func (f *fakeMgr) GetPlugin(name string) (interface{}, error) { 49 return nil, types.ErrPluginNotFound 50 } 51 func (f *fakeMgr) GetPipe(name string) (<-chan types.Transaction, error) { 52 return nil, types.ErrPipeNotFound 53 } 54 func (f *fakeMgr) SetPipe(name string, prod <-chan types.Transaction) {} 55 func (f *fakeMgr) UnsetPipe(name string, prod <-chan types.Transaction) {} 56 57 func TestDedupe(t *testing.T) { 58 rndText1 := randStringRunes(20) 59 rndText2 := randStringRunes(15) 60 doc1 := []byte(rndText1) 61 doc2 := []byte(rndText1) // duplicate 62 doc3 := []byte(rndText2) 63 64 testLog := log.Noop() 65 66 memCache, cacheErr := cache.NewMemory(cache.NewConfig(), nil, testLog, metrics.Noop()) 67 if cacheErr != nil { 68 t.Fatal(cacheErr) 69 } 70 mgr := &fakeMgr{ 71 caches: map[string]types.Cache{ 72 "foocache": memCache, 73 }, 74 } 75 76 conf := NewConfig() 77 conf.Dedupe.Cache = "foocache" 78 proc, err1 := NewDedupe(conf, mgr, testLog, metrics.Noop()) 79 if err1 != nil { 80 t.Error(err1) 81 return 82 } 83 84 msgIn := message.New([][]byte{doc1}) 85 msgOut, err := proc.ProcessMessage(msgIn) 86 if err != nil && err.Error() != nil { 87 t.Error("Message 1 told not to propagate even if it was expected to propagate. Cache error:", err.Error()) 88 } 89 if msgOut == nil { 90 t.Error("Message 1 told not to propagate even if it was expected to propagate") 91 } 92 93 msgIn = message.New([][]byte{doc2}) 94 msgOut, err = proc.ProcessMessage(msgIn) 95 if err != nil && err.Error() != nil { 96 t.Error("Message 1 told to propagate even if it was expected not to propagate. Cache error:", err.Error()) 97 } 98 if msgOut != nil { 99 t.Error("Message 2 told to propagate even if it was expected not to propagate") 100 } 101 102 msgIn = message.New([][]byte{doc3}) 103 msgOut, err = proc.ProcessMessage(msgIn) 104 if err != nil && err.Error() != nil { 105 t.Error("Message 1 told not to propagate even if it was expected to propagate. Cache error:", err.Error()) 106 } 107 if msgOut == nil { 108 t.Error("Message 3 told not to propagate even if it was expected to propagate") 109 } 110 } 111 112 func TestDedupeInterpolation(t *testing.T) { 113 rndText1 := randStringRunes(20) 114 rndText2 := randStringRunes(15) 115 doc1 := []byte(fmt.Sprintf(`{"id":%q,"content":"foo"}`, rndText1)) 116 doc2 := []byte(fmt.Sprintf(`{"id":%q,"content":"bar"}`, rndText1)) // duplicate 117 doc3 := []byte(fmt.Sprintf(`{"id":%q,"content":"foo"}`, rndText2)) 118 doc4 := []byte(`{"content":"foo"}`) 119 120 memCache, cacheErr := cache.NewMemory(cache.NewConfig(), nil, log.Noop(), metrics.Noop()) 121 if cacheErr != nil { 122 t.Fatal(cacheErr) 123 } 124 mgr := &fakeMgr{ 125 caches: map[string]types.Cache{ 126 "foocache": memCache, 127 }, 128 } 129 130 conf := NewConfig() 131 conf.Dedupe.Cache = "foocache" 132 conf.Dedupe.Key = "${! json(\"id\") }${! json(\"never.exists\") }" 133 conf.Dedupe.DropOnCacheErr = false 134 proc, err1 := NewDedupe(conf, mgr, log.Noop(), metrics.Noop()) 135 if err1 != nil { 136 t.Error(err1) 137 return 138 } 139 140 msgIn := message.New([][]byte{doc1}) 141 msgOut, err := proc.ProcessMessage(msgIn) 142 if err != nil && err.Error() != nil { 143 t.Error("Message 1 told not to propagate even if it was expected to propagate. Cache error:", err.Error()) 144 } 145 if msgOut == nil { 146 t.Error("Message 1 told not to propagate even if it was expected to propagate") 147 } 148 149 msgIn = message.New([][]byte{doc2}) 150 msgOut, err = proc.ProcessMessage(msgIn) 151 if err != nil && err.Error() != nil { 152 t.Error("Message 3 told to propagate even if it was expected not to propagate. Cache error:", err.Error()) 153 } 154 if msgOut != nil { 155 t.Error("Message 2 told to propagate even if it was expected not to propagate") 156 } 157 158 msgIn = message.New([][]byte{doc3}) 159 msgOut, err = proc.ProcessMessage(msgIn) 160 if err != nil && err.Error() != nil { 161 t.Error("Message 3 told not to propagate even if it was expected to propagate. Cache error:", err.Error()) 162 } 163 if msgOut == nil { 164 t.Error("Message 3 told not to propagate even if it was expected to propagate") 165 } 166 167 msgIn = message.New([][]byte{doc4}) 168 msgOut, err = proc.ProcessMessage(msgIn) 169 if err != nil && err.Error() != nil { 170 t.Error("Message 4 told not to propagate even if it was expected to propagate. Cache error:", err.Error()) 171 } 172 if msgOut == nil { 173 t.Error("Message 4 told not to propagate even if it was expected to propagate") 174 } 175 } 176 177 func TestDedupeXXHash(t *testing.T) { 178 rndText1 := randStringRunes(20) 179 rndText2 := randStringRunes(15) 180 doc1 := []byte(rndText1) 181 doc2 := []byte(rndText1) // duplicate 182 doc3 := []byte(rndText2) 183 184 testLog := log.Noop() 185 186 memCache, cacheErr := cache.NewMemory(cache.NewConfig(), nil, testLog, metrics.Noop()) 187 if cacheErr != nil { 188 t.Fatal(cacheErr) 189 } 190 mgr := &fakeMgr{ 191 caches: map[string]types.Cache{ 192 "foocache": memCache, 193 }, 194 } 195 196 conf := NewConfig() 197 conf.Dedupe.Cache = "foocache" 198 conf.Dedupe.HashType = "xxhash" 199 proc, err1 := NewDedupe(conf, mgr, testLog, metrics.Noop()) 200 if err1 != nil { 201 t.Error(err1) 202 return 203 } 204 205 msgIn := message.New([][]byte{doc1}) 206 msgOut, err := proc.ProcessMessage(msgIn) 207 if err != nil && err.Error() != nil { 208 t.Error("Message 1 told not to propagate even if it was expected to propagate. Cache error:", err.Error()) 209 } 210 if msgOut == nil { 211 t.Error("Message 1 told not to propagate even if it was expected to propagate") 212 } 213 214 msgIn = message.New([][]byte{doc2}) 215 msgOut, err = proc.ProcessMessage(msgIn) 216 if err != nil && err.Error() != nil { 217 t.Error("Message 1 told to propagate even if it was expected not to propagate. Cache error:", err.Error()) 218 } 219 if msgOut != nil { 220 t.Error("Message 2 told to propagate even if it was expected not to propagate") 221 } 222 223 msgIn = message.New([][]byte{doc3}) 224 msgOut, err = proc.ProcessMessage(msgIn) 225 if err != nil && err.Error() != nil { 226 t.Error("Message 1 told not to propagate even if it was expected to propagate. Cache error:", err.Error()) 227 } 228 if msgOut == nil { 229 t.Error("Message 3 told not to propagate even if it was expected to propagate") 230 } 231 } 232 233 func TestDedupePartSelection(t *testing.T) { 234 hdr := []byte(`some header`) 235 rndText1 := randStringRunes(20) 236 rndText2 := randStringRunes(15) 237 doc1 := []byte(rndText1) 238 doc2 := []byte(rndText1) // duplicate 239 doc3 := []byte(rndText2) 240 241 testLog := log.Noop() 242 243 memCache, cacheErr := cache.NewMemory(cache.NewConfig(), nil, testLog, metrics.Noop()) 244 if cacheErr != nil { 245 t.Fatal(cacheErr) 246 } 247 mgr := &fakeMgr{ 248 caches: map[string]types.Cache{ 249 "foocache": memCache, 250 }, 251 } 252 253 conf := NewConfig() 254 conf.Dedupe.Cache = "foocache" 255 conf.Dedupe.Parts = []int{1} // only take the 2nd part 256 proc, err1 := NewDedupe(conf, mgr, testLog, metrics.Noop()) 257 if err1 != nil { 258 t.Error(err1) 259 return 260 } 261 262 msgIn := message.New([][]byte{hdr, doc1}) 263 msgOut, err := proc.ProcessMessage(msgIn) 264 if err != nil && err.Error() != nil { 265 t.Error("Message 1 told not to propagate even if it was expected to propagate. Cache error:", err.Error()) 266 } 267 if msgOut == nil { 268 t.Error("Message 1 told not to propagate even if it was expected to propagate") 269 } 270 271 msgIn = message.New([][]byte{hdr, doc2}) 272 msgOut, err = proc.ProcessMessage(msgIn) 273 if err != nil && err.Error() != nil { 274 t.Error("Message 1 told to propagate even if it was expected not to propagate. Cache error:", err.Error()) 275 } 276 if msgOut != nil { 277 t.Error("Message 2 told to propagate even if it was expected not to propagate") 278 } 279 280 msgIn = message.New([][]byte{hdr, doc3}) 281 msgOut, err = proc.ProcessMessage(msgIn) 282 if err != nil && err.Error() != nil { 283 t.Error("Message 1 told not to propagate even if it was expected to propagate. Cache error:", err.Error()) 284 } 285 if msgOut == nil { 286 t.Error("Message 3 told not to propagate even if it was expected to propagate") 287 } 288 } 289 290 func TestDedupeBadCache(t *testing.T) { 291 conf := NewConfig() 292 conf.Dedupe.Cache = "foocache" 293 294 testLog := log.Noop() 295 296 mgr := &fakeMgr{ 297 caches: map[string]types.Cache{}, 298 } 299 if _, err := NewDedupe(conf, mgr, testLog, metrics.Noop()); err == nil { 300 t.Error("Expected error from missing cache") 301 } 302 } 303 304 type errCache struct{} 305 306 func (e errCache) Get(key string) ([]byte, error) { 307 return nil, errors.New("test err") 308 } 309 func (e errCache) Set(key string, value []byte) error { 310 return errors.New("test err") 311 } 312 func (e errCache) SetWithTTL(key string, value []byte, ttl *time.Duration) error { 313 return errors.New("test err") 314 } 315 func (e errCache) SetMulti(items map[string][]byte) error { 316 return errors.New("test err") 317 } 318 func (e errCache) SetMultiWithTTL(items map[string][]byte, ttl *time.Duration) error { 319 return errors.New("test err") 320 } 321 func (e errCache) Add(key string, value []byte) error { 322 return errors.New("test err") 323 } 324 func (e errCache) AddWithTTL(key string, value []byte, ttl *time.Duration) error { 325 return errors.New("test err") 326 } 327 func (e errCache) Delete(key string) error { 328 return errors.New("test err") 329 } 330 func (e errCache) CloseAsync() { 331 } 332 func (e errCache) WaitForClose(timeout time.Duration) error { 333 return nil 334 } 335 336 func TestDedupeCacheErrors(t *testing.T) { 337 conf := NewConfig() 338 conf.Dedupe.Cache = "foocache" 339 340 testLog := log.Noop() 341 342 mgr := &fakeMgr{ 343 caches: map[string]types.Cache{ 344 "foocache": errCache{}, 345 }, 346 } 347 348 proc, err := NewDedupe(conf, mgr, testLog, metrics.Noop()) 349 if err != nil { 350 t.Fatal(err) 351 } 352 353 msgs, res := proc.ProcessMessage(message.New([][]byte{[]byte("foo"), []byte("bar")})) 354 if exp := response.NewAck(); !reflect.DeepEqual(exp, res) || len(msgs) > 0 { 355 t.Errorf("Expected message drop on error: %v - %v", res, len(msgs)) 356 } 357 358 conf.Dedupe.DropOnCacheErr = false 359 360 proc, err = NewDedupe(conf, mgr, testLog, metrics.Noop()) 361 if err != nil { 362 t.Fatal(err) 363 } 364 365 msgs, res = proc.ProcessMessage(message.New([][]byte{[]byte("foo"), []byte("bar")})) 366 if res != nil || len(msgs) != 1 { 367 t.Errorf("Expected message propagate on error: %v - %v", res, len(msgs)) 368 } 369 } 370 371 func TestDedupeBadHash(t *testing.T) { 372 conf := NewConfig() 373 conf.Dedupe.Cache = "foocache" 374 conf.Dedupe.HashType = "notexist" 375 376 testLog := log.Noop() 377 378 memCache, cacheErr := cache.NewMemory(cache.NewConfig(), nil, testLog, metrics.Noop()) 379 if cacheErr != nil { 380 t.Fatal(cacheErr) 381 } 382 mgr := &fakeMgr{ 383 caches: map[string]types.Cache{ 384 "foocache": memCache, 385 }, 386 } 387 if _, err := NewDedupe(conf, mgr, testLog, metrics.Noop()); err == nil { 388 t.Error("Expected error from bad hash") 389 } 390 } 391 392 func TestDedupeBoundsCheck(t *testing.T) { 393 conf := NewConfig() 394 conf.Dedupe.Cache = "foocache" 395 conf.Dedupe.Parts = []int{5} 396 397 testLog := log.Noop() 398 399 memCache, cacheErr := cache.NewMemory(cache.NewConfig(), nil, testLog, metrics.Noop()) 400 if cacheErr != nil { 401 t.Fatal(cacheErr) 402 } 403 mgr := &fakeMgr{ 404 caches: map[string]types.Cache{ 405 "foocache": memCache, 406 }, 407 } 408 409 proc, err1 := NewDedupe(conf, mgr, testLog, metrics.Noop()) 410 if err1 != nil { 411 t.Fatal(err1) 412 } 413 414 msgIn := message.New([][]byte{}) 415 msgs, res := proc.ProcessMessage(msgIn) 416 if len(msgs) > 0 { 417 t.Error("OOB message told to propagate") 418 } 419 420 if exp, act := response.NewAck(), res; !reflect.DeepEqual(exp, act) { 421 t.Errorf("Wrong response returned: %v != %v", act, exp) 422 } 423 } 424 425 func TestDedupeNegBoundsCheck(t *testing.T) { 426 conf := NewConfig() 427 conf.Dedupe.Cache = "foocache" 428 conf.Dedupe.Parts = []int{-5} 429 430 testLog := log.Noop() 431 432 memCache, cacheErr := cache.NewMemory(cache.NewConfig(), nil, testLog, metrics.Noop()) 433 if cacheErr != nil { 434 t.Fatal(cacheErr) 435 } 436 mgr := &fakeMgr{ 437 caches: map[string]types.Cache{ 438 "foocache": memCache, 439 }, 440 } 441 442 proc, err1 := NewDedupe(conf, mgr, testLog, metrics.Noop()) 443 if err1 != nil { 444 t.Fatal(err1) 445 } 446 447 msgIn := message.New([][]byte{}) 448 msgs, res := proc.ProcessMessage(msgIn) 449 if len(msgs) > 0 { 450 t.Error("OOB message told to propagate") 451 } 452 453 if exp, act := response.NewAck(), res; !reflect.DeepEqual(exp, act) { 454 t.Errorf("Wrong response returned: %v != %v", act, exp) 455 } 456 } 457 458 func randStringRunes(n int) string { 459 b := make([]rune, n) 460 for i := range b { 461 b[i] = letterRunes[rand.Intn(len(letterRunes))] 462 } 463 return string(b) 464 }