Skip to content

Commit 61fc9a1

Browse files
authored
chore: add unit tests for NGAP handover (#1172)
Signed-off-by: Guillaume Belanger <guillaume.belanger27@gmail.com>
1 parent 7f64b7a commit 61fc9a1

File tree

3 files changed

+217
-0
lines changed

3 files changed

+217
-0
lines changed

internal/amf/ngap/handle_handover_cancel_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,66 @@ func TestHandleHandoverCancel_EmptyIEs(t *testing.T) {
1818
ngap.HandleHandoverCancel(context.Background(), ran, msg)
1919
})
2020
}
21+
22+
// TestHandleHandoverCancel_UnknownRanUeNgapID verifies that a HandoverCancel
23+
// with a RAN_UE_NGAP_ID that doesn't match any existing UE context is handled
24+
// gracefully — no panic, and an ErrorIndication is sent.
25+
// Regression test inspired by open5gs/open5gs#4378.
26+
func TestHandleHandoverCancel_UnknownRanUeNgapID(t *testing.T) {
27+
ran := newTestRadio()
28+
sender := ran.NGAPSender.(*FakeNGAPSender)
29+
30+
msg := &ngapType.HandoverCancel{}
31+
ies := &msg.ProtocolIEs
32+
33+
// AMF UE NGAP ID
34+
amfIE := ngapType.HandoverCancelIEs{}
35+
amfIE.Id.Value = ngapType.ProtocolIEIDAMFUENGAPID
36+
amfIE.Value.Present = ngapType.HandoverCancelIEsPresentAMFUENGAPID
37+
amfIE.Value.AMFUENGAPID = &ngapType.AMFUENGAPID{Value: 1099511627776}
38+
ies.List = append(ies.List, amfIE)
39+
40+
// RAN UE NGAP ID — bogus value
41+
ranIE := ngapType.HandoverCancelIEs{}
42+
ranIE.Id.Value = ngapType.ProtocolIEIDRANUENGAPID
43+
ranIE.Value.Present = ngapType.HandoverCancelIEsPresentRANUENGAPID
44+
ranIE.Value.RANUENGAPID = &ngapType.RANUENGAPID{Value: 99}
45+
ies.List = append(ies.List, ranIE)
46+
47+
// Cause
48+
causeIE := ngapType.HandoverCancelIEs{}
49+
causeIE.Id.Value = ngapType.ProtocolIEIDCause
50+
causeIE.Value.Present = ngapType.HandoverCancelIEsPresentCause
51+
causeIE.Value.Cause = &ngapType.Cause{
52+
Present: ngapType.CausePresentRadioNetwork,
53+
RadioNetwork: &ngapType.CauseRadioNetwork{Value: ngapType.CauseRadioNetworkPresentHoFailureInTarget5GCNgranNodeOrTargetSystem},
54+
}
55+
ies.List = append(ies.List, causeIE)
56+
57+
assertNoPanic(t, "HandleHandoverCancel(unknown RAN UE NGAP ID)", func() {
58+
ngap.HandleHandoverCancel(context.Background(), ran, msg)
59+
})
60+
61+
if len(sender.SentErrorIndications) != 1 {
62+
t.Fatalf("expected 1 ErrorIndication, got %d", len(sender.SentErrorIndications))
63+
}
64+
65+
errInd := sender.SentErrorIndications[0]
66+
if errInd.Cause == nil || errInd.Cause.Present != ngapType.CausePresentRadioNetwork {
67+
t.Fatal("expected RadioNetwork cause in ErrorIndication")
68+
}
69+
70+
if errInd.Cause.RadioNetwork.Value != ngapType.CauseRadioNetworkPresentUnknownLocalUENGAPID {
71+
t.Fatalf("expected UnknownLocalUENGAPID, got %d", errInd.Cause.RadioNetwork.Value)
72+
}
73+
}
74+
75+
// TestHandleHandoverCancel_NilMessage verifies that passing a nil message does
76+
// not panic.
77+
func TestHandleHandoverCancel_NilMessage(t *testing.T) {
78+
ran := newTestRadio()
79+
80+
assertNoPanic(t, "HandleHandoverCancel(nil)", func() {
81+
ngap.HandleHandoverCancel(context.Background(), ran, nil)
82+
})
83+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright 2026 Ella Networks
2+
3+
package ngap_test
4+
5+
import (
6+
"context"
7+
"testing"
8+
9+
"github.com/ellanetworks/core/internal/amf/ngap"
10+
"github.com/free5gc/ngap/ngapType"
11+
)
12+
13+
func TestHandlePDUSessionResourceSetupResponse_EmptyIEs(t *testing.T) {
14+
ran := newTestRadio()
15+
amfInstance := newTestAMF()
16+
msg := &ngapType.PDUSessionResourceSetupResponse{}
17+
18+
assertNoPanic(t, "HandlePDUSessionResourceSetupResponse(empty IEs)", func() {
19+
ngap.HandlePDUSessionResourceSetupResponse(context.Background(), amfInstance, ran, msg)
20+
})
21+
}
22+
23+
// TestHandlePDUSessionResourceSetupResponse_UnknownAMFUENGAPID verifies that a
24+
// PDUSessionResourceSetupResponse with an AMF_UE_NGAP_ID that doesn't match
25+
// any existing UE context is handled gracefully — no panic, no further
26+
// processing.
27+
// Regression test inspired by open5gs/open5gs#4377.
28+
func TestHandlePDUSessionResourceSetupResponse_UnknownAMFUENGAPID(t *testing.T) {
29+
ran := newTestRadio()
30+
amfInstance := newTestAMF()
31+
32+
msg := &ngapType.PDUSessionResourceSetupResponse{}
33+
ies := &msg.ProtocolIEs
34+
35+
// AMF UE NGAP ID — bogus value with no matching context
36+
amfIE := ngapType.PDUSessionResourceSetupResponseIEs{}
37+
amfIE.Id.Value = ngapType.ProtocolIEIDAMFUENGAPID
38+
amfIE.Value.Present = ngapType.PDUSessionResourceSetupResponseIEsPresentAMFUENGAPID
39+
amfIE.Value.AMFUENGAPID = &ngapType.AMFUENGAPID{Value: 1379640380095}
40+
ies.List = append(ies.List, amfIE)
41+
42+
// RAN UE NGAP ID — bogus value
43+
ranIE := ngapType.PDUSessionResourceSetupResponseIEs{}
44+
ranIE.Id.Value = ngapType.ProtocolIEIDRANUENGAPID
45+
ranIE.Value.Present = ngapType.PDUSessionResourceSetupResponseIEsPresentRANUENGAPID
46+
ranIE.Value.RANUENGAPID = &ngapType.RANUENGAPID{Value: 99}
47+
ies.List = append(ies.List, ranIE)
48+
49+
assertNoPanic(t, "HandlePDUSessionResourceSetupResponse(unknown AMF UE NGAP ID)", func() {
50+
ngap.HandlePDUSessionResourceSetupResponse(context.Background(), amfInstance, ran, msg)
51+
})
52+
}
53+
54+
// TestHandlePDUSessionResourceSetupResponse_NilMessage verifies that passing a
55+
// nil message does not panic.
56+
func TestHandlePDUSessionResourceSetupResponse_NilMessage(t *testing.T) {
57+
ran := newTestRadio()
58+
amfInstance := newTestAMF()
59+
60+
assertNoPanic(t, "HandlePDUSessionResourceSetupResponse(nil)", func() {
61+
ngap.HandlePDUSessionResourceSetupResponse(context.Background(), amfInstance, ran, nil)
62+
})
63+
}
64+
65+
// TestHandlePDUSessionResourceSetupResponse_OnlyUnknownRANUENGAPID verifies
66+
// that when only the RAN_UE_NGAP_ID is present but unknown, the handler does
67+
// not crash or process further.
68+
func TestHandlePDUSessionResourceSetupResponse_OnlyUnknownRANUENGAPID(t *testing.T) {
69+
ran := newTestRadio()
70+
amfInstance := newTestAMF()
71+
72+
msg := &ngapType.PDUSessionResourceSetupResponse{}
73+
ies := &msg.ProtocolIEs
74+
75+
ranIE := ngapType.PDUSessionResourceSetupResponseIEs{}
76+
ranIE.Id.Value = ngapType.ProtocolIEIDRANUENGAPID
77+
ranIE.Value.Present = ngapType.PDUSessionResourceSetupResponseIEsPresentRANUENGAPID
78+
ranIE.Value.RANUENGAPID = &ngapType.RANUENGAPID{Value: 42}
79+
ies.List = append(ies.List, ranIE)
80+
81+
assertNoPanic(t, "HandlePDUSessionResourceSetupResponse(only unknown RAN ID)", func() {
82+
ngap.HandlePDUSessionResourceSetupResponse(context.Background(), amfInstance, ran, msg)
83+
})
84+
}

internal/amf/ngap/handle_ue_context_release_request_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,73 @@ func TestHandleUEContextReleaseRequest_EmptyIEs(t *testing.T) {
1919
ngap.HandleUEContextReleaseRequest(context.Background(), amf, ran, msg)
2020
})
2121
}
22+
23+
// TestHandleUEContextReleaseRequest_UnknownUENGAPIDs verifies that a
24+
// UEContextReleaseRequest with AMF_UE_NGAP_ID and RAN_UE_NGAP_ID values that
25+
// don't match any existing UE context is handled gracefully — no panic, and an
26+
// ErrorIndication with UnknownLocalUENGAPID cause is sent back.
27+
// Regression test inspired by open5gs/open5gs#4371.
28+
func TestHandleUEContextReleaseRequest_UnknownUENGAPIDs(t *testing.T) {
29+
ran := newTestRadio()
30+
amfInstance := newTestAMF()
31+
sender := ran.NGAPSender.(*FakeNGAPSender)
32+
33+
msg := &ngapType.UEContextReleaseRequest{}
34+
ies := &msg.ProtocolIEs
35+
36+
// AMF UE NGAP ID — bogus value with no matching context
37+
amfIE := ngapType.UEContextReleaseRequestIEs{}
38+
amfIE.Id.Value = ngapType.ProtocolIEIDAMFUENGAPID
39+
amfIE.Value.Present = ngapType.UEContextReleaseRequestIEsPresentAMFUENGAPID
40+
amfIE.Value.AMFUENGAPID = &ngapType.AMFUENGAPID{Value: 999999}
41+
ies.List = append(ies.List, amfIE)
42+
43+
// RAN UE NGAP ID — bogus value
44+
ranIE := ngapType.UEContextReleaseRequestIEs{}
45+
ranIE.Id.Value = ngapType.ProtocolIEIDRANUENGAPID
46+
ranIE.Value.Present = ngapType.UEContextReleaseRequestIEsPresentRANUENGAPID
47+
ranIE.Value.RANUENGAPID = &ngapType.RANUENGAPID{Value: 888888}
48+
ies.List = append(ies.List, ranIE)
49+
50+
// Cause
51+
causeIE := ngapType.UEContextReleaseRequestIEs{}
52+
causeIE.Id.Value = ngapType.ProtocolIEIDCause
53+
causeIE.Value.Present = ngapType.UEContextReleaseRequestIEsPresentCause
54+
causeIE.Value.Cause = &ngapType.Cause{
55+
Present: ngapType.CausePresentRadioNetwork,
56+
RadioNetwork: &ngapType.CauseRadioNetwork{Value: ngapType.CauseRadioNetworkPresentUserInactivity},
57+
}
58+
ies.List = append(ies.List, causeIE)
59+
60+
assertNoPanic(t, "HandleUEContextReleaseRequest(unknown IDs)", func() {
61+
ngap.HandleUEContextReleaseRequest(context.Background(), amfInstance, ran, msg)
62+
})
63+
64+
if len(sender.SentErrorIndications) != 1 {
65+
t.Fatalf("expected 1 ErrorIndication, got %d", len(sender.SentErrorIndications))
66+
}
67+
68+
errInd := sender.SentErrorIndications[0]
69+
if errInd.Cause == nil || errInd.Cause.Present != ngapType.CausePresentRadioNetwork {
70+
t.Fatal("expected RadioNetwork cause in ErrorIndication")
71+
}
72+
73+
if errInd.Cause.RadioNetwork.Value != ngapType.CauseRadioNetworkPresentUnknownLocalUENGAPID {
74+
t.Fatalf("expected UnknownLocalUENGAPID, got %d", errInd.Cause.RadioNetwork.Value)
75+
}
76+
77+
if len(sender.SentUEContextReleaseCommands) != 0 {
78+
t.Fatalf("expected no UEContextReleaseCommand, got %d", len(sender.SentUEContextReleaseCommands))
79+
}
80+
}
81+
82+
// TestHandleUEContextReleaseRequest_NilMessage verifies that passing a nil
83+
// message does not panic.
84+
func TestHandleUEContextReleaseRequest_NilMessage(t *testing.T) {
85+
ran := newTestRadio()
86+
amfInstance := newTestAMF()
87+
88+
assertNoPanic(t, "HandleUEContextReleaseRequest(nil)", func() {
89+
ngap.HandleUEContextReleaseRequest(context.Background(), amfInstance, ran, nil)
90+
})
91+
}

0 commit comments

Comments
 (0)