@@ -57,6 +57,7 @@ func expectNoResponse(t *testing.T, tx sip.ClientTransaction) {
5757type TestHandler struct {
5858 GetAuthCredentialsFunc func (ctx context.Context , call * rpc.SIPCall ) (AuthInfo , error )
5959 DispatchCallFunc func (ctx context.Context , info * CallInfo ) CallDispatch
60+ OnCallEndFunc func (ctx context.Context , callInfo * livekit.SIPCallInfo , reason string )
6061}
6162
6263func (h TestHandler ) GetAuthCredentials (ctx context.Context , call * rpc.SIPCall ) (AuthInfo , error ) {
@@ -80,6 +81,12 @@ func (h TestHandler) DeregisterTransferSIPParticipantTopic(sipCallId string) {
8081 // no-op
8182}
8283
84+ func (h TestHandler ) OnCallEnd (ctx context.Context , callInfo * livekit.SIPCallInfo , reason string ) {
85+ if h .OnCallEndFunc != nil {
86+ h .OnCallEndFunc (ctx , callInfo , reason )
87+ }
88+ }
89+
8390func testInvite (t * testing.T , h Handler , hidden bool , from , to string , test func (tx sip.ClientTransaction )) {
8491 sipPort := rand .Intn (testPortSIPMax - testPortSIPMin ) + testPortSIPMin
8592 localIP , err := config .GetLocalIP ()
@@ -169,3 +176,73 @@ func TestService_AuthDrop(t *testing.T) {
169176 expectNoResponse (t , tx )
170177 })
171178}
179+
180+ func TestService_OnCallEnd (t * testing.T ) {
181+ const (
182+ expectedCallID = "test-call-id"
183+ expectedReason = "test-reason"
184+ )
185+
186+ callEnded := make (chan struct {})
187+ var receivedCallInfo * livekit.SIPCallInfo
188+ var receivedReason string
189+
190+ h := & TestHandler {
191+ GetAuthCredentialsFunc : func (ctx context.Context , call * rpc.SIPCall ) (AuthInfo , error ) {
192+ return AuthInfo {Result : AuthAccept }, nil
193+ },
194+ DispatchCallFunc : func (ctx context.Context , info * CallInfo ) CallDispatch {
195+ return CallDispatch {
196+ Result : DispatchAccept ,
197+ Room : RoomConfig {
198+ RoomName : "test-room" ,
199+ Participant : ParticipantConfig {
200+ Identity : "test-participant" ,
201+ },
202+ },
203+ }
204+ },
205+ OnCallEndFunc : func (ctx context.Context , callInfo * livekit.SIPCallInfo , reason string ) {
206+ receivedCallInfo = callInfo
207+ receivedReason = reason
208+ close (callEnded )
209+ },
210+ }
211+
212+ // Create a new service
213+ sipPort := rand .Intn (testPortSIPMax - testPortSIPMin ) + testPortSIPMin
214+
215+ mon , err := stats .NewMonitor (& config.Config {MaxCpuUtilization : 0.9 })
216+ require .NoError (t , err )
217+
218+ log := logger .NewTestLogger (t )
219+ s , err := NewService ("" , & config.Config {
220+ SIPPort : sipPort ,
221+ SIPPortListen : sipPort ,
222+ RTPPort : rtcconfig.PortRange {Start : testPortRTPMin , End : testPortRTPMax },
223+ }, mon , log , func (projectID string ) rpc.IOInfoClient { return nil })
224+ require .NoError (t , err )
225+ require .NotNil (t , s )
226+ t .Cleanup (s .Stop )
227+
228+ s .SetHandler (h )
229+ require .NoError (t , s .Start ())
230+
231+ // Call OnCallEnd directly
232+ h .OnCallEnd (context .Background (), & livekit.SIPCallInfo {
233+ CallId : expectedCallID ,
234+ }, expectedReason )
235+
236+ // Wait for OnCallEnd to be called
237+ select {
238+ case <- callEnded :
239+ // Success
240+ case <- time .After (time .Second ):
241+ t .Fatal ("OnCallEnd was not called" )
242+ }
243+
244+ // Verify the parameters passed to OnCallEnd
245+ require .NotNil (t , receivedCallInfo , "CallInfo should not be nil" )
246+ require .Equal (t , expectedCallID , receivedCallInfo .CallId , "CallInfo.CallId should match" )
247+ require .Equal (t , expectedReason , receivedReason , "Reason should match" )
248+ }
0 commit comments