Skip to content

Commit

Permalink
test: unit test/mock quay client
Browse files Browse the repository at this point in the history
Adds test coverage for the quay client by implementing a mock http
client interface and mocking json body responses.

Topic: client-unit-test

Relative: quay-lint
Signed-off-by: Ross Bryan <[email protected]>
  • Loading branch information
arborite-rh committed Mar 3, 2024
1 parent b342531 commit b35742e
Show file tree
Hide file tree
Showing 31 changed files with 8,685 additions and 44 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and Cust

generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
go generate ./...

fmt: ## Run go fmt against code.
go fmt ./...
Expand Down
10 changes: 5 additions & 5 deletions controllers/namespace_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,8 @@ func (r *NamespaceIntegrationReconciler) Reconcile(ctx context.Context, req ctrl
return reconcile.Result{}, nil
}

func (r *NamespaceIntegrationReconciler) setupResources(ctx context.Context, request reconcile.Request, namespace *corev1.Namespace, quayClient *qclient.QuayClient, quayOrganizationName string, quayName string, quayHostname string) (reconcile.Result, error) {
_, organizationResponse, organizationError := quayClient.GetOrganizationByname(quayOrganizationName)
func (r *NamespaceIntegrationReconciler) setupResources(ctx context.Context, request reconcile.Request, namespace *corev1.Namespace, quayClient *qclient.Client, quayOrganizationName string, quayName string, quayHostname string) (reconcile.Result, error) {
_, organizationResponse, organizationError := quayClient.GetOrganizationByName(quayOrganizationName)

if organizationError.Error != nil {
return r.CoreComponents.ManageError(&core.QuayIntegrationCoreError{
Expand Down Expand Up @@ -334,7 +334,7 @@ func (r *NamespaceIntegrationReconciler) setupResources(ctx context.Context, req
}

// createRobotAccountAndSecret creates a robot account, creates a secret and adds the secret to the service account
func (r *NamespaceIntegrationReconciler) createRobotAccountAssociateToSA(ctx context.Context, request reconcile.Request, namespace *corev1.Namespace, quayClient *qclient.QuayClient, quayOrganizationName string, serviceAccount qotypes.OpenShiftServiceAccount, role qclient.QuayRole, quayName string, quayHostname string) (reconcile.Result, error) {
func (r *NamespaceIntegrationReconciler) createRobotAccountAssociateToSA(ctx context.Context, request reconcile.Request, namespace *corev1.Namespace, quayClient *qclient.Client, quayOrganizationName string, serviceAccount qotypes.OpenShiftServiceAccount, role qclient.QuayRole, quayName string, quayHostname string) (reconcile.Result, error) {
// Setup Robot Account
robotAccount, robotAccountResponse, robotAccountError := quayClient.GetOrganizationRobotAccount(quayOrganizationName, string(serviceAccount))
if robotAccountResponse == nil {
Expand Down Expand Up @@ -456,10 +456,10 @@ func (r *NamespaceIntegrationReconciler) createRobotAccountAssociateToSA(ctx con
return reconcile.Result{}, nil
}

func (r *NamespaceIntegrationReconciler) cleanupResources(request reconcile.Request, namespace *corev1.Namespace, quayClient *qclient.QuayClient, quayOrganizationName string) (reconcile.Result, error) {
func (r *NamespaceIntegrationReconciler) cleanupResources(request reconcile.Request, namespace *corev1.Namespace, quayClient *qclient.Client, quayOrganizationName string) (reconcile.Result, error) {
logging.Log.Info("Deleting Organization", "Organization Name", quayOrganizationName)

_, organizationResponse, organizationError := quayClient.GetOrganizationByname(quayOrganizationName)
_, organizationResponse, organizationError := quayClient.GetOrganizationByName(quayOrganizationName)
if organizationError.Error != nil {
return r.CoreComponents.ManageError(&core.QuayIntegrationCoreError{
Object: namespace,
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ require (
github.com/onsi/gomega v1.29.0
github.com/openshift/api v0.0.0-20210202165416-a9e731090f5e
github.com/redhat-cop/operator-utils v1.3.5
github.com/stretchr/testify v1.8.4
go.uber.org/mock v0.4.0
golang.org/x/sync v0.3.0
gomodules.xyz/jsonpatch/v2 v2.4.0
k8s.io/api v0.26.6
Expand Down Expand Up @@ -52,6 +54,7 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.15.1 // indirect
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
Expand Down
5 changes: 4 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
Expand All @@ -243,6 +244,8 @@ go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
Expand Down
82 changes: 44 additions & 38 deletions pkg/client/quay/client.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package quay

//go:generate mockgen -source=$GOFILE -destination=mocks/client_mock.go -package=quay

import (
"bytes"
"encoding/json"
Expand All @@ -9,14 +11,28 @@ import (
"net/url"
)

type QuayClient struct {
type HttpClient interface {
Do(req *http.Request) (*http.Response, error)
}

type Client struct {
BaseURL *url.URL
httpClient *http.Client
httpClient HttpClient
AuthToken string
}

func (c *QuayClient) GetUser() (User, *http.Response, QuayApiError) {
req, err := c.newRequest("GET", "/api/v1/user", nil)
func NewClient(httpClient HttpClient, baseUrl, authToken string) *Client {
quayClient := Client{
httpClient: httpClient,
AuthToken: authToken,
}

quayClient.BaseURL, _ = url.Parse(baseUrl)
return &quayClient
}

func (c *Client) GetUser() (User, *http.Response, QuayApiError) {
req, err := c.NewRequest("GET", "/api/v1/user", nil)
if err != nil {
return User{}, nil, QuayApiError{Error: err}
}
Expand All @@ -26,8 +42,8 @@ func (c *QuayClient) GetUser() (User, *http.Response, QuayApiError) {
return user, resp, QuayApiError{Error: err}
}

func (c *QuayClient) GetOrganizationByname(orgName string) (Organization, *http.Response, QuayApiError) {
req, err := c.newRequest("GET", fmt.Sprintf("/api/v1/organization/%s", orgName), nil)
func (c *Client) GetOrganizationByName(orgName string) (Organization, *http.Response, QuayApiError) {
req, err := c.NewRequest("GET", fmt.Sprintf("/api/v1/organization/%s", orgName), nil)
if err != nil {
return Organization{}, nil, QuayApiError{Error: err}
}
Expand All @@ -37,13 +53,13 @@ func (c *QuayClient) GetOrganizationByname(orgName string) (Organization, *http.
return organization, resp, QuayApiError{Error: err}
}

func (c *QuayClient) CreateOrganization(name string) (StringValue, *http.Response, QuayApiError) {
func (c *Client) CreateOrganization(name string) (StringValue, *http.Response, QuayApiError) {
newOrganization := OrganizationRequest{
Name: name,
Email: fmt.Sprintf("%[email protected]", name),
}

req, err := c.newRequest("POST", "/api/v1/organization/", newOrganization)
req, err := c.NewRequest("POST", "/api/v1/organization/", newOrganization)
if err != nil {
return StringValue{}, nil, QuayApiError{Error: err}
}
Expand All @@ -54,8 +70,8 @@ func (c *QuayClient) CreateOrganization(name string) (StringValue, *http.Respons
return newOrganizationResponse, resp, QuayApiError{Error: err}
}

func (c *QuayClient) GetOrganizationRobotAccount(organizationName string, robotName string) (RobotAccount, *http.Response, QuayApiError) {
req, err := c.newRequest("GET", fmt.Sprintf("/api/v1/organization/%s/robots/%s", organizationName, robotName), nil)
func (c *Client) GetOrganizationRobotAccount(organizationName, robotName string) (RobotAccount, *http.Response, QuayApiError) {
req, err := c.NewRequest("GET", fmt.Sprintf("/api/v1/organization/%s/robots/%s", organizationName, robotName), nil)
if err != nil {
return RobotAccount{}, nil, QuayApiError{Error: err}
}
Expand All @@ -66,8 +82,8 @@ func (c *QuayClient) GetOrganizationRobotAccount(organizationName string, robotN
return getOrganizationRobotResponse, resp, QuayApiError{Error: err}
}

func (c *QuayClient) GetPrototypesByOrganization(organizationName string) (PrototypesResponse, *http.Response, QuayApiError) {
req, err := c.newRequest("GET", fmt.Sprintf("/api/v1/organization/%s/prototypes", organizationName), nil)
func (c *Client) GetPrototypesByOrganization(organizationName string) (PrototypesResponse, *http.Response, QuayApiError) {
req, err := c.NewRequest("GET", fmt.Sprintf("/api/v1/organization/%s/prototypes", organizationName), nil)
if err != nil {
return PrototypesResponse{}, nil, QuayApiError{Error: err}
}
Expand All @@ -78,8 +94,8 @@ func (c *QuayClient) GetPrototypesByOrganization(organizationName string) (Proto
return getPrototypeResponse, resp, QuayApiError{Error: err}
}

func (c *QuayClient) CreateOrganizationRobotAccount(organizationName string, robotName string) (RobotAccount, *http.Response, QuayApiError) {
req, err := c.newRequest("PUT", fmt.Sprintf("/api/v1/organization/%s/robots/%s", organizationName, robotName), nil)
func (c *Client) CreateOrganizationRobotAccount(organizationName, robotName string) (RobotAccount, *http.Response, QuayApiError) {
req, err := c.NewRequest("PUT", fmt.Sprintf("/api/v1/organization/%s/robots/%s", organizationName, robotName), nil)
if err != nil {
return RobotAccount{}, nil, QuayApiError{Error: err}
}
Expand All @@ -90,8 +106,8 @@ func (c *QuayClient) CreateOrganizationRobotAccount(organizationName string, rob
return createOrganizationRobotResponse, resp, QuayApiError{Error: err}
}

func (c *QuayClient) DeleteOrganization(orgName string) (*http.Response, QuayApiError) {
req, err := c.newRequest("DELETE", fmt.Sprintf("/api/v1/organization/%s", orgName), nil)
func (c *Client) DeleteOrganization(orgName string) (*http.Response, QuayApiError) {
req, err := c.NewRequest("DELETE", fmt.Sprintf("/api/v1/organization/%s", orgName), nil)
if err != nil {
return nil, QuayApiError{Error: err}
}
Expand All @@ -101,7 +117,7 @@ func (c *QuayClient) DeleteOrganization(orgName string) (*http.Response, QuayApi
return resp, QuayApiError{Error: err}
}

func (c *QuayClient) CreateRobotPermissionForOrganization(organizationName string, robotAccount string, role string) (Prototype, *http.Response, QuayApiError) {
func (c *Client) CreateRobotPermissionForOrganization(organizationName, robotAccount, role string) (Prototype, *http.Response, QuayApiError) {
robotOrganizationPermission := Prototype{
Role: role,
Delegate: PrototypeDelegate{
Expand All @@ -112,7 +128,7 @@ func (c *QuayClient) CreateRobotPermissionForOrganization(organizationName strin
},
}

req, err := c.newRequest("POST", fmt.Sprintf("/api/v1/organization/%s/prototypes", organizationName), robotOrganizationPermission)
req, err := c.NewRequest("POST", fmt.Sprintf("/api/v1/organization/%s/prototypes", organizationName), robotOrganizationPermission)
if err != nil {
return Prototype{}, nil, QuayApiError{Error: err}
}
Expand All @@ -123,8 +139,8 @@ func (c *QuayClient) CreateRobotPermissionForOrganization(organizationName strin
return newPrototypeResponse, resp, QuayApiError{Error: err}
}

func (c *QuayClient) GetRepository(orgName string, repositoryName string) (Repository, *http.Response, QuayApiError) {
req, err := c.newRequest("GET", fmt.Sprintf("/api/v1/repository/%s/%s", orgName, repositoryName), nil)
func (c *Client) GetRepository(orgName, repositoryName string) (Repository, *http.Response, QuayApiError) {
req, err := c.NewRequest("GET", fmt.Sprintf("/api/v1/repository/%s/%s", orgName, repositoryName), nil)
if err != nil {
return Repository{}, nil, QuayApiError{Error: err}
}
Expand All @@ -135,7 +151,7 @@ func (c *QuayClient) GetRepository(orgName string, repositoryName string) (Repos
return repository, resp, QuayApiError{Error: err}
}

func (c *QuayClient) CreateRepository(namespace, name string) (RepositoryRequest, *http.Response, QuayApiError) {
func (c *Client) CreateRepository(namespace, name string) (RepositoryRequest, *http.Response, QuayApiError) {
newRepository := RepositoryRequest{
Repository: name,
Namespace: namespace,
Expand All @@ -144,7 +160,7 @@ func (c *QuayClient) CreateRepository(namespace, name string) (RepositoryRequest
Description: "",
}

req, err := c.newRequest("POST", "/api/v1/repository", newRepository)
req, err := c.NewRequest("POST", "/api/v1/repository", newRepository)
if err != nil {
return RepositoryRequest{}, nil, QuayApiError{Error: err}
}
Expand All @@ -155,7 +171,7 @@ func (c *QuayClient) CreateRepository(namespace, name string) (RepositoryRequest
return newRepositoryResponse, resp, QuayApiError{Error: err}
}

func (c *QuayClient) newRequest(method, path string, body interface{}) (*http.Request, error) {
func (c *Client) NewRequest(method, path string, body interface{}) (*http.Request, error) {
rel := &url.URL{Path: path}
u := c.BaseURL.ResolveReference(rel)

Expand All @@ -169,14 +185,14 @@ func (c *QuayClient) newRequest(method, path string, body interface{}) (*http.Re
}

req, err := http.NewRequest(method, u.String(), buf)
if c.AuthToken != "" {
req.Header.Set("Authorization", "Bearer "+c.AuthToken)
}

if err != nil {
return nil, err
}

if c.AuthToken != "" {
req.Header.Set("Authorization", "Bearer "+c.AuthToken)
}

if body != nil {
req.Header.Set("Content-Type", "application/json")
}
Expand All @@ -185,7 +201,7 @@ func (c *QuayClient) newRequest(method, path string, body interface{}) (*http.Re
return req, nil
}

func (c *QuayClient) do(req *http.Request, v interface{}) (*http.Response, error) {
func (c *Client) do(req *http.Request, v interface{}) (*http.Response, error) {
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
Expand All @@ -211,13 +227,3 @@ func (c *QuayClient) do(req *http.Request, v interface{}) (*http.Response, error

return resp, err
}

func NewClient(httpClient *http.Client, baseUrl string, authToken string) *QuayClient {
quayClient := QuayClient{
httpClient: httpClient,
AuthToken: authToken,
}

quayClient.BaseURL, _ = url.Parse(baseUrl)
return &quayClient
}
Loading

0 comments on commit b35742e

Please sign in to comment.