8b35f55ea5a326e91cb096ba57760cfb078854d2
[nonrtric/plt/sme.git] / capifcore / internal / publishservice / publishservice_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 publishservice
22
23 import (
24         "fmt"
25         "net/http"
26         "os"
27         "testing"
28         "time"
29
30         "oransc.org/nonrtric/capifcore/internal/common29122"
31         "oransc.org/nonrtric/capifcore/internal/eventsapi"
32         "oransc.org/nonrtric/capifcore/internal/providermanagement"
33
34         "github.com/labstack/echo/v4"
35
36         publishapi "oransc.org/nonrtric/capifcore/internal/publishserviceapi"
37
38         "oransc.org/nonrtric/capifcore/internal/helmmanagement"
39         helmMocks "oransc.org/nonrtric/capifcore/internal/helmmanagement/mocks"
40         serviceMocks "oransc.org/nonrtric/capifcore/internal/providermanagement/mocks"
41
42         "github.com/deepmap/oapi-codegen/pkg/middleware"
43         "github.com/deepmap/oapi-codegen/pkg/testutil"
44         echomiddleware "github.com/labstack/echo/v4/middleware"
45         "github.com/stretchr/testify/assert"
46         "github.com/stretchr/testify/mock"
47 )
48
49 func TestPublishUnpublishService(t *testing.T) {
50
51         apfId := "apfId"
52         aefId := "aefId"
53         serviceRegisterMock := serviceMocks.ServiceRegister{}
54         serviceRegisterMock.On("GetAefsForPublisher", apfId).Return([]string{aefId, "otherAefId"})
55         helmManagerMock := helmMocks.HelmManager{}
56         helmManagerMock.On("InstallHelmChart", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
57         serviceUnderTest, eventChannel, requestHandler := getEcho(&serviceRegisterMock, &helmManagerMock)
58
59         // Check no services published for provider
60         result := testutil.NewRequest().Get("/"+apfId+"/service-apis").Go(t, requestHandler)
61
62         assert.Equal(t, http.StatusNotFound, result.Code())
63
64         apiName := "app-management"
65         namespace := "namespace"
66         repoName := "repoName"
67         chartName := "chartName"
68         releaseName := "releaseName"
69         description := fmt.Sprintf("Description,%s,%s,%s,%s", namespace, repoName, chartName, releaseName)
70         newServiceDescription := getServiceAPIDescription(aefId, apiName, description)
71
72         // Publish a service for provider
73         result = testutil.NewRequest().Post("/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler)
74
75         assert.Equal(t, http.StatusCreated, result.Code())
76         var resultService publishapi.ServiceAPIDescription
77         err := result.UnmarshalBodyToObject(&resultService)
78         assert.NoError(t, err, "error unmarshaling response")
79         newApiId := "api_id_" + apiName
80         assert.Equal(t, *resultService.ApiId, newApiId)
81         assert.Equal(t, "http://example.com/"+apfId+"/service-apis/"+*resultService.ApiId, result.Recorder.Header().Get(echo.HeaderLocation))
82         newServiceDescription.ApiId = &newApiId
83         wantedAPILIst := []publishapi.ServiceAPIDescription{newServiceDescription}
84         assert.True(t, serviceUnderTest.AreAPIsPublished(&wantedAPILIst))
85         assert.True(t, serviceUnderTest.IsAPIPublished(aefId, apiName))
86         serviceRegisterMock.AssertCalled(t, "GetAefsForPublisher", apfId)
87         helmManagerMock.AssertCalled(t, "InstallHelmChart", namespace, repoName, chartName, releaseName)
88         assert.ElementsMatch(t, []string{aefId}, serviceUnderTest.getAllAefIds())
89         if publishEvent, ok := waitForEvent(eventChannel, 1*time.Second); ok {
90                 assert.Fail(t, "No event sent")
91         } else {
92                 assert.Equal(t, *resultService.ApiId, (*publishEvent.EventDetail.ApiIds)[0])
93                 assert.Equal(t, eventsapi.CAPIFEventSERVICEAPIAVAILABLE, publishEvent.Events)
94         }
95
96         // Check that the service is published for the provider
97         result = testutil.NewRequest().Get("/"+apfId+"/service-apis/"+newApiId).Go(t, requestHandler)
98
99         assert.Equal(t, http.StatusOK, result.Code())
100         err = result.UnmarshalBodyToObject(&resultService)
101         assert.NoError(t, err, "error unmarshaling response")
102         assert.Equal(t, *resultService.ApiId, newApiId)
103
104         // Delete the service
105         helmManagerMock.On("UninstallHelmChart", mock.Anything, mock.Anything).Return(nil)
106
107         result = testutil.NewRequest().Delete("/"+apfId+"/service-apis/"+newApiId).Go(t, requestHandler)
108
109         assert.Equal(t, http.StatusNoContent, result.Code())
110         helmManagerMock.AssertCalled(t, "UninstallHelmChart", namespace, chartName)
111         assert.Empty(t, serviceUnderTest.getAllAefIds())
112
113         // Check no services published
114         result = testutil.NewRequest().Get("/"+apfId+"/service-apis/"+newApiId).Go(t, requestHandler)
115
116         if publishEvent, ok := waitForEvent(eventChannel, 1*time.Second); ok {
117                 assert.Fail(t, "No event sent")
118         } else {
119                 assert.Equal(t, *resultService.ApiId, (*publishEvent.EventDetail.ApiIds)[0])
120                 assert.Equal(t, eventsapi.CAPIFEventSERVICEAPIUNAVAILABLE, publishEvent.Events)
121         }
122
123         assert.Equal(t, http.StatusNotFound, result.Code())
124 }
125
126 func TestPostUnpublishedServiceWithUnregisteredFunction(t *testing.T) {
127         apfId := "apfId"
128         aefId := "aefId"
129         serviceRegisterMock := serviceMocks.ServiceRegister{}
130         serviceRegisterMock.On("GetAefsForPublisher", apfId).Return([]string{"otherAefId"})
131         _, _, requestHandler := getEcho(&serviceRegisterMock, nil)
132
133         newServiceDescription := getServiceAPIDescription(aefId, "apiName", "description")
134
135         // Publish a service
136         result := testutil.NewRequest().Post("/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler)
137
138         assert.Equal(t, http.StatusNotFound, result.Code())
139         var resultError common29122.ProblemDetails
140         err := result.UnmarshalBodyToObject(&resultError)
141         assert.NoError(t, err, "error unmarshaling response")
142         assert.Contains(t, *resultError.Cause, aefId)
143         assert.Contains(t, *resultError.Cause, "not registered")
144         notFound := http.StatusNotFound
145         assert.Equal(t, &notFound, resultError.Status)
146 }
147
148 func TestGetServices(t *testing.T) {
149         apfId := "apfId"
150         aefId := "aefId"
151         serviceRegisterMock := serviceMocks.ServiceRegister{}
152         serviceRegisterMock.On("GetAefsForPublisher", apfId).Return([]string{aefId})
153         _, _, requestHandler := getEcho(&serviceRegisterMock, nil)
154
155         // Check no services published for provider
156         result := testutil.NewRequest().Get("/"+apfId+"/service-apis").Go(t, requestHandler)
157
158         assert.Equal(t, http.StatusNotFound, result.Code())
159
160         serviceDescription1 := getServiceAPIDescription(aefId, "api1", "Description")
161         serviceDescription2 := getServiceAPIDescription(aefId, "api2", "Description")
162
163         // Publish a service for provider
164         testutil.NewRequest().Post("/"+apfId+"/service-apis").WithJsonBody(serviceDescription1).Go(t, requestHandler)
165         testutil.NewRequest().Post("/"+apfId+"/service-apis").WithJsonBody(serviceDescription2).Go(t, requestHandler)
166
167         // Get all services for provider
168         result = testutil.NewRequest().Get("/"+apfId+"/service-apis").Go(t, requestHandler)
169         assert.Equal(t, http.StatusOK, result.Code())
170         var resultServices []publishapi.ServiceAPIDescription
171         err := result.UnmarshalBodyToObject(&resultServices)
172         assert.NoError(t, err, "error unmarshaling response")
173         assert.Len(t, resultServices, 2)
174         apiId1 := "api_id_api1"
175         serviceDescription1.ApiId = &apiId1
176         apiId2 := "api_id_api2"
177         serviceDescription2.ApiId = &apiId2
178         assert.Contains(t, resultServices, serviceDescription1)
179         assert.Contains(t, resultServices, serviceDescription2)
180 }
181
182 func TestGetPublishedServices(t *testing.T) {
183         serviceUnderTest := NewPublishService(nil, nil, nil)
184
185         profiles := make([]publishapi.AefProfile, 1)
186         serviceDescription := publishapi.ServiceAPIDescription{
187                 AefProfiles: &profiles,
188         }
189         serviceUnderTest.publishedServices["publisher1"] = []publishapi.ServiceAPIDescription{
190                 serviceDescription,
191         }
192         serviceUnderTest.publishedServices["publisher2"] = []publishapi.ServiceAPIDescription{
193                 serviceDescription,
194         }
195         result := serviceUnderTest.GetAllPublishedServices()
196         assert.Len(t, result, 2)
197 }
198
199 func TestUpdateDescription(t *testing.T) {
200         apfId := "apfId"
201         serviceApiId := "serviceApiId"
202         aefId := "aefId"
203         apiName := "apiName"
204         description := "description"
205
206         serviceRegisterMock := serviceMocks.ServiceRegister{}
207         serviceRegisterMock.On("GetAefsForPublisher", apfId).Return([]string{aefId, "otherAefId", "aefIdNew"})
208         helmManagerMock := helmMocks.HelmManager{}
209         helmManagerMock.On("InstallHelmChart", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
210         serviceUnderTest, eventChannel, requestHandler := getEcho(&serviceRegisterMock, &helmManagerMock)
211         serviceDescription := getServiceAPIDescription(aefId, apiName, description)
212         serviceDescription.ApiId = &serviceApiId
213         serviceUnderTest.publishedServices[apfId] = []publishapi.ServiceAPIDescription{serviceDescription}
214         (*serviceDescription.AefProfiles)[0].AefId = aefId
215
216         //Modify the service
217         updatedServiceDescription := getServiceAPIDescription(aefId, apiName, description)
218         updatedServiceDescription.ApiId = &serviceApiId
219         (*updatedServiceDescription.AefProfiles)[0].AefId = aefId
220         newDescription := "new description"
221         updatedServiceDescription.Description = &newDescription
222         newDomainName := "new domainName"
223         (*updatedServiceDescription.AefProfiles)[0].DomainName = &newDomainName
224
225         newProfileDomain := "new profile Domain name"
226         var protocol publishapi.Protocol = "HTTP_1_1"
227         test := make([]publishapi.AefProfile, 1)
228         test = *updatedServiceDescription.AefProfiles
229         test = append(test, publishapi.AefProfile{
230
231                 AefId:      "aefIdNew",
232                 DomainName: &newProfileDomain,
233                 Protocol:   &protocol,
234                 Versions: []publishapi.Version{
235                         {
236                                 ApiVersion: "v1",
237                                 Resources: &[]publishapi.Resource{
238                                         {
239                                                 CommType: "REQUEST_RESPONSE",
240                                                 Operations: &[]publishapi.Operation{
241                                                         "POST",
242                                                 },
243                                                 ResourceName: "app",
244                                                 Uri:          "app",
245                                         },
246                                 },
247                         },
248                 },
249         },
250         )
251
252         updatedServiceDescription.AefProfiles = &test
253
254         result := testutil.NewRequest().Put("/"+apfId+"/service-apis/"+serviceApiId).WithJsonBody(updatedServiceDescription).Go(t, requestHandler)
255
256         var resultService publishapi.ServiceAPIDescription
257         assert.Equal(t, http.StatusOK, result.Code())
258         err := result.UnmarshalBodyToObject(&resultService)
259         assert.NoError(t, err, "error unmarshaling response")
260         assert.Equal(t, newDescription, *resultService.Description)
261         assert.Equal(t, newDomainName, *(*resultService.AefProfiles)[0].DomainName)
262         assert.Equal(t, "aefIdNew", (*resultService.AefProfiles)[1].AefId)
263         assert.True(t, serviceUnderTest.IsAPIPublished("aefIdNew", "path"))
264
265         if publishEvent, ok := waitForEvent(eventChannel, 1*time.Second); ok {
266                 assert.Fail(t, "No event sent")
267         } else {
268                 assert.Equal(t, *resultService.ApiId, (*publishEvent.EventDetail.ApiIds)[0])
269                 assert.Equal(t, eventsapi.CAPIFEventSERVICEAPIUPDATE, publishEvent.Events)
270         }
271 }
272
273 func TestUpdateValidServiceWithDeletedFunction(t *testing.T) {
274         apfId := "apfId"
275         serviceApiId := "serviceApiId"
276         aefId := "aefId"
277         apiName := "apiName"
278         description := "description"
279
280         serviceRegisterMock := serviceMocks.ServiceRegister{}
281         serviceRegisterMock.On("GetAefsForPublisher", apfId).Return([]string{aefId, "otherAefId", "aefIdNew"})
282         helmManagerMock := helmMocks.HelmManager{}
283         helmManagerMock.On("InstallHelmChart", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
284         serviceUnderTest, _, requestHandler := getEcho(&serviceRegisterMock, &helmManagerMock)
285
286         serviceDescription := getServiceAPIDescription(aefId, apiName, description)
287         serviceDescription.ApiId = &serviceApiId
288         (*serviceDescription.AefProfiles)[0].AefId = aefId
289
290         newProfileDomain := "new profile Domain name"
291         var protocol publishapi.Protocol = "HTTP_1_1"
292         test := make([]publishapi.AefProfile, 1)
293         test = *serviceDescription.AefProfiles
294         test = append(test, publishapi.AefProfile{
295
296                 AefId:      "aefIdNew",
297                 DomainName: &newProfileDomain,
298                 Protocol:   &protocol,
299                 Versions: []publishapi.Version{
300                         {
301                                 ApiVersion: "v1",
302                                 Resources: &[]publishapi.Resource{
303                                         {
304                                                 CommType: "REQUEST_RESPONSE",
305                                                 Operations: &[]publishapi.Operation{
306                                                         "POST",
307                                                 },
308                                                 ResourceName: "app",
309                                                 Uri:          "app",
310                                         },
311                                 },
312                         },
313                 },
314         },
315         )
316         serviceDescription.AefProfiles = &test
317         serviceUnderTest.publishedServices[apfId] = []publishapi.ServiceAPIDescription{serviceDescription}
318
319         //Modify the service
320         updatedServiceDescription := getServiceAPIDescription(aefId, apiName, description)
321         updatedServiceDescription.ApiId = &serviceApiId
322         test1 := make([]publishapi.AefProfile, 1)
323         test1 = *updatedServiceDescription.AefProfiles
324         test1 = append(test1, publishapi.AefProfile{
325
326                 AefId:      "aefIdNew",
327                 DomainName: &newProfileDomain,
328                 Protocol:   &protocol,
329                 Versions: []publishapi.Version{
330                         {
331                                 ApiVersion: "v1",
332                                 Resources: &[]publishapi.Resource{
333                                         {
334                                                 CommType: "REQUEST_RESPONSE",
335                                                 Operations: &[]publishapi.Operation{
336                                                         "POST",
337                                                 },
338                                                 ResourceName: "app",
339                                                 Uri:          "app",
340                                         },
341                                 },
342                         },
343                 },
344         },
345         )
346         updatedServiceDescription.AefProfiles = &test1
347         testFunc := []publishapi.AefProfile{
348                 (*updatedServiceDescription.AefProfiles)[1],
349         }
350
351         updatedServiceDescription.AefProfiles = &testFunc
352         result := testutil.NewRequest().Put("/"+apfId+"/service-apis/"+serviceApiId).WithJsonBody(updatedServiceDescription).Go(t, requestHandler)
353         var resultService publishapi.ServiceAPIDescription
354         assert.Equal(t, http.StatusOK, result.Code())
355         err := result.UnmarshalBodyToObject(&resultService)
356         assert.NoError(t, err, "error unmarshaling response")
357         assert.Len(t, (*resultService.AefProfiles), 1)
358         assert.False(t, serviceUnderTest.IsAPIPublished("aefId", "path"))
359
360 }
361 func getEcho(serviceRegister providermanagement.ServiceRegister, helmManager helmmanagement.HelmManager) (*PublishService, chan eventsapi.EventNotification, *echo.Echo) {
362         swagger, err := publishapi.GetSwagger()
363         if err != nil {
364                 fmt.Fprintf(os.Stderr, "Error loading swagger spec\n: %s", err)
365                 os.Exit(1)
366         }
367
368         swagger.Servers = nil
369
370         eventChannel := make(chan eventsapi.EventNotification)
371         ps := NewPublishService(serviceRegister, helmManager, eventChannel)
372
373         e := echo.New()
374         e.Use(echomiddleware.Logger())
375         e.Use(middleware.OapiRequestValidator(swagger))
376
377         publishapi.RegisterHandlers(e, ps)
378         return ps, eventChannel, e
379 }
380
381 func getServiceAPIDescription(aefId, apiName, description string) publishapi.ServiceAPIDescription {
382         domainName := "domainName"
383         var protocol publishapi.Protocol = "HTTP_1_1"
384         return publishapi.ServiceAPIDescription{
385                 AefProfiles: &[]publishapi.AefProfile{
386                         {
387                                 AefId:      aefId,
388                                 DomainName: &domainName,
389                                 Protocol:   &protocol,
390                                 Versions: []publishapi.Version{
391                                         {
392                                                 ApiVersion: "v1",
393                                                 Resources: &[]publishapi.Resource{
394                                                         {
395                                                                 CommType: "REQUEST_RESPONSE",
396                                                                 Operations: &[]publishapi.Operation{
397                                                                         "POST",
398                                                                 },
399                                                                 ResourceName: "app",
400                                                                 Uri:          "app",
401                                                         },
402                                                 },
403                                         },
404                                 },
405                         },
406                 },
407                 ApiName:     apiName,
408                 Description: &description,
409         }
410 }
411
412 // waitForEvent waits for the channel to receive an event for the specified max timeout.
413 // Returns true if waiting timed out.
414 func waitForEvent(ch chan eventsapi.EventNotification, timeout time.Duration) (*eventsapi.EventNotification, bool) {
415         select {
416         case event := <-ch:
417                 return &event, false // completed normally
418         case <-time.After(timeout):
419                 return nil, true // timed out
420         }
421 }