Add apiId filtering for eventservice
[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/restclient"
43 )
44
45 func TestRegisterSubscriptions(t *testing.T) {
46         subscription1 := eventsapi.EventSubscription{
47                 Events: []eventsapi.CAPIFEvent{
48                         eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
49                 },
50                 NotificationDestination: common29122.Uri("notificationUrl"),
51         }
52         serviceUnderTest, requestHandler := getEcho(nil)
53         subscriberId := "subscriberId"
54
55         result := testutil.NewRequest().Post("/"+subscriberId+"/subscriptions").WithJsonBody(subscription1).Go(t, requestHandler)
56         assert.Equal(t, http.StatusCreated, result.Code())
57         var resultEvent eventsapi.EventSubscription
58         err := result.UnmarshalBodyToObject(&resultEvent)
59         assert.NoError(t, err, "error unmarshaling response")
60         assert.Equal(t, resultEvent, subscription1)
61         assert.Regexp(t, "http://example.com/"+subscriberId+"/subscriptions/"+subscriberId+"[0-9]+", result.Recorder.Header().Get(echo.HeaderLocation))
62         subscriptionId1 := path.Base(result.Recorder.Header().Get(echo.HeaderLocation))
63
64         subscription2 := subscription1
65         subscription2.Events = []eventsapi.CAPIFEvent{
66                 eventsapi.CAPIFEventAPIINVOKERUPDATED,
67         }
68         result = testutil.NewRequest().Post("/"+subscriberId+"/subscriptions").WithJsonBody(subscription2).Go(t, requestHandler)
69         assert.Regexp(t, "http://example.com/"+subscriberId+"/subscriptions/"+subscriberId+"[0-9]+", result.Recorder.Header().Get(echo.HeaderLocation))
70         subscriptionId2 := path.Base(result.Recorder.Header().Get(echo.HeaderLocation))
71
72         assert.NotEqual(t, subscriptionId1, subscriptionId2)
73         registeredSub1 := serviceUnderTest.getSubscription(subscriptionId1)
74         assert.Equal(t, subscription1, *registeredSub1)
75         registeredSub2 := serviceUnderTest.getSubscription(subscriptionId2)
76         assert.Equal(t, subscription2, *registeredSub2)
77 }
78
79 func TestDeregisterSubscription(t *testing.T) {
80         subscription := eventsapi.EventSubscription{
81                 Events: []eventsapi.CAPIFEvent{
82                         eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
83                 },
84                 NotificationDestination: common29122.Uri(""),
85         }
86         serviceUnderTest, requestHandler := getEcho(nil)
87         subId := "sub1"
88         serviceUnderTest.addSubscription(subId, subscription)
89
90         result := testutil.NewRequest().Delete("/subscriberId/subscriptions/"+subId).Go(t, requestHandler)
91         assert.Equal(t, http.StatusNoContent, result.Code())
92         assert.Nil(t, serviceUnderTest.getSubscription(subId))
93 }
94
95 func TestSendEvent(t *testing.T) {
96         notificationUrl := "url"
97         apiIds := []string{"apiId"}
98         subId := "sub1"
99         newEvent := eventsapi.EventNotification{
100                 EventDetail: &eventsapi.CAPIFEventDetail{
101                         ApiIds: &apiIds,
102                 },
103                 Events: eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
104         }
105         wg := sync.WaitGroup{}
106         clientMock := NewTestClient(func(req *http.Request) *http.Response {
107                 if req.URL.String() == notificationUrl {
108                         assert.Equal(t, req.Method, "PUT")
109                         assert.Equal(t, "application/json", req.Header.Get("Content-Type"))
110                         newEvent.SubscriptionId = subId
111                         assert.Equal(t, newEvent, getBodyAsEvent(req, t))
112                         wg.Done()
113                         return &http.Response{
114                                 StatusCode: 200,
115                                 Body:       ioutil.NopCloser(bytes.NewBufferString(`OK`)),
116                                 Header:     make(http.Header), // Must be set to non-nil value or it panics
117                         }
118                 }
119                 t.Error("Wrong call to client: ", req)
120                 t.Fail()
121                 return nil
122         })
123         serviceUnderTest, _ := getEcho(clientMock)
124
125         serviceUnderTest.addSubscription(subId, eventsapi.EventSubscription{
126                 Events: []eventsapi.CAPIFEvent{
127                         eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
128                 },
129                 NotificationDestination: common29122.Uri(notificationUrl),
130         })
131         sub2 := eventsapi.EventSubscription{
132                 Events: []eventsapi.CAPIFEvent{
133                         eventsapi.CAPIFEventACCESSCONTROLPOLICYUNAVAILABLE,
134                 },
135                 NotificationDestination: common29122.Uri(notificationUrl),
136         }
137         serviceUnderTest.addSubscription("other", sub2)
138
139         wg.Add(1)
140         go func() {
141                 serviceUnderTest.GetNotificationChannel() <- newEvent
142         }()
143
144         if waitTimeout(&wg, 1*time.Second) {
145                 t.Error("Not all calls to server were made")
146                 t.Fail()
147         }
148 }
149
150 func TestMatchEventType(t *testing.T) {
151         notificationUrl := "url"
152         subId := "sub1"
153         serviceUnderTest := NewEventService(nil)
154         serviceUnderTest.addSubscription(subId, eventsapi.EventSubscription{
155                 Events: []eventsapi.CAPIFEvent{
156                         eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
157                 },
158                 NotificationDestination: common29122.Uri(notificationUrl),
159                 EventFilters:            &[]eventsapi.CAPIFEventFilter{},
160         })
161         serviceUnderTest.addSubscription("other", eventsapi.EventSubscription{
162                 Events: []eventsapi.CAPIFEvent{
163                         eventsapi.CAPIFEventACCESSCONTROLPOLICYUNAVAILABLE,
164                 },
165                 NotificationDestination: common29122.Uri(notificationUrl),
166         })
167
168         event := eventsapi.EventNotification{
169                 SubscriptionId: subId,
170                 Events:         eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
171         }
172
173         matchingSubs := serviceUnderTest.getMatchingSubs(event)
174         assert.Len(t, matchingSubs, 1)
175         assert.Equal(t, subId, matchingSubs[0])
176 }
177
178 func TestMatchesApiIds(t *testing.T) {
179         apiId := "apiId"
180         apiIds := []string{apiId, "otherApiId"}
181         eventFilters := []eventsapi.CAPIFEventFilter{
182                 {},
183                 {
184                         ApiIds: &apiIds,
185                 },
186         }
187
188         eventApiIds := []string{apiId}
189         assert.True(t, matchesApiIds(eventApiIds, eventFilters))
190         assert.True(t, matchesApiIds(nil, eventFilters))
191
192         altApiIds := []string{"anotherApiId"}
193         unMatchingFilterAdded := append(eventFilters, eventsapi.CAPIFEventFilter{
194                 ApiIds: &altApiIds,
195         })
196         assert.False(t, matchesApiIds(eventApiIds, unMatchingFilterAdded))
197
198         apiIds[0] = "anotherId"
199         assert.False(t, matchesApiIds(eventApiIds, eventFilters))
200 }
201
202 func getEcho(client restclient.HTTPClient) (*EventService, *echo.Echo) {
203         swagger, err := eventsapi.GetSwagger()
204         if err != nil {
205                 fmt.Fprintf(os.Stderr, "Error loading swagger spec\n: %s", err)
206                 os.Exit(1)
207         }
208
209         swagger.Servers = nil
210
211         es := NewEventService(client)
212
213         e := echo.New()
214         e.Use(echomiddleware.Logger())
215         e.Use(middleware.OapiRequestValidator(swagger))
216
217         eventsapi.RegisterHandlers(e, es)
218         return es, e
219 }
220
221 type RoundTripFunc func(req *http.Request) *http.Response
222
223 func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
224         return f(req), nil
225 }
226
227 // NewTestClient returns *http.Client with Transport replaced to avoid making real calls
228 func NewTestClient(fn RoundTripFunc) *http.Client {
229         return &http.Client{
230                 Transport: RoundTripFunc(fn),
231         }
232 }
233
234 // waitTimeout waits for the waitgroup for the specified max timeout.
235 // Returns true if waiting timed out.
236 func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
237         c := make(chan struct{})
238         go func() {
239                 defer close(c)
240                 wg.Wait()
241         }()
242         select {
243         case <-c:
244                 return false // completed normally
245         case <-time.After(timeout):
246                 return true // timed out
247         }
248 }
249
250 func getBodyAsEvent(req *http.Request, t *testing.T) eventsapi.EventNotification {
251         buf := new(bytes.Buffer)
252         if _, err := buf.ReadFrom(req.Body); err != nil {
253                 t.Fail()
254         }
255         var event eventsapi.EventNotification
256         err := json.Unmarshal(buf.Bytes(), &event)
257         if err != nil {
258                 t.Fail()
259         }
260         return event
261 }