cad151c0c635993593b073c1932fc95da62f3cf4
[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         serviceRegisterMock := serviceMocks.ServiceRegister{}
204         serviceRegisterMock.On("GetAefsForPublisher", apfId).Return([]string{aefId, "otherAefId"})
205         helmManagerMock := helmMocks.HelmManager{}
206         helmManagerMock.On("InstallHelmChart", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
207         serviceUnderTest, eventChannel, requestHandler := getEcho(&serviceRegisterMock, &helmManagerMock)
208
209         serviceDescription := getServiceAPIDescription(aefId, apiName, description)
210         serviceDescription.ApiId = &serviceApiId
211         serviceUnderTest.publishedServices[apfId] = []publishapi.ServiceAPIDescription{serviceDescription}
212
213         //Modify the service
214         updatedServiceDescription := getServiceAPIDescription(aefId, apiName, description)
215         updatedServiceDescription.ApiId = &description
216         newDescription := "new description"
217         updatedServiceDescription.Description = &newDescription
218         result := testutil.NewRequest().Put("/"+apfId+"/service-apis/"+serviceApiId).WithJsonBody(updatedServiceDescription).Go(t, requestHandler)
219
220         var resultService publishapi.ServiceAPIDescription
221         assert.Equal(t, http.StatusOK, result.Code())
222         err := result.UnmarshalBodyToObject(&resultService)
223         assert.NoError(t, err, "error unmarshaling response")
224         assert.Equal(t, resultService.Description, &newDescription)
225
226         if publishEvent, ok := waitForEvent(eventChannel, 1*time.Second); ok {
227                 assert.Fail(t, "No event sent")
228         } else {
229                 assert.Equal(t, *resultService.ApiId, (*publishEvent.EventDetail.ApiIds)[0])
230                 assert.Equal(t, eventsapi.CAPIFEventSERVICEAPIUPDATE, publishEvent.Events)
231         }
232 }
233
234 func getEcho(serviceRegister providermanagement.ServiceRegister, helmManager helmmanagement.HelmManager) (*PublishService, chan eventsapi.EventNotification, *echo.Echo) {
235         swagger, err := publishapi.GetSwagger()
236         if err != nil {
237                 fmt.Fprintf(os.Stderr, "Error loading swagger spec\n: %s", err)
238                 os.Exit(1)
239         }
240
241         swagger.Servers = nil
242
243         eventChannel := make(chan eventsapi.EventNotification)
244         ps := NewPublishService(serviceRegister, helmManager, eventChannel)
245
246         e := echo.New()
247         e.Use(echomiddleware.Logger())
248         e.Use(middleware.OapiRequestValidator(swagger))
249
250         publishapi.RegisterHandlers(e, ps)
251         return ps, eventChannel, e
252 }
253
254 func getServiceAPIDescription(aefId, apiName, description string) publishapi.ServiceAPIDescription {
255         domainName := "domainName"
256         var protocol publishapi.Protocol = "HTTP_1_1"
257         return publishapi.ServiceAPIDescription{
258                 AefProfiles: &[]publishapi.AefProfile{
259                         {
260                                 AefId:      aefId,
261                                 DomainName: &domainName,
262                                 Protocol:   &protocol,
263                                 Versions: []publishapi.Version{
264                                         {
265                                                 ApiVersion: "v1",
266                                                 Resources: &[]publishapi.Resource{
267                                                         {
268                                                                 CommType: "REQUEST_RESPONSE",
269                                                                 Operations: &[]publishapi.Operation{
270                                                                         "POST",
271                                                                 },
272                                                                 ResourceName: "app",
273                                                                 Uri:          "app",
274                                                         },
275                                                 },
276                                         },
277                                 },
278                         },
279                 },
280                 ApiName:     apiName,
281                 Description: &description,
282         }
283 }
284
285 // waitForEvent waits for the channel to receive an event for the specified max timeout.
286 // Returns true if waiting timed out.
287 func waitForEvent(ch chan eventsapi.EventNotification, timeout time.Duration) (*eventsapi.EventNotification, bool) {
288         select {
289         case event := <-ch:
290                 return &event, false // completed normally
291         case <-time.After(timeout):
292                 return nil, true // timed out
293         }
294 }