2 // ========================LICENSE_START=================================
5 // Copyright (C) 2022: Nordix Foundation
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
11 // http://www.apache.org/licenses/LICENSE-2.0
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===================================
21 package publishservice
30 "oransc.org/nonrtric/capifcore/internal/common29122"
31 "oransc.org/nonrtric/capifcore/internal/eventsapi"
32 "oransc.org/nonrtric/capifcore/internal/providermanagement"
34 "github.com/labstack/echo/v4"
36 publishapi "oransc.org/nonrtric/capifcore/internal/publishserviceapi"
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"
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"
49 func TestPublishUnpublishService(t *testing.T) {
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)
59 // Check no services published for provider
60 result := testutil.NewRequest().Get("/"+apfId+"/service-apis").Go(t, requestHandler)
62 assert.Equal(t, http.StatusNotFound, result.Code())
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)
72 // Publish a service for provider
73 result = testutil.NewRequest().Post("/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler)
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, newApiId, *resultService.ApiId)
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")
92 assert.Equal(t, *resultService.ApiId, (*publishEvent.EventDetail.ApiIds)[0])
93 assert.Equal(t, eventsapi.CAPIFEventSERVICEAPIAVAILABLE, publishEvent.Events)
96 // Check that the service is published for the provider
97 result = testutil.NewRequest().Get("/"+apfId+"/service-apis/"+newApiId).Go(t, requestHandler)
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)
104 // Delete the service
105 helmManagerMock.On("UninstallHelmChart", mock.Anything, mock.Anything).Return(nil)
107 result = testutil.NewRequest().Delete("/"+apfId+"/service-apis/"+newApiId).Go(t, requestHandler)
109 assert.Equal(t, http.StatusNoContent, result.Code())
110 helmManagerMock.AssertCalled(t, "UninstallHelmChart", namespace, chartName)
111 assert.Empty(t, serviceUnderTest.getAllAefIds())
113 // Check no services published
114 result = testutil.NewRequest().Get("/"+apfId+"/service-apis/"+newApiId).Go(t, requestHandler)
116 if publishEvent, ok := waitForEvent(eventChannel, 1*time.Second); ok {
117 assert.Fail(t, "No event sent")
119 assert.Equal(t, *resultService.ApiId, (*publishEvent.EventDetail.ApiIds)[0])
120 assert.Equal(t, eventsapi.CAPIFEventSERVICEAPIUNAVAILABLE, publishEvent.Events)
123 assert.Equal(t, http.StatusNotFound, result.Code())
126 func TestPostUnpublishedServiceWithUnregisteredFunction(t *testing.T) {
129 serviceRegisterMock := serviceMocks.ServiceRegister{}
130 serviceRegisterMock.On("GetAefsForPublisher", apfId).Return([]string{"otherAefId"})
131 _, _, requestHandler := getEcho(&serviceRegisterMock, nil)
133 newServiceDescription := getServiceAPIDescription(aefId, "apiName", "description")
136 result := testutil.NewRequest().Post("/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler)
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, ¬Found, resultError.Status)
148 func TestGetServices(t *testing.T) {
151 serviceRegisterMock := serviceMocks.ServiceRegister{}
152 serviceRegisterMock.On("GetAefsForPublisher", apfId).Return([]string{aefId})
153 _, _, requestHandler := getEcho(&serviceRegisterMock, nil)
155 // Check no services published for provider
156 result := testutil.NewRequest().Get("/"+apfId+"/service-apis").Go(t, requestHandler)
158 assert.Equal(t, http.StatusNotFound, result.Code())
160 serviceDescription1 := getServiceAPIDescription(aefId, "api1", "Description")
161 serviceDescription2 := getServiceAPIDescription(aefId, "api2", "Description")
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)
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)
182 func TestGetPublishedServices(t *testing.T) {
183 serviceUnderTest := NewPublishService(nil, nil, nil)
185 profiles := make([]publishapi.AefProfile, 1)
186 serviceDescription := publishapi.ServiceAPIDescription{
187 AefProfiles: &profiles,
189 serviceUnderTest.publishedServices["publisher1"] = []publishapi.ServiceAPIDescription{
192 serviceUnderTest.publishedServices["publisher2"] = []publishapi.ServiceAPIDescription{
195 result := serviceUnderTest.GetAllPublishedServices()
196 assert.Len(t, result, 2)
199 func TestUpdateDescription(t *testing.T) {
201 serviceApiId := "serviceApiId"
204 description := "description"
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
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
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{
232 DomainName: &newProfileDomain,
234 Versions: []publishapi.Version{
237 Resources: &[]publishapi.Resource{
239 CommType: "REQUEST_RESPONSE",
240 Operations: &[]publishapi.Operation{
252 updatedServiceDescription.AefProfiles = &test
254 result := testutil.NewRequest().Put("/"+apfId+"/service-apis/"+serviceApiId).WithJsonBody(updatedServiceDescription).Go(t, requestHandler)
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"))
265 if publishEvent, ok := waitForEvent(eventChannel, 1*time.Second); ok {
266 assert.Fail(t, "No event sent")
268 assert.Equal(t, *resultService.ApiId, (*publishEvent.EventDetail.ApiIds)[0])
269 assert.Equal(t, eventsapi.CAPIFEventSERVICEAPIUPDATE, publishEvent.Events)
273 func TestUpdateValidServiceWithDeletedFunction(t *testing.T) {
275 serviceApiId := "serviceApiId"
278 description := "description"
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)
286 serviceDescription := getServiceAPIDescription(aefId, apiName, description)
287 serviceDescription.ApiId = &serviceApiId
288 (*serviceDescription.AefProfiles)[0].AefId = aefId
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{
297 DomainName: &newProfileDomain,
299 Versions: []publishapi.Version{
302 Resources: &[]publishapi.Resource{
304 CommType: "REQUEST_RESPONSE",
305 Operations: &[]publishapi.Operation{
316 serviceDescription.AefProfiles = &test
317 serviceUnderTest.publishedServices[apfId] = []publishapi.ServiceAPIDescription{serviceDescription}
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{
327 DomainName: &newProfileDomain,
329 Versions: []publishapi.Version{
332 Resources: &[]publishapi.Resource{
334 CommType: "REQUEST_RESPONSE",
335 Operations: &[]publishapi.Operation{
346 updatedServiceDescription.AefProfiles = &test1
347 testFunc := []publishapi.AefProfile{
348 (*updatedServiceDescription.AefProfiles)[1],
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"))
362 func TestPublishInvalidService(t *testing.T) {
363 _, _, requestHandler := getEcho(nil, nil)
364 newServiceDescription := getServiceAPIDescription("aefId", " ", "description")
367 result := testutil.NewRequest().Post("/apfId/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler)
369 assert.Equal(t, http.StatusBadRequest, result.Code())
370 var resultError common29122.ProblemDetails
371 err := result.UnmarshalBodyToObject(&resultError)
372 assert.NoError(t, err, "error unmarshaling response")
373 assert.Contains(t, *resultError.Cause, "missing")
374 assert.Contains(t, *resultError.Cause, "apiName")
375 assert.Equal(t, http.StatusBadRequest, *resultError.Status)
378 func getEcho(serviceRegister providermanagement.ServiceRegister, helmManager helmmanagement.HelmManager) (*PublishService, chan eventsapi.EventNotification, *echo.Echo) {
379 swagger, err := publishapi.GetSwagger()
381 fmt.Fprintf(os.Stderr, "Error loading swagger spec\n: %s", err)
385 swagger.Servers = nil
387 eventChannel := make(chan eventsapi.EventNotification)
388 ps := NewPublishService(serviceRegister, helmManager, eventChannel)
391 e.Use(echomiddleware.Logger())
392 e.Use(middleware.OapiRequestValidator(swagger))
394 publishapi.RegisterHandlers(e, ps)
395 return ps, eventChannel, e
398 func getServiceAPIDescription(aefId, apiName, description string) publishapi.ServiceAPIDescription {
399 domainName := "domainName"
400 var protocol publishapi.Protocol = "HTTP_1_1"
401 return publishapi.ServiceAPIDescription{
402 AefProfiles: &[]publishapi.AefProfile{
405 DomainName: &domainName,
407 Versions: []publishapi.Version{
410 Resources: &[]publishapi.Resource{
412 CommType: "REQUEST_RESPONSE",
413 Operations: &[]publishapi.Operation{
425 Description: &description,
429 // waitForEvent waits for the channel to receive an event for the specified max timeout.
430 // Returns true if waiting timed out.
431 func waitForEvent(ch chan eventsapi.EventNotification, timeout time.Duration) (*eventsapi.EventNotification, bool) {
434 return &event, false // completed normally
435 case <-time.After(timeout):
436 return nil, true // timed out