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 //------------------------------------------------------------------------------