a328e5799010825e0252ff18b509d3b3b38d3e32
[nonrtric/plt/sme.git] / capifcore / internal / eventservice / eventservice_test.go
1 // -
2 //   ========================LICENSE_START=================================
3 //   O-RAN-SC
4 //   %%
5 //   Copyright (C) 2022: Nordix Foundation
6 //   %%
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
10 //
11 //        http://www.apache.org/licenses/LICENSE-2.0
12 //
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===================================
19 //
20
21 package eventservice
22
23 import (
24         "bytes"
25         "encoding/json"
26         "fmt"
27         "io/ioutil"
28         "net/http"
29         "os"
30         "path"
31         "sync"
32         "testing"
33         "time"
34
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"
44 )
45
46 func TestRegisterSubscriptions(t *testing.T) {
47         subscription1 := eventsapi.EventSubscription{
48                 Events: []eventsapi.CAPIFEvent{
49                         eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
50                 },
51                 NotificationDestination: common29122.Uri("notificationUrl"),
52         }
53         serviceUnderTest, requestHandler := getEcho(nil)
54         subscriberId := "subscriberId"
55
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))
64
65         subscription2 := subscription1
66         subscription2.Events = []eventsapi.CAPIFEvent{
67                 eventsapi.CAPIFEventAPIINVOKERUPDATED,
68         }
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))
72
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)
78 }
79
80 func TestDeregisterSubscription(t *testing.T) {
81         subscription := eventsapi.EventSubscription{
82                 Events: []eventsapi.CAPIFEvent{
83                         eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
84                 },
85                 NotificationDestination: common29122.Uri(""),
86         }
87         serviceUnderTest, requestHandler := getEcho(nil)
88         subId := "sub1"
89         serviceUnderTest.addSubscription(subId, subscription)
90
91         result := testutil.NewRequest().Delete("/subscriberId/subscriptions/"+subId).Go(t, requestHandler)
92         assert.Equal(t, http.StatusNoContent, result.Code())
93         assert.Nil(t, serviceUnderTest.getSubscription(subId))
94 }
95
96 func TestSendEvent(t *testing.T) {
97         notificationUrl := "url"
98         apiIds := []string{"apiId"}
99         subId := "sub1"
100         newEvent := eventsapi.EventNotification{
101                 EventDetail: &eventsapi.CAPIFEventDetail{
102                         ApiIds: &apiIds,
103                 },
104                 Events: eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
105         }
106         wg := sync.WaitGroup{}
107         clientMock := NewTestClient(func(req *http.Request) *http.Response {
108                 if req.URL.String() == notificationUrl {
109                         assert.Equal(t, req.Method, "PUT")
110                         assert.Equal(t, "application/json", req.Header.Get("Content-Type"))
111                         newEvent.SubscriptionId = subId
112                         assert.Equal(t, newEvent, getBodyAsEvent(req, t))
113                         wg.Done()
114                         return &http.Response{
115                                 StatusCode: 200,
116                                 Body:       ioutil.NopCloser(bytes.NewBufferString(`OK`)),
117                                 Header:     make(http.Header), // Must be set to non-nil value or it panics
118                         }
119                 }
120                 t.Error("Wrong call to client: ", req)
121                 t.Fail()
122                 return nil
123         })
124         serviceUnderTest, _ := getEcho(clientMock)
125
126         serviceUnderTest.addSubscription(subId, eventsapi.EventSubscription{
127                 Events: []eventsapi.CAPIFEvent{
128                         eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
129                 },
130                 NotificationDestination: common29122.Uri(notificationUrl),
131         })
132         sub2 := eventsapi.EventSubscription{
133                 Events: []eventsapi.CAPIFEvent{
134                         eventsapi.CAPIFEventACCESSCONTROLPOLICYUNAVAILABLE,
135                 },
136                 NotificationDestination: common29122.Uri(notificationUrl),
137         }
138         serviceUnderTest.addSubscription("other", sub2)
139
140         wg.Add(1)
141         go func() {
142                 serviceUnderTest.GetNotificationChannel() <- newEvent
143         }()
144
145         if waitTimeout(&wg, 1*time.Second) {
146                 t.Error("Not all calls to server were made")
147                 t.Fail()
148         }
149 }
150
151 func TestMatchEventType(t *testing.T) {
152         notificationUrl := "url"
153         subId := "sub1"
154         serviceUnderTest := NewEventService(nil)
155         serviceUnderTest.addSubscription(subId, eventsapi.EventSubscription{
156                 Events: []eventsapi.CAPIFEvent{
157                         eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
158                 },
159                 NotificationDestination: common29122.Uri(notificationUrl),
160                 EventFilters:            &[]eventsapi.CAPIFEventFilter{},
161         })
162         serviceUnderTest.addSubscription("other", eventsapi.EventSubscription{
163                 Events: []eventsapi.CAPIFEvent{
164                         eventsapi.CAPIFEventACCESSCONTROLPOLICYUNAVAILABLE,
165                 },
166                 NotificationDestination: common29122.Uri(notificationUrl),
167         })
168
169         event := eventsapi.EventNotification{
170                 SubscriptionId: subId,
171                 Events:         eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
172         }
173
174         matchingSubs := serviceUnderTest.filterOnEventType(event)
175         assert.Len(t, matchingSubs, 1)
176         assert.Equal(t, subId, matchingSubs[0])
177 }
178
179 func TestMatchEventTypeAndFilters(t *testing.T) {
180         subId := "sub1"
181         apiIds := []string{"apiId"}
182         invokerIds := []string{"invokerId"}
183         aefId := "aefId"
184         aefIds := []string{aefId}
185         serviceUnderTest := NewEventService(nil)
186         serviceUnderTest.addSubscription(subId, eventsapi.EventSubscription{
187                 Events: []eventsapi.CAPIFEvent{
188                         eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
189                 },
190                 EventFilters: &[]eventsapi.CAPIFEventFilter{
191                         {
192                                 ApiIds:        &apiIds,
193                                 ApiInvokerIds: &invokerIds,
194                                 AefIds:        &aefIds,
195                         },
196                 },
197         })
198         serviceUnderTest.addSubscription("otherSameType", eventsapi.EventSubscription{
199                 Events: []eventsapi.CAPIFEvent{
200                         eventsapi.CAPIFEventACCESSCONTROLPOLICYUNAVAILABLE,
201                 },
202         })
203         serviceUnderTest.addSubscription("other", eventsapi.EventSubscription{
204                 Events: []eventsapi.CAPIFEvent{
205                         eventsapi.CAPIFEventACCESSCONTROLPOLICYUNAVAILABLE,
206                 },
207         })
208
209         event := eventsapi.EventNotification{
210                 Events: eventsapi.CAPIFEventACCESSCONTROLPOLICYUNAVAILABLE,
211         }
212
213         // Only match type
214         matchingSubs := serviceUnderTest.getMatchingSubs(event)
215         assert.Len(t, matchingSubs, 2)
216
217         // Match with all filter ids
218         aefProfiles := []publishserviceapi.AefProfile{
219                 {
220                         AefId: aefId,
221                 },
222         }
223         serviceDescriptions := []publishserviceapi.ServiceAPIDescription{
224                 {
225                         AefProfiles: &aefProfiles,
226                 },
227         }
228         event.Events = eventsapi.CAPIFEventSERVICEAPIAVAILABLE
229         event.EventDetail = &eventsapi.CAPIFEventDetail{
230                 ApiIds:                 &apiIds,
231                 ApiInvokerIds:          &invokerIds,
232                 ServiceAPIDescriptions: &serviceDescriptions,
233         }
234         matchingSubs = serviceUnderTest.getMatchingSubs(event)
235         assert.Len(t, matchingSubs, 1)
236         assert.Equal(t, subId, matchingSubs[0])
237
238         // Un match apiId
239         otherApiIds := []string{"otherApiId"}
240         (*serviceUnderTest.subscriptions[subId].EventFilters)[0].ApiIds = &otherApiIds
241         matchingSubs = serviceUnderTest.getMatchingSubs(event)
242         assert.Len(t, matchingSubs, 0)
243
244         // Un match invokerId
245         otherInvokerIds := []string{"otherInvokerId"}
246         (*serviceUnderTest.subscriptions[subId].EventFilters)[0].ApiIds = nil
247         (*serviceUnderTest.subscriptions[subId].EventFilters)[0].ApiInvokerIds = &otherInvokerIds
248         matchingSubs = serviceUnderTest.getMatchingSubs(event)
249         assert.Len(t, matchingSubs, 0)
250
251         // Un match aefId
252         otherAefIds := []string{"otherAefId"}
253         (*serviceUnderTest.subscriptions[subId].EventFilters)[0].ApiInvokerIds = nil
254         (*serviceUnderTest.subscriptions[subId].EventFilters)[0].AefIds = &otherAefIds
255         matchingSubs = serviceUnderTest.getMatchingSubs(event)
256         assert.Len(t, matchingSubs, 0)
257
258         // Match with empty subscription filter id list
259         (*serviceUnderTest.subscriptions[subId].EventFilters)[0].AefIds = &[]string{}
260         matchingSubs = serviceUnderTest.getMatchingSubs(event)
261         assert.Len(t, matchingSubs, 1)
262
263         // Match with empty event id list
264         event.EventDetail.ApiIds = nil
265         event.EventDetail.ApiInvokerIds = nil
266         event.EventDetail.ServiceAPIDescriptions = &[]publishserviceapi.ServiceAPIDescription{}
267         matchingSubs = serviceUnderTest.getMatchingSubs(event)
268         assert.Len(t, matchingSubs, 1)
269 }
270
271 func getEcho(client restclient.HTTPClient) (*EventService, *echo.Echo) {
272         swagger, err := eventsapi.GetSwagger()
273         if err != nil {
274                 fmt.Fprintf(os.Stderr, "Error loading swagger spec\n: %s", err)
275                 os.Exit(1)
276         }
277
278         swagger.Servers = nil
279
280         es := NewEventService(client)
281
282         e := echo.New()
283         e.Use(echomiddleware.Logger())
284         e.Use(middleware.OapiRequestValidator(swagger))
285
286         eventsapi.RegisterHandlers(e, es)
287         return es, e
288 }
289
290 type RoundTripFunc func(req *http.Request) *http.Response
291
292 func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
293         return f(req), nil
294 }
295
296 // NewTestClient returns *http.Client with Transport replaced to avoid making real calls
297 func NewTestClient(fn RoundTripFunc) *http.Client {
298         return &http.Client{
299                 Transport: RoundTripFunc(fn),
300         }
301 }
302
303 // waitTimeout waits for the waitgroup for the specified max timeout.
304 // Returns true if waiting timed out.
305 func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
306         c := make(chan struct{})
307         go func() {
308                 defer close(c)
309                 wg.Wait()
310         }()
311         select {
312         case <-c:
313                 return false // completed normally
314         case <-time.After(timeout):
315                 return true // timed out
316         }
317 }
318
319 func getBodyAsEvent(req *http.Request, t *testing.T) eventsapi.EventNotification {
320         buf := new(bytes.Buffer)
321         if _, err := buf.ReadFrom(req.Body); err != nil {
322                 t.Fail()
323         }
324         var event eventsapi.EventNotification
325         err := json.Unmarshal(buf.Bytes(), &event)
326         if err != nil {
327                 t.Fail()
328         }
329         return event
330 }