make notifier store to local interface for testability
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user