2 // ========================LICENSE_START=================================
5 // Copyright (C) 2023-2024: OpenInfra Foundation Europe
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 "github.com/deepmap/oapi-codegen/pkg/middleware"
31 "github.com/deepmap/oapi-codegen/pkg/testutil"
32 echo "github.com/labstack/echo/v4"
33 echomiddleware "github.com/labstack/echo/v4/middleware"
34 log "github.com/sirupsen/logrus"
35 "github.com/stretchr/testify/assert"
37 "oransc.org/nonrtric/servicemanager/internal/common29122"
38 "oransc.org/nonrtric/servicemanager/internal/envreader"
39 "oransc.org/nonrtric/servicemanager/internal/kongclear"
40 "oransc.org/nonrtric/servicemanager/internal/providermanagement"
41 provapi "oransc.org/nonrtric/servicemanager/internal/providermanagementapi"
42 publishapi "oransc.org/nonrtric/servicemanager/internal/publishserviceapi"
45 var requestHandler *echo.Echo
47 func TestMain(m *testing.M) {
60 func setupTest() error {
61 myEnv, myPorts, err := envreader.ReadDotEnv()
63 log.Fatal("error loading environment file on setupTest")
67 requestHandler, err = getEcho(myEnv, myPorts)
69 log.Fatal("getEcho fatal error on setupTest")
74 log.Fatal("getEcho fatal error on teardown")
81 func getProvider() provapi.APIProviderEnrolmentDetails {
84 funcInfoAPF = "rApp Kong as APF"
85 funcInfoAEF = "rApp Kong as AEF"
88 testFuncs := []provapi.APIProviderFunctionDetails{
90 ApiProvFuncInfo: &funcInfoAPF,
91 ApiProvFuncRole: provapi.ApiProviderFuncRoleAPF,
92 RegInfo: provapi.RegistrationInformation{
93 ApiProvPubKey: "APF-PublicKey",
97 ApiProvFuncInfo: &funcInfoAEF,
98 ApiProvFuncRole: provapi.ApiProviderFuncRoleAEF,
99 RegInfo: provapi.RegistrationInformation{
100 ApiProvPubKey: "AEF-PublicKey",
104 return provapi.APIProviderEnrolmentDetails{
106 ApiProvDomInfo: &domainInfo,
107 ApiProvFuncs: &testFuncs,
111 func teardown() error {
112 log.Trace("entering teardown")
114 t := new(testing.T) // Create a new testing.T instance for teardown
116 // Delete the invoker
117 invokerInfo := "invoker a"
118 invokerId := "api_invoker_id_" + strings.Replace(invokerInfo, " ", "_", 1)
120 result := testutil.NewRequest().Delete("/api-invoker-management/v1/onboardedInvokers/"+invokerId).Go(t, requestHandler)
121 assert.Equal(t, http.StatusNoContent, result.Code())
123 // Delete the original published service
124 apfId := "APF_id_rApp_Kong_as_APF"
126 apiId := "api_id_" + apiName
128 result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, requestHandler)
129 assert.Equal(t, http.StatusNoContent, result.Code())
131 // Delete the first published service
132 apfId = "APF_id_rApp_Kong_as_APF"
134 apiId = "api_id_" + apiName
136 result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, requestHandler)
137 assert.Equal(t, http.StatusNoContent, result.Code())
139 // Delete the second published service
141 apiId = "api_id_" + apiName
143 result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, requestHandler)
144 assert.Equal(t, http.StatusNoContent, result.Code())
146 // Delete the provider
147 domainID := "domain_id_Kong"
148 result = testutil.NewRequest().Delete("/api-provider-management/v1/registrations/"+domainID).Go(t, requestHandler)
149 assert.Equal(t, http.StatusNoContent, result.Code())
151 myEnv, myPorts, err := envreader.ReadDotEnv()
153 log.Fatal("error loading environment file")
157 err = kongclear.KongClear(myEnv, myPorts)
159 log.Fatal("error clearing Kong on teardown")
164 func TestPostUnpublishedServiceWithUnregisteredPublisher(t *testing.T) {
167 apfId := "APF_id_rApp_Kong_as_APF"
170 // Check no services published
171 result := testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis").Go(t, requestHandler)
172 assert.Equal(t, http.StatusNotFound, result.Code())
174 var resultError common29122.ProblemDetails
175 err := result.UnmarshalBodyToObject(&resultError)
176 assert.NoError(t, err, "error unmarshaling response")
178 assert.Contains(t, *resultError.Cause, apfId)
179 assert.Contains(t, *resultError.Cause, "api is only available for publishers")
181 aefId := "AEF_id_rApp_Kong_as_AEF"
182 namespace := "namespace"
183 repoName := "repoName"
184 chartName := "chartName"
185 releaseName := "releaseName"
186 description := fmt.Sprintf("Description,%s,%s,%s,%s", namespace, repoName, chartName, releaseName)
188 myEnv, myPorts, err := envreader.ReadDotEnv()
189 assert.Nil(t, err, "error reading env file")
191 testServiceIpv4 := common29122.Ipv4Addr(myEnv["TEST_SERVICE_IPV4"])
192 testServicePort := common29122.Port(myPorts["TEST_SERVICE_PORT"])
194 assert.NotEmpty(t, testServiceIpv4, "TEST_SERVICE_IPV4 is required in .env file for unit testing")
195 assert.NotZero(t, testServicePort, "TEST_SERVICE_PORT is required in .env file for unit testing")
197 newServiceDescription := getServiceAPIDescription(aefId, apiName, description, testServiceIpv4, testServicePort)
199 // Attempt to publish a service for provider
200 result = testutil.NewRequest().Post("/published-apis/v1/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler)
201 assert.Equal(t, http.StatusForbidden, result.Code())
203 // var resultError common29122.ProblemDetails
204 err = result.UnmarshalBodyToObject(&resultError)
205 assert.NoError(t, err, "error unmarshaling response")
207 assert.Contains(t, *resultError.Cause, apfId)
208 assert.Contains(t, *resultError.Cause, "Unable to publish the service due to api is only available for publishers")
211 func TestRegisterValidProvider(t *testing.T) {
212 newProvider := getProvider()
214 // Register a valid provider
215 result := testutil.NewRequest().Post("/api-provider-management/v1/registrations").WithJsonBody(newProvider).Go(t, requestHandler)
216 assert.Equal(t, http.StatusCreated, result.Code())
218 var resultProvider provapi.APIProviderEnrolmentDetails
219 err := result.UnmarshalBodyToObject(&resultProvider)
220 assert.NoError(t, err, "error unmarshaling response")
223 func TestPublishUnpublishService(t *testing.T) {
224 apfId := "APF_id_rApp_Kong_as_APF"
226 newApiId := "api_id_" + apiName
228 myEnv, myPorts, err := envreader.ReadDotEnv()
229 assert.Nil(t, err, "error reading env file")
231 testServiceIpv4 := common29122.Ipv4Addr(myEnv["TEST_SERVICE_IPV4"])
232 testServicePort := common29122.Port(myPorts["TEST_SERVICE_PORT"])
234 assert.NotEmpty(t, testServiceIpv4, "TEST_SERVICE_IPV4 is required in .env file for unit testing")
235 assert.NotZero(t, testServicePort, "TEST_SERVICE_PORT is required in .env file for unit testing")
237 // Check no services published
238 result := testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis").Go(t, requestHandler)
239 assert.Equal(t, http.StatusOK, result.Code())
241 // Parse JSON from the response body
242 var resultServices []publishapi.ServiceAPIDescription
243 err = result.UnmarshalJsonToObject(&resultServices)
244 assert.NoError(t, err, "error unmarshaling response")
246 // Check if the parsed array is empty
247 assert.Zero(t, len(resultServices))
249 aefId := "AEF_id_rApp_Kong_as_AEF"
250 namespace := "namespace"
251 repoName := "repoName"
252 chartName := "chartName"
253 releaseName := "releaseName"
254 description := fmt.Sprintf("Description,%s,%s,%s,%s", namespace, repoName, chartName, releaseName)
256 newServiceDescription := getServiceAPIDescriptionMissingInterface(aefId, apiName, description)
258 // Publish a service for provider
259 result = testutil.NewRequest().Post("/published-apis/v1/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler)
260 assert.Equal(t, http.StatusBadRequest, result.Code())
262 var resultError common29122.ProblemDetails
263 err = result.UnmarshalJsonToObject(&resultError)
264 assert.NoError(t, err, "error unmarshaling response")
266 assert.Contains(t, *resultError.Cause, "cannot read interfaceDescription")
268 newServiceDescription = getServiceAPIDescription(aefId, apiName, description, testServiceIpv4, testServicePort)
270 // Publish a service for provider
271 result = testutil.NewRequest().Post("/published-apis/v1/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler)
272 assert.Equal(t, http.StatusCreated, result.Code())
274 if result.Code() != http.StatusCreated {
275 log.Fatalf("failed to publish the service with HTTP result code %d", result.Code())
279 var resultService publishapi.ServiceAPIDescription
280 err = result.UnmarshalJsonToObject(&resultService)
281 assert.NoError(t, err, "error unmarshaling response")
282 assert.Equal(t, newApiId, *resultService.ApiId)
284 assert.Equal(t, "http://example.com/published-apis/v1/"+apfId+"/service-apis/"+*resultService.ApiId, result.Recorder.Header().Get(echo.HeaderLocation))
286 // Check that the service is published for the provider
287 result = testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis/"+newApiId).Go(t, requestHandler)
288 assert.Equal(t, http.StatusOK, result.Code())
290 err = result.UnmarshalJsonToObject(&resultService)
291 assert.NoError(t, err, "error unmarshaling response")
292 assert.Equal(t, newApiId, *resultService.ApiId)
294 aefProfile := (*resultService.AefProfiles)[0]
295 interfaceDescription := (*aefProfile.InterfaceDescriptions)[0]
297 resultServiceIpv4 := *interfaceDescription.Ipv4Addr
298 resultServicePort := *interfaceDescription.Port
300 kongIPv4 := common29122.Ipv4Addr(myEnv["KONG_IPV4"])
301 kongDataPlanePort := common29122.Port(myPorts["KONG_DATA_PLANE_PORT"])
303 assert.NotEmpty(t, kongIPv4, "KONG_IPV4 is required in .env file for unit testing")
304 assert.NotZero(t, kongDataPlanePort, "KONG_DATA_PLANE_PORT is required in .env file for unit testing")
306 assert.Equal(t, kongIPv4, resultServiceIpv4)
307 assert.Equal(t, kongDataPlanePort, resultServicePort)
309 // Publish the same service again should result in Forbidden
310 newServiceDescription.ApiId = &newApiId
311 result = testutil.NewRequest().Post("/published-apis/v1/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler)
312 assert.Equal(t, http.StatusForbidden, result.Code())
314 err = result.UnmarshalBodyToObject(&resultError)
315 assert.NoError(t, err, "error unmarshaling response")
316 assert.Contains(t, *resultError.Cause, "already published")
317 assert.Equal(t, http.StatusForbidden, *resultError.Status)
319 // Delete the service
320 result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+newApiId).Go(t, requestHandler)
321 assert.Equal(t, http.StatusNoContent, result.Code())
323 // Check no services published
324 result = testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis").Go(t, requestHandler)
325 assert.Equal(t, http.StatusOK, result.Code())
327 // Parse JSON from the response body
328 err = result.UnmarshalJsonToObject(&resultServices)
329 assert.NoError(t, err, "error unmarshaling response")
331 // Check if the parsed array is empty
332 assert.Zero(t, len(resultServices))
336 func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error) {
337 capifProtocol := myEnv["CAPIF_PROTOCOL"]
338 capifIPv4 := common29122.Ipv4Addr(myEnv["CAPIF_IPV4"])
339 capifPort := common29122.Port(myPorts["CAPIF_PORT"])
340 kongDomain := myEnv["KONG_DOMAIN"]
341 kongProtocol := myEnv["KONG_PROTOCOL"]
342 kongIPv4 := common29122.Ipv4Addr(myEnv["KONG_IPV4"])
343 kongDataPlanePort := common29122.Port(myPorts["KONG_DATA_PLANE_PORT"])
344 kongControlPlanePort := common29122.Port(myPorts["KONG_CONTROL_PLANE_PORT"])
348 // Register ProviderManagement
349 providerManagerSwagger, err := provapi.GetSwagger()
351 log.Fatalf("error loading ProviderManagement swagger spec\n: %s", err)
354 providerManagerSwagger.Servers = nil
355 providerManager := providermanagement.NewProviderManager(capifProtocol, capifIPv4, capifPort)
357 var group *echo.Group
359 group = e.Group("/api-provider-management/v1")
360 group.Use(middleware.OapiRequestValidator(providerManagerSwagger))
361 provapi.RegisterHandlersWithBaseURL(e, providerManager, "/api-provider-management/v1")
363 publishServiceSwagger, err := publishapi.GetSwagger()
365 fmt.Fprintf(os.Stderr, "Error loading PublishService swagger spec\n: %s", err)
369 publishServiceSwagger.Servers = nil
371 ps := NewPublishService(kongDomain, kongProtocol, kongIPv4, kongDataPlanePort, kongControlPlanePort, capifProtocol, capifIPv4, capifPort)
373 group = e.Group("/published-apis/v1")
374 group.Use(echomiddleware.Logger())
375 group.Use(middleware.OapiRequestValidator(publishServiceSwagger))
376 publishapi.RegisterHandlersWithBaseURL(e, ps, "/published-apis/v1")
378 // return ps, eventChannel, e
382 func getServiceAPIDescription(aefId, apiName, description string, testServiceIpv4 common29122.Ipv4Addr, testServicePort common29122.Port) publishapi.ServiceAPIDescription {
384 var protocol publishapi.Protocol = "HTTP_1_1"
386 return publishapi.ServiceAPIDescription{
387 AefProfiles: &[]publishapi.AefProfile{
390 InterfaceDescriptions: &[]publishapi.InterfaceDescription{
392 Ipv4Addr: &testServiceIpv4,
393 Port: &testServicePort,
394 SecurityMethods: &[]publishapi.SecurityMethod{
399 DomainName: &domainName,
401 Versions: []publishapi.Version{
404 Resources: &[]publishapi.Resource{
406 CommType: "REQUEST_RESPONSE",
407 Operations: &[]publishapi.Operation{
410 ResourceName: "helloworld",
419 Description: &description,
423 func getServiceAPIDescriptionMissingInterface(aefId, apiName, description string) publishapi.ServiceAPIDescription {
425 var protocol publishapi.Protocol = "HTTP_1_1"
427 return publishapi.ServiceAPIDescription{
428 AefProfiles: &[]publishapi.AefProfile{
431 DomainName: &domainName,
433 Versions: []publishapi.Version{
436 Resources: &[]publishapi.Resource{
438 CommType: "REQUEST_RESPONSE",
439 Operations: &[]publishapi.Operation{
442 ResourceName: "helloworld",
451 Description: &description,