Add event type matching for sending events
[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         subscription := eventsapi.EventSubscription{
126                 Events: []eventsapi.CAPIFEvent{
127                         eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
128                 },
129                 NotificationDestination: common29122.Uri(notificationUrl),
130         }
131         serviceUnderTest.addSubscription(subId, subscription)
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         subscription := eventsapi.EventSubscription{
156                 Events: []eventsapi.CAPIFEvent{
157                         eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
158                 },
159                 NotificationDestination: common29122.Uri(notificationUrl),
160         }
161         serviceUnderTest.addSubscription(subId, subscription)
162         sub2 := eventsapi.EventSubscription{
163                 Events: []eventsapi.CAPIFEvent{
164                         eventsapi.CAPIFEventACCESSCONTROLPOLICYUNAVAILABLE,
165                 },
166                 NotificationDestination: common29122.Uri(notificationUrl),
167         }
168         serviceUnderTest.addSubscription("other", sub2)
169
170         event := eventsapi.EventNotification{
171                 SubscriptionId: subId,
172                 Events:         eventsapi.CAPIFEventSERVICEAPIAVAILABLE,
173         }
174
175         matchingSubs := serviceUnderTest.getMatchingSubs(event)
176         assert.Len(t, matchingSubs, 1)
177         assert.Equal(t, subId, matchingSubs[0])
178 }
179
180 func getEcho(client restclient.HTTPClient) (*EventService, *echo.Echo) {
181         swagger, err := eventsapi.GetSwagger()
182         if err != nil {
183                 fmt.Fprintf(os.Stderr, "Error loading swagger spec\n: %s", err)
184                 os.Exit(1)
185         }
186
187         swagger.Servers = nil
188
189         es := NewEventService(client)
190
191         e := echo.New()
192         e.Use(echomiddleware.Logger())
193         e.Use(middleware.OapiRequestValidator(swagger))
194
195         eventsapi.RegisterHandlers(e, es)
196         return es, e
197 }
198
199 type RoundTripFunc func(req *http.Request) *http.Response
200
201 func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
202         return f(req), nil
203 }
204
205 // NewTestClient returns *http.Client with Transport replaced to avoid making real calls
206 func NewTestClient(fn RoundTripFunc) *http.Client {
207         return &http.Client{
208                 Transport: RoundTripFunc(fn),
209         }
210 }
211
212 // waitTimeout waits for the waitgroup for the specified max timeout.
213 // Returns true if waiting timed out.
214 func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
215         c := make(chan struct{})
216         go func() {
217                 defer close(c)
218                 wg.Wait()
219         }()
220         select {
221         case <-c:
222                 return false // completed normally
223         case <-time.After(timeout):
224                 return true // timed out
225         }
226 }
227
228 func getBodyAsEvent(req *http.Request, t *testing.T) eventsapi.EventNotification {
229         buf := new(bytes.Buffer)
230         if _, err := buf.ReadFrom(req.Body); err != nil {
231                 t.Fail()
232         }
233         var event eventsapi.EventNotification
234         err := json.Unmarshal(buf.Bytes(), &event)
235         if err != nil {
236                 t.Fail()
237         }
238         return event
239 }