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