@@ -284,3 +284,113 @@ func TestHandlePfcpSessionEstablishmentResponseNilTunnel(t *testing.T) {
284284 t .Errorf ("expected pending PFCP txn for seq %d to be consumed on the nil-Tunnel ignore path, but it was still present" , seq )
285285 }
286286}
287+
288+ // TestHandlePfcpSessionEstablishmentResponseChannelGatedByState covers the SMContextState gate on
289+ // SBIPFCPCommunicationChan: the normal establishment path waits in SmStatePfcpCreatePending and
290+ // must receive the signal, while restoration's reissue leaves the context in some other state and
291+ // must not receive it (an unconditional send would leave a stale value for the next unrelated
292+ // modification or release that waits on the channel).
293+ func TestHandlePfcpSessionEstablishmentResponseChannelGatedByState (t * testing.T ) {
294+ for _ , tc := range []struct {
295+ name string
296+ imsi string
297+ state context.SMContextState
298+ wantSignal bool
299+ }{
300+ {
301+ name : "awaited establishment sends the signal" ,
302+ imsi : "imsi-100000000000001" ,
303+ state : context .SmStatePfcpCreatePending ,
304+ wantSignal : true ,
305+ },
306+ {
307+ name : "unawaited response (e.g. restoration) withholds the signal" ,
308+ imsi : "imsi-100000000000002" ,
309+ state : context .SmStateActive ,
310+ wantSignal : false ,
311+ },
312+ } {
313+ t .Run (tc .name , func (t * testing.T ) {
314+ // AllocateLocalSEID reads factory.SmfConfig.Configuration.EnableDbStore, so the config
315+ // must be initialized for the SEID allocation path not to panic when this test runs in
316+ // isolation.
317+ if factory .SmfConfig .Configuration == nil {
318+ factory .SmfConfig = factory.Config {
319+ Configuration : & factory.Configuration {
320+ KafkaInfo : factory.KafkaInfo {EnableKafka : boolPointer (false )},
321+ EnableUpfAdapter : false ,
322+ },
323+ }
324+ }
325+
326+ nodeID := context .NewNodeID ("1.1.1.1" )
327+ smContext := context .NewSMContext (tc .imsi , 10 )
328+ smContext .SMContextState = tc .state
329+
330+ smContext .Tunnel = & context.UPTunnel {
331+ DataPathPool : context.DataPathPool {
332+ 10 : & context.DataPath {
333+ IsDefaultPath : true ,
334+ FirstDPNode : & context.DataPathNode {
335+ UPF : & context.UPF {NodeID : * nodeID },
336+ },
337+ },
338+ },
339+ }
340+
341+ datapath := & context.DataPath {
342+ FirstDPNode : & context.DataPathNode {
343+ UPF : & context.UPF {NodeID : * nodeID },
344+ },
345+ }
346+ smContext .AllocateLocalSEIDForDataPath (datapath )
347+
348+ var localSEID uint64
349+ for _ , pfcpCtx := range smContext .PFCPContext {
350+ if pfcpCtx .LocalSEID != 0 {
351+ localSEID = pfcpCtx .LocalSEID
352+ }
353+ }
354+ if localSEID == 0 {
355+ t .Fatal ("failed to allocate a local SEID for the test SMContext" )
356+ }
357+
358+ seq := uint32 (localSEID )
359+ pfcp_message .InsertPfcpTxn (seq , nodeID )
360+
361+ rsp := message .NewSessionEstablishmentResponse (
362+ 0 ,
363+ 0 ,
364+ localSEID ,
365+ seq ,
366+ 0 ,
367+ ie .NewCause (ie .CauseRequestAccepted ),
368+ ie .NewNodeID ("1.1.1.1" , "" , "" ),
369+ ie .NewRecoveryTimeStamp (time .Now ()),
370+ )
371+
372+ udpMessage := udp.Message {
373+ RemoteAddr : & net.UDPAddr {
374+ IP : net .ParseIP ("1.1.1.1" ),
375+ Port : 8809 ,
376+ },
377+ PfcpMessage : rsp ,
378+ }
379+
380+ handler .HandlePfcpSessionEstablishmentResponse (& udpMessage )
381+
382+ select {
383+ case status := <- smContext .SBIPFCPCommunicationChan :
384+ if ! tc .wantSignal {
385+ t .Errorf ("expected no send to SBIPFCPCommunicationChan when SMContextState is %v, got signal %v" , tc .state , status )
386+ } else if status != context .SessionEstablishSuccess {
387+ t .Errorf ("expected SessionEstablishSuccess, got %v" , status )
388+ }
389+ default :
390+ if tc .wantSignal {
391+ t .Error ("expected a send to SBIPFCPCommunicationChan when SMContextState is SmStatePfcpCreatePending, got none" )
392+ }
393+ }
394+ })
395+ }
396+ }
0 commit comments