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

     1  package broker
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"reflect"
     8  	"sort"
     9  	"sync"
    10  	"testing"
    11  	"time"
    12  
    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  var _ types.Producer = &DynamicFanIn{}
    21  var _ types.Closable = &DynamicFanIn{}
    22  
    23  func TestStaticBasicDynamicFanIn(t *testing.T) {
    24  	nInputs, nMsgs := 10, 1000
    25  
    26  	Inputs := map[string]DynamicInput{}
    27  	mockInputs := []*MockInputType{}
    28  	resChan := make(chan types.Response)
    29  
    30  	for i := 0; i < nInputs; i++ {
    31  		mockInputs = append(mockInputs, &MockInputType{
    32  			TChan: make(chan types.Transaction),
    33  		})
    34  		Inputs[fmt.Sprintf("testinput%v", i)] = mockInputs[i]
    35  	}
    36  
    37  	fanIn, err := NewDynamicFanIn(Inputs, log.Noop(), metrics.Noop())
    38  	if err != nil {
    39  		t.Error(err)
    40  		return
    41  	}
    42  
    43  	for i := 0; i < nMsgs; i++ {
    44  		for j := 0; j < nInputs; j++ {
    45  			content := [][]byte{[]byte(fmt.Sprintf("hello world %v", i))}
    46  			select {
    47  			case mockInputs[j].TChan <- types.NewTransaction(message.New(content), resChan):
    48  			case <-time.After(time.Second):
    49  				t.Errorf("Timed out waiting for broker send: %v, %v", i, j)
    50  				return
    51  			}
    52  			go func() {
    53  				var ts types.Transaction
    54  				select {
    55  				case ts = <-fanIn.TransactionChan():
    56  					if !bytes.Equal(ts.Payload.Get(0).Get(), content[0]) {
    57  						t.Errorf("Wrong content returned %s != %s", ts.Payload.Get(0).Get(), content[0])
    58  					}
    59  				case <-time.After(time.Second):
    60  					t.Errorf("Timed out waiting for broker propagate: %v, %v", i, j)
    61  					return
    62  				}
    63  				select {
    64  				case ts.ResponseChan <- response.NewAck():
    65  				case <-time.After(time.Second):
    66  					t.Errorf("Timed out waiting for response to broker: %v, %v", i, j)
    67  					return
    68  				}
    69  			}()
    70  			select {
    71  			case <-resChan:
    72  			case <-time.After(time.Second):
    73  				t.Errorf("Timed out waiting for response to input: %v, %v", i, j)
    74  				return
    75  			}
    76  		}
    77  	}
    78  
    79  	fanIn.CloseAsync()
    80  
    81  	if err := fanIn.WaitForClose(time.Second * 10); err != nil {
    82  		t.Error(err)
    83  	}
    84  }
    85  
    86  func TestBasicDynamicFanIn(t *testing.T) {
    87  	nMsgs := 1000
    88  
    89  	inputOne := &MockInputType{
    90  		TChan: make(chan types.Transaction),
    91  	}
    92  	inputTwo := &MockInputType{
    93  		TChan: make(chan types.Transaction),
    94  	}
    95  
    96  	fanIn, err := NewDynamicFanIn(nil, log.Noop(), metrics.Noop())
    97  	if err != nil {
    98  		t.Error(err)
    99  		return
   100  	}
   101  
   102  	if err = fanIn.SetInput("foo", inputOne, time.Second); err != nil {
   103  		t.Fatal(err)
   104  	}
   105  
   106  	wg := sync.WaitGroup{}
   107  	sendAllTestMessages := func(input *MockInputType, label string) {
   108  		rChan := make(chan types.Response)
   109  		for i := 0; i < nMsgs; i++ {
   110  			content := [][]byte{[]byte(fmt.Sprintf("%v-%v", label, i))}
   111  			input.TChan <- types.NewTransaction(message.New(content), rChan)
   112  			select {
   113  			case <-rChan:
   114  			case <-time.After(time.Second):
   115  				t.Errorf("Timed out waiting for response to input: %v", i)
   116  				return
   117  			}
   118  		}
   119  		wg.Done()
   120  	}
   121  
   122  	wg.Add(2)
   123  	go sendAllTestMessages(inputOne, "inputOne")
   124  	go sendAllTestMessages(inputTwo, "inputTwo")
   125  
   126  	for i := 0; i < nMsgs; i++ {
   127  		var ts types.Transaction
   128  		expContent := fmt.Sprintf("inputOne-%v", i)
   129  		select {
   130  		case ts = <-fanIn.TransactionChan():
   131  			if string(ts.Payload.Get(0).Get()) != expContent {
   132  				t.Errorf("Wrong content returned %s != %s", ts.Payload.Get(0).Get(), expContent)
   133  			}
   134  		case <-time.After(time.Second):
   135  			t.Errorf("Timed out waiting for broker propagate: %v", i)
   136  			return
   137  		}
   138  		select {
   139  		case ts.ResponseChan <- response.NewAck():
   140  		case <-time.After(time.Second):
   141  			t.Errorf("Timed out waiting for response to broker: %v", i)
   142  			return
   143  		}
   144  	}
   145  
   146  	if err = fanIn.SetInput("foo", inputTwo, time.Second); err != nil {
   147  		t.Fatal(err)
   148  	}
   149  
   150  	for i := 0; i < nMsgs; i++ {
   151  		var ts types.Transaction
   152  		expContent := fmt.Sprintf("inputTwo-%v", i)
   153  		select {
   154  		case ts = <-fanIn.TransactionChan():
   155  			if string(ts.Payload.Get(0).Get()) != expContent {
   156  				t.Errorf("Wrong content returned %s != %s", ts.Payload.Get(0).Get(), expContent)
   157  			}
   158  		case <-time.After(time.Second):
   159  			t.Errorf("Timed out waiting for broker propagate: %v", i)
   160  			return
   161  		}
   162  		select {
   163  		case ts.ResponseChan <- response.NewAck():
   164  		case <-time.After(time.Second):
   165  			t.Errorf("Timed out waiting for response to broker: %v", i)
   166  			return
   167  		}
   168  	}
   169  
   170  	wg.Wait()
   171  
   172  	fanIn.CloseAsync()
   173  
   174  	if err := fanIn.WaitForClose(time.Second * 10); err != nil {
   175  		t.Error(err)
   176  	}
   177  }
   178  
   179  func TestStaticDynamicFanInShutdown(t *testing.T) {
   180  	nInputs := 10
   181  
   182  	Inputs := map[string]DynamicInput{}
   183  	mockInputs := []*MockInputType{}
   184  
   185  	expInputAddedList := []string{}
   186  	expInputRemovedList := []string{}
   187  	for i := 0; i < nInputs; i++ {
   188  		mockInputs = append(mockInputs, &MockInputType{
   189  			TChan: make(chan types.Transaction),
   190  		})
   191  		label := fmt.Sprintf("testinput%v", i)
   192  		Inputs[label] = mockInputs[i]
   193  		expInputAddedList = append(expInputAddedList, label)
   194  		expInputRemovedList = append(expInputRemovedList, label)
   195  	}
   196  
   197  	var mapMut sync.Mutex
   198  	inputAddedList := []string{}
   199  	inputRemovedList := []string{}
   200  
   201  	fanIn, err := NewDynamicFanIn(
   202  		Inputs, log.Noop(), metrics.Noop(),
   203  		OptDynamicFanInSetOnAdd(func(label string) {
   204  			mapMut.Lock()
   205  			inputAddedList = append(inputAddedList, label)
   206  			mapMut.Unlock()
   207  		}), OptDynamicFanInSetOnRemove(func(label string) {
   208  			mapMut.Lock()
   209  			inputRemovedList = append(inputRemovedList, label)
   210  			mapMut.Unlock()
   211  		}),
   212  	)
   213  	if err != nil {
   214  		t.Error(err)
   215  		return
   216  	}
   217  
   218  	for _, mockIn := range mockInputs {
   219  		select {
   220  		case _, open := <-mockIn.TransactionChan():
   221  			if !open {
   222  				t.Error("fan in closed early")
   223  			} else {
   224  				t.Error("fan in sent unexpected message")
   225  			}
   226  		default:
   227  		}
   228  	}
   229  
   230  	fanIn.CloseAsync()
   231  
   232  	// All inputs should be closed.
   233  	for _, mockIn := range mockInputs {
   234  		select {
   235  		case _, open := <-mockIn.TransactionChan():
   236  			if open {
   237  				t.Error("fan in sent unexpected message")
   238  			}
   239  		case <-time.After(time.Second):
   240  			t.Error("fan in failed to close an input")
   241  		}
   242  	}
   243  
   244  	if err := fanIn.WaitForClose(time.Second); err != nil {
   245  		t.Error(err)
   246  	}
   247  
   248  	mapMut.Lock()
   249  
   250  	sort.Strings(expInputAddedList)
   251  	sort.Strings(inputAddedList)
   252  	sort.Strings(expInputRemovedList)
   253  	sort.Strings(inputRemovedList)
   254  
   255  	if exp, act := expInputAddedList, inputAddedList; !reflect.DeepEqual(exp, act) {
   256  		t.Errorf("Wrong list of added inputs: %v != %v", act, exp)
   257  	}
   258  	if exp, act := expInputRemovedList, inputRemovedList; !reflect.DeepEqual(exp, act) {
   259  		t.Errorf("Wrong list of removed inputs: %v != %v", act, exp)
   260  	}
   261  
   262  	mapMut.Unlock()
   263  }
   264  
   265  func TestStaticDynamicFanInAsync(t *testing.T) {
   266  	nInputs, nMsgs := 10, 1000
   267  
   268  	Inputs := map[string]DynamicInput{}
   269  	mockInputs := []*MockInputType{}
   270  
   271  	for i := 0; i < nInputs; i++ {
   272  		mockInputs = append(mockInputs, &MockInputType{
   273  			TChan: make(chan types.Transaction),
   274  		})
   275  		Inputs[fmt.Sprintf("testinput%v", i)] = mockInputs[i]
   276  	}
   277  
   278  	fanIn, err := NewDynamicFanIn(Inputs, log.Noop(), metrics.Noop())
   279  	if err != nil {
   280  		t.Error(err)
   281  		return
   282  	}
   283  	defer fanIn.CloseAsync()
   284  
   285  	wg := sync.WaitGroup{}
   286  	wg.Add(nInputs)
   287  
   288  	for j := 0; j < nInputs; j++ {
   289  		go func(index int) {
   290  			rChan := make(chan types.Response)
   291  			for i := 0; i < nMsgs; i++ {
   292  				content := [][]byte{[]byte(fmt.Sprintf("hello world %v %v", i, index))}
   293  				select {
   294  				case mockInputs[index].TChan <- types.NewTransaction(message.New(content), rChan):
   295  				case <-time.After(time.Second):
   296  					t.Errorf("Timed out waiting for broker send: %v, %v", i, index)
   297  					return
   298  				}
   299  				select {
   300  				case res := <-rChan:
   301  					if expected, actual := string(content[0]), res.Error().Error(); expected != actual {
   302  						t.Errorf("Wrong response: %v != %v", expected, actual)
   303  					}
   304  				case <-time.After(time.Second):
   305  					t.Errorf("Timed out waiting for response to input: %v, %v", i, index)
   306  					return
   307  				}
   308  			}
   309  			wg.Done()
   310  		}(j)
   311  	}
   312  
   313  	for i := 0; i < nMsgs*nInputs; i++ {
   314  		var ts types.Transaction
   315  		select {
   316  		case ts = <-fanIn.TransactionChan():
   317  		case <-time.After(time.Second):
   318  			t.Errorf("Timed out waiting for broker propagate: %v", i)
   319  			return
   320  		}
   321  		select {
   322  		case ts.ResponseChan <- response.NewError(errors.New(string(ts.Payload.Get(0).Get()))):
   323  		case <-time.After(time.Second):
   324  			t.Errorf("Timed out waiting for response to broker: %v", i)
   325  			return
   326  		}
   327  	}
   328  
   329  	wg.Wait()
   330  }
   331  
   332  //------------------------------------------------------------------------------