routing+routerrpc: test stream cancellation

Test stream cancellation of the TrackPayments rpc call. In order to achieve
this, ControlTowerSubscriber is converted to an interface, to avoid trying to
close a null channel when closing the subscription. By returning a mock
implementation of the ControlTowerSubscriber in the test that problem is
avoided.
This commit is contained in:
Jesse de Wit committed 2022-09-10 21:53:01 +02:00
1 parent 4bc3007668
commit 0266ab77ab
5 files changed
+131 -70

No files matched your search

+2 -2
View File
@@ -791,7 +791,7 @@ func (s *Server) TrackPayments(request *TrackPaymentsRequest,
// trackPaymentStream streams payment updates to the client.
func (s *Server) trackPaymentStream(context context.Context,
subscription *routing.ControlTowerSubscriber, noInflightUpdates bool,
subscription routing.ControlTowerSubscriber, noInflightUpdates bool,
send func(*lnrpc.Payment) error) error {
defer subscription.Close()
@@ -799,7 +799,7 @@ func (s *Server) trackPaymentStream(context context.Context,
// Stream updates back to the client.
for {
select {
case item, ok := <-subscription.Updates:
case item, ok := <-subscription.Updates():
if !ok {
// No more payment updates.
return nil
+77 -30
View File
@@ -13,21 +13,46 @@ import (
"google.golang.org/grpc"
)
func makeStreamMock() *StreamMock {
return &StreamMock{
ctx: context.Background(),
sentFromServer: make(chan *lnrpc.Payment, 10),
}
}
type StreamMock struct {
type streamMock struct {
grpc.ServerStream
ctx context.Context
sentFromServer chan *lnrpc.Payment
}
func makeControlTowerMock() *ControlTowerMock {
towerMock := &ControlTowerMock{
func makeStreamMock(ctx context.Context) *streamMock {
return &streamMock{
ctx: ctx,
sentFromServer: make(chan *lnrpc.Payment, 10),
}
}
func (m *streamMock) Context() context.Context {
return m.ctx
}
func (m *streamMock) Send(p *lnrpc.Payment) error {
m.sentFromServer <- p
return nil
}
type controlTowerSubscriberMock struct {
updates <-chan interface{}
}
func (s controlTowerSubscriberMock) Updates() <-chan interface{} {
return s.updates
}
func (s controlTowerSubscriberMock) Close() {
}
type controlTowerMock struct {
queue *queue.ConcurrentQueue
routing.ControlTower
}
func makeControlTowerMock() *controlTowerMock {
towerMock := &controlTowerMock{
queue: queue.NewConcurrentQueue(20),
}
towerMock.queue.Start()
@@ -35,26 +60,40 @@ func makeControlTowerMock() *ControlTowerMock {
return towerMock
}
type ControlTowerMock struct {
queue *queue.ConcurrentQueue
routing.ControlTower
}
func (t *controlTowerMock) SubscribeAllPayments() (
routing.ControlTowerSubscriber, error) {
func (t *ControlTowerMock) SubscribeAllPayments() (
*routing.ControlTowerSubscriber, error) {
return &routing.ControlTowerSubscriber{
Updates: t.queue.ChanOut(),
return &controlTowerSubscriberMock{
updates: t.queue.ChanOut(),
}, nil
}
func (m *StreamMock) Context() context.Context {
return m.ctx
}
// TestTrackPaymentsReturnsOnCancelContext tests whether TrackPayments returns
// when the stream context is cancelled.
func TestTrackPaymentsReturnsOnCancelContext(t *testing.T) {
// Setup mocks and request.
request := &TrackPaymentsRequest{
NoInflightUpdates: false,
}
towerMock := makeControlTowerMock()
func (m *StreamMock) Send(p *lnrpc.Payment) error {
m.sentFromServer <- p
return nil
streamCtx, cancelStream := context.WithCancel(context.Background())
stream := makeStreamMock(streamCtx)
server := &Server{
cfg: &Config{
RouterBackend: &RouterBackend{
Tower: towerMock,
},
},
}
// Cancel stream immediately
cancelStream()
// Make sure the call returns.
err := server.TrackPayments(request, stream)
require.Equal(t, context.Canceled, err)
}
// TestTrackPaymentsInflightUpdate tests whether all updates from the control
@@ -65,7 +104,11 @@ func TestTrackPaymentsInflightUpdates(t *testing.T) {
NoInflightUpdates: false,
}
towerMock := makeControlTowerMock()
stream := makeStreamMock()
streamCtx, cancelStream := context.WithCancel(context.Background())
stream := makeStreamMock(streamCtx)
defer cancelStream()
server := &Server{
cfg: &Config{
RouterBackend: &RouterBackend{
@@ -77,7 +120,7 @@ func TestTrackPaymentsInflightUpdates(t *testing.T) {
// Listen to payment updates in a goroutine.
go func() {
err := server.TrackPayments(request, stream)
require.NoError(t, err)
require.Equal(t, context.Canceled, err)
}()
// Enqueue some payment updates on the mock.
@@ -119,11 +162,15 @@ func TestTrackPaymentsNoInflightUpdates(t *testing.T) {
request := &TrackPaymentsRequest{
NoInflightUpdates: true,
}
towerMock := &ControlTowerMock{
towerMock := &controlTowerMock{
queue: queue.NewConcurrentQueue(20),
}
towerMock.queue.Start()
stream := makeStreamMock()
streamCtx, cancelStream := context.WithCancel(context.Background())
stream := makeStreamMock(streamCtx)
defer cancelStream()
server := &Server{
cfg: &Config{
RouterBackend: &RouterBackend{
@@ -135,7 +182,7 @@ func TestTrackPaymentsNoInflightUpdates(t *testing.T) {
// Listen to payment updates in a goroutine.
go func() {
err := server.TrackPayments(request, stream)
require.NoError(t, err)
require.Equal(t, context.Canceled, err)
}()
// Enqueue some payment updates on the mock.