make notifier store to local interface for testability

This commit is contained in:
Umputun
2018-12-19 13:24:23 -06:00
parent e5afa2fb74
commit b6b818253f
2 changed files with 54 additions and 16 deletions
+12 -9
View File
@@ -9,18 +9,11 @@ import (
"sync/atomic"
"github.com/umputun/remark/backend/app/store"
"github.com/umputun/remark/backend/app/store/service"
)
// Destination defines interface for a given destination service, like telegram, email and so on
type Destination interface {
fmt.Stringer
Send(ctx context.Context, req request) error
}
// Service delivers notifications to multiple destinations
type Service struct {
dataService *service.DataStore
dataService Store
destinations []Destination
queue chan request
@@ -29,6 +22,16 @@ type Service struct {
cancel context.CancelFunc
}
// Destination defines interface for a given destination service, like telegram, email and so on
type Destination interface {
fmt.Stringer
Send(ctx context.Context, req request) error
}
// Store defines the minimal interface accessing stored commens used by notifier
type Store interface {
Get(locator store.Locator, id string) (store.Comment, error)
}
type request struct {
comment store.Comment
parent store.Comment
@@ -38,7 +41,7 @@ const defaultQueueSize = 100
const uiNav = "#remark42__comment-"
// NewService makes notification service routing comments to all destinations.
func NewService(dataService *service.DataStore, size int, destinations ...Destination) *Service {
func NewService(dataService Store, size int, destinations ...Destination) *Service {
if size <= 0 {
size = defaultQueueSize
}
+42 -7
View File
@@ -2,6 +2,7 @@ package notify
import (
"context"
"errors"
"fmt"
"log"
"math/rand"
@@ -39,9 +40,9 @@ func TestService_WithDestinations(t *testing.T) {
assert.Equal(t, 3, len(d1.get()), "got all comments to d1")
assert.Equal(t, 3, len(d2.get()), "got all comments to d2")
assert.Equal(t, "100", d1.get()[0].ID)
assert.Equal(t, "101", d1.get()[1].ID)
assert.Equal(t, "102", d1.get()[2].ID)
assert.Equal(t, "100", d1.get()[0].comment.ID)
assert.Equal(t, "101", d1.get()[1].comment.ID)
assert.Equal(t, "102", d1.get()[2].comment.ID)
}
func TestService_WithDrops(t *testing.T) {
@@ -81,6 +82,30 @@ func TestService_Many(t *testing.T) {
assert.True(t, d2.closed)
}
func TestService_WithParent(t *testing.T) {
dest := &mockDest{id: 1}
dataStore := &mockStore{data: map[string]store.Comment{}}
dataStore.data["p1"] = store.Comment{ID: "p1"}
dataStore.data["p2"] = store.Comment{ID: "p2"}
s := NewService(dataStore, 1, dest)
assert.NotNil(t, s)
s.Submit(store.Comment{ID: "c1", ParentID: "p1"})
time.Sleep(time.Millisecond * 110)
s.Submit(store.Comment{ID: "c11", ParentID: "p11"})
time.Sleep(time.Millisecond * 110)
s.Close()
destRes := dest.get()
assert.Equal(t, 2, len(destRes), "two comment notified")
assert.Equal(t, "p1", destRes[0].comment.ParentID)
assert.Equal(t, "p1", destRes[0].parent.ID)
assert.Equal(t, "p11", destRes[1].comment.ParentID)
assert.Equal(t, "", destRes[1].parent.ID)
}
func TestService_Nop(t *testing.T) {
s := NopService
s.Submit(store.Comment{})
@@ -89,7 +114,7 @@ func TestService_Nop(t *testing.T) {
}
type mockDest struct {
data []store.Comment
data []request
id int
closed bool
lock sync.Mutex
@@ -100,7 +125,7 @@ func (m *mockDest) Send(ctx context.Context, r request) error {
defer m.lock.Unlock()
select {
case <-time.After(100 * time.Millisecond):
m.data = append(m.data, r.comment)
m.data = append(m.data, r)
log.Printf("sent %s -> %d", r.comment.ID, m.id)
case <-ctx.Done():
log.Printf("ctx closed %d", m.id)
@@ -109,11 +134,21 @@ func (m *mockDest) Send(ctx context.Context, r request) error {
return nil
}
func (m *mockDest) get() []store.Comment {
func (m *mockDest) get() []request {
m.lock.Lock()
defer m.lock.Unlock()
res := make([]store.Comment, len(m.data))
res := make([]request, len(m.data))
copy(res, m.data)
return res
}
func (m *mockDest) String() string { return fmt.Sprintf("mock id=%d, closed=%v", m.id, m.closed) }
type mockStore struct{ data map[string]store.Comment }
func (m *mockStore) Get(_ store.Locator, id string) (store.Comment, error) {
res, ok := m.data[id]
if !ok {
return store.Comment{}, errors.New("no such id")
}
return res, nil
}