Skip to content

Commit

Permalink
chore(RHTAPWATCH-973): functional tests
Browse files Browse the repository at this point in the history
Adds functional tests for the different endpoints in
the workspace-manager webserver

Signed-off-by: Omer Amsalem <[email protected]>
  • Loading branch information
Omer Amsalem committed May 19, 2024
1 parent 06df38d commit 51ed4f0
Show file tree
Hide file tree
Showing 4 changed files with 279 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ vet: ## Run go vet against code.

.PHONY: test
test: manifests generate fmt vet envtest ## Run tests.
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -v -ginkgo.v -coverprofile cover.out

# Utilize Kind or modify the e2e tests to load the image locally, enabling compatibility with other vendors.
.PHONY: test-e2e # Run the e2e tests against a Kind k8s instance that is spun up.
Expand Down
269 changes: 269 additions & 0 deletions cmd/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
package main

import (
"fmt"
"io"
"log"
"net/http"
"os/exec"
"strings"
"time"

k8sapi "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"context"
"os"
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
)

type HTTPResponse struct {
Body string
StatusCode int
}

type HTTPheader struct {
name string
value string
}

var k8sClient client.Client
var testEnv *envtest.Environment

func createRole(k8sClient client.Client, nsName string, roleName string) {
role := &rbacv1.Role{
ObjectMeta: metav1.ObjectMeta{
Name: roleName,
Namespace: nsName,
},
Rules: []rbacv1.PolicyRule{
{
APIGroups: []string{"appstudio.redhat.com"},
Resources: []string{"applications", "components"},
Verbs: []string{"create", "list", "watch", "delete"},
},
},
}
err := k8sClient.Create(context.Background(), role)
Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error creating 'Role' resource: %v", err))
}

func createRoleBinding(k8sClient client.Client, bindingName string, nsName string, userName string, roleName string) {
roleBinding := &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: bindingName,
Namespace: nsName,
},
Subjects: []rbacv1.Subject{
{
Kind: "User",
Name: userName,
APIGroup: "rbac.authorization.k8s.io",
},
},
RoleRef: rbacv1.RoleRef{
Kind: "Role",
Name: roleName,
APIGroup: "rbac.authorization.k8s.io",
},
}
err := k8sClient.Create(context.Background(), roleBinding)
Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error creating 'roleBinding' resource: %v", err))
}

func createNamespace(k8sClient client.Client, name string) {
namespaced := &k8sapi.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: map[string]string{
"konflux.ci/type": "user",
"kubernetes.io/metadata.name": name,
},
},
}
err := k8sClient.Create(context.Background(), namespaced)
Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error creating 'Namespace' resource: %v", err))
}

func performHTTPGetCall(url string, header HTTPheader) (*HTTPResponse, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Printf("Error creating request: %s", err)
return nil, err
}
if header.name != "" {
req.Header.Add(header.name, header.value)
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Printf("Error making request: %s", err)
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Printf("Error reading response body: %s", err)
return nil, err
}
response := &HTTPResponse{
Body: string(body),
StatusCode: resp.StatusCode,
}
return response, nil
}

func TestCmd(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Main Suite")
}

var _ = Describe("Signup endpoint", func() {
Context("Calling the signup endpoint with GET", func() {
It("responds with ready and signedup", func() {
url := "http://localhost:5000/api/v1/signup"
expectedCode := http.StatusOK
expectedBody := `{"status":{"ready":true,"reason":"SignedUp"}}`

resp, err := performHTTPGetCall(url, HTTPheader{})
Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Unexpected error testing the \"%s\" endpoint: %v", url, err))
Expect(resp.StatusCode).To(Equal(expectedCode))
Expect(strings.TrimSpace(expectedBody)).To(Equal(strings.TrimSpace(resp.Body)))
})
})
})

var _ = DescribeTable("Workspace endpoint", func(header HTTPheader, expectedCode int, expectedBody string) {
url := "http://localhost:5000/workspaces"
resp, err := performHTTPGetCall(url, header)
Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Unexpected error testing the \"%s\" endpoint: %v", url, err))
Expect(resp.StatusCode).To(Equal(expectedCode))
Expect(strings.TrimSpace(expectedBody)).To(Equal(strings.TrimSpace(resp.Body)))
},
Entry(
"Calling the workspace endpoint for user1 responds only with the 'test-tenant' workspace info",
HTTPheader{"X-Email", "[email protected]"},
http.StatusOK,
`{"kind":"WorkspaceList","apiVersion":"toolchain.dev.openshift.com/v1alpha1","metadata":{},`+
`"items":[{"kind":"Workspace","apiVersion":"toolchain.dev.openshift.com/v1alpha1",`+
`"metadata":{"name":"test-tenant","creationTimestamp":null},"status":`+
`{"namespaces":[{"name":"test-tenant","type":"default"}]}}]}`),
Entry(
"Workspace endpoint for user2 responds with 2 namespaces info",
HTTPheader{"X-Email", "[email protected]"},
http.StatusOK,
`{"kind":"WorkspaceList","apiVersion":"toolchain.dev.openshift.com/v1alpha1","metadata":{},`+
`"items":[{"kind":"Workspace","apiVersion":"toolchain.dev.openshift.com/v1alpha1",`+
`"metadata":{"name":"test-tenant","creationTimestamp":null},"status":{"namespaces":`+
`[{"name":"test-tenant","type":"default"}]}},{"kind":"Workspace","apiVersion":`+
`"toolchain.dev.openshift.com/v1alpha1","metadata":{"name":"test-tenant-2",`+
`"creationTimestamp":null},"status":{"namespaces":[{"name":"test-tenant-2",`+
`"type":"default"}]}}]}`),
Entry(
"Workspace endpoint for user3 responds with no namespaces",
HTTPheader{"X-Email", "[email protected]"},
http.StatusOK,
`{"kind":"WorkspaceList","apiVersion":"toolchain.dev.openshift.com/v1alpha1","metadata":{},"items":null}`),
Entry(
"Workspace endpoint with no header",
HTTPheader{},
500,
`{"message":"Internal Server Error"}`),
)

var _ = DescribeTable("Specific workspace endpoint", func(endpoint string, header HTTPheader, expectedCode int, expectedBody string) {
url := "http://localhost:5000/workspaces/" + endpoint
resp, err := performHTTPGetCall(url, header)
Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Unexpected error testing the \"%s\" endpoint: %v", url, err))
Expect(resp.StatusCode).To(Equal(expectedCode))
Expect(strings.TrimSpace(expectedBody)).To(Equal(strings.TrimSpace(resp.Body)))
},
Entry(
"Calling the workspace endpoint for the test-tenant workspace for user2",
"test-tenant",
HTTPheader{"X-Email", "[email protected]"},
http.StatusOK,
`{"kind":"Workspace","apiVersion":"toolchain.dev.openshift.com/v1alpha1","metadata":`+
`{"name":"test-tenant","creationTimestamp":null},"status":{"namespaces":`+
`[{"name":"test-tenant","type":"default"}]}}`),
Entry(
"Specific workspace endpoint for test-tenant-2 for user1 only",
"test-tenant-2",
HTTPheader{"X-Email", "[email protected]"},
404,
`{"message":"Not Found"}`),
)

func CreateKubeconfigFileForRestConfig(restConfig rest.Config) string {
clusters := make(map[string]*clientcmdapi.Cluster)
clusters["default-cluster"] = &clientcmdapi.Cluster{
Server: restConfig.Host,
CertificateAuthorityData: restConfig.CAData,
}
contexts := make(map[string]*clientcmdapi.Context)
contexts["default-context"] = &clientcmdapi.Context{
Cluster: "default-cluster",
AuthInfo: "default-user",
}
authinfos := make(map[string]*clientcmdapi.AuthInfo)
authinfos["default-user"] = &clientcmdapi.AuthInfo{
ClientCertificateData: restConfig.CertData,
ClientKeyData: restConfig.KeyData,
}
clientConfig := clientcmdapi.Config{
Kind: "Config",
APIVersion: "v1",
Clusters: clusters,
Contexts: contexts,
CurrentContext: "default-context",
AuthInfos: authinfos,
}
kubeConfigFile, _ := os.CreateTemp("", "kubeconfig")
_ = clientcmd.WriteToFile(clientConfig, kubeConfigFile.Name())
return kubeConfigFile.Name()
}

var serverProcess *exec.Cmd

var _ = BeforeSuite(func() {
testEnv = &envtest.Environment{}
cfg, err := testEnv.Start()
Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error creating the envtest environment during test setup: %v", err))
kubeconfigPath := CreateKubeconfigFileForRestConfig(*cfg)
os.Setenv("KUBECONFIG", kubeconfigPath)
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme})
Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error creating the client during test setup: %v", err))
Expect(k8sClient).NotTo(BeNil())

user1 := "[email protected]"
user2 := "[email protected]"
createNamespace(k8sClient, "test-tenant")
createNamespace(k8sClient, "test-tenant-2")
createNamespace(k8sClient, "test-tenant-3")
createRole(k8sClient, "test-tenant", "namespace-access")
createRole(k8sClient, "test-tenant-2", "namespace-access-2")
createRoleBinding(k8sClient, "namespace-access-user-binding", "test-tenant", user1, "namespace-access")
createRoleBinding(k8sClient, "namespace-access-user-binding-2", "test-tenant", user2, "namespace-access")
createRoleBinding(k8sClient, "namespace-access-user-binding-3", "test-tenant-2", user2, "namespace-access-2")
serverProcess = exec.Command("go", "run", "main.go")
err = serverProcess.Start()
Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error starting the server during test setup: %v", err))
time.Sleep(5 * time.Second)
})

var _ = AfterSuite(func() {
Expect(os.Unsetenv("KUBECONFIG")).To(Succeed())
if serverProcess != nil && serverProcess.Process != nil {
err := serverProcess.Process.Kill()
Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error killing the server during test teardown: %v", err))
}
})
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ go 1.20
require (
github.com/codeready-toolchain/api v0.0.0-20240322110702-5ab3840476e9
github.com/labstack/echo/v4 v4.11.4
github.com/onsi/ginkgo/v2 v2.1.4
github.com/onsi/gomega v1.19.0
k8s.io/api v0.25.0
k8s.io/apimachinery v0.25.0
k8s.io/client-go v0.25.0
Expand Down Expand Up @@ -53,6 +55,7 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.25.0 // indirect
k8s.io/klog/v2 v2.70.1 // indirect
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
Expand Down Expand Up @@ -134,6 +135,7 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
Expand Down Expand Up @@ -182,7 +184,9 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY=
github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/openshift/api v0.0.0-20230213134911-7ba313770556 h1:7W2fOhJicyEff24VaF7ASNzPtYvr+iSCVft4SIBAzaE=
github.com/openshift/api v0.0.0-20230213134911-7ba313770556/go.mod h1:aQ6LDasvHMvHZXqLHnX2GRmnfTWCF/iIwz8EMTTIE9A=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand Down Expand Up @@ -504,10 +508,12 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
k8s.io/api v0.25.0 h1:H+Q4ma2U/ww0iGB78ijZx6DRByPz6/733jIuFpX70e0=
k8s.io/api v0.25.0/go.mod h1:ttceV1GyV1i1rnmvzT3BST08N6nGt+dudGrquzVQWPk=
k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY=
k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E=
k8s.io/apimachinery v0.25.0 h1:MlP0r6+3XbkUG2itd6vp3oxbtdQLQI94fD5gCS+gnoU=
k8s.io/apimachinery v0.25.0/go.mod h1:qMx9eAk0sZQGsXGu86fab8tZdffHbwUfsvzqKn4mfB0=
k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E=
k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8=
k8s.io/component-base v0.25.0 h1:haVKlLkPCFZhkcqB6WCvpVxftrg6+FK5x1ZuaIDaQ5Y=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ=
k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
Expand Down

0 comments on commit 51ed4f0

Please sign in to comment.