github.com/Axway/agent-sdk@v1.1.101/pkg/apic/apiserver/clients/api/v1/client_test.go (about)

     1  package v1
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"log"
     7  	"net/http"
     8  	"net/url"
     9  	"os"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/Axway/agent-sdk/pkg/apic"
    14  	v1 "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/api/v1"
    15  	management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1alpha1"
    16  	"github.com/stretchr/testify/assert"
    17  	"gopkg.in/h2non/gock.v1"
    18  )
    19  
    20  const privKey = "testdata/privatekey"
    21  const pubKey = "testdata/publickey"
    22  const uaHeader = "fake-agent"
    23  
    24  const mockJSONEnv = `{
    25    "group": "management",
    26    "apiVersion": "v1alpha1",
    27    "kind": "Environment",
    28    "name": "test-env-1",
    29    "title": "test-env-1",
    30    "metadata": {
    31      "id": "e4f1cf5371cd390b0171cd43d7460056",
    32      "audit": {
    33        "createTimestamp": "2020-04-30T22:45:07.533+0000",
    34        "createUserId": "serviceaccount_1234",
    35        "modifyTimestamp": "2020-04-30T22:45:07.533+0000",
    36        "modifyUserId": "serviceaccount_1234"
    37      },
    38      "resourceVersion": "297",
    39      "references": []
    40    },
    41    "attributes": {
    42      "attr": "value"
    43    },
    44    "tags": ["tag1", "tag2"],
    45    "spec": {
    46      "description": "desc"
    47    }
    48  }`
    49  
    50  const mockJSONApiSvc = `{
    51  	"group": "management",
    52  	"apiVersion": "v1alpha1",
    53  	"kind": "APIService",
    54  	"name": "test-api-svc",
    55  	"title": "test-api-svc",
    56  	"metadata": {
    57  		"id": "e4e7efa47287250c017296c54e3f01a6",
    58  		"audit": {
    59  			"createTimestamp": "2020-06-09T01:50:12.548+0000",
    60  			"modifyTimestamp": "2020-06-09T01:50:12.548+0000"
    61  		},
    62  		"scope": {
    63  			"id": "e4e7efa47287250c017296c54dd301a3",
    64  			"kind": "Environment",
    65  			"name": "test-env-1"
    66  		},
    67  		"resourceVersion": "696",
    68  		"references": []
    69  	},
    70  	"attributes": {
    71  		"attr": "value"
    72  	},
    73  	"tags": [
    74  		"atag"
    75  	],
    76  	"spec": {}
    77  }`
    78  
    79  var mockEnv = &v1.ResourceInstance{}
    80  var mockEnvUpdated = &v1.ResourceInstance{}
    81  var mockAPISvc = &v1.ResourceInstance{}
    82  var client = &Client{}
    83  
    84  func createEnv(client Unscoped) (*v1.ResourceInstance, error) {
    85  	created, err := client.Create(&v1.ResourceInstance{
    86  		ResourceMeta: v1.ResourceMeta{
    87  			GroupVersionKind: management.EnvironmentGVK(),
    88  			Name:             "test-env-1",
    89  			Title:            "test-env-1",
    90  			Tags:             []string{"atag"},
    91  			Attributes:       map[string]string{"attr": "value"},
    92  		},
    93  		Spec: map[string]interface{}{},
    94  	})
    95  	return created, err
    96  }
    97  
    98  func TestMain(m *testing.M) {
    99  	json.Unmarshal([]byte(mockJSONEnv), mockEnv)
   100  	json.Unmarshal([]byte(mockJSONEnv), mockEnvUpdated)
   101  	mockEnvUpdated.Title = "updated-testenv-title"
   102  	json.Unmarshal([]byte(mockJSONApiSvc), mockAPISvc)
   103  
   104  	newClient, err := NewClient(
   105  		"http://localhost:8080/apis",
   106  		UserAgent(uaHeader),
   107  		BasicAuth(
   108  			"admin",
   109  			"servicesecret",
   110  			"admin",
   111  			"123",
   112  		),
   113  	).ForKind(management.EnvironmentGVK())
   114  	client = newClient.(*Client)
   115  	if err != nil {
   116  		log.Fatalf("Error in test setup: %s", err)
   117  		os.Exit(1)
   118  	}
   119  	os.Exit(m.Run())
   120  }
   121  
   122  func TestUnscoped(t *testing.T) {
   123  	defer gock.Off()
   124  	// Create env
   125  	gock.New("http://localhost:8080/apis").
   126  		Post("/management/v1alpha1/environments").
   127  		MatchHeader("User-Agent", "fake-agent").
   128  		Reply(201).
   129  		JSON(mockEnv)
   130  
   131  	gock.New("http://localhost:8080/apis").
   132  		Put("/management/v1alpha1/environments/test-env-1").
   133  		Reply(200).
   134  		JSON(mockEnvUpdated)
   135  
   136  	gock.New("http://localhost:8080/apis").
   137  		Get("/management/v1alpha1/environments/test-env-1").
   138  		Reply(200).
   139  		JSON(mockEnv)
   140  
   141  	gock.New("http://localhost:8080/apis").
   142  		Get("/management/v1alpha1/environments").
   143  		Reply(200).
   144  		JSON([]*v1.ResourceInstance{mockEnv})
   145  
   146  	gock.New("http://localhost:8080/apis").
   147  		Delete("/management/v1alpha1/environments/test-env-1").
   148  		Reply(204)
   149  
   150  	created, err := createEnv(client)
   151  
   152  	if err != nil {
   153  		t.Fatalf("Failed to create: %s", err)
   154  	}
   155  
   156  	// Get env by name
   157  	_, err = client.Get(created.Name)
   158  	if err != nil {
   159  		t.Fatalf("Failed to get env by name: %s", err)
   160  	}
   161  
   162  	// Update env
   163  	created.Title = "updated-testenv-title"
   164  	updatedEnv, err := client.Update(created)
   165  
   166  	if updatedEnv.Title != mockEnvUpdated.Title {
   167  		t.Fatalf("Updated resource name does not match %s. Received %s", mockEnvUpdated.Title, updatedEnv.Title)
   168  	}
   169  
   170  	if err != nil {
   171  		t.Fatalf("Failed to update: %s", err)
   172  	}
   173  
   174  	// Get all envs
   175  	envList, err := client.List()
   176  	if err != nil {
   177  		t.Fatalf("Failed to list environments: %s", err)
   178  	}
   179  
   180  	found := false
   181  	for _, env := range envList {
   182  		if env.Name == created.Name {
   183  			found = true
   184  			t.Log("Found in list: ", env)
   185  			break
   186  		}
   187  	}
   188  
   189  	if !found {
   190  		t.Fatalf("Cannot find created environment %v", created)
   191  	}
   192  
   193  	err = client.Delete(created)
   194  	if err != nil {
   195  		t.Fatalf("Failed to delete: %s", err)
   196  	}
   197  }
   198  
   199  func TestScoped(t *testing.T) {
   200  	defer gock.Off()
   201  	// Create env
   202  	gock.New("http://localhost:8080/apis").
   203  		Post("/management/v1alpha1/environments").
   204  		Reply(201).
   205  		JSON(mockEnv)
   206  
   207  	gock.New("http://localhost:8080/apis").
   208  		Post("/management/v1alpha1/environments/test-env-1/apiservices").
   209  		Reply(201).
   210  		JSON(mockAPISvc)
   211  
   212  	gock.New("http://localhost:8080/apis").
   213  		Get("/management/v1alpha1/environments/test-env-1/apiservices").
   214  		Reply(200).
   215  		JSON([]*v1.ResourceInstance{mockAPISvc})
   216  
   217  	gock.New("http://localhost:8080/apis").
   218  		Delete("/management/v1alpha1/environments/test-env-1/apiservices/test-api-svc").
   219  		Reply(204)
   220  
   221  	gock.New("http://localhost:8080/apis").
   222  		Delete("/management/v1alpha1/environments/test-env-1").
   223  		Reply(204)
   224  
   225  	env, err := createEnv(client)
   226  
   227  	defer func() {
   228  		err = client.Delete(env)
   229  		if err != nil {
   230  			t.Fatalf("Failed: %s", err)
   231  		}
   232  	}()
   233  
   234  	svcClient, err := client.ForKind(management.APIServiceGVK())
   235  	if err != nil {
   236  		t.Fatalf("Failed: %s", err)
   237  	}
   238  	svcClient = svcClient.WithScope(env.Name).(*Client)
   239  
   240  	svc, err := svcClient.Create(&v1.ResourceInstance{
   241  		ResourceMeta: v1.ResourceMeta{
   242  			Name:       "test-api-svc",
   243  			Tags:       []string{"atag"},
   244  			Attributes: map[string]string{"attr": "value"},
   245  		},
   246  		Spec: map[string]interface{}{},
   247  	})
   248  	if err != nil {
   249  		t.Fatalf("Failed: %s", err)
   250  	}
   251  
   252  	svcs, err := svcClient.List()
   253  	if err != nil {
   254  		t.Fatalf("Failed to list api services: %s", err)
   255  	}
   256  	found := false
   257  	for _, s := range svcs {
   258  		svcClient.Delete(svc)
   259  		if s.Name == svc.Name {
   260  			t.Logf("Found created svc %v", s)
   261  
   262  			found = true
   263  			return
   264  		}
   265  	}
   266  	if !found {
   267  		t.Fatalf("Cannot find created service %v", svc)
   268  	}
   269  
   270  	err = svcClient.Delete(svc)
   271  	if err != nil {
   272  		t.Fatalf("Failed: %s", err)
   273  	}
   274  }
   275  
   276  func TestListWithQuery(t *testing.T) {
   277  	defer gock.Off()
   278  	// List envs
   279  	gock.New("http://localhost:8080/apis").
   280  		Get("/management/v1alpha1/environments").
   281  		MatchHeader("User-Agent", uaHeader).
   282  		MatchParam("query", `(tags=="test";attributes.attr==("val"))`).Reply(200).
   283  		JSON([]*v1.ResourceInstance{mockEnv, mockEnv})
   284  
   285  	_, err := client.List(WithQuery(And(TagsIn("test"), AttrIn("attr", "val"))))
   286  	if err != nil {
   287  		t.Fatalf("Error: %s", err)
   288  	}
   289  }
   290  
   291  func Test_listAll(t *testing.T) {
   292  	// Follow the link headers
   293  	defer gock.Off()
   294  	gock.New("http://localhost:8080/apis").
   295  		Get("/management/v1alpha1/environments").
   296  		Reply(200).
   297  		AddHeader("Link", "</apis/management/v1alpha1/environments?pageSize=20&query=tags%3D%3D%22test%22&page=2>; rel=\"next\", </apis/management/v1alpha1/environments?pageSize=20&query=tags%3D%3D%22test%22&page=1>; rel=\"self\", </apis/management/v1alpha1/environments?pageSize=20&query=tags%3D%3D%22test%22&page=1>; rel=\"first\", </apis/management/v1alpha1/environments?pageSize=20&query=tags%3D%3D%22test%22&page=4>; rel=\"last\"").
   298  		JSON([]*v1.ResourceInstance{mockEnv})
   299  
   300  	gock.New("http://localhost:8080/apis").
   301  		Get("/management/v1alpha1/environments").
   302  		Reply(200).
   303  		AddHeader("Link", "</apis/management/v1alpha1/environments?pageSize=20&query=tags%3D%3D%22test%22&page=1>; rel=\"self\", </apis/management/v1alpha1/environments?pageSize=20&query=tags%3D%3D%22test%22&page=1>; rel=\"first\", </apis/management/v1alpha1/environments?pageSize=20&query=tags%3D%3D%22test%22&page=4>; rel=\"last\"").
   304  		JSON([]*v1.ResourceInstance{mockEnv})
   305  
   306  	items, err := client.List(WithQuery(TagsIn("test")))
   307  	if err != nil {
   308  		t.Fatalf("Error: %s", err)
   309  	}
   310  	assert.Equal(t, 2, len(items))
   311  
   312  	// handle an error from the client
   313  	gock.New("http://localhost:8080/apis").
   314  		Get("/management/v1alpha1/environments").
   315  		Reply(500).
   316  		SetError(&url.Error{})
   317  
   318  	_, err = client.List()
   319  	assert.NotNil(t, err)
   320  
   321  	// handle a successful response, but a 500 error
   322  	gock.New("http://localhost:8080/apis").
   323  		Get("/management/v1alpha1/environments").
   324  		Reply(500)
   325  
   326  	_, err = client.List()
   327  	assert.NotNil(t, err)
   328  
   329  	// handle a successful request, then a failed request
   330  	gock.New("http://localhost:8080/apis").
   331  		Get("/management/v1alpha1/environments").
   332  		Reply(200).
   333  		AddHeader("Link", "</apis/management/v1alpha1/environments?pageSize=20&query=tags%3D%3D%22test%22&page=2>; rel=\"next\", </apis/management/v1alpha1/environments?pageSize=20&query=tags%3D%3D%22test%22&page=1>; rel=\"self\", </apis/management/v1alpha1/environments?pageSize=20&query=tags%3D%3D%22test%22&page=1>; rel=\"first\", </apis/management/v1alpha1/environments?pageSize=20&query=tags%3D%3D%22test%22&page=4>; rel=\"last\"").
   334  		JSON([]*v1.ResourceInstance{mockEnv})
   335  
   336  	gock.New("http://localhost:8080/apis").
   337  		Get("/management/v1alpha1/environments").
   338  		Reply(500).
   339  		AddHeader("Link", "</apis/management/v1alpha1/environments?pageSize=20&query=tags%3D%3D%22test%22&page=1>; rel=\"self\", </apis/management/v1alpha1/environments?pageSize=20&query=tags%3D%3D%22test%22&page=1>; rel=\"first\", </apis/management/v1alpha1/environments?pageSize=20&query=tags%3D%3D%22test%22&page=4>; rel=\"last\"").
   340  		JSON([]*v1.ResourceInstance{})
   341  
   342  	items, err = client.List()
   343  	assert.Equal(t, 0, len(items))
   344  	assert.NotNil(t, err)
   345  }
   346  
   347  func TestJWTAuth(t *testing.T) {
   348  	tenantId := "1234"
   349  	c := NewClient("http://localhost:8080",
   350  		JWTAuth(
   351  			tenantId,
   352  			privKey,
   353  			pubKey,
   354  			"",
   355  			"http://localhost:8080/auth/realms/Broker/protocol/openid-connect/token",
   356  			"http://localhost:8080/auth/realms/Broker",
   357  			"serviceaccount_1234",
   358  			10*time.Second,
   359  		),
   360  		UserAgent(uaHeader),
   361  	)
   362  
   363  	assert.Equal(t, uaHeader, c.userAgent)
   364  
   365  	jAuthStruct, ok := c.auth.(*jwtAuth)
   366  
   367  	assert.True(t, ok)
   368  	jAuthStruct.tokenGetter = apic.MockTokenGetter
   369  
   370  	req := &http.Request{
   371  		Header: make(http.Header),
   372  	}
   373  	err := c.intercept(req)
   374  	assert.Nil(t, err)
   375  
   376  	userAgent := req.Header.Get("User-Agent")
   377  	assert.Equal(t, uaHeader, userAgent)
   378  
   379  	authorization := req.Header.Get("Authorization")
   380  	assert.NotEmpty(t, authorization)
   381  
   382  	tenant := req.Header.Get("X-Axway-Tenant-Id")
   383  	assert.Equal(t, tenantId, tenant)
   384  
   385  	instance := req.Header.Get("X-Axway-Instance-Id")
   386  	assert.Equal(t, "", instance)
   387  }
   388  
   389  func TestResponseErrors(t *testing.T) {
   390  	tests := []struct {
   391  		status int
   392  		err    error
   393  	}{
   394  		{status: 400, err: BadRequestError{}},
   395  		{status: 401, err: UnauthorizedError{}},
   396  		{status: 403, err: ForbiddenError{}},
   397  		{status: 404, err: NotFoundError{}},
   398  		{status: 409, err: ConflictError{}},
   399  		{status: 500, err: InternalServerError{}},
   400  		{status: 600, err: UnexpectedError{}},
   401  	}
   402  
   403  	for i := range tests {
   404  		tc := tests[i]
   405  		t.Run(fmt.Sprintf("%d error", tc.status), func(t *testing.T) {
   406  			defer gock.Off()
   407  			gock.New("http://localhost:8080/apis").
   408  				Get("/management/v1alpha1/environments").
   409  				Reply(tc.status).
   410  				JSON(mockEnv)
   411  
   412  			_, err := client.List()
   413  
   414  			switch tc.status {
   415  			case 400:
   416  				errType, ok := err.(BadRequestError)
   417  				assert.True(t, ok)
   418  				assert.NotEmpty(t, errType.Error())
   419  				assert.IsType(t, BadRequestError{}, errType)
   420  			case 401:
   421  				errType, ok := err.(UnauthorizedError)
   422  				assert.True(t, ok)
   423  				assert.NotEmpty(t, errType.Error())
   424  				assert.IsType(t, UnauthorizedError{}, errType)
   425  			case 403:
   426  				errType, ok := err.(ForbiddenError)
   427  				assert.True(t, ok)
   428  				assert.NotEmpty(t, errType.Error())
   429  				assert.IsType(t, ForbiddenError{}, errType)
   430  			case 404:
   431  				errType, ok := err.(NotFoundError)
   432  				assert.True(t, ok)
   433  				assert.NotEmpty(t, errType.Error())
   434  				assert.IsType(t, NotFoundError{}, errType)
   435  			case 409:
   436  				errType, ok := err.(ConflictError)
   437  				assert.True(t, ok)
   438  				assert.NotEmpty(t, errType.Error())
   439  				assert.IsType(t, ConflictError{}, errType)
   440  			case 500:
   441  				errType, ok := err.(InternalServerError)
   442  				assert.True(t, ok)
   443  				assert.NotEmpty(t, errType.Error())
   444  				assert.IsType(t, InternalServerError{}, errType)
   445  			default:
   446  				errType, ok := err.(UnexpectedError)
   447  				assert.True(t, ok)
   448  				assert.NotEmpty(t, errType.Error())
   449  				assert.IsType(t, UnexpectedError{}, errType)
   450  			}
   451  
   452  		})
   453  	}
   454  }
   455  
   456  func TestGetError(t *testing.T) {
   457  	defer gock.Off()
   458  	gock.New("http://localhost:8080/apis").
   459  		Get("/management/v1alpha1/environments").
   460  		Reply(500).
   461  		JSON(mockEnv)
   462  
   463  	_, err := client.Get("name")
   464  
   465  	if err == nil {
   466  		t.Fatalf("Expected Update to fail: %s", err)
   467  	}
   468  }
   469  
   470  func TestDeleteError(t *testing.T) {
   471  	defer gock.Off()
   472  	gock.New("http://localhost:8080/apis").
   473  		Delete("/management/v1alpha1/environments").
   474  		Reply(500).
   475  		JSON(mockEnv)
   476  
   477  	err := client.Delete(&v1.ResourceInstance{
   478  		ResourceMeta: v1.ResourceMeta{
   479  			GroupVersionKind: management.EnvironmentGVK(),
   480  			Name:             "test-env-1",
   481  			Title:            "test-env-1",
   482  			Tags:             []string{"atag"},
   483  			Attributes:       map[string]string{"attr": "value"},
   484  		},
   485  		Spec: map[string]interface{}{},
   486  	})
   487  
   488  	if err == nil {
   489  		t.Fatalf("Expected delete to fail: %s", err)
   490  	}
   491  }
   492  
   493  func TestCreateError(t *testing.T) {
   494  	defer gock.Off()
   495  	gock.New("http://localhost:8080/apis").
   496  		Post("/management/v1alpha1/environments").
   497  		Reply(500).
   498  		JSON(mockEnv)
   499  
   500  	_, err := createEnv(client)
   501  
   502  	if err == nil {
   503  		t.Fatalf("Expected create to fail: %s", err)
   504  	}
   505  }
   506  
   507  func TestUpdateError(t *testing.T) {
   508  	defer gock.Off()
   509  	gock.New("http://localhost:8080/apis").
   510  		Put("/management/v1alpha1/environments").
   511  		Reply(500).
   512  		JSON(mockEnv)
   513  
   514  	_, err := client.Update(&v1.ResourceInstance{
   515  		ResourceMeta: v1.ResourceMeta{
   516  			GroupVersionKind: management.EnvironmentGVK(),
   517  			Name:             "test-env-1",
   518  			Title:            "test-env-1",
   519  			Tags:             []string{"atag"},
   520  			Attributes:       map[string]string{"attr": "value"},
   521  		},
   522  		Spec: map[string]interface{}{},
   523  	})
   524  
   525  	if err == nil {
   526  		t.Fatalf("Expected update to fail: %s", err)
   527  	}
   528  }
   529  
   530  func TestHTTPClient(t *testing.T) {
   531  	newClient := &http.Client{}
   532  
   533  	client := NewClient(
   534  		"http://localhost:8080/apis",
   535  		BasicAuth(
   536  			"admin",
   537  			"servicesecret",
   538  			"admin",
   539  			"123",
   540  		),
   541  		HTTPClient(newClient),
   542  	)
   543  
   544  	assert.Empty(t, client.userAgent)
   545  
   546  	if newClient != client.client {
   547  		t.Fatalf("Error: expected client.client to be %v but received %v", newClient, client.client)
   548  	}
   549  }
   550  
   551  func TestUpdateMerge(t *testing.T) {
   552  	oldAPISvc := &management.APIService{
   553  		ResourceMeta: v1.ResourceMeta{
   554  			GroupVersionKind: v1.GroupVersionKind{},
   555  			Name:             "name",
   556  			Metadata: v1.Metadata{
   557  				Scope: v1.MetadataScope{
   558  					Name: "myenv",
   559  				},
   560  				References: []v1.Reference{},
   561  			},
   562  			Tags: []string{"old"},
   563  		},
   564  	}
   565  
   566  	newAPISvc := &management.APIService{
   567  		ResourceMeta: v1.ResourceMeta{
   568  			GroupVersionKind: v1.GroupVersionKind{
   569  				GroupKind: v1.GroupKind{
   570  					Group: "management",
   571  					Kind:  "APIService",
   572  				},
   573  				APIVersion: "v1alpha1",
   574  			},
   575  			Name: "name",
   576  			Metadata: v1.Metadata{
   577  				Scope: v1.MetadataScope{
   578  					Name: "myenv",
   579  				},
   580  			},
   581  			Tags: []string{"new"},
   582  		},
   583  	}
   584  
   585  	thisBytes, _ := json.Marshal(newAPISvc)
   586  	t.Log(string(thisBytes))
   587  
   588  	mergedTags := &management.APIService{
   589  		ResourceMeta: v1.ResourceMeta{
   590  			GroupVersionKind: v1.GroupVersionKind{},
   591  			Name:             "name",
   592  			Metadata: v1.Metadata{
   593  				Scope: v1.MetadataScope{
   594  					Name: "myenv",
   595  				},
   596  				References: []v1.Reference{},
   597  			},
   598  			Tags: []string{"old", "new"},
   599  		},
   600  	}
   601  
   602  	mergeError := fmt.Errorf("merge errror")
   603  
   604  	getError := InternalServerError{
   605  		Errors: []v1.Error{{Status: 500, Detail: "Unkown error"}},
   606  	}
   607  
   608  	testCases := []struct {
   609  		name             string
   610  		replyFunc        func(*gock.Response) error
   611  		getStatus        int
   612  		otherStatus      int
   613  		getResponse      interface{}
   614  		newResource      v1.Interface
   615  		mf               MergeFunc
   616  		expectedErr      error
   617  		expectedResource interface{}
   618  	}{
   619  		{
   620  			name:        "it's a create",
   621  			getResponse: oldAPISvc,
   622  			newResource: newAPISvc,
   623  			getStatus:   404,
   624  			otherStatus: 201,
   625  			mf: func(fetched v1.Interface, new v1.Interface) (v1.Interface, error) {
   626  				return new, nil
   627  			},
   628  			expectedErr:      nil,
   629  			expectedResource: newAPISvc,
   630  		},
   631  		{
   632  			name:        "overwriting update",
   633  			getResponse: oldAPISvc,
   634  			newResource: newAPISvc,
   635  			getStatus:   200,
   636  			otherStatus: 200,
   637  			mf: func(fetched v1.Interface, new v1.Interface) (v1.Interface, error) {
   638  				return new, nil
   639  			},
   640  			expectedErr:      nil,
   641  			expectedResource: newAPISvc,
   642  		},
   643  		{
   644  			name:        "merging tags update",
   645  			getResponse: oldAPISvc,
   646  			newResource: newAPISvc,
   647  			getStatus:   200,
   648  			otherStatus: 200,
   649  			mf: func(fetched v1.Interface, new v1.Interface) (v1.Interface, error) {
   650  				f, err := fetched.AsInstance()
   651  				if err != nil {
   652  					return nil, err
   653  				}
   654  
   655  				f.SetTags(append(f.GetTags(), new.GetTags()...))
   656  
   657  				return f, nil
   658  			},
   659  			expectedErr:      nil,
   660  			expectedResource: mergedTags,
   661  		},
   662  		{
   663  			name:        "merge error",
   664  			getResponse: oldAPISvc,
   665  			newResource: newAPISvc,
   666  			getStatus:   200,
   667  			otherStatus: 200,
   668  			mf: func(fetched v1.Interface, new v1.Interface) (v1.Interface, error) {
   669  				return nil, mergeError
   670  			},
   671  			expectedErr:      mergeError,
   672  			expectedResource: nil,
   673  		},
   674  		{
   675  			name:        "get error",
   676  			getResponse: v1.ErrorResponse{Errors: getError.Errors},
   677  			newResource: newAPISvc,
   678  			getStatus:   500,
   679  			otherStatus: 200,
   680  			mf: func(fetched v1.Interface, new v1.Interface) (v1.Interface, error) {
   681  				return nil, mergeError
   682  			},
   683  			expectedErr:      getError,
   684  			expectedResource: nil,
   685  		}}
   686  	logger := WithLogger(noOpLogger{})
   687  	c, err := NewClient("http://localhost:8080/apis", logger).ForKind(management.APIServiceGVK())
   688  
   689  	if err != nil {
   690  		t.Fatalf("Failed due: %s ", err)
   691  	}
   692  	for i := range testCases {
   693  		tc := testCases[i]
   694  		t.Run(tc.name, func(t *testing.T) {
   695  			defer gock.Off()
   696  			gock.Observe(gock.DumpRequest)
   697  
   698  			gock.New("http://localhost:8080/apis").
   699  				Get("/management/v1alpha1/environments/myenv/apiservices/name").
   700  				Reply(tc.getStatus).
   701  				JSON(tc.getResponse)
   702  
   703  			switch {
   704  			case tc.expectedErr == mergeError:
   705  			case tc.getStatus == 404:
   706  				gock.New("http://localhost:8080/apis").
   707  					Post("/management/v1alpha1/environments/myenv/apiservices").
   708  					JSON(tc.expectedResource).
   709  					Reply(tc.otherStatus).
   710  					JSON(tc.expectedResource)
   711  			case tc.getStatus == 200:
   712  				gock.New("http://localhost:8080/apis").
   713  					Put("/management/v1alpha1/environments/myenv/apiservices/name").
   714  					JSON(tc.expectedResource).
   715  					Reply(tc.otherStatus).
   716  					JSON(tc.expectedResource)
   717  			}
   718  
   719  			newRI, err := tc.newResource.AsInstance()
   720  			if err != nil {
   721  				t.Fatal(err)
   722  			}
   723  
   724  			_, err = c.Update(newRI, Merge(tc.mf))
   725  
   726  			switch {
   727  			case err == nil && tc.expectedErr == nil:
   728  			case err != nil && tc.expectedErr == nil:
   729  				t.Error("Not expecting error, got: ", err)
   730  			case err == nil && tc.expectedErr != nil:
   731  				t.Error("Expected error: ", tc.expectedErr, "; got no error")
   732  			case err.Error() != tc.expectedErr.Error():
   733  				t.Error("Expected error: ", tc.expectedErr, "; got: ", err)
   734  			}
   735  
   736  			if gock.HasUnmatchedRequest() {
   737  				t.Errorf("Expected more requests: %+v", gock.GetUnmatchedRequests())
   738  			}
   739  		})
   740  	}
   741  }