loop/notifications/manager_test.go
sputn1ck 5df53cd81a
notifications: add notification manager
This commit adds a generic notification manager
that can be used to subscribe to different types
of notifications.
2024-10-08 14:58:41 +02:00

173 lines
4.2 KiB
Go

package notifications
import (
"context"
"io"
"sync"
"testing"
"time"
"github.com/lightninglabs/loop/swapserverrpc"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
var (
testReservationId = []byte{0x01, 0x02}
testReservationId2 = []byte{0x01, 0x02}
)
// mockNotificationsClient implements the NotificationsClient interface for testing.
type mockNotificationsClient struct {
mockStream swapserverrpc.SwapServer_SubscribeNotificationsClient
subscribeErr error
timesCalled int
sync.Mutex
}
func (m *mockNotificationsClient) SubscribeNotifications(ctx context.Context,
in *swapserverrpc.SubscribeNotificationsRequest,
opts ...grpc.CallOption) (
swapserverrpc.SwapServer_SubscribeNotificationsClient, error) {
m.Lock()
defer m.Unlock()
m.timesCalled++
if m.subscribeErr != nil {
return nil, m.subscribeErr
}
return m.mockStream, nil
}
// mockSubscribeNotificationsClient simulates the server stream.
type mockSubscribeNotificationsClient struct {
grpc.ClientStream
recvChan chan *swapserverrpc.SubscribeNotificationsResponse
recvErrChan chan error
}
func (m *mockSubscribeNotificationsClient) Recv() (
*swapserverrpc.SubscribeNotificationsResponse, error) {
select {
case err := <-m.recvErrChan:
return nil, err
case notif, ok := <-m.recvChan:
if !ok {
return nil, io.EOF
}
return notif, nil
}
}
func (m *mockSubscribeNotificationsClient) Header() (metadata.MD, error) {
return nil, nil
}
func (m *mockSubscribeNotificationsClient) Trailer() metadata.MD {
return nil
}
func (m *mockSubscribeNotificationsClient) CloseSend() error {
return nil
}
func (m *mockSubscribeNotificationsClient) Context() context.Context {
return context.TODO()
}
func (m *mockSubscribeNotificationsClient) SendMsg(interface{}) error {
return nil
}
func (m *mockSubscribeNotificationsClient) RecvMsg(interface{}) error {
return nil
}
func TestManager_ReservationNotification(t *testing.T) {
// Create a mock notification client
recvChan := make(chan *swapserverrpc.SubscribeNotificationsResponse, 1)
errChan := make(chan error, 1)
mockStream := &mockSubscribeNotificationsClient{
recvChan: recvChan,
recvErrChan: errChan,
}
mockClient := &mockNotificationsClient{
mockStream: mockStream,
}
// Create a Manager with the mock client
mgr := NewManager(&Config{
Client: mockClient,
FetchL402: func(ctx context.Context) error {
// Simulate successful fetching of L402
return nil
},
})
// Subscribe to reservation notifications.
subCtx, subCancel := context.WithCancel(context.Background())
subChan := mgr.SubscribeReservations(subCtx)
// Run the manager.
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
err := mgr.Run(ctx)
require.NoError(t, err)
}()
// Wait a bit to ensure manager is running and has subscribed
require.Eventually(t, func() bool {
mgr.Lock()
defer mgr.Unlock()
return len(mgr.subscribers[NotificationTypeReservation]) > 0
}, time.Second*5, 10*time.Millisecond)
mockClient.Lock()
require.Equal(t, 1, mockClient.timesCalled)
mockClient.Unlock()
// Send a test notification
testNotif := getTestNotification(testReservationId)
// Send the notification to the recvChan
recvChan <- testNotif
// Collect the notification in the callback
receivedNotification := <-subChan
// Now, check that the notification received in the callback matches the one sent
require.NotNil(t, receivedNotification)
require.Equal(t, testReservationId, receivedNotification.ReservationId)
// Cancel the subscription
subCancel()
// Send another test notification`
testNotif2 := getTestNotification(testReservationId2)
recvChan <- testNotif2
// Check that the subChan is eventually closed.
require.Eventually(t, func() bool {
select {
case _, ok := <-subChan:
return !ok
default:
return false
}
}, time.Second*5, 10*time.Millisecond)
}
func getTestNotification(resId []byte) *swapserverrpc.SubscribeNotificationsResponse {
return &swapserverrpc.SubscribeNotificationsResponse{
Notification: &swapserverrpc.SubscribeNotificationsResponse_ReservationNotification{
ReservationNotification: &swapserverrpc.ServerReservationNotification{
ReservationId: resId,
},
},
}
}