github.com/Jeffail/benthos/v3@v3.65.0/lib/broker/try_test.go (about)

     1  package broker
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"sync"
     8  	"sync/atomic"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/Jeffail/benthos/v3/lib/message"
    13  	"github.com/Jeffail/benthos/v3/lib/metrics"
    14  	"github.com/Jeffail/benthos/v3/lib/response"
    15  	"github.com/Jeffail/benthos/v3/lib/types"
    16  )
    17  
    18  var _ types.Consumer = &Try{}
    19  var _ types.Closable = &Try{}
    20  
    21  func TestTryDoubleClose(t *testing.T) {
    22  	oTM, err := NewTry([]types.Output{&MockOutputType{}}, metrics.Noop())
    23  	if err != nil {
    24  		t.Fatal(err)
    25  	}
    26  
    27  	// This shouldn't cause a panic
    28  	oTM.CloseAsync()
    29  	oTM.CloseAsync()
    30  }
    31  
    32  //------------------------------------------------------------------------------
    33  
    34  func TestTryHappyPath(t *testing.T) {
    35  	outputs := []types.Output{}
    36  	mockOutputs := []*MockOutputType{
    37  		{},
    38  		{},
    39  		{},
    40  	}
    41  
    42  	for _, o := range mockOutputs {
    43  		outputs = append(outputs, o)
    44  	}
    45  
    46  	readChan := make(chan types.Transaction)
    47  	resChan := make(chan types.Response)
    48  
    49  	oTM, err := NewTry(outputs, metrics.Noop())
    50  	if err != nil {
    51  		t.Error(err)
    52  		return
    53  	}
    54  	if err = oTM.Consume(readChan); err != nil {
    55  		t.Error(err)
    56  		return
    57  	}
    58  
    59  	for i := 0; i < 10; i++ {
    60  		content := [][]byte{[]byte(fmt.Sprintf("hello world %v", i))}
    61  		select {
    62  		case readChan <- types.NewTransaction(message.New(content), resChan):
    63  		case <-time.After(time.Second):
    64  			t.Errorf("Timed out waiting for broker send")
    65  			return
    66  		}
    67  
    68  		go func() {
    69  			var ts types.Transaction
    70  			select {
    71  			case ts = <-mockOutputs[0].TChan:
    72  				if !bytes.Equal(ts.Payload.Get(0).Get(), content[0]) {
    73  					t.Errorf("Wrong content returned %s != %s", ts.Payload.Get(0).Get(), content[0])
    74  				}
    75  			case <-mockOutputs[1].TChan:
    76  				t.Error("Received message in wrong order")
    77  				return
    78  			case <-mockOutputs[2].TChan:
    79  				t.Error("Received message in wrong order")
    80  				return
    81  			case <-time.After(time.Second):
    82  				t.Errorf("Timed out waiting for broker propagate")
    83  				return
    84  			}
    85  
    86  			select {
    87  			case ts.ResponseChan <- response.NewAck():
    88  			case <-time.After(time.Second):
    89  				t.Errorf("Timed out responding to broker")
    90  				return
    91  			}
    92  		}()
    93  
    94  		select {
    95  		case res := <-resChan:
    96  			if res.Error() != nil {
    97  				t.Error(res.Error())
    98  			}
    99  		case <-time.After(time.Second):
   100  			t.Errorf("Timed out responding to broker")
   101  			return
   102  		}
   103  	}
   104  
   105  	oTM.CloseAsync()
   106  	if err := oTM.WaitForClose(time.Second * 10); err != nil {
   107  		t.Error(err)
   108  	}
   109  }
   110  
   111  func TestTryHappyishPath(t *testing.T) {
   112  	outputs := []types.Output{}
   113  	mockOutputs := []*MockOutputType{
   114  		{},
   115  		{},
   116  		{},
   117  	}
   118  
   119  	for _, o := range mockOutputs {
   120  		outputs = append(outputs, o)
   121  	}
   122  
   123  	readChan := make(chan types.Transaction)
   124  	resChan := make(chan types.Response)
   125  
   126  	oTM, err := NewTry(outputs, metrics.Noop())
   127  	if err != nil {
   128  		t.Error(err)
   129  		return
   130  	}
   131  	if err = oTM.Consume(readChan); err != nil {
   132  		t.Error(err)
   133  		return
   134  	}
   135  
   136  	for i := 0; i < 10; i++ {
   137  		content := [][]byte{[]byte(fmt.Sprintf("hello world %v", i))}
   138  		select {
   139  		case readChan <- types.NewTransaction(message.New(content), resChan):
   140  		case <-time.After(time.Second):
   141  			t.Errorf("Timed out waiting for broker send")
   142  			return
   143  		}
   144  
   145  		go func() {
   146  			var ts types.Transaction
   147  			select {
   148  			case ts = <-mockOutputs[0].TChan:
   149  				if !bytes.Equal(ts.Payload.Get(0).Get(), content[0]) {
   150  					t.Errorf("Wrong content returned %s != %s", ts.Payload.Get(0).Get(), content[0])
   151  				}
   152  			case <-mockOutputs[1].TChan:
   153  				t.Error("Received message in wrong order")
   154  				return
   155  			case <-mockOutputs[2].TChan:
   156  				t.Error("Received message in wrong order")
   157  				return
   158  			case <-time.After(time.Second):
   159  				t.Errorf("Timed out waiting for broker propagate")
   160  				return
   161  			}
   162  
   163  			select {
   164  			case ts.ResponseChan <- response.NewError(errors.New("test err")):
   165  			case <-time.After(time.Second):
   166  				t.Errorf("Timed out responding to broker")
   167  				return
   168  			}
   169  
   170  			select {
   171  			case ts = <-mockOutputs[1].TChan:
   172  				if !bytes.Equal(ts.Payload.Get(0).Get(), content[0]) {
   173  					t.Errorf("Wrong content returned %s != %s", ts.Payload.Get(0).Get(), content[0])
   174  				}
   175  			case <-mockOutputs[0].TChan:
   176  				t.Error("Received message in wrong order")
   177  				return
   178  			case <-mockOutputs[2].TChan:
   179  				t.Error("Received message in wrong order")
   180  				return
   181  			case <-time.After(time.Second):
   182  				t.Errorf("Timed out waiting for broker propagate")
   183  				return
   184  			}
   185  
   186  			select {
   187  			case ts.ResponseChan <- response.NewAck():
   188  			case <-time.After(time.Second):
   189  				t.Errorf("Timed out responding to broker")
   190  				return
   191  			}
   192  		}()
   193  
   194  		select {
   195  		case res := <-resChan:
   196  			if res.Error() != nil {
   197  				t.Error(res.Error())
   198  			}
   199  		case <-time.After(time.Second):
   200  			t.Errorf("Timed out responding to broker")
   201  			return
   202  		}
   203  	}
   204  
   205  	oTM.CloseAsync()
   206  	if err := oTM.WaitForClose(time.Second * 10); err != nil {
   207  		t.Error(err)
   208  	}
   209  }
   210  
   211  func TestTryAllFail(t *testing.T) {
   212  	outputs := []types.Output{}
   213  	mockOutputs := []*MockOutputType{
   214  		{},
   215  		{},
   216  		{},
   217  	}
   218  
   219  	for _, o := range mockOutputs {
   220  		outputs = append(outputs, o)
   221  	}
   222  
   223  	readChan := make(chan types.Transaction)
   224  	resChan := make(chan types.Response)
   225  
   226  	oTM, err := NewTry(outputs, metrics.Noop())
   227  	if err != nil {
   228  		t.Fatal(err)
   229  	}
   230  	if err = oTM.Consume(readChan); err != nil {
   231  		t.Fatal(err)
   232  	}
   233  
   234  	for i := 0; i < 10; i++ {
   235  		content := [][]byte{[]byte(fmt.Sprintf("hello world %v", i))}
   236  		select {
   237  		case readChan <- types.NewTransaction(message.New(content), resChan):
   238  		case <-time.After(time.Second):
   239  			t.Fatalf("Timed out waiting for broker send")
   240  		}
   241  
   242  		testErr := errors.New("test error")
   243  		go func() {
   244  			for j := 0; j < 3; j++ {
   245  				var ts types.Transaction
   246  				select {
   247  				case ts = <-mockOutputs[j%3].TChan:
   248  					if !bytes.Equal(ts.Payload.Get(0).Get(), content[0]) {
   249  						t.Errorf("Wrong content returned %s != %s", ts.Payload.Get(0).Get(), content[0])
   250  					}
   251  				case <-mockOutputs[(j+1)%3].TChan:
   252  					t.Errorf("Received message in wrong order: %v != %v", j%3, (j+1)%3)
   253  					return
   254  				case <-mockOutputs[(j+2)%3].TChan:
   255  					t.Errorf("Received message in wrong order: %v != %v", j%3, (j+2)%3)
   256  					return
   257  				case <-time.After(time.Second):
   258  					t.Errorf("Timed out waiting for broker propagate")
   259  					return
   260  				}
   261  
   262  				select {
   263  				case ts.ResponseChan <- response.NewError(testErr):
   264  				case <-time.After(time.Second):
   265  					t.Errorf("Timed out responding to broker")
   266  				}
   267  			}
   268  		}()
   269  
   270  		select {
   271  		case res := <-resChan:
   272  			if exp, act := testErr, res.Error(); exp != act {
   273  				t.Errorf("Wrong error returned: %v != %v", act, exp)
   274  			}
   275  		case <-time.After(time.Second):
   276  			t.Fatal("Timed out responding to broker")
   277  		}
   278  	}
   279  
   280  	oTM.CloseAsync()
   281  	if err := oTM.WaitForClose(time.Second * 10); err != nil {
   282  		t.Error(err)
   283  	}
   284  }
   285  
   286  func TestTryAllFailParallel(t *testing.T) {
   287  	outputs := []types.Output{}
   288  	mockOutputs := []*MockOutputType{
   289  		{},
   290  		{},
   291  		{},
   292  	}
   293  
   294  	for _, o := range mockOutputs {
   295  		outputs = append(outputs, o)
   296  	}
   297  
   298  	readChan := make(chan types.Transaction)
   299  
   300  	oTM, err := NewTry(outputs, metrics.Noop())
   301  	if err != nil {
   302  		t.Fatal(err)
   303  	}
   304  	oTM = oTM.WithMaxInFlight(50)
   305  	if err = oTM.Consume(readChan); err != nil {
   306  		t.Fatal(err)
   307  	}
   308  
   309  	resChans := make([]chan types.Response, 10)
   310  	for i := range resChans {
   311  		resChans[i] = make(chan types.Response)
   312  	}
   313  
   314  	tallies := [3]int32{}
   315  
   316  	wg, wgStart := sync.WaitGroup{}, sync.WaitGroup{}
   317  	testErr := errors.New("test error")
   318  	startChan := make(chan struct{})
   319  	for _, resChan := range resChans {
   320  		wg.Add(1)
   321  		wgStart.Add(1)
   322  		go func() {
   323  			defer wg.Done()
   324  			for j := 0; j < 3; j++ {
   325  				var ts types.Transaction
   326  				var index int
   327  				select {
   328  				case ts = <-mockOutputs[j%3].TChan:
   329  					index = j % 3
   330  				case ts = <-mockOutputs[(j+1)%3].TChan:
   331  					index = (j + 1) % 3
   332  				case ts = <-mockOutputs[(j+2)%3].TChan:
   333  					index = (j + 2) % 3
   334  				case <-time.After(time.Second):
   335  					t.Errorf("Timed out waiting for broker propagate")
   336  					if j == 0 {
   337  						wgStart.Done()
   338  					}
   339  					return
   340  				}
   341  				atomic.AddInt32(&tallies[index], 1)
   342  				if j == 0 {
   343  					wgStart.Done()
   344  				}
   345  
   346  				<-startChan
   347  
   348  				select {
   349  				case ts.ResponseChan <- response.NewError(testErr):
   350  				case <-time.After(time.Second):
   351  					t.Errorf("Timed out responding to broker")
   352  				}
   353  			}
   354  		}()
   355  		select {
   356  		case readChan <- types.NewTransaction(message.New([][]byte{[]byte("foo")}), resChan):
   357  		case <-time.After(time.Second):
   358  			t.Fatalf("Timed out waiting for broker send")
   359  		}
   360  	}
   361  	wgStart.Wait()
   362  	close(startChan)
   363  
   364  	for _, resChan := range resChans {
   365  		select {
   366  		case res := <-resChan:
   367  			if exp, act := testErr, res.Error(); exp != act {
   368  				t.Errorf("Wrong error returned: %v != %v", act, exp)
   369  			}
   370  		case <-time.After(time.Second):
   371  			t.Error("Timed out responding to broker")
   372  		}
   373  	}
   374  
   375  	wg.Wait()
   376  	for _, tally := range tallies {
   377  		if int(tally) != len(resChans) {
   378  			t.Errorf("Wrong count of propagated messages: %v", tally)
   379  		}
   380  	}
   381  
   382  	oTM.CloseAsync()
   383  	if err := oTM.WaitForClose(time.Second * 10); err != nil {
   384  		t.Error(err)
   385  	}
   386  }
   387  
   388  //------------------------------------------------------------------------------