116 lines
2.8 KiB
Go
116 lines
2.8 KiB
Go
package stream
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/consul/agent/consul/state/db"
|
|
"github.com/hashicorp/go-memdb"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var testTopic Topic = 999
|
|
|
|
func TestEventPublisher_PublishChangesAndSubscribe_WithSnapshot(t *testing.T) {
|
|
subscription := &SubscribeRequest{
|
|
Topic: testTopic,
|
|
Key: "sub-key",
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
|
defer cancel()
|
|
|
|
publisher := NewEventPublisher(ctx, newTestTopicHandlers(), 0)
|
|
sub, err := publisher.Subscribe(ctx, subscription)
|
|
require.NoError(t, err)
|
|
eventCh := consumeSubscription(sub)
|
|
|
|
result := nextResult(t, eventCh)
|
|
require.NoError(t, result.Err)
|
|
expected := []Event{{Payload: "snapshot-event-payload", Key: "sub-key"}}
|
|
require.Equal(t, expected, result.Events)
|
|
|
|
result = nextResult(t, eventCh)
|
|
require.Len(t, result.Events, 1)
|
|
require.True(t, result.Events[0].IsEndOfSnapshot())
|
|
|
|
// Now subscriber should block waiting for updates
|
|
assertNoResult(t, eventCh)
|
|
|
|
err = publisher.PublishChanges(&memdb.Txn{}, db.Changes{})
|
|
require.NoError(t, err)
|
|
|
|
// Subscriber should see the published event
|
|
result = nextResult(t, eventCh)
|
|
require.NoError(t, result.Err)
|
|
expected = []Event{{Payload: "the-published-event-payload", Key: "sub-key", Topic: testTopic}}
|
|
require.Equal(t, expected, result.Events)
|
|
}
|
|
|
|
func newTestTopicHandlers() map[Topic]TopicHandler {
|
|
return map[Topic]TopicHandler{
|
|
testTopic: {
|
|
Snapshot: func(req *SubscribeRequest, buf SnapshotAppender) (uint64, error) {
|
|
if req.Topic != testTopic {
|
|
return 0, fmt.Errorf("unexpected topic: %v", req.Topic)
|
|
}
|
|
buf.Append([]Event{{Payload: "snapshot-event-payload", Key: "sub-key"}})
|
|
return 1, nil
|
|
},
|
|
ProcessChanges: func(tx db.ReadTxn, changes db.Changes) ([]Event, error) {
|
|
events := []Event{{
|
|
Topic: testTopic,
|
|
Key: "sub-key",
|
|
Payload: "the-published-event-payload",
|
|
}}
|
|
return events, nil
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func consumeSubscription(sub *Subscription) <-chan subNextResult {
|
|
eventCh := make(chan subNextResult, 1)
|
|
go func() {
|
|
for {
|
|
es, err := sub.Next()
|
|
eventCh <- subNextResult{
|
|
Events: es,
|
|
Err: err,
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
return eventCh
|
|
}
|
|
|
|
type subNextResult struct {
|
|
Events []Event
|
|
Err error
|
|
}
|
|
|
|
func nextResult(t *testing.T, eventCh <-chan subNextResult) subNextResult {
|
|
t.Helper()
|
|
select {
|
|
case next := <-eventCh:
|
|
return next
|
|
case <-time.After(100 * time.Millisecond):
|
|
t.Fatalf("no event after 100ms")
|
|
}
|
|
return subNextResult{}
|
|
}
|
|
|
|
func assertNoResult(t *testing.T, eventCh <-chan subNextResult) {
|
|
t.Helper()
|
|
select {
|
|
case next := <-eventCh:
|
|
require.NoError(t, next.Err)
|
|
require.Len(t, next.Events, 1)
|
|
t.Fatalf("received unexpected event: %#v", next.Events[0].Payload)
|
|
case <-time.After(100 * time.Millisecond):
|
|
}
|
|
}
|