Update more functionality to publish service
[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         apfId := "apfId"
51         aefId := "aefId"
52         serviceRegisterMock := serviceMocks.ServiceRegister{}
53         serviceRegisterMock.On("GetAefsForPublisher", apfId).Return([]string{aefId, "otherAefId"})
54         helmManagerMock := helmMocks.HelmManager{}
55         helmManagerMock.On("InstallHelmChart", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
56         serviceUnderTest, eventChannel, requestHandler := getEcho(&serviceRegisterMock, &helmManagerMock)
57
58         // Check no services published for provider
59         result := testutil.NewRequest().Get("/"+apfId+"/service-apis").Go(t, requestHandler)
60
61         assert.Equal(t, http.StatusNotFound, result.Code())
62
63         apiName := "app-management"
64         namespace := "namespace"
65         repoName := "repoName"
66         chartName := "chartName"
67         releaseName := "releaseName"
68         description := fmt.Sprintf("Description,%s,%s,%s,%s", namespace, repoName, chartName, releaseName)
69         newServiceDescription := getServiceAPIDescription(aefId, apiName, description)
70
71         // Publish a service for provider
72         result = testutil.NewRequest().Post("/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler)
73
74         assert.Equal(t, http.StatusCreated, result.Code())
75         var resultService publishapi.ServiceAPIDescription
76         err := result.UnmarshalBodyToObject(&resultService)
77         assert.NoError(t, err, "error unmarshaling response")
78         newApiId := "api_id_" + apiName
79         assert.Equal(t, *resultService.ApiId, newApiId)
80         assert.Equal(t, "http://example.com/"+apfId+"/service-apis/"+*resultService.ApiId, result.Recorder.Header().Get(echo.HeaderLocation))
81         newServiceDescription.ApiId = &newApiId
82         wantedAPILIst := []publishapi.ServiceAPIDescription{newServiceDescription}
83         assert.True(t, serviceUnderTest.AreAPIsPublished(&wantedAPILIst))
84         assert.True(t, serviceUnderTest.IsAPIPublished(aefId, apiName))
85         serviceRegisterMock.AssertCalled(t, "GetAefsForPublisher", apfId)
86         helmManagerMock.AssertCalled(t, "InstallHelmChart", namespace, repoName, chartName, releaseName)
87         assert.ElementsMatch(t, []string{aefId}, serviceUnderTest.getAllAefIds())
88         if publishEvent, ok := waitForEvent(eventChannel, 1*time.Second); ok {
89                 assert.Fail(t, "No event sent")
90         } else {
91                 assert.Equal(t, *resultService.ApiId, (*publishEvent.EventDetail.ApiIds)[0])
92                 assert.Equal(t, eventsapi.CAPIFEventSERVICEAPIAVAILABLE, publishEvent.Events)
93         }
94
95         // Check that the service is published for the provider
96         result = testutil.NewRequest().Get("/"+apfId+"/service-apis/"+newApiId).Go(t, requestHandler)
97
98         assert.Equal(t, http.StatusOK, result.Code())
99         err = result.UnmarshalBodyToObject(&resultService)
100         assert.NoError(t, err, "error unmarshaling response")
101         assert.Equal(t, *resultService.ApiId, newApiId)
102
103         // Delete the service
104         helmManagerMock.On("UninstallHelmChart", mock.Anything, mock.Anything).Return(nil)
105         result = testutil.NewRequest().Delete("/"+apfId+"/service-apis/"+newApiId).Go(t, requestHandler)
106
107         assert.Equal(t, http.StatusNoContent, result.Code())
108         helmManagerMock.AssertCalled(t, "UninstallHelmChart", namespace, chartName)
109         assert.Empty(t, serviceUnderTest.getAllAefIds())
110
111         // Check no services published
112         result = testutil.NewRequest().Get("/"+apfId+"/service-apis/"+newApiId).Go(t, requestHandler)
113
114         if publishEvent, ok := waitForEvent(eventChannel, 1*time.Second); ok {
115                 assert.Fail(t, "No event sent")
116         } else {
117                 assert.Equal(t, *resultService.ApiId, (*publishEvent.EventDetail.ApiIds)[0])
118                 assert.Equal(t, eventsapi.CAPIFEventSERVICEAPIUNAVAILABLE, publishEvent.Events)
119         }
120
121         assert.Equal(t, http.StatusNotFound, result.Code())
122 }
123
124 func TestPostUnpublishedServiceWithUnregisteredFunction(t *testing.T) {
125         apfId := "apfId"
126         aefId := "aefId"
127         serviceRegisterMock := serviceMocks.ServiceRegister{}
128         serviceRegisterMock.On("GetAefsForPublisher", apfId).Return([]string{"otherAefId"})
129         _, _, requestHandler := getEcho(&serviceRegisterMock, nil)
130
131         newServiceDescription := getServiceAPIDescription(aefId, "apiName", "description")
132
133         // Publish a service
134         result := testutil.NewRequest().Post("/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler)
135
136         assert.Equal(t, http.StatusNotFound, result.Code())
137         var resultError common29122.ProblemDetails
138         err := result.UnmarshalBodyToObject(&resultError)
139         assert.NoError(t, err, "error unmarshaling response")
140         assert.Contains(t, *resultError.Cause, aefId)
141         assert.Contains(t, *resultError.Cause, "not registered")
142         notFound := http.StatusNotFound
143         assert.Equal(t, &notFound, resultError.Status)
144 }
145
146 func TestGetServices(t *testing.T) {
147         apfId := "apfId"
148         aefId := "aefId"
149         serviceRegisterMock := serviceMocks.ServiceRegister{}
150         serviceRegisterMock.On("GetAefsForPublisher", apfId).Return([]string{aefId})
151         _, _, requestHandler := getEcho(&serviceRegisterMock, nil)
152
153         // Check no services published for provider
154         result := testutil.NewRequest().Get("/"+apfId+"/service-apis").Go(t, requestHandler)
155
156         assert.Equal(t, http.StatusNotFound, result.Code())
157
158         serviceDescription1 := getServiceAPIDescription(aefId, "api1", "Description")
159         serviceDescription2 := getServiceAPIDescription(aefId, "api2", "Description")
160
161         // Publish a service for provider
162         testutil.NewRequest().Post("/"+apfId+"/service-apis").WithJsonBody(serviceDescription1).Go(t, requestHandler)
163         testutil.NewRequest().Post("/"+apfId+"/service-apis").WithJsonBody(serviceDescription2).Go(t, requestHandler)
164
165         // Get all services for provider
166         result = testutil.NewRequest().Get("/"+apfId+"/service-apis").Go(t, requestHandler)
167         assert.Equal(t, http.StatusOK, result.Code())
168         var resultServices []publishapi.ServiceAPIDescription
169         err := result.UnmarshalBodyToObject(&resultServices)
170         assert.NoError(t, err, "error unmarshaling response")
171         assert.Len(t, resultServices, 2)
172         apiId1 := "api_id_api1"
173         serviceDescription1.ApiId = &apiId1
174         apiId2 := "api_id_api2"
175         serviceDescription2.ApiId = &apiId2
176         assert.Contains(t, resultServices, serviceDescription1)
177         assert.Contains(t, resultServices, serviceDescription2)
178 }
179
180 func TestGetPublishedServices(t *testing.T) {
181         serviceUnderTest := NewPublishService(nil, nil, nil)
182
183         profiles := make([]publishapi.AefProfile, 1)
184         serviceDescription := publishapi.ServiceAPIDescription{
185                 AefProfiles: &profiles,
186         }
187         serviceUnderTest.publishedServices["publisher1"] = []publishapi.ServiceAPIDescription{
188                 serviceDescription,
189         }
190         serviceUnderTest.publishedServices["publisher2"] = []publishapi.ServiceAPIDescription{
191                 serviceDescription,
192         }
193         result := serviceUnderTest.GetAllPublishedServices()
194         assert.Len(t, result, 2)
195 }
196
197 func TestUpdateDescription(t *testing.T) {
198         apfId := "apfId"
199         serviceApiId := "serviceApiId"
200         aefId := "aefId"
201         apiName := "apiName"
202         description := "description"
203
204         serviceRegisterMock := serviceMocks.ServiceRegister{}
205         serviceRegisterMock.On("GetAefsForPublisher", apfId).Return([]string{aefId, "otherAefId", "aefIdNew"})
206         helmManagerMock := helmMocks.HelmManager{}
207         helmManagerMock.On("InstallHelmChart", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
208         serviceUnderTest, eventChannel, requestHandler := getEcho(&serviceRegisterMock, &helmManagerMock)
209         serviceDescription := getServiceAPIDescription(aefId, apiName, description)
210         serviceDescription.ApiId = &serviceApiId
211         serviceUnderTest.publishedServices[apfId] = []publishapi.ServiceAPIDescription{serviceDescription}
212         (*serviceDescription.AefProfiles)[0].AefId = aefId
213
214         //Modify the service
215         updatedServiceDescription := getServiceAPIDescription(aefId, apiName, description)
216         updatedServiceDescription.ApiId = &serviceApiId
217         (*updatedServiceDescription.AefProfiles)[0].AefId = aefId
218         newDescription := "new description"
219         updatedServiceDescription.Description = &newDescription
220         newDomainName := "new domainName"
221         (*updatedServiceDescription.AefProfiles)[0].DomainName = &newDomainName
222
223         newProfileDomain := "new profile Domain name"
224         var protocol publishapi.Protocol = "HTTP_1_1"
225         test := make([]publishapi.AefProfile, 1)
226         test = *updatedServiceDescription.AefProfiles
227         test = append(test, publishapi.AefProfile{
228
229                 AefId:      "aefIdNew",
230                 DomainName: &newProfileDomain,
231                 Protocol:   &protocol,
232                 Versions: []publishapi.Version{
233                         {
234                                 ApiVersion: "v1",
235                                 Resources: &[]publishapi.Resource{
236                                         {
237                                                 CommType: "REQUEST_RESPONSE",
238                                                 Operations: &[]publishapi.Operation{
239                                                         "POST",
240                                                 },
241                                                 ResourceName: "app",
242                                                 Uri:          "app",
243                                         },
244                                 },
245                         },
246                 },
247         },
248         )
249
250         updatedServiceDescription.AefProfiles = &test
251
252         result := testutil.NewRequest().Put("/"+apfId+"/service-apis/"+serviceApiId).WithJsonBody(updatedServiceDescription).Go(t, requestHandler)
253
254         var resultService publishapi.ServiceAPIDescription
255         assert.Equal(t, http.StatusOK, result.Code())
256         err := result.UnmarshalBodyToObject(&resultService)
257         assert.NoError(t, err, "error unmarshaling response")
258         assert.Equal(t, newDescription, *resultService.Description)
259         assert.Equal(t, newDomainName, *(*resultService.AefProfiles)[0].DomainName)
260         assert.Equal(t, "aefIdNew", (*resultService.AefProfiles)[1].AefId)
261
262         if publishEvent, ok := waitForEvent(eventChannel, 1*time.Second); ok {
263                 assert.Fail(t, "No event sent")
264         } else {
265                 assert.Equal(t, *resultService.ApiId, (*publishEvent.EventDetail.ApiIds)[0])
266                 assert.Equal(t, eventsapi.CAPIFEventSERVICEAPIUPDATE, publishEvent.Events)
267         }
268 }
269
270 func getEcho(serviceRegister providermanagement.ServiceRegister, helmManager helmmanagement.HelmManager) (*PublishService, chan eventsapi.EventNotification, *echo.Echo) {
271         swagger, err := publishapi.GetSwagger()
272         if err != nil {
273                 fmt.Fprintf(os.Stderr, "Error loading swagger spec\n: %s", err)
274                 os.Exit(1)
275         }
276
277         swagger.Servers = nil
278
279         eventChannel := make(chan eventsapi.EventNotification)
280         ps := NewPublishService(serviceRegister, helmManager, eventChannel)
281
282         e := echo.New()
283         e.Use(echomiddleware.Logger())
284         e.Use(middleware.OapiRequestValidator(swagger))
285
286         publishapi.RegisterHandlers(e, ps)
287         return ps, eventChannel, e
288 }
289
290 func getServiceAPIDescription(aefId, apiName, description string) publishapi.ServiceAPIDescription {
291         domainName := "domainName"
292         var protocol publishapi.Protocol = "HTTP_1_1"
293         return publishapi.ServiceAPIDescription{
294                 AefProfiles: &[]publishapi.AefProfile{
295                         {
296                                 AefId:      aefId,
297                                 DomainName: &domainName,
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                 ApiName:     apiName,
317                 Description: &description,
318         }
319 }
320
321 // waitForEvent waits for the channel to receive an event for the specified max timeout.
322 // Returns true if waiting timed out.
323 func waitForEvent(ch chan eventsapi.EventNotification, timeout time.Duration) (*eventsapi.EventNotification, bool) {
324         select {
325         case event := <-ch:
326                 return &event, false // completed normally
327         case <-time.After(timeout):
328                 return nil, true // timed out
329         }
330 }