Add tests for noop event stream

This commit is contained in:
9seconds
2021-03-16 11:38:29 +03:00
parent f0063ba089
commit e009d05a90
8 changed files with 160 additions and 13 deletions
+22 -3
View File
@@ -2,6 +2,7 @@ package events
import ( import (
"context" "context"
"math/rand"
"runtime" "runtime"
"github.com/9seconds/mtg/v2/mtglib" "github.com/9seconds/mtg/v2/mtglib"
@@ -15,12 +16,20 @@ type eventStream struct {
} }
func (e eventStream) Send(ctx context.Context, evt mtglib.Event) { func (e eventStream) Send(ctx context.Context, evt mtglib.Event) {
chanNo := int(xxhash.ChecksumString32(evt.ConnectionID())) % len(e.chans) var chanNo uint32
streamID := evt.StreamID()
if streamID == "" {
chanNo = rand.Uint32()
} else {
chanNo = xxhash.ChecksumString32(streamID)
}
select { select {
case <-ctx.Done(): case <-ctx.Done():
case <-e.ctx.Done(): case <-e.ctx.Done():
case e.chans[chanNo] <- evt: case e.chans[int(chanNo)%len(e.chans)] <- evt:
} }
} }
@@ -29,6 +38,10 @@ func (e eventStream) Shutdown() {
} }
func NewEventStream(observerFactories []ObserverFactory) mtglib.EventStream { func NewEventStream(observerFactories []ObserverFactory) mtglib.EventStream {
if len(observerFactories) == 0 {
observerFactories = append(observerFactories, NewNoopObserver)
}
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
rv := eventStream{ rv := eventStream{
ctx: ctx, ctx: ctx,
@@ -39,7 +52,11 @@ func NewEventStream(observerFactories []ObserverFactory) mtglib.EventStream {
for i := 0; i < runtime.NumCPU(); i++ { for i := 0; i < runtime.NumCPU(); i++ {
rv.chans[i] = make(chan mtglib.Event, 1) rv.chans[i] = make(chan mtglib.Event, 1)
go eventStreamProcessor(ctx, rv.chans[i], newMultiObserver(observerFactories)) if len(observerFactories) == 1 {
go eventStreamProcessor(ctx, rv.chans[i], observerFactories[0]())
} else {
go eventStreamProcessor(ctx, rv.chans[i], newMultiObserver(observerFactories))
}
} }
return rv return rv
@@ -58,6 +75,8 @@ func eventStreamProcessor(ctx context.Context, eventChan <-chan mtglib.Event, ob
observer.EventStart(typedEvt) observer.EventStart(typedEvt)
case mtglib.EventFinish: case mtglib.EventFinish:
observer.EventFinish(typedEvt) observer.EventFinish(typedEvt)
case mtglib.EventConcurrencyLimited:
observer.EventConcurrencyLimited(typedEvt)
} }
} }
} }
+1
View File
@@ -5,6 +5,7 @@ import "github.com/9seconds/mtg/v2/mtglib"
type Observer interface { type Observer interface {
EventStart(mtglib.EventStart) EventStart(mtglib.EventStart)
EventFinish(mtglib.EventFinish) EventFinish(mtglib.EventFinish)
EventConcurrencyLimited(mtglib.EventConcurrencyLimited)
Shutdown() Shutdown()
} }
+4
View File
@@ -17,6 +17,10 @@ func (o *ObserverMock) EventFinish(evt mtglib.EventStart) {
o.Called(evt) o.Called(evt)
} }
func (o *ObserverMock) EventConcurrencyLimited(evt mtglib.EventConcurrencyLimited) {
o.Called(evt)
}
func (o *ObserverMock) Shutdown() { func (o *ObserverMock) Shutdown() {
o.Called() o.Called()
} }
+15
View File
@@ -40,6 +40,21 @@ func (m multiObserver) EventFinish(evt mtglib.EventFinish) {
wg.Wait() wg.Wait()
} }
func (m multiObserver) EventConcurrencyLimited(evt mtglib.EventConcurrencyLimited) {
wg := &sync.WaitGroup{}
wg.Add(len(m.observers))
for _, v := range m.observers {
go func(obs Observer) {
defer wg.Done()
obs.EventConcurrencyLimited(evt)
}(v)
}
wg.Wait()
}
func (m multiObserver) Shutdown() { func (m multiObserver) Shutdown() {
for _, v := range m.observers { for _, v := range m.observers {
v.Shutdown() v.Shutdown()
+27
View File
@@ -0,0 +1,27 @@
package events
import (
"context"
"github.com/9seconds/mtg/v2/mtglib"
)
type noop struct{}
func (n noop) Send(ctx context.Context, evt mtglib.Event) {}
func (n noop) Shutdown() {}
func NewNoopStream() mtglib.EventStream {
return noop{}
}
type noopObserver struct{}
func (n noopObserver) EventStart(_ mtglib.EventStart) {}
func (n noopObserver) EventFinish(_ mtglib.EventFinish) {}
func (n noopObserver) EventConcurrencyLimited(_ mtglib.EventConcurrencyLimited) {}
func (n noopObserver) Shutdown() {}
func NewNoopObserver() Observer {
return noopObserver{}
}
+75
View File
@@ -0,0 +1,75 @@
package events_test
import (
"context"
"net"
"testing"
"time"
"github.com/9seconds/mtg/v2/events"
"github.com/9seconds/mtg/v2/mtglib"
"github.com/stretchr/testify/suite"
)
type NoopTestSuite struct {
suite.Suite
testData map[string]mtglib.Event
ctx context.Context
}
func (suite *NoopTestSuite) SetupSuite() {
suite.testData = map[string]mtglib.Event{
"start": mtglib.EventStart{
CreatedAt: time.Now(),
ConnID: "connID",
RemoteIP: net.ParseIP("127.0.0.1"),
},
"finish": mtglib.EventFinish{
CreatedAt: time.Now(),
ConnID: "connID",
},
"concurrency-limited": mtglib.EventConcurrencyLimited{},
}
suite.ctx = context.Background()
}
func (suite *NoopTestSuite) TestStream() {
stream := events.NewNoopStream()
for name, v := range suite.testData {
value := v
suite.T().Run(name, func(t *testing.T) {
stream.Send(suite.ctx, value)
})
}
stream.Shutdown()
}
func (suite *NoopTestSuite) TestObserver() {
observer := events.NewNoopObserver()
for name, v := range suite.testData {
value := v
suite.T().Run(name, func(t *testing.T) {
switch typedEvt := value.(type) {
case mtglib.EventStart:
observer.EventStart(typedEvt)
case mtglib.EventFinish:
observer.EventFinish(typedEvt)
case mtglib.EventConcurrencyLimited:
observer.EventConcurrencyLimited(typedEvt)
}
})
}
observer.Shutdown()
}
func TestNoop(t *testing.T) {
t.Parallel()
suite.Run(t, &NoopTestSuite{})
}
+15 -9
View File
@@ -5,21 +5,27 @@ import (
"time" "time"
) )
type eventBase struct { type EventStart struct {
CreatedAt time.Time
ConnID string
RemoteIP net.IP
}
func (e EventStart) StreamID() string {
return e.ConnID
}
type EventFinish struct {
CreatedAt time.Time CreatedAt time.Time
ConnID string ConnID string
} }
func (e eventBase) ConnectionID() string { func (e EventFinish) StreamID() string {
return e.ConnID return e.ConnID
} }
type EventStart struct { type EventConcurrencyLimited struct{}
eventBase
RemoteIP net.IP func (e EventConcurrencyLimited) StreamID() string {
} return ""
type EventFinish struct {
eventBase
} }
+1 -1
View File
@@ -28,7 +28,7 @@ type IPBlocklist interface {
} }
type Event interface { type Event interface {
ConnectionID() string StreamID() string
} }
type EventStream interface { type EventStream interface {