github.com/Axway/agent-sdk@v1.1.101/pkg/apic/apiservice_test.go (about)

     1  package apic
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"net/http"
     7  	"os"
     8  	"testing"
     9  
    10  	"github.com/Axway/agent-sdk/pkg/util"
    11  
    12  	defs "github.com/Axway/agent-sdk/pkg/apic/definitions"
    13  
    14  	"github.com/Axway/agent-sdk/pkg/api"
    15  	apiv1 "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/api/v1"
    16  	management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1alpha1"
    17  	"github.com/stretchr/testify/assert"
    18  )
    19  
    20  var serviceBody = ServiceBody{
    21  	APIName:          "daleapi",
    22  	Documentation:    []byte("\"docs\""),
    23  	Image:            "abcde",
    24  	ImageContentType: "image/jpeg",
    25  	ResourceType:     Oas2,
    26  	RestAPIID:        "12345",
    27  }
    28  
    29  func TestIsValidAuthPolicy(t *testing.T) {
    30  	assert.False(t, isValidAuthPolicy("foobar"))
    31  	assert.True(t, isValidAuthPolicy(Apikey))
    32  	assert.True(t, isValidAuthPolicy(Passthrough))
    33  	assert.True(t, isValidAuthPolicy(Oauth))
    34  }
    35  
    36  func TestCreateService(t *testing.T) {
    37  	client, httpClient := GetTestServiceClient()
    38  	serviceBody.AuthPolicy = "pass-through"
    39  
    40  	// this should be a full go right path
    41  	httpClient.SetResponses([]api.MockResponse{
    42  		{
    43  			FileName: "./testdata/apiservice.json", // this for call to create the service
    44  			RespCode: http.StatusCreated,
    45  		},
    46  		{
    47  			FileName: "./testdata/agent-details-sr.json", // this for call to create the service
    48  			RespCode: http.StatusOK,
    49  		},
    50  		{
    51  			FileName: "./testdata/agent-details-sr.json", // this for call to create the service
    52  			RespCode: http.StatusOK,
    53  		},
    54  		{
    55  			FileName: "./testdata/servicerevision.json", // this for call to create the serviceRevision
    56  			RespCode: http.StatusCreated,
    57  		},
    58  		{
    59  			FileName: "./testdata/agent-details-sr.json", // this for call to create the service
    60  			RespCode: http.StatusOK,
    61  		},
    62  		{
    63  			FileName: "./testdata/serviceinstance.json", // this for call to create the serviceInstance
    64  			RespCode: http.StatusCreated,
    65  		},
    66  		{
    67  			FileName: "./testdata/agent-details-sr.json", // this for call to create the service
    68  			RespCode: http.StatusOK,
    69  		},
    70  		{
    71  			FileName: "./testdata/agent-details-sr.json", // this for call to create the service
    72  			RespCode: http.StatusOK,
    73  		},
    74  	})
    75  
    76  	// Test oas2 object
    77  	oas2Json, _ := os.Open("./testdata/petstore-swagger2.json") // OAS2
    78  	defer oas2Json.Close()
    79  	oas2Bytes, _ := io.ReadAll(oas2Json)
    80  	cloneServiceBody := serviceBody
    81  	cloneServiceBody.SpecDefinition = oas2Bytes
    82  
    83  	apiSvc, err := client.PublishService(&cloneServiceBody)
    84  	assert.Nil(t, err)
    85  	assert.NotNil(t, apiSvc)
    86  	// this should fail
    87  	httpClient.SetResponses([]api.MockResponse{
    88  		{
    89  			RespCode: http.StatusNotFound,
    90  		},
    91  		{
    92  			FileName: "./testdata/apiservice.json", // this for call to create the service
    93  			RespCode: http.StatusRequestTimeout,
    94  		},
    95  	})
    96  
    97  	apiSvc, err = client.PublishService(&serviceBody)
    98  	assert.NotNil(t, err)
    99  	assert.Nil(t, apiSvc)
   100  
   101  	// this should fail
   102  	httpClient.SetResponses([]api.MockResponse{
   103  		{
   104  			RespCode: http.StatusNotFound,
   105  		},
   106  		{
   107  			FileName: "./testdata/apiservice.json", // this for call to create the service
   108  			RespCode: http.StatusOK,
   109  		},
   110  		{
   111  			FileName: "./testdata/servicerevision.json", // this for call to create the serviceRevision
   112  			RespCode: http.StatusRequestTimeout,
   113  		},
   114  		{
   115  			FileName: "./testdata/empty-list.json", // this for call to rollback apiservice
   116  			RespCode: http.StatusOK,
   117  		},
   118  	})
   119  
   120  	apiSvc, err = client.PublishService(&serviceBody)
   121  	assert.NotNil(t, err)
   122  	assert.Nil(t, apiSvc)
   123  
   124  	// this should fail
   125  	httpClient.SetResponses([]api.MockResponse{
   126  		{
   127  			RespCode: http.StatusNotFound,
   128  		},
   129  		{
   130  			FileName: "./testdata/apiservice.json", // this for call to create the service
   131  			RespCode: http.StatusCreated,
   132  		},
   133  		{
   134  			FileName: "./testdata/servicerevision.json", // this for call to create the serviceRevision
   135  			RespCode: http.StatusCreated,
   136  		},
   137  		{
   138  			FileName: "./testdata/serviceinstance.json", // this for call to create the serviceInstance
   139  			RespCode: http.StatusRequestTimeout,
   140  		},
   141  		{
   142  			FileName: "./testdata/empty-list.json", // this for call to rollback apiservice
   143  			RespCode: http.StatusOK,
   144  		},
   145  	})
   146  
   147  	apiSvc, err = client.PublishService(&serviceBody)
   148  	assert.NotNil(t, err)
   149  	assert.Nil(t, apiSvc)
   150  
   151  	// this should fail
   152  	httpClient.SetResponses([]api.MockResponse{
   153  		{
   154  			RespCode: http.StatusNotFound,
   155  		},
   156  		{
   157  			FileName: "./testdata/apiservice.json", // this for call to create the service
   158  			RespCode: http.StatusCreated,
   159  		},
   160  		{
   161  			FileName: "./testdata/servicerevision.json", // this for call to create the serviceRevision
   162  			RespCode: http.StatusCreated,
   163  		},
   164  		{
   165  			FileName: "./testdata/serviceinstance.json", // this for call to create the serviceInstance
   166  			RespCode: http.StatusCreated,
   167  		},
   168  		{
   169  			RespCode: http.StatusOK, // this for call to rollback
   170  		},
   171  	})
   172  
   173  	apiSvc, err = client.PublishService(&serviceBody)
   174  	assert.NotNil(t, err)
   175  	assert.Nil(t, apiSvc)
   176  }
   177  
   178  func Test_getAPIServiceFromCache(t *testing.T) {
   179  	cloneServiceBody := serviceBody
   180  	cloneServiceBody.APIName = "fake-name"
   181  	cloneServiceBody.RestAPIID = "123"
   182  	client, _ := GetTestServiceClient()
   183  
   184  	// Should return nil for the service and error when the api is not in the cache
   185  	svc, err := client.getAPIServiceFromCache(&cloneServiceBody)
   186  	assert.Nil(t, err)
   187  	assert.Nil(t, svc)
   188  
   189  	// Should return the service and no error
   190  	apiSvc := &management.APIService{
   191  		ResourceMeta: apiv1.ResourceMeta{
   192  			Name:  "abc",
   193  			Title: "abc",
   194  			SubResources: map[string]interface{}{
   195  				defs.XAgentDetails: map[string]interface{}{
   196  					defs.AttrExternalAPIID:   cloneServiceBody.RestAPIID,
   197  					defs.AttrExternalAPIName: serviceBody.APIName,
   198  				},
   199  			},
   200  		},
   201  		Spec: management.ApiServiceSpec{},
   202  	}
   203  	// should return the resource when found by the external api id
   204  	ri, _ := apiSvc.AsInstance()
   205  	client.caches.AddAPIService(ri)
   206  	svc, err = client.getAPIServiceFromCache(&cloneServiceBody)
   207  	assert.Nil(t, err)
   208  	assert.NotNil(t, svc)
   209  
   210  	// should return the resource when found by the primary key
   211  	cloneServiceBody.PrimaryKey = "555"
   212  	err = util.SetAgentDetailsKey(apiSvc, defs.AttrExternalAPIPrimaryKey, cloneServiceBody.PrimaryKey)
   213  	assert.Nil(t, err)
   214  
   215  	ri, _ = apiSvc.AsInstance()
   216  	client.caches.AddAPIService(ri)
   217  	svc, err = client.getAPIServiceFromCache(&cloneServiceBody)
   218  	assert.Nil(t, err)
   219  	assert.NotNil(t, svc)
   220  
   221  	// should return the resource when primary key is not found but external api id is
   222  	cloneServiceBody.PrimaryKey = "4563"
   223  	ri, _ = apiSvc.AsInstance()
   224  	client.caches.AddAPIService(ri)
   225  	svc, err = client.getAPIServiceFromCache(&cloneServiceBody)
   226  	assert.Nil(t, err)
   227  	assert.NotNil(t, svc)
   228  
   229  	// should return the nil for the error and resource when primary key and external api id are not found
   230  	cloneServiceBody.RestAPIID = "4563"
   231  	ri, _ = apiSvc.AsInstance()
   232  	client.caches.AddAPIService(ri)
   233  	svc, err = client.getAPIServiceFromCache(&cloneServiceBody)
   234  	assert.Nil(t, err)
   235  	assert.Nil(t, svc)
   236  }
   237  
   238  func TestUpdateService(t *testing.T) {
   239  	client, httpClient := GetTestServiceClient()
   240  	// tests for updating existing revision
   241  	httpClient.SetResponses([]api.MockResponse{
   242  		{
   243  			FileName: "./testdata/apiservice.json", // for call to update the service
   244  			RespCode: http.StatusOK,
   245  		},
   246  		{
   247  			FileName: "./testdata/apiservice.json", // for call to update the service subresource
   248  			RespCode: http.StatusOK,
   249  		},
   250  		{
   251  			FileName: "./testdata/servicerevision.json", // for call to update the service subresource
   252  			RespCode: http.StatusOK,
   253  		},
   254  		{
   255  			FileName: "./testdata/servicerevision.json", // for call to update the serviceRevision
   256  			RespCode: http.StatusOK,
   257  		},
   258  		{
   259  			FileName: "./testdata/serviceinstance.json", // for call to update the serviceRevision subresource
   260  			RespCode: http.StatusOK,
   261  		},
   262  		{
   263  			FileName: "./testdata/serviceinstance.json", // for call to update the serviceRevision subresource
   264  			RespCode: http.StatusOK,
   265  		},
   266  		{
   267  			FileName: "./testdata/apiservice.json", // for call to update the service subresource
   268  			RespCode: http.StatusOK,
   269  		},
   270  	})
   271  
   272  	cloneServiceBody := serviceBody
   273  	cloneServiceBody.APIUpdateSeverity = "MINOR"
   274  	oas2Json, _ := os.Open("./testdata/petstore-swagger2.json") // OAS2
   275  	defer oas2Json.Close()
   276  	oas2Bytes, _ := io.ReadAll(oas2Json)
   277  	cloneServiceBody.SpecDefinition = oas2Bytes
   278  	apiSvc, err := client.PublishService(&cloneServiceBody)
   279  	assert.Nil(t, err)
   280  	assert.NotNil(t, apiSvc)
   281  
   282  	fmt.Println("*********************")
   283  
   284  	// tests for updating existing instance with same endpoint
   285  	httpClient.SetResponses([]api.MockResponse{
   286  		{
   287  			FileName: "./testdata/apiservice.json", // for call to update the service
   288  			RespCode: http.StatusOK,
   289  		},
   290  		{
   291  			FileName: "./testdata/apiservice.json", // for call to update the service subresource
   292  			RespCode: http.StatusOK,
   293  		},
   294  		{
   295  			FileName: "./testdata/servicerevision.json", // for call to get the serviceRevision count
   296  			RespCode: http.StatusOK,
   297  		},
   298  		{
   299  			FileName: "./testdata/servicerevision.json", // for call to get the serviceRevision count based on name
   300  			RespCode: http.StatusOK,
   301  		},
   302  		{
   303  			FileName: "./testdata/servicerevision.json", // for call to update the serviceRevision
   304  			RespCode: http.StatusOK,
   305  		},
   306  		{
   307  			FileName: "./testdata/servicerevision.json", // for call to update the serviceRevision subresource
   308  			RespCode: http.StatusOK,
   309  		},
   310  		{
   311  			FileName: "./testdata/serviceinstance.json", // for call to update the serviceinstance
   312  			RespCode: http.StatusOK,
   313  		},
   314  		{
   315  			FileName: "./testdata/serviceinstance.json", // for call to update the serviceinstance
   316  			RespCode: http.StatusOK,
   317  		},
   318  		{
   319  			FileName: "./testdata/serviceinstance.json", // for call to update the serviceinstance subresource
   320  			RespCode: http.StatusOK,
   321  		},
   322  	})
   323  	// Test oas2 object
   324  	oas2Json, _ = os.Open("./testdata/petstore-swagger2.json") // OAS2
   325  	defer oas2Json.Close()
   326  	oas2Bytes, _ = io.ReadAll(oas2Json)
   327  
   328  	cloneServiceBody = serviceBody
   329  	cloneServiceBody.SpecDefinition = oas2Bytes
   330  	apiSvc, err = client.PublishService(&cloneServiceBody)
   331  	assert.Nil(t, err)
   332  	assert.NotNil(t, apiSvc)
   333  }
   334  
   335  func Test_PublishServiceError(t *testing.T) {
   336  	client, httpClient := GetTestServiceClient()
   337  
   338  	// this is a failure test
   339  	httpClient.SetResponses([]api.MockResponse{
   340  		{
   341  			RespCode: http.StatusRequestTimeout,
   342  		},
   343  	})
   344  
   345  	apiSvc, err := client.PublishService(&serviceBody)
   346  	assert.NotNil(t, err)
   347  	assert.Nil(t, apiSvc)
   348  }
   349  
   350  func Test_processRevision(t *testing.T) {
   351  	client, httpClient := GetTestServiceClient()
   352  
   353  	// tests for updating existing revision
   354  	httpClient.SetResponses([]api.MockResponse{
   355  		{
   356  			FileName: "./testdata/servicerevision.json", // for call to update the serviceRevision
   357  			RespCode: http.StatusOK,
   358  		},
   359  		{
   360  			FileName: "./testdata/servicerevision.json", // for call to update the serviceRevision x-agent-details
   361  			RespCode: http.StatusOK,
   362  		},
   363  		{
   364  			FileName: "./testdata/servicerevision.json", // for call to update the serviceRevision
   365  			RespCode: http.StatusOK,
   366  		},
   367  		{
   368  			FileName: "./testdata/servicerevision.json", // for call to update the serviceRevision x-agent-details
   369  			RespCode: http.StatusOK,
   370  		},
   371  	})
   372  	cloneServiceBody := serviceBody
   373  	// Normal Revision
   374  	client.processRevision(&cloneServiceBody)
   375  	assert.NotEqual(t, "", cloneServiceBody.serviceContext.revisionName)
   376  }
   377  
   378  func TestDeleteServiceByAPIID(t *testing.T) {
   379  	client, httpClient := GetTestServiceClient()
   380  	httpClient.ResponseCode = http.StatusRequestTimeout
   381  	err := client.DeleteServiceByName("12345")
   382  	assert.NotNil(t, err)
   383  
   384  	// list - ok
   385  	httpClient.SetResponses([]api.MockResponse{
   386  		{
   387  			RespCode: http.StatusNoContent, // delete OK
   388  		},
   389  	})
   390  	svc := &management.APIService{
   391  		ResourceMeta: apiv1.ResourceMeta{
   392  			Name:  "abc",
   393  			Title: "abc",
   394  			SubResources: map[string]interface{}{
   395  				defs.XAgentDetails: map[string]interface{}{
   396  					defs.AttrExternalAPIID: "12345",
   397  				},
   398  			},
   399  		},
   400  		Spec: management.ApiServiceSpec{},
   401  	}
   402  	ri, _ := svc.AsInstance()
   403  	client.caches.AddAPIService(ri)
   404  	err = client.DeleteServiceByName("12345")
   405  	assert.Nil(t, err)
   406  }
   407  
   408  func TestServiceClient_buildAPIService(t *testing.T) {
   409  	body := &ServiceBody{
   410  		Description:      "description",
   411  		ImageContentType: "content-type",
   412  		Image:            "image-data",
   413  		NameToPush:       "nametopush",
   414  		APIName:          "apiname",
   415  		RestAPIID:        "restapiid",
   416  		PrimaryKey:       "primarykey",
   417  		Stage:            "staging",
   418  		Version:          "v1",
   419  		Tags: map[string]interface{}{
   420  			"tag1": "value1",
   421  			"tag2": "value2",
   422  		},
   423  		CreatedBy:          "createdby",
   424  		ServiceAttributes:  map[string]string{"service_attribute": "value"},
   425  		RevisionAttributes: map[string]string{"revision_attribute": "value"},
   426  		InstanceAttributes: map[string]string{"instance_attribute": "value"},
   427  		ServiceAgentDetails: map[string]interface{}{
   428  			"subresource_svc_key": "value",
   429  		},
   430  		InstanceAgentDetails: map[string]interface{}{
   431  			"subresource_instance_key": "value",
   432  		},
   433  		RevisionAgentDetails: map[string]interface{}{
   434  			"subresource_revision_key": "value",
   435  		},
   436  	}
   437  
   438  	tags := []string{"tag1_value1", "tag2_value2"}
   439  
   440  	client, _ := GetTestServiceClient()
   441  	svc := client.buildAPIService(body)
   442  
   443  	assert.Equal(t, management.APIServiceGVK(), svc.GroupVersionKind)
   444  	assert.Empty(t, svc.Name)
   445  	assert.Equal(t, body.NameToPush, svc.Title)
   446  	assert.Contains(t, svc.Tags, tags[0])
   447  	assert.Contains(t, svc.Tags, tags[1])
   448  	assert.Equal(t, body.ServiceAttributes, svc.Attributes)
   449  	assert.Equal(t, body.ImageContentType, svc.Spec.Icon.ContentType)
   450  	assert.Equal(t, body.Image, svc.Spec.Icon.Data)
   451  	assert.Equal(t, body.Description, svc.Spec.Description)
   452  	assert.Equal(t, body.ServiceAttributes, svc.Attributes)
   453  
   454  	assert.Contains(t, svc.Attributes, "service_attribute")
   455  	assert.NotContains(t, svc.Attributes, "revision_attribute")
   456  	assert.NotContains(t, svc.Attributes, "instance_attribute")
   457  	assert.NotContains(t, svc.Attributes, defs.AttrExternalAPIStage)
   458  	assert.NotContains(t, svc.Attributes, defs.AttrExternalAPIPrimaryKey)
   459  	assert.NotContains(t, svc.Attributes, defs.AttrExternalAPIID)
   460  	assert.NotContains(t, svc.Attributes, defs.AttrExternalAPIName)
   461  	assert.NotContains(t, svc.Attributes, defs.AttrCreatedBy)
   462  
   463  	sub := util.GetAgentDetails(svc)
   464  	// stage is not set for api services
   465  	assert.Empty(t, sub[defs.AttrExternalAPIStage])
   466  	assert.Equal(t, body.PrimaryKey, sub[defs.AttrExternalAPIPrimaryKey])
   467  	assert.Equal(t, body.RestAPIID, sub[defs.AttrExternalAPIID])
   468  	assert.Equal(t, body.APIName, sub[defs.AttrExternalAPIName])
   469  	assert.Equal(t, body.CreatedBy, sub[defs.AttrCreatedBy])
   470  	assert.Contains(t, sub, "subresource_svc_key")
   471  	assert.NotContains(t, sub, "subresource_instance_key")
   472  	assert.NotContains(t, sub, "subresource_revision_key")
   473  }
   474  
   475  func TestServiceClient_updateAPIService(t *testing.T) {
   476  	body := &ServiceBody{
   477  		Description:      "description",
   478  		ImageContentType: "content-type",
   479  		Image:            "image-data",
   480  		NameToPush:       "nametopush",
   481  		APIName:          "apiname",
   482  		RestAPIID:        "restapiid",
   483  		PrimaryKey:       "primarykey",
   484  		Stage:            "staging",
   485  		Version:          "v1",
   486  		Tags: map[string]interface{}{
   487  			"tag1": "value1",
   488  			"tag2": "value2",
   489  		},
   490  		CreatedBy:          "createdby",
   491  		ServiceAttributes:  map[string]string{"service_attribute": "value"},
   492  		RevisionAttributes: map[string]string{"revision_attribute": "value"},
   493  		InstanceAttributes: map[string]string{"instance_attribute": "value"},
   494  		ServiceAgentDetails: map[string]interface{}{
   495  			"subresource_svc_key": "value",
   496  		},
   497  		InstanceAgentDetails: map[string]interface{}{
   498  			"subresource_instance_key": "value",
   499  		},
   500  		RevisionAgentDetails: map[string]interface{}{
   501  			"subresource_revision_key": "value",
   502  		},
   503  	}
   504  
   505  	svc := &management.APIService{
   506  		ResourceMeta: apiv1.ResourceMeta{
   507  			Metadata: apiv1.Metadata{
   508  				ResourceVersion: "123",
   509  			},
   510  			SubResources: map[string]interface{}{
   511  				defs.XAgentDetails: map[string]interface{}{
   512  					"old_subresource_svc_key": "old_val",
   513  				},
   514  			},
   515  		},
   516  	}
   517  
   518  	tags := []string{"tag1_value1", "tag2_value2"}
   519  
   520  	client, _ := GetTestServiceClient()
   521  	client.updateAPIService(body, svc)
   522  
   523  	assert.Equal(t, management.APIServiceGVK(), svc.GroupVersionKind)
   524  	assert.Empty(t, svc.Metadata.ResourceVersion)
   525  	assert.Empty(t, svc.Name)
   526  
   527  	assert.Equal(t, body.NameToPush, svc.Title)
   528  	assert.Contains(t, svc.Tags, tags[0])
   529  	assert.Contains(t, svc.Tags, tags[1])
   530  	assert.Equal(t, body.ServiceAttributes, svc.Attributes)
   531  	assert.NotContains(t, svc.Attributes, "instance_attribute")
   532  	assert.NotContains(t, svc.Attributes, "revision_attribute")
   533  
   534  	assert.Equal(t, body.ImageContentType, svc.Spec.Icon.ContentType)
   535  	assert.Equal(t, body.Image, svc.Spec.Icon.Data)
   536  	assert.Equal(t, body.Description, svc.Spec.Description)
   537  
   538  	assert.Contains(t, svc.Attributes, "service_attribute")
   539  	assert.NotContains(t, svc.Attributes, "revision_attribute")
   540  	assert.NotContains(t, svc.Attributes, "instance_attribute")
   541  	assert.NotContains(t, svc.Attributes, defs.AttrExternalAPIStage)
   542  	assert.NotContains(t, svc.Attributes, defs.AttrExternalAPIPrimaryKey)
   543  	assert.NotContains(t, svc.Attributes, defs.AttrExternalAPIID)
   544  	assert.NotContains(t, svc.Attributes, defs.AttrExternalAPIName)
   545  	assert.NotContains(t, svc.Attributes, defs.AttrCreatedBy)
   546  
   547  	sub := util.GetAgentDetails(svc)
   548  	assert.Empty(t, sub[defs.AttrExternalAPIStage])
   549  	assert.Equal(t, body.PrimaryKey, sub[defs.AttrExternalAPIPrimaryKey])
   550  	assert.Equal(t, body.RestAPIID, sub[defs.AttrExternalAPIID])
   551  	assert.Equal(t, body.APIName, sub[defs.AttrExternalAPIName])
   552  	assert.Equal(t, body.CreatedBy, sub[defs.AttrCreatedBy])
   553  	assert.Contains(t, sub, "subresource_svc_key")
   554  	assert.Contains(t, sub, "old_subresource_svc_key")
   555  	assert.NotContains(t, sub, "subresource_instance_key")
   556  	assert.NotContains(t, sub, "subresource_revision_key")
   557  }
   558  
   559  func Test_buildAPIServiceNilAttributes(t *testing.T) {
   560  	client, _ := GetTestServiceClient()
   561  	body := &ServiceBody{}
   562  
   563  	svc := client.buildAPIService(body)
   564  	assert.NotNil(t, svc.Attributes)
   565  
   566  	svc.Attributes = nil
   567  	client.updateAPIService(body, svc)
   568  	assert.NotNil(t, svc.Attributes)
   569  }
   570  
   571  func createAPIService(name, id string, refSvc string, dpType string, isDesign bool) *management.APIService {
   572  	apiSvc := &management.APIService{
   573  		ResourceMeta: apiv1.ResourceMeta{
   574  			Name:  name,
   575  			Title: name,
   576  			SubResources: map[string]interface{}{
   577  				defs.XAgentDetails: map[string]interface{}{
   578  					defs.AttrExternalAPIID:   id,
   579  					defs.AttrExternalAPIName: name,
   580  				},
   581  			},
   582  		},
   583  		Spec: management.ApiServiceSpec{},
   584  	}
   585  	if refSvc != "" || dpType != "" {
   586  		apiSvc.Source = &management.ApiServiceSource{
   587  			References: &management.ApiServiceSourceReferences{
   588  				ApiService: refSvc,
   589  			},
   590  		}
   591  		apiSvc.Source.DataplaneType = &management.ApiServiceSourceDataplaneType{}
   592  		if isDesign {
   593  			apiSvc.Source.DataplaneType.Design = dpType
   594  		} else {
   595  			apiSvc.Source.DataplaneType.Managed = dpType
   596  		}
   597  	}
   598  	return apiSvc
   599  }
   600  
   601  func TestServiceSourceUpdates(t *testing.T) {
   602  	// case 1 - new service, source managed dataplane, sub resource updated
   603  	// case 2 - new service, source design dataplane, sub resource updated
   604  	// case 3 - new service, source unmanaged dataplane with reference, sub resource updated
   605  	// case 4 - existing service, no source, source updated
   606  	// case 5 - existing service, existing source, different dataplane type, source updated
   607  	// case 6 - existing service, existing source, different reference, source updated
   608  	// case 7 - existing service, existing source, same dataplane type and same reference, no source updated
   609  	testCases := []struct {
   610  		name               string
   611  		svcName            string
   612  		managedDataplane   DataplaneType
   613  		designDataplane    DataplaneType
   614  		existingSvc        *management.APIService
   615  		referenceService   string
   616  		apiserverResponses []api.MockResponse
   617  	}{
   618  		{
   619  			name:             "new service for managed dataplane",
   620  			svcName:          "newSvcManaged",
   621  			managedDataplane: AWS,
   622  			apiserverResponses: []api.MockResponse{
   623  				{
   624  					FileName: "./testdata/apiservice.json", // call to create the service
   625  					RespCode: http.StatusCreated,
   626  				},
   627  				{
   628  					FileName: "./testdata/apiservice.json", // call to update x-agent-details subresource
   629  					RespCode: http.StatusOK,
   630  				},
   631  				{
   632  					FileName: "./testdata/apiservice.json", // call to update source subresource
   633  					RespCode: http.StatusOK,
   634  				},
   635  			},
   636  		},
   637  		{
   638  			name:            "new service for design dataplane",
   639  			svcName:         "newSvcDesign",
   640  			designDataplane: GitLab,
   641  			apiserverResponses: []api.MockResponse{
   642  				{
   643  					FileName: "./testdata/apiservice.json", // call to create the service
   644  					RespCode: http.StatusCreated,
   645  				},
   646  				{
   647  					FileName: "./testdata/apiservice.json", // call to update x-agent-details subresource
   648  					RespCode: http.StatusOK,
   649  				},
   650  				{
   651  					FileName: "./testdata/apiservice.json", // call to update source subresource
   652  					RespCode: http.StatusOK,
   653  				},
   654  			},
   655  		},
   656  		{
   657  			name:             "new service for unmanaged dataplane with referenced service",
   658  			svcName:          "newSvcUnmanaged",
   659  			managedDataplane: Unclassified,
   660  			referenceService: "refSvc",
   661  			apiserverResponses: []api.MockResponse{
   662  				{
   663  					FileName: "./testdata/apiservice.json", // call to create the service
   664  					RespCode: http.StatusCreated,
   665  				},
   666  				{
   667  					FileName: "./testdata/apiservice.json", // call to update x-agent-details subresource
   668  					RespCode: http.StatusOK,
   669  				},
   670  				{
   671  					FileName: "./testdata/apiservice.json", // call to update source subresource
   672  					RespCode: http.StatusOK,
   673  				},
   674  			},
   675  		},
   676  		{
   677  			name:             "existing service with no source",
   678  			svcName:          "existingSvcNoSource",
   679  			managedDataplane: AWS,
   680  			existingSvc:      createAPIService("existingSvcNoSource", "existingSvcNoSource", "", "", false),
   681  			apiserverResponses: []api.MockResponse{
   682  				{
   683  					FileName: "./testdata/apiservice.json", // call to update the service
   684  					RespCode: http.StatusOK,
   685  				},
   686  				{
   687  					FileName: "./testdata/apiservice.json", // call to update x-agent-details subresource
   688  					RespCode: http.StatusOK,
   689  				},
   690  				{
   691  					FileName: "./testdata/apiservice.json", // call to update source subresource
   692  					RespCode: http.StatusOK,
   693  				},
   694  			},
   695  		},
   696  		{
   697  			name:             "existing service with different dataplane type",
   698  			svcName:          "existingSvcDiffDpType",
   699  			managedDataplane: AWS,
   700  			existingSvc:      createAPIService("existingSvcDiffDpType", "existingSvcDiffDpType", "", Unidentified.String(), false),
   701  			apiserverResponses: []api.MockResponse{
   702  				{
   703  					FileName: "./testdata/apiservice.json", // call to update the service
   704  					RespCode: http.StatusOK,
   705  				},
   706  				{
   707  					FileName: "./testdata/apiservice.json", // call to update x-agent-details subresource
   708  					RespCode: http.StatusOK,
   709  				},
   710  				{
   711  					FileName: "./testdata/apiservice.json", // call to update source subresource
   712  					RespCode: http.StatusOK,
   713  				},
   714  			},
   715  		},
   716  		{
   717  			name:             "existing service with different referenced service",
   718  			svcName:          "existingSvcDiffRefSvc",
   719  			managedDataplane: AWS,
   720  			existingSvc:      createAPIService("existingSvcDiffRefSvc", "existingSvcDiffRefSvc", "existingRefSvc", AWS.String(), false),
   721  			referenceService: "newRefSvc",
   722  			apiserverResponses: []api.MockResponse{
   723  				{
   724  					FileName: "./testdata/apiservice.json", // call to update the service
   725  					RespCode: http.StatusOK,
   726  				},
   727  				{
   728  					FileName: "./testdata/apiservice.json", // call to update x-agent-details subresource
   729  					RespCode: http.StatusOK,
   730  				},
   731  				{
   732  					FileName: "./testdata/apiservice.json", // call to update source subresource
   733  					RespCode: http.StatusOK,
   734  				},
   735  			},
   736  		},
   737  		{
   738  			name:             "existing service with same source",
   739  			svcName:          "existingSvcSameSource",
   740  			managedDataplane: AWS,
   741  			existingSvc:      createAPIService("existingSvcSameSource", "existingSvcSameSource", "refSvc", AWS.String(), false),
   742  			referenceService: "refSvc",
   743  			apiserverResponses: []api.MockResponse{
   744  				{
   745  					FileName: "./testdata/apiservice.json", // call to update the service
   746  					RespCode: http.StatusOK,
   747  				},
   748  				{
   749  					FileName: "./testdata/apiservice.json", // call to update x-agent-details subresource
   750  					RespCode: http.StatusOK,
   751  				},
   752  				// no source subresource update
   753  			},
   754  		},
   755  	}
   756  	for _, test := range testCases {
   757  		t.Run(test.name, func(t *testing.T) {
   758  			client, httpClient := GetTestServiceClient()
   759  			if test.existingSvc != nil {
   760  				ri, _ := test.existingSvc.AsInstance()
   761  				client.caches.AddAPIService(ri)
   762  			}
   763  
   764  			body := &ServiceBody{
   765  				RestAPIID: test.svcName,
   766  			}
   767  			if test.managedDataplane != "" {
   768  				body.dataplaneType = test.managedDataplane
   769  			}
   770  			if test.designDataplane != "" {
   771  				body.dataplaneType = test.designDataplane
   772  				body.isDesignDataplane = true
   773  			}
   774  			body.referencedServiceName = test.referenceService
   775  			httpClient.SetResponses(test.apiserverResponses)
   776  
   777  			svc, err := client.processService(body)
   778  			assert.Nil(t, err)
   779  			assert.NotNil(t, svc)
   780  			assert.Equal(t, len(test.apiserverResponses), httpClient.RespCount)
   781  		})
   782  	}
   783  }