@@ -14,6 +14,15 @@ import (
1414 "github.com/stretchr/testify/require"
1515)
1616
17+ func unavailableRTSPPort (t * testing.T ) int {
18+ t .Helper ()
19+ ln , err := net .Listen ("tcp" , "127.0.0.1:0" )
20+ require .NoError (t , err )
21+ port := ln .Addr ().(* net.TCPAddr ).Port
22+ require .NoError (t , ln .Close ())
23+ return port
24+ }
25+
1726func startIngestRTSPMockServer (t * testing.T ) (host string , port int , serverRTPPort int ) {
1827 t .Helper ()
1928 ln , err := net .Listen ("tcp" , "127.0.0.1:0" )
@@ -282,3 +291,62 @@ func TestIngestSessionReceivesFrame(t *testing.T) {
282291 require .FailNow (t , "expected decoded frame" )
283292 }
284293}
294+
295+ func TestReconnectBackoffDelay (t * testing.T ) {
296+ t .Parallel ()
297+
298+ assert .Equal (t , 2 * time .Second , reconnectBackoffDelay (2 * time .Second , 1 ))
299+ assert .Equal (t , 4 * time .Second , reconnectBackoffDelay (2 * time .Second , 2 ))
300+ assert .Equal (t , 8 * time .Second , reconnectBackoffDelay (2 * time .Second , 3 ))
301+ assert .Equal (t , 16 * time .Second , reconnectBackoffDelay (2 * time .Second , 4 ))
302+ assert .Equal (t , 30 * time .Second , reconnectBackoffDelay (2 * time .Second , 5 ))
303+ assert .Equal (t , 30 * time .Second , reconnectBackoffDelay (2 * time .Second , 20 ))
304+ assert .Equal (t , 2 * time .Second , reconnectBackoffDelay (0 , 1 ))
305+ }
306+
307+ func TestIngestSessionReconnectBudgetHandling (t * testing.T ) {
308+ t .Parallel ()
309+
310+ t .Run ("bounded_budget_emits_fatal" , func (t * testing.T ) {
311+ ctx , cancel := context .WithCancel (t .Context ())
312+ defer cancel ()
313+
314+ s , err := NewSession (ctx , Config {
315+ Address : "127.0.0.1" ,
316+ RTSPPort : unavailableRTSPPort (t ),
317+ ReconnectDelay : 5 * time .Millisecond ,
318+ MaxReconnectFails : 2 ,
319+ })
320+ require .NoError (t , err )
321+ defer func () { _ = s .Close () }()
322+
323+ select {
324+ case fatalErr := <- s .Fatal ():
325+ require .Error (t , fatalErr )
326+ assert .Contains (t , fatalErr .Error (), "audio ingest reconnect budget exceeded" )
327+ assert .Contains (t , fatalErr .Error (), "after 2 consecutive failures" )
328+ case <- time .After (2 * time .Second ):
329+ require .FailNow (t , "expected reconnect budget fatal" )
330+ }
331+ })
332+
333+ t .Run ("unbounded_budget_keeps_retrying" , func (t * testing.T ) {
334+ ctx , cancel := context .WithCancel (t .Context ())
335+ defer cancel ()
336+
337+ s , err := NewSession (ctx , Config {
338+ Address : "127.0.0.1" ,
339+ RTSPPort : unavailableRTSPPort (t ),
340+ ReconnectDelay : 5 * time .Millisecond ,
341+ MaxReconnectFails : 0 ,
342+ })
343+ require .NoError (t , err )
344+ defer func () { _ = s .Close () }()
345+
346+ select {
347+ case fatalErr := <- s .Fatal ():
348+ require .FailNowf (t , "unexpected fatal" , "received fatal in unbounded mode: %v" , fatalErr )
349+ case <- time .After (150 * time .Millisecond ):
350+ }
351+ })
352+ }
0 commit comments