Repository: redhat-openshift-ecosystem/openshift-preflight
Pull request #714 (2022-06-28 13:47:19)

remove flaky docker-config test
This PR removes a flaky test that doesn't test any functionality in our code.

Signed-off-by: Jose R. Gonzalez <jose@flutes.dev>

openshift-ci[bot]: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/redhat-openshift-ecosystem/openshift-preflight/pull/714#" title="Author self-approved">komish</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=redhat-openshift-ecosystem%2Fopenshift-preflight).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/redhat-openshift-ecosystem/openshift-preflight/blob/main/OWNERS)~~ [komish]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
openshift-ci[bot]: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/redhat-openshift-ecosystem/openshift-preflight/pull/714#pullrequestreview-1021780657" title="LGTM">bcrochet</a>*, *<a href="https://github.com/redhat-openshift-ecosystem/openshift-preflight/pull/714#" title="Author self-approved">komish</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=redhat-openshift-ecosystem%2Fopenshift-preflight).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/redhat-openshift-ecosystem/openshift-preflight/blob/main/OWNERS)~~ [bcrochet,komish]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
dcibot: -  FAILURE https://www.distributed-ci.io/jobs/47bcd205-a9ba-402c-9b55-3fa0920fe883/jobStates
 from https://github.com/redhat-openshift-ecosystem/openshift-preflight/pull/714
coveralls: 
[![Coverage Status](https://coveralls.io/builds/50425780/badge)](https://coveralls.io/builds/50425780)

Coverage decreased (-0.05%) to 66.09% when pulling **a016ded109ca7d78659ef733ebca08401e47a766 on komish:fix-flaky-docker-config-test** into **365e4a92b674086ef952a75a09ca933c9829da75 on redhat-openshift-ecosystem:main**.

openshift-ci[bot]: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/redhat-openshift-ecosystem/openshift-preflight/pull/714#pullrequestreview-1021874324" title="Approved">acornett21</a>*, *<a href="https://github.com/redhat-openshift-ecosystem/openshift-preflight/pull/714#pullrequestreview-1021780657" title="LGTM">bcrochet</a>*, *<a href="https://github.com/redhat-openshift-ecosystem/openshift-preflight/pull/714#" title="Author self-approved">komish</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=redhat-openshift-ecosystem%2Fopenshift-preflight).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/redhat-openshift-ecosystem/openshift-preflight/blob/main/OWNERS)~~ [acornett21,bcrochet,komish]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->

Patch:
diff --git a/cmd/support.go b/cmd/support.go
index c236231..dbb47cf 100644
--- a/cmd/support.go
+++ b/cmd/support.go
@@ -1,12 +1,12 @@
+// go:test !race
 package cmd
 
 import (
+	"errors"
 	"fmt"
 	"net/url"
 	"regexp"
 
-	"github.com/manifoldco/promptui"
-	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
 )
 
@@ -23,107 +23,156 @@ const (
 	containerImage       = "Container Image"
 )
 
+var projectTypeMapping = map[string]string{
+	"container": containerImage,
+	"operator":  operatorBundleImage,
+}
+
 var supportCmd = &cobra.Command{
-	Use:   "support",
-	Short: "Submits a support request",
-	Long: `This interactive command will generate a URL; based on user input which can then be used to create a Red Hat Support Ticket.
-	This command can be used when you'd like assistance from Red Hat Support when attempting to pass your certification checks. `,
+	Use:   "support <operator|container> <your project ID> [pullRequestURL if operator project]",
+	Short: "Creates a support request",
+	Args: cobra.MatchAll(
+		cobra.MinimumNArgs(2),
+		cobra.MaximumNArgs(3)),
+	Long: `Generate a URL that can be used to open a ticket with Red Hat Support if you're having an issue passing certification checks.`,
 	RunE: supportRunE,
 }
 
 func supportRunE(cmd *cobra.Command, args []string) error {
-	certProjectTypeLabel := promptui.Select{
-		Label: "Select a Certification Project Type",
-		Items: []string{containerImage, operatorBundleImage},
+	// the last positional argument is optional, so
+	// if the user didn't provide it, we'll make it an empty
+	// string.
+	if len(args) == 2 {
+		args = append(args, "")
 	}
 
-	_, certProjectTypeValue, err := certProjectTypeLabel.Run()
+	ptype := args[0]
+	pid := args[1]
+	prurl := args[2]
+
+	support, err := newSupportTextGenerator(ptype, pid, prurl)
 	if err != nil {
-		return fmt.Errorf("project type prompt failed, please try re-running support command")
+		return err
 	}
 
-	log.Debugf("certification project type: %s", certProjectTypeValue)
+	fmt.Fprint(cmd.OutOrStdout(), support.Generate())
+	return nil
+}
+
+type supportTextGenerator struct {
+	ProjectType    string
+	ProjectID      string
+	PullRequestURL string
+}
 
-	certProjectIDLabel := promptui.Prompt{
-		Label: "Please Enter Connect Certification Project ID",
+func newSupportTextGenerator(ptype, pid, pullReqURL string) (*supportTextGenerator, error) {
+	gen := &supportTextGenerator{
+		ProjectType:    ptype,
+		ProjectID:      pid,
+		PullRequestURL: pullReqURL,
+	}
 
-		// validate makes sure that the project id is not blank, does not contain special characters,
-		// and is in the proper format
-		Validate: func(s string) error {
-			if s == "" {
-				return fmt.Errorf("please enter a non empty project id")
-			}
+	if err := gen.validate(); err != nil {
+		return nil, err
+	}
 
-			isLegacy, _ := regexp.MatchString(`^p.*`, s)
-			if isLegacy {
-				return fmt.Errorf("please remove leading character p from project id")
-			}
+	return gen, nil
+}
 
-			isOSPID, _ := regexp.MatchString(`^ospid-.*`, s)
-			if isOSPID {
-				return fmt.Errorf("please remove leading character ospid- from project id")
-			}
+func (g *supportTextGenerator) queryParams() url.Values {
+	// base parameters
+	qp := url.Values{}
+	qp.Add(typeParam, typeValue)
+	qp.Add(sourceParam, sourceValue)
 
-			isAlphaNumeric := regexp.MustCompile(`^[a-zA-Z0-9]*$`).MatchString(s)
-			if !isAlphaNumeric {
-				return fmt.Errorf("please remove all special characters from project id")
-			}
+	// user parameters
+	qp.Add(certProjectTypeParam, projectTypeMapping[g.ProjectType])
+	qp.Add(certProjectIDParam, g.ProjectID)
 
-			return nil
-		},
+	if g.PullRequestURL != "" {
+		qp.Add(pullRequestURLParam, g.PullRequestURL)
 	}
+	return qp
+}
 
-	certProjectIDValue, err := certProjectIDLabel.Run()
-	if err != nil {
-		return fmt.Errorf("project ID prompt failed, please try re-running support command")
+func (g *supportTextGenerator) Generate() string {
+	params := g.queryParams()
+	return fmt.Sprintf("Create a support ticket by: \n"+
+		"\t1. Copying URL: %s\n"+
+		"\t2. Paste above URL in a browser\n"+
+		"\t3. Login with Red Hat SSO\n"+
+		"\t4. Enter an issue summary and description\n"+
+		"\t5. Preview and Submit your ticket",
+		baseURL+params.Encode())
+}
+
+func (g *supportTextGenerator) validate() error {
+	if err := projectTypeValidation(g.ProjectType); err != nil {
+		return err
 	}
 
-	log.Debugf("certification project id: %s", certProjectIDValue)
-
-	// building and encoding query params
-	queryParams := url.Values{}
-	queryParams.Add(typeParam, typeValue)
-	queryParams.Add(sourceParam, sourceValue)
-	queryParams.Add(certProjectTypeParam, certProjectTypeValue)
-	queryParams.Add(certProjectIDParam, certProjectIDValue)
-
-	// checking project type to see if we need to add additional query params
-	if certProjectTypeValue == operatorBundleImage {
-		pullRequestURLLabel := promptui.Prompt{
-			Label: "Please Enter Your Pull Request URL",
-
-			// validate makes sure that the url entered has a valid scheme, host and path to the pull request
-			Validate: func(s string) error {
-				_, err := url.ParseRequestURI(s)
-				if err != nil {
-					return fmt.Errorf("please enter a valid url: including scheme, host, and path to pull request")
-				}
-
-				url, err := url.Parse(s)
-				if err != nil || url.Scheme == "" || url.Host == "" || url.Path == "" {
-					return fmt.Errorf("please enter a valid url: including scheme, host, and path to pull request")
-				}
-
-				return nil
-			},
+	if err := projectIDValidation(g.ProjectID); err != nil {
+		return err
+	}
+
+	if g.ProjectType == "operator" {
+		if len(g.PullRequestURL) == 0 {
+			return errors.New("a pull request URL is required for operator project support requests")
 		}
 
-		pullRequestURLValue, err := pullRequestURLLabel.Run()
-		if err != nil {
-			return fmt.Errorf("pull request URL prompt failed, please try re-running support command")
+		if err := pullRequestURLValidation(g.PullRequestURL); err != nil {
+			return err
 		}
+	}
 
-		log.Debugf("pull request url: %s", pullRequestURLValue)
+	return nil
+}
 
-		queryParams.Add(pullRequestURLParam, pullRequestURLValue)
+// pullRequestURLValidation validates urlstr matches expected formats.
+// This implements promptui.ValidateFunc.
+func pullRequestURLValidation(urlstr string) error {
+	_, err := url.ParseRequestURI(urlstr)
+	if err != nil {
+		return fmt.Errorf("please enter a valid url: including scheme, host, and path to pull request")
 	}
 
-	fmt.Printf("Create a support ticket by: \n"+
-		"\t1. Copying URL: %s\n"+
-		"\t2. Paste above URL in a browser\n"+
-		"\t3. Login with Red Hat SSO\n"+
-		"\t4. Enter an issue summary and description\n"+
-		"\t5. Preview and Submit your ticket\n", baseURL+queryParams.Encode())
+	url, err := url.Parse(urlstr)
+	if err != nil || url.Scheme == "" || url.Host == "" || url.Path == "" {
+		return fmt.Errorf("please enter a valid url: including scheme, host, and path to pull request")
+	}
+
+	return nil
+}
+
+// projectIDValidation validates id to ensure it conforms with expected formats.
+// This implements promptui.ValidateFunc.
+func projectIDValidation(id string) error {
+	if id == "" {
+		return errors.New("please enter a non empty project id")
+	}
+
+	isLegacy, _ := regexp.MatchString(`^p.*`, id)
+	if isLegacy {
+		return errors.New("please remove leading character p from project id")
+	}
+
+	isOSPID, _ := regexp.MatchString(`^ospid-.*`, id)
+	if isOSPID {
+		return errors.New("please remove leading characters ospid- from project id")
+	}
+
+	isAlphaNumeric := regexp.MustCompile(`^[a-zA-Z0-9]*$`).MatchString(id)
+	if !isAlphaNumeric {
+		return errors.New("please remove all special characters from project id")
+	}
+
+	return nil
+}
+
+func projectTypeValidation(s string) error {
+	if !(s == "operator" || s == "container") {
+		return fmt.Errorf(`the project type must be "operator" or "container, received %s"`, s)
+	}
 
 	return nil
 }
diff --git a/go.mod b/go.mod
index 9fc61a4..928df6b 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,6 @@ require (
 	github.com/docker/cli v20.10.12+incompatible
 	github.com/google/go-containerregistry v0.8.1-0.20220425181515-fc6ff852e45e
 	github.com/knqyf263/go-rpmdb v0.0.0-20201215100354-a9e3110d8ee1
-	github.com/manifoldco/promptui v0.9.0
 	github.com/onsi/ginkgo/v2 v2.1.3
 	github.com/onsi/gomega v1.18.1
 	github.com/openshift/api v0.0.0-20210910062324-a41d3573a3ba
@@ -30,7 +29,6 @@ require (
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/blang/semver/v4 v4.0.0 // indirect
 	github.com/cespare/xxhash/v2 v2.1.2 // indirect
-	github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
 	github.com/containerd/stargz-snapshotter/estargz v0.11.1 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/docker/distribution v2.8.0+incompatible // indirect
diff --git a/go.sum b/go.sum
index 3990493..9d3b666 100644
--- a/go.sum
+++ b/go.sum
@@ -145,11 +145,8 @@ github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cb
 github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/charithe/durationcheck v0.0.9/go.mod h1:SSbRIBVfMjCi/kEB6K65XEA83D6prSM8ap1UCpNKtgg=
 github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af/go.mod h1:Qjyv4H3//PWVzTeCezG2b9IRn6myJxJSr4TD/xo6ojU=
-github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
-github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
@@ -570,8 +567,6 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN
 github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
 github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
 github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
-github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
-github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
 github.com/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU=
 github.com/matoous/godox v0.0.0-20210227103229-6504466cf951/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
 github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
@@ -1109,7 +1104,6 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=


Test patch:
diff --git a/cmd/check_test.go b/cmd/check_test.go
index b60ebd9..71c16dc 100644
--- a/cmd/check_test.go
+++ b/cmd/check_test.go
@@ -13,17 +13,6 @@ import (
 )
 
 var _ = Describe("cmd package check command", func() {
-	Describe("Test Flags", func() {
-		Context("Docker Config", func() {
-			It("docker-config from Flags() should be nil, but PersistentFlags() should be set", func() {
-				expected := "/my/docker/config.json"
-				checkCmd.PersistentFlags().Set("docker-config", expected)
-				Expect(checkCmd.Flags().Lookup("docker-config")).To(BeNil())
-				Expect(checkCmd.PersistentFlags().Lookup("docker-config").Value.String()).To(Equal(expected))
-			})
-		})
-	})
-
 	DescribeTable("Checking overall pass/fail",
 		func(result bool, expected string) {
 			Expect(convertPassedOverall(result)).To(Equal(expected))
diff --git a/cmd/support_test.go b/cmd/support_test.go
new file mode 100644
index 0000000..aa61497
--- /dev/null
+++ b/cmd/support_test.go
@@ -0,0 +1,113 @@
+// go:test !race
+package cmd
+
+import (
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+)
+
+var _ = Describe("support command tests", func() {
+	Context("When running the support cobra command", func() {
+		Context("with valid inputs for a container project", func() {
+			It("should run without error", func() {
+				_, err := executeCommand(rootCmd, "support", "container", "000000000000")
+				Expect(err).ToNot(HaveOccurred())
+			})
+		})
+		Context("with valid inputs for an operator project", func() {
+			It("should run without error", func() {
+				_, err := executeCommand(rootCmd, "support", "operator", "000000000000", "https://github.com/example/pull/2")
+				Expect(err).ToNot(HaveOccurred())
+			})
+		})
+
+		Context("with an invalid project type", func() {
+			It("should throw an error", func() {
+				out, err := executeCommand(rootCmd, "support", "fooproject", "000000000000")
+				Expect(err).To(HaveOccurred())
+				Expect(out).To(ContainSubstring("the project type must be"))
+			})
+		})
+
+		Context("with an invalid project ID", func() {
+			It("should throw an error", func() {
+				out, err := executeCommand(rootCmd, "support", "container", "ospid-00000000")
+				Expect(err).To(HaveOccurred())
+				Expect(out).To(ContainSubstring("please remove leading characters ospid-"))
+			})
+		})
+
+		Context("with an operator project designation but no pull request url", func() {
+			It("should throw an error", func() {
+				out, err := executeCommand(rootCmd, "support", "operator", "000000000000")
+				Expect(err).To(HaveOccurred())
+				Expect(out).To(ContainSubstring("a pull request URL is required"))
+			})
+		})
+
+		Context("with an operator project designation but an invalid pull request url", func() {
+			It("should throw an error", func() {
+				out, err := executeCommand(rootCmd, "support", "operator", "000000000000", "github.com")
+				Expect(err).To(HaveOccurred())
+				Expect(out).To(ContainSubstring("please enter a valid url"))
+			})
+		})
+	})
+	Context("When validating a pull request URL", func() {
+		urlNoScheme := "example.com"
+		urlNoHost := "https:///foo"
+		urlNoPath := "https://example.com"
+		urlCorrect := "https://example.com/pull/example"
+
+		It("should fail when no scheme is provided", func() {
+			err := pullRequestURLValidation(urlNoScheme)
+			Expect(err).To(HaveOccurred())
+		})
+		It("should fail when no host is provided", func() {
+			err := pullRequestURLValidation(urlNoHost)
+			Expect(err).To(HaveOccurred())
+		})
+		It("should fail when no path is provided", func() {
+			err := pullRequestURLValidation(urlNoPath)
+			Expect(err).To(HaveOccurred())
+		})
+
+		It("should succeed when the url is in the proper format", func() {
+			err := pullRequestURLValidation(urlCorrect)
+			Expect(err).ToNot(HaveOccurred())
+		})
+	})
+
+	Context("When validating a project ID", func() {
+		projectIDEmpty := ""
+		projectIDStartWithP := "p1"
+		projectIDStartWithOSPID := "ospid-0000"
+		projectIDContainSpecialChar := "$c$"
+		projectIDCorrect := "000011112222"
+
+		It("should fail if the input is empty", func() {
+			err := projectIDValidation(projectIDEmpty)
+			Expect(err).To(HaveOccurred())
+		})
+
+		It("should fail if if the input contains a leading p", func() {
+			err := projectIDValidation(projectIDStartWithP)
+			Expect(err).To(HaveOccurred())
+		})
+
+		It("should fail if the input begins with ospid-", func() {
+			err := projectIDValidation(projectIDStartWithOSPID)
+			Expect(err).To(HaveOccurred())
+		})
+
+		It("should fail if the input has special characters", func() {
+			err := projectIDValidation(projectIDContainSpecialChar)
+			Expect(err).To(HaveOccurred())
+		})
+
+		It("should succeed if the format is correct", func() {
+			err := projectIDValidation(projectIDCorrect)
+			Expect(err).ToNot(HaveOccurred())
+		})
+	})
+})