gitee.com/sasukebo/go-micro/v4@v4.7.1/events/stream_test.go (about) 1 package events 2 3 import ( 4 "sync" 5 "testing" 6 "time" 7 8 "github.com/google/uuid" 9 "github.com/stretchr/testify/assert" 10 ) 11 12 type testPayload struct { 13 Message string 14 } 15 16 type testCase struct { 17 str Stream 18 name string 19 } 20 21 func TestStream(t *testing.T) { 22 tcs := []testCase{} 23 24 stream, err := NewStream() 25 assert.Nilf(t, err, "NewStream should not return an error") 26 assert.NotNilf(t, stream, "NewStream should return a stream object") 27 tcs = append(tcs, testCase{str: stream, name: "memory"}) 28 29 for _, tc := range tcs { 30 t.Run(tc.name, func(t *testing.T) { 31 runTestStream(t, tc.str) 32 }) 33 } 34 35 } 36 37 func runTestStream(t *testing.T, stream Stream) { 38 // TestMissingTopic will test the topic validation on publish 39 t.Run("TestMissingTopic", func(t *testing.T) { 40 err := stream.Publish("", nil) 41 assert.Equalf(t, err, ErrMissingTopic, "Publishing to a blank topic should return an error") 42 }) 43 44 // TestConsumeTopic will publish a message to the test topic. The subscriber will subscribe to the 45 // same test topic. 46 t.Run("TestConsumeTopic", func(t *testing.T) { 47 payload := &testPayload{Message: "HelloWorld"} 48 metadata := map[string]string{"foo": "bar"} 49 50 // create the subscriber 51 evChan, err := stream.Consume("test") 52 assert.Nilf(t, err, "Consume should not return an error") 53 54 // setup the subscriber async 55 var wg sync.WaitGroup 56 57 go func() { 58 timeout := time.NewTimer(time.Millisecond * 250) 59 60 select { 61 case event, _ := <-evChan: 62 assert.NotNilf(t, event, "The message was nil") 63 assert.Equal(t, event.Metadata, metadata, "Metadata didn't match") 64 65 var result testPayload 66 err = event.Unmarshal(&result) 67 assert.Nil(t, err, "Error decoding result") 68 assert.Equal(t, result, *payload, "Payload didn't match") 69 70 wg.Done() 71 case <-timeout.C: 72 t.Fatalf("Event was not recieved") 73 } 74 }() 75 76 err = stream.Publish("test", payload, WithMetadata(metadata)) 77 assert.Nil(t, err, "Publishing a valid message should not return an error") 78 wg.Add(1) 79 80 // wait for the subscriber to recieve the message or timeout 81 wg.Wait() 82 }) 83 84 // TestConsumeGroup will publish a message to a random topic. Two subscribers will then consume 85 // the message from the firehose topic with different queues. The second subscriber will be registered 86 // after the message is published to test durability. 87 t.Run("TestConsumeGroup", func(t *testing.T) { 88 topic := uuid.New().String() 89 payload := &testPayload{Message: "HelloWorld"} 90 metadata := map[string]string{"foo": "bar"} 91 92 // create the first subscriber 93 evChan1, err := stream.Consume(topic) 94 assert.Nilf(t, err, "Consume should not return an error") 95 96 // setup the subscriber async 97 var wg sync.WaitGroup 98 99 go func() { 100 timeout := time.NewTimer(time.Millisecond * 250) 101 102 select { 103 case event, _ := <-evChan1: 104 assert.NotNilf(t, event, "The message was nil") 105 assert.Equal(t, event.Metadata, metadata, "Metadata didn't match") 106 107 var result testPayload 108 err = event.Unmarshal(&result) 109 assert.Nil(t, err, "Error decoding result") 110 assert.Equal(t, result, *payload, "Payload didn't match") 111 112 wg.Done() 113 case <-timeout.C: 114 t.Fatalf("Event was not recieved") 115 } 116 }() 117 118 err = stream.Publish(topic, payload, WithMetadata(metadata)) 119 assert.Nil(t, err, "Publishing a valid message should not return an error") 120 wg.Add(2) 121 122 // create the second subscriber 123 evChan2, err := stream.Consume(topic, 124 WithGroup("second_queue"), 125 WithOffset(time.Now().Add(time.Minute*-1)), 126 ) 127 assert.Nilf(t, err, "Consume should not return an error") 128 129 go func() { 130 timeout := time.NewTimer(time.Second * 1) 131 132 select { 133 case event, _ := <-evChan2: 134 assert.NotNilf(t, event, "The message was nil") 135 assert.Equal(t, event.Metadata, metadata, "Metadata didn't match") 136 137 var result testPayload 138 err = event.Unmarshal(&result) 139 assert.Nil(t, err, "Error decoding result") 140 assert.Equal(t, result, *payload, "Payload didn't match") 141 142 wg.Done() 143 case <-timeout.C: 144 t.Fatalf("Event was not recieved") 145 } 146 }() 147 148 // wait for the subscriber to recieve the message or timeout 149 wg.Wait() 150 }) 151 152 t.Run("AckingNacking", func(t *testing.T) { 153 ch, err := stream.Consume("foobarAck", WithAutoAck(false, 5*time.Second)) 154 assert.NoError(t, err, "Unexpected error subscribing") 155 assert.NoError(t, stream.Publish("foobarAck", map[string]string{"foo": "message 1"})) 156 assert.NoError(t, stream.Publish("foobarAck", map[string]string{"foo": "message 2"})) 157 158 ev := <-ch 159 ev.Ack() 160 ev = <-ch 161 nacked := ev.ID 162 ev.Nack() 163 select { 164 case ev = <-ch: 165 assert.Equal(t, ev.ID, nacked, "Nacked message should have been received again") 166 assert.NoError(t, ev.Ack()) 167 case <-time.After(7 * time.Second): 168 t.Fatalf("Timed out waiting for message to be put back on queue") 169 } 170 171 }) 172 173 t.Run("Retries", func(t *testing.T) { 174 ch, err := stream.Consume("foobarRetries", WithAutoAck(false, 5*time.Second), WithRetryLimit(1)) 175 assert.NoError(t, err, "Unexpected error subscribing") 176 assert.NoError(t, stream.Publish("foobarRetries", map[string]string{"foo": "message 1"})) 177 178 ev := <-ch 179 id := ev.ID 180 ev.Nack() 181 ev = <-ch 182 assert.Equal(t, id, ev.ID, "Nacked message should have been received again") 183 ev.Nack() 184 select { 185 case ev = <-ch: 186 t.Fatalf("Unexpected event received") 187 case <-time.After(7 * time.Second): 188 } 189 190 }) 191 192 t.Run("InfiniteRetries", func(t *testing.T) { 193 ch, err := stream.Consume("foobarRetriesInf", WithAutoAck(false, 2*time.Second)) 194 assert.NoError(t, err, "Unexpected error subscribing") 195 assert.NoError(t, stream.Publish("foobarRetriesInf", map[string]string{"foo": "message 1"})) 196 197 count := 0 198 id := "" 199 for { 200 select { 201 case ev := <-ch: 202 if id != "" { 203 assert.Equal(t, id, ev.ID, "Nacked message should have been received again") 204 } 205 id = ev.ID 206 case <-time.After(3 * time.Second): 207 t.Fatalf("Unexpected event received") 208 } 209 210 count++ 211 if count == 11 { 212 break 213 } 214 } 215 216 }) 217 218 t.Run("twoSubs", func(t *testing.T) { 219 ch1, err := stream.Consume("foobarTwoSubs1", WithAutoAck(false, 5*time.Second)) 220 assert.NoError(t, err, "Unexpected error subscribing to topic 1") 221 ch2, err := stream.Consume("foobarTwoSubs2", WithAutoAck(false, 5*time.Second)) 222 assert.NoError(t, err, "Unexpected error subscribing to topic 2") 223 224 assert.NoError(t, stream.Publish("foobarTwoSubs2", map[string]string{"foo": "message 1"})) 225 assert.NoError(t, stream.Publish("foobarTwoSubs1", map[string]string{"foo": "message 1"})) 226 227 wg := sync.WaitGroup{} 228 wg.Add(2) 229 go func() { 230 ev := <-ch1 231 assert.Equal(t, "foobarTwoSubs1", ev.Topic, "Received message from unexpected topic") 232 wg.Done() 233 }() 234 go func() { 235 ev := <-ch2 236 assert.Equal(t, "foobarTwoSubs2", ev.Topic, "Received message from unexpected topic") 237 wg.Done() 238 }() 239 wg.Wait() 240 }) 241 }