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  }