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  }