diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 34a70b5..703e08f 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -18,6 +18,7 @@ import ( var ( ErrEnvVarMissing = errors.New("required environment variable is missing") ErrEnvVarInvalidFormat = errors.New("required environment variable is malformed") + ErrAPIEndpointUnknown = errors.New("unknown api endpoint") ) var ( @@ -59,8 +60,6 @@ func ConfigureLogger(logLevel string, logTarget io.Writer) (context.Context, *sl return appContext, l, nil } -var ErrAPIEndpointUnknown = errors.New("unknown api endpoint") - // ResolveAPIEndpoint parses a referenceString for known endpoint abbreviations // possible catalog environments, and returns the APIEndpoint that corresponds. // Common use is parsing a CLI environment flag value. diff --git a/internal/cli/cli_suite_test.go b/internal/cli/cli_suite_test.go new file mode 100644 index 0000000..02b35de --- /dev/null +++ b/internal/cli/cli_suite_test.go @@ -0,0 +1,13 @@ +package cli_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestCli(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Cli Suite") +} diff --git a/internal/cli/cli_test.go b/internal/cli/cli_test.go new file mode 100644 index 0000000..290e020 --- /dev/null +++ b/internal/cli/cli_test.go @@ -0,0 +1,139 @@ +package cli_test + +import ( + "bytes" + "fmt" + "io" + "os" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/opdev/productctl/internal/catalogapi" + "github.com/opdev/productctl/internal/cli" + "github.com/opdev/productctl/internal/logger" +) + +var _ = Describe("CLI", func() { + When("ensuring the environment", func() { + var ( + expectedToken string + expectedOrgID string + ) + + AfterEach(func() { + os.Setenv(cli.EnvAPIToken, "") + os.Setenv(cli.EnvOrgID, "") + }) + + When("the caller sets the expected environment variables", func() { + BeforeEach(func() { + expectedToken = "foo" + expectedOrgID = "1234" + os.Setenv(cli.EnvAPIToken, expectedToken) + os.Setenv(cli.EnvOrgID, expectedOrgID) + }) + It("should return the orgID as an integer equivalent of the string input", func() { + resolvedOrgID, _, err := cli.EnsureEnv() + Expect(err).ToNot(HaveOccurred()) + Expect(expectedOrgID).To(BeEquivalentTo(fmt.Sprintf("%d", resolvedOrgID))) + }) + It("should return the token value from the environment", func() { + _, resolvedToken, err := cli.EnsureEnv() + Expect(err).ToNot(HaveOccurred()) + Expect(resolvedToken).To(Equal(expectedToken)) + }) + + When("the orgID format is not a valid integer", func() { + // The catalog API expects OrgID to be an integer type, but + // environment variables are always strings, so we do the + // conversion and throw an error if it doesn't succeed. + // + // This also implies that OrgIDs can't lead with 0 because + // integer conversion would drop that. Therefore, we assume + // OrgIDs can never have a leading 0. + BeforeEach(func() { + expectedOrgID = "abcd" + os.Setenv(cli.EnvOrgID, expectedOrgID) + }) + It("should throw an error indicating the value is malformed", func() { + _, _, err := cli.EnsureEnv() + Expect(err).To(MatchError(cli.ErrEnvVarInvalidFormat)) + }) + }) + }) + + When("the caller is missing the token", func() { + BeforeEach(func() { + os.Setenv(cli.EnvAPIToken, "") + os.Setenv(cli.EnvOrgID, "1234") + }) + It("should throw the expected error when the token is missing", func() { + _, _, err := cli.EnsureEnv() + Expect(err).To(MatchError(cli.ErrEnvVarMissing)) + }) + }) + + When("the caller is missing the orgID", func() { + BeforeEach(func() { + os.Setenv(cli.EnvAPIToken, "foo") + os.Setenv(cli.EnvOrgID, "") + }) + It("should throw the expected error when the org ID is missing", func() { + _, _, err := cli.EnsureEnv() + Expect(err).To(MatchError(cli.ErrEnvVarMissing)) + }) + }) + }) + + When("configuring the logger", func() { + var ( + loglevel string + logTarget *bytes.Buffer + ) + + BeforeEach(func() { + loglevel = "info" + logTarget = bytes.NewBuffer([]byte{}) + }) + + It("should return a context containing the logger", func() { + ctx, L, err := cli.ConfigureLogger(loglevel, logTarget) + Expect(err).ToNot(HaveOccurred()) + Expect(ctx).ToNot(BeNil()) + loggerFromContext, err := logger.FromContext(ctx) + Expect(err).ToNot(HaveOccurred()) + Expect(loggerFromContext).To(Equal(L)) + }) + It("should write to the provided io.Writer", func() { + _, L, err := cli.ConfigureLogger(loglevel, logTarget) + Expect(err).ToNot(HaveOccurred()) + msg := "hello from test case" + L.Info(msg) + b, err := io.ReadAll(logTarget) + Expect(err).ToNot(HaveOccurred()) + Expect(string(b)).To(ContainSubstring(msg)) + }) + }) + + When("resolving catalog API endpoints", func() { + DescribeTable("given known shortnames", + func(endpointShortname string, expectedEndpoint catalogapi.APIEndpoint) { + resolved, err := cli.ResolveAPIEndpoint(endpointShortname) + Expect(err).ToNot(HaveOccurred()) + Expect(resolved).To(Equal(expectedEndpoint)) + }, + Entry("specifying prod", "prod", catalogapi.EndpointProduction), + Entry("specifying stage", "stage", catalogapi.EndpointStage), + Entry("specifying uat", "uat", catalogapi.EndpointUAT), + Entry("specifying qa", "qa", catalogapi.EndpointQA), + ) + + When("the user provides an unknown shortname", func() { + It("should throw an error", func() { + _, err := cli.ResolveAPIEndpoint("foo") + Expect(err).To(MatchError(cli.ErrAPIEndpointUnknown)) + }) + }) + }) +})