2 // ========================LICENSE_START=================================
5 // Copyright (C) 2022: Nordix Foundation
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
11 // http://www.apache.org/licenses/LICENSE-2.0
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 // ========================LICENSE_END===================================
35 "github.com/deepmap/oapi-codegen/pkg/middleware"
36 "github.com/deepmap/oapi-codegen/pkg/testutil"
37 "github.com/labstack/echo/v4"
38 echomiddleware "github.com/labstack/echo/v4/middleware"
39 "github.com/stretchr/testify/assert"
40 "oransc.org/nonrtric/capifcore/internal/common29122"
41 "oransc.org/nonrtric/capifcore/internal/eventsapi"
42 "oransc.org/nonrtric/capifcore/internal/publishserviceapi"
43 "oransc.org/nonrtric/capifcore/internal/restclient"
46 func TestRegisterSubscriptions(t *testing.T) {
47 subscription1 := eventsapi.EventSubscription{
48 Events: []eventsapi.CAPIFEvent{
49 eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
51 NotificationDestination: common29122.Uri("http://golang.cafe/"),
53 serviceUnderTest, requestHandler := getEcho(nil)
54 subscriberId := "subscriberId"
56 result := testutil.NewRequest().Post("/"+subscriberId+"/subscriptions").WithJsonBody(subscription1).Go(t, requestHandler)
57 assert.Equal(t, http.StatusCreated, result.Code())
58 var resultEvent eventsapi.EventSubscription
59 err := result.UnmarshalBodyToObject(&resultEvent)
60 assert.NoError(t, err, "error unmarshaling response")
61 assert.Equal(t, resultEvent, subscription1)
62 assert.Regexp(t, "http://example.com/"+subscriberId+"/subscriptions/"+subscriberId+"[0-9]+", result.Recorder.Header().Get(echo.HeaderLocation))
63 subscriptionId1 := path.Base(result.Recorder.Header().Get(echo.HeaderLocation))
65 subscription2 := subscription1
66 subscription2.Events = []eventsapi.CAPIFEvent{
67 eventsapi.CAPIFEventAPIINVOKERUPDATED,
69 result = testutil.NewRequest().Post("/"+subscriberId+"/subscriptions").WithJsonBody(subscription2).Go(t, requestHandler)
70 assert.Regexp(t, "http://example.com/"+subscriberId+"/subscriptions/"+subscriberId+"[0-9]+", result.Recorder.Header().Get(echo.HeaderLocation))
71 subscriptionId2 := path.Base(result.Recorder.Header().Get(echo.HeaderLocation))
73 assert.NotEqual(t, subscriptionId1, subscriptionId2)
74 registeredSub1 := serviceUnderTest.getSubscription(subscriptionId1)
75 assert.Equal(t, subscription1, *registeredSub1)
76 registeredSub2 := serviceUnderTest.getSubscription(subscriptionId2)
77 assert.Equal(t, subscription2, *registeredSub2)
80 func TestRegisterInvalidSubscription(t *testing.T) {
81 subscription1 := eventsapi.EventSubscription{
82 Events: []eventsapi.CAPIFEvent{eventsapi.CAPIFEventACCESSCONTROLPOLICYUNAVAILABLE},
84 serviceUnderTest, requestHandler := getEcho(nil)
85 subscriberId := "subscriberId"
87 result := testutil.NewRequest().Post("/"+subscriberId+"/subscriptions").WithJsonBody(subscription1).Go(t, requestHandler)
88 assert.Equal(t, http.StatusBadRequest, result.Code())
89 var problemDetails common29122.ProblemDetails
90 err := result.UnmarshalBodyToObject(&problemDetails)
91 assert.NoError(t, err, "error unmarshaling response")
92 badRequest := http.StatusBadRequest
93 assert.Equal(t, &badRequest, problemDetails.Status)
94 assert.Contains(t, *problemDetails.Cause, "missing")
95 assert.Contains(t, *problemDetails.Cause, "notificationDestination")
96 subscriptionId := path.Base(result.Recorder.Header().Get(echo.HeaderLocation))
97 registeredSub := serviceUnderTest.getSubscription(subscriptionId)
98 assert.Nil(t, registeredSub)
101 func TestDeregisterSubscription(t *testing.T) {
102 subscription := eventsapi.EventSubscription{
103 Events: []eventsapi.CAPIFEvent{
104 eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
106 NotificationDestination: common29122.Uri(""),
108 serviceUnderTest, requestHandler := getEcho(nil)
110 serviceUnderTest.addSubscription(subId, subscription)
112 result := testutil.NewRequest().Delete("/subscriberId/subscriptions/"+subId).Go(t, requestHandler)
113 assert.Equal(t, http.StatusNoContent, result.Code())
114 assert.Nil(t, serviceUnderTest.getSubscription(subId))
117 func TestSendEvent(t *testing.T) {
118 notificationUrl := "url"
119 apiIds := []string{"apiId"}
121 newEvent := eventsapi.EventNotification{
122 EventDetail: &eventsapi.CAPIFEventDetail{
125 Events: eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
127 wg := sync.WaitGroup{}
128 clientMock := NewTestClient(func(req *http.Request) *http.Response {
129 if req.URL.String() == notificationUrl {
130 assert.Equal(t, req.Method, "PUT")
131 assert.Equal(t, "application/json", req.Header.Get("Content-Type"))
132 newEvent.SubscriptionId = subId
133 assert.Equal(t, newEvent, getBodyAsEvent(req, t))
135 return &http.Response{
137 Body: io.NopCloser(bytes.NewBufferString(`OK`)),
138 Header: make(http.Header), // Must be set to non-nil value or it panics
141 t.Error("Wrong call to client: ", req)
145 serviceUnderTest, _ := getEcho(clientMock)
147 serviceUnderTest.addSubscription(subId, eventsapi.EventSubscription{
148 Events: []eventsapi.CAPIFEvent{
149 eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
151 NotificationDestination: common29122.Uri(notificationUrl),
153 sub2 := eventsapi.EventSubscription{
154 Events: []eventsapi.CAPIFEvent{
155 eventsapi.CAPIFEventACCESSCONTROLPOLICYUNAVAILABLE,
157 NotificationDestination: common29122.Uri(notificationUrl),
159 serviceUnderTest.addSubscription("other", sub2)
163 serviceUnderTest.GetNotificationChannel() <- newEvent
166 if waitTimeout(&wg, 1*time.Second) {
167 t.Error("No event notification was sent")
172 func TestMatchEventType(t *testing.T) {
173 notificationUrl := "url"
175 serviceUnderTest := NewEventService(nil)
176 serviceUnderTest.addSubscription(subId, eventsapi.EventSubscription{
177 Events: []eventsapi.CAPIFEvent{
178 eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
180 NotificationDestination: common29122.Uri(notificationUrl),
181 EventFilters: &[]eventsapi.CAPIFEventFilter{},
183 serviceUnderTest.addSubscription("other", eventsapi.EventSubscription{
184 Events: []eventsapi.CAPIFEvent{
185 eventsapi.CAPIFEventACCESSCONTROLPOLICYUNAVAILABLE,
187 NotificationDestination: common29122.Uri(notificationUrl),
190 event := eventsapi.EventNotification{
191 SubscriptionId: subId,
192 Events: eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
195 matchingSubs := serviceUnderTest.filterOnEventType(event)
196 assert.Len(t, matchingSubs, 1)
197 assert.Equal(t, subId, matchingSubs[0])
200 func TestMatchEventTypeAndFilters(t *testing.T) {
202 apiIds := []string{"apiId"}
203 invokerIds := []string{"invokerId"}
205 aefIds := []string{aefId}
206 serviceUnderTest := NewEventService(nil)
207 serviceUnderTest.addSubscription(subId, eventsapi.EventSubscription{
208 Events: []eventsapi.CAPIFEvent{
209 eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
211 EventFilters: &[]eventsapi.CAPIFEventFilter{
214 ApiInvokerIds: &invokerIds,
219 serviceUnderTest.addSubscription("otherSameType", eventsapi.EventSubscription{
220 Events: []eventsapi.CAPIFEvent{
221 eventsapi.CAPIFEventACCESSCONTROLPOLICYUNAVAILABLE,
224 serviceUnderTest.addSubscription("other", eventsapi.EventSubscription{
225 Events: []eventsapi.CAPIFEvent{
226 eventsapi.CAPIFEventACCESSCONTROLPOLICYUNAVAILABLE,
230 event := eventsapi.EventNotification{
231 Events: eventsapi.CAPIFEventACCESSCONTROLPOLICYUNAVAILABLE,
235 matchingSubs := serviceUnderTest.getMatchingSubs(event)
236 assert.Len(t, matchingSubs, 2)
238 // Match with all filter ids
239 aefProfiles := []publishserviceapi.AefProfile{
244 serviceDescriptions := []publishserviceapi.ServiceAPIDescription{
246 AefProfiles: &aefProfiles,
249 event.Events = eventsapi.CAPIFEventSERVICEAPIAVAILABLE
250 event.EventDetail = &eventsapi.CAPIFEventDetail{
252 ApiInvokerIds: &invokerIds,
253 ServiceAPIDescriptions: &serviceDescriptions,
255 matchingSubs = serviceUnderTest.getMatchingSubs(event)
256 assert.Len(t, matchingSubs, 1)
257 assert.Equal(t, subId, matchingSubs[0])
260 otherApiIds := []string{"otherApiId"}
261 (*serviceUnderTest.subscriptions[subId].EventFilters)[0].ApiIds = &otherApiIds
262 matchingSubs = serviceUnderTest.getMatchingSubs(event)
263 assert.Len(t, matchingSubs, 0)
265 // Un match invokerId
266 otherInvokerIds := []string{"otherInvokerId"}
267 (*serviceUnderTest.subscriptions[subId].EventFilters)[0].ApiIds = nil
268 (*serviceUnderTest.subscriptions[subId].EventFilters)[0].ApiInvokerIds = &otherInvokerIds
269 matchingSubs = serviceUnderTest.getMatchingSubs(event)
270 assert.Len(t, matchingSubs, 0)
273 otherAefIds := []string{"otherAefId"}
274 (*serviceUnderTest.subscriptions[subId].EventFilters)[0].ApiInvokerIds = nil
275 (*serviceUnderTest.subscriptions[subId].EventFilters)[0].AefIds = &otherAefIds
276 matchingSubs = serviceUnderTest.getMatchingSubs(event)
277 assert.Len(t, matchingSubs, 0)
279 // Match with empty subscription filter id list
280 (*serviceUnderTest.subscriptions[subId].EventFilters)[0].AefIds = &[]string{}
281 matchingSubs = serviceUnderTest.getMatchingSubs(event)
282 assert.Len(t, matchingSubs, 1)
284 // Match with empty event id list
285 event.EventDetail.ApiIds = nil
286 event.EventDetail.ApiInvokerIds = nil
287 event.EventDetail.ServiceAPIDescriptions = &[]publishserviceapi.ServiceAPIDescription{}
288 matchingSubs = serviceUnderTest.getMatchingSubs(event)
289 assert.Len(t, matchingSubs, 1)
292 func getEcho(client restclient.HTTPClient) (*EventService, *echo.Echo) {
293 swagger, err := eventsapi.GetSwagger()
295 fmt.Fprintf(os.Stderr, "Error loading swagger spec\n: %s", err)
299 swagger.Servers = nil
301 es := NewEventService(client)
304 e.Use(echomiddleware.Logger())
305 e.Use(middleware.OapiRequestValidator(swagger))
307 eventsapi.RegisterHandlers(e, es)
311 type RoundTripFunc func(req *http.Request) *http.Response
313 func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
317 // NewTestClient returns *http.Client with Transport replaced to avoid making real calls
318 func NewTestClient(fn RoundTripFunc) *http.Client {
320 Transport: RoundTripFunc(fn),
324 // waitTimeout waits for the waitgroup for the specified max timeout.
325 // Returns true if waiting timed out.
326 func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
327 c := make(chan struct{})
334 return false // completed normally
335 case <-time.After(timeout):
336 return true // timed out
340 func getBodyAsEvent(req *http.Request, t *testing.T) eventsapi.EventNotification {
341 buf := new(bytes.Buffer)
342 if _, err := buf.ReadFrom(req.Body); err != nil {
345 var event eventsapi.EventNotification
346 err := json.Unmarshal(buf.Bytes(), &event)