github.com/livekit/protocol@v1.16.1-0.20240517185851-47e4c6bba773/webhook/webhook_test.go (about)

     1  // Copyright 2023 LiveKit, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package webhook
    16  
    17  import (
    18  	"context"
    19  	"net"
    20  	"net/http"
    21  	"sync"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/stretchr/testify/require"
    26  	"go.uber.org/atomic"
    27  
    28  	"github.com/livekit/protocol/auth"
    29  	"github.com/livekit/protocol/livekit"
    30  )
    31  
    32  const (
    33  	apiKey               = "mykey"
    34  	apiSecret            = "mysecret"
    35  	testAddr             = ":8765"
    36  	testUrl              = "http://localhost:8765"
    37  	webhookCheckInterval = 100 * time.Millisecond
    38  )
    39  
    40  var authProvider = auth.NewSimpleKeyProvider(
    41  	apiKey, apiSecret,
    42  )
    43  
    44  func TestWebHook(t *testing.T) {
    45  	s := newServer(testAddr)
    46  	require.NoError(t, s.Start())
    47  	defer s.Stop()
    48  
    49  	notifier := NewDefaultNotifier(apiKey, apiSecret, []string{testUrl})
    50  
    51  	t.Run("test event payload", func(t *testing.T) {
    52  		event := &livekit.WebhookEvent{
    53  			Event: EventTrackPublished,
    54  			Participant: &livekit.ParticipantInfo{
    55  				Identity: "test",
    56  			},
    57  			Track: &livekit.TrackInfo{
    58  				Sid: "TR_abcde",
    59  			},
    60  		}
    61  
    62  		wg := sync.WaitGroup{}
    63  		wg.Add(1)
    64  		s.handler = func(r *http.Request) {
    65  			defer wg.Done()
    66  			decodedEvent, err := ReceiveWebhookEvent(r, authProvider)
    67  			require.NoError(t, err)
    68  
    69  			require.EqualValues(t, event, decodedEvent)
    70  		}
    71  		require.NoError(t, notifier.QueueNotify(context.Background(), event))
    72  		wg.Wait()
    73  	})
    74  
    75  }
    76  
    77  func TestURLNotifierDropped(t *testing.T) {
    78  	s := newServer(testAddr)
    79  	require.NoError(t, s.Start())
    80  	defer s.Stop()
    81  
    82  	urlNotifier := newTestNotifier()
    83  	defer urlNotifier.Stop(true)
    84  	totalDropped := atomic.Int32{}
    85  	totalReceived := atomic.Int32{}
    86  	s.handler = func(r *http.Request) {
    87  		decodedEvent, err := ReceiveWebhookEvent(r, authProvider)
    88  		require.NoError(t, err)
    89  		totalReceived.Inc()
    90  		totalDropped.Add(decodedEvent.NumDropped)
    91  	}
    92  	// send multiple notifications
    93  	for i := 0; i < 10; i++ {
    94  		_ = urlNotifier.QueueNotify(&livekit.WebhookEvent{Event: EventRoomStarted})
    95  		_ = urlNotifier.QueueNotify(&livekit.WebhookEvent{Event: EventParticipantJoined})
    96  		_ = urlNotifier.QueueNotify(&livekit.WebhookEvent{Event: EventRoomFinished})
    97  	}
    98  
    99  	time.Sleep(webhookCheckInterval)
   100  
   101  	require.Equal(t, int32(30), totalDropped.Load()+totalReceived.Load())
   102  	// at least one request dropped
   103  	require.Less(t, int32(0), totalDropped.Load())
   104  }
   105  
   106  func TestURLNotifierLifecycle(t *testing.T) {
   107  	s := newServer(testAddr)
   108  	require.NoError(t, s.Start())
   109  	defer s.Stop()
   110  
   111  	t.Run("start/stop without use", func(t *testing.T) {
   112  		urlNotifier := newTestNotifier()
   113  		urlNotifier.Stop(false)
   114  	})
   115  
   116  	t.Run("stop allowing to drain", func(t *testing.T) {
   117  		urlNotifier := newTestNotifier()
   118  		numCalled := atomic.Int32{}
   119  		s.handler = func(r *http.Request) {
   120  			numCalled.Inc()
   121  		}
   122  		for i := 0; i < 10; i++ {
   123  			_ = urlNotifier.QueueNotify(&livekit.WebhookEvent{Event: EventRoomStarted})
   124  			_ = urlNotifier.QueueNotify(&livekit.WebhookEvent{Event: EventRoomFinished})
   125  		}
   126  		urlNotifier.Stop(false)
   127  		require.Eventually(t, func() bool { return numCalled.Load() == 20 }, 5*time.Second, webhookCheckInterval)
   128  	})
   129  
   130  	t.Run("force stop", func(t *testing.T) {
   131  		urlNotifier := newTestNotifier()
   132  		numCalled := atomic.Int32{}
   133  		s.handler = func(r *http.Request) {
   134  			numCalled.Inc()
   135  		}
   136  		for i := 0; i < 10; i++ {
   137  			_ = urlNotifier.QueueNotify(&livekit.WebhookEvent{Event: EventRoomStarted})
   138  			_ = urlNotifier.QueueNotify(&livekit.WebhookEvent{Event: EventRoomFinished})
   139  		}
   140  		urlNotifier.Stop(true)
   141  		time.Sleep(time.Second)
   142  		require.Greater(t, int32(20), numCalled.Load())
   143  	})
   144  }
   145  
   146  func newTestNotifier() *URLNotifier {
   147  	return NewURLNotifier(URLNotifierParams{
   148  		QueueSize: 20,
   149  		URL:       testUrl,
   150  		APIKey:    apiKey,
   151  		APISecret: apiSecret,
   152  	})
   153  }
   154  
   155  type testServer struct {
   156  	handler func(r *http.Request)
   157  	server  *http.Server
   158  }
   159  
   160  func newServer(addr string) *testServer {
   161  	s := &testServer{}
   162  	s.server = &http.Server{
   163  		Addr:    addr,
   164  		Handler: s,
   165  	}
   166  	return s
   167  }
   168  
   169  func (s *testServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   170  	if s.handler != nil {
   171  		s.handler(r)
   172  	}
   173  }
   174  
   175  func (s *testServer) Start() error {
   176  	l, err := net.Listen("tcp", s.server.Addr)
   177  	if err != nil {
   178  		return err
   179  	}
   180  	go s.server.Serve(l)
   181  	return nil
   182  }
   183  
   184  func (s *testServer) Stop() {
   185  	_ = s.server.Shutdown(context.Background())
   186  }