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  }