Skip to content

Commit

Permalink
Added tests and validation of required select options.
Browse files Browse the repository at this point in the history
  • Loading branch information
kristofferahl committed Sep 11, 2021
1 parent 7968d43 commit 29b764f
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 14 deletions.
52 changes: 46 additions & 6 deletions cmd/centry/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/kristofferahl/go-centry/internal/pkg/cmd"
"github.com/kristofferahl/go-centry/internal/pkg/shell"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
)

Expand Down Expand Up @@ -98,12 +99,12 @@ func optionsSetToFlags(options *cmd.OptionsSet) []cli.Flag {
def = o.Default.(bool)
}
flags = append(flags, &cli.BoolFlag{
Name: o.Name,
Aliases: short,
Usage: o.Description,
Value: def,
// Required: o.Required, // NOTE: We currently do not support specifying select options as required
Hidden: o.Hidden,
Name: o.Name,
Aliases: short,
Usage: o.Description,
Value: def,
Required: false,
Hidden: o.Hidden,
})
case cmd.BoolOption:
def := false
Expand Down Expand Up @@ -181,3 +182,42 @@ func optionsSetToEnvVars(c *cli.Context, set *cmd.OptionsSet, prefix string) []s

return shell.SortEnvironmentVariables(envVars)
}

func validateOptionsSet(c *cli.Context, set *cmd.OptionsSet, cmdName string, level string, log *logrus.Entry) error {
selectOptions := make(map[string][]string)
selectOptionRequired := make(map[string]bool)
selectOptionSelected := make(map[string]string)

for _, o := range set.Sorted() {
o := o

switch o.Type {
case cmd.SelectOption:
group := o.EnvName
selectOptions[o.EnvName] = append(selectOptions[group], o.Name)
if o.Required {
selectOptionRequired[group] = true
}
v := c.String(o.Name)
log.Debugf("found select option %s (group=%s value=%v required=%v)\n", o.Name, group, v, o.Required)
if v == "true" {
selectOptionSelected[group] = o.Name
}
break
}
}

for group, _ := range selectOptions {
if selectOptionRequired[group] {
if option, ok := selectOptionSelected[group]; ok && option != "" {
log.Debugf("select option group %s was set by option %s", group, option)
} else {
cli.ShowCommandHelp(c, cmdName)
return fmt.Errorf("Required %s flag missing for select option group %s (one of \" %s \" must be provided)\n", level, group, strings.Join(selectOptions[group], " | "))
}
} else {
log.Debugf("select option group %s does not require a value", group)
}
}
return nil
}
26 changes: 21 additions & 5 deletions cmd/centry/runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,16 +249,32 @@ func TestMain(t *testing.T) {
})

g.Describe("invoke without required option", func() {
g.It("should fail with error message", func() {
out := execCentry("optiontest required", false, "test/data/runtime_test.yaml")
test.AssertStringContains(g, out.Stderr, "level=error msg=\"Required flag \\\"abc\\\" not set\"")
g.Describe("of type string", func() {
g.It("should fail with error message", func() {
out := execCentry("optiontest required --boolopt --selectopt1", false, "test/data/runtime_test.yaml")
test.AssertStringContains(g, out.Stderr, "level=error msg=\"Required flag \\\"stringopt\\\" not set\"")
})
})
g.Describe("of type bool", func() {
g.It("should fail with error message", func() {
out := execCentry("optiontest required --stringopt=foo --selectopt1", false, "test/data/runtime_test.yaml")
test.AssertStringContains(g, out.Stderr, "level=error msg=\"Required flag \\\"boolopt\\\" not set\"")
})
})
g.Describe("of type select", func() {
g.It("should fail with error message", func() {
out := execCentry("optiontest required --stringopt=foo --boolopt", false, "test/data/runtime_test.yaml")
test.AssertStringContains(g, out.Stderr, "level=error msg=\"Required command flag missing for select option group SELECT (one of \\\" selectopt1 | selectopt2 \\\" must be provided)")
})
})
})

g.Describe("invoke with required option", func() {
g.It("should pass", func() {
out := execCentry("optiontest required --abc=foo", false, "test/data/runtime_test.yaml")
test.AssertStringHasKeyValue(g, out.Stdout, "ABC", "foo")
out := execCentry("optiontest required --stringopt=foo --boolopt --selectopt1", false, "test/data/runtime_test.yaml")
test.AssertStringHasKeyValue(g, out.Stdout, "STRINGOPT", "foo")
test.AssertStringHasKeyValue(g, out.Stdout, "BOOLOPT", "true")
test.AssertStringHasKeyValue(g, out.Stdout, "SELECTOPT", "selectopt1")
})
})
})
Expand Down
15 changes: 15 additions & 0 deletions cmd/centry/script.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ func (sc *ScriptCommand) ToCLICommand() *cli.Command {
UsageText: sc.Command.Help,
Hidden: cmdHidden,
Action: func(c *cli.Context) error {
err := validateOptions(c, sc, cmdName)
if err != nil {
return err
}

ec := sc.Run(c, c.Args().Slice())
if ec > 0 {
return cli.Exit("Command exited with non zero exit code", ec)
Expand Down Expand Up @@ -86,6 +91,16 @@ func (sc *ScriptCommand) Run(c *cli.Context, args []string) int {
return 0
}

func validateOptions(c *cli.Context, sc *ScriptCommand, cmdName string) error {
if err := validateOptionsSet(c, sc.GlobalOptions, cmdName, "global", sc.Log.WithField("option-valiation", "global")); err != nil {
return err
}
if err := validateOptionsSet(c, sc.Function.Options, cmdName, "command", sc.Log.WithField("option-valiation", "command")); err != nil {
return err
}
return nil
}

func generateBashSource(c *cli.Context, sc *ScriptCommand, args []string) string {
source := []string{}
source = append(source, "#!/usr/bin/env bash")
Expand Down
6 changes: 5 additions & 1 deletion examples/centry/centry.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,28 @@ options:
- name: ops
type: select
env_name: CONTEXT
required: true
description: Set the context to ops (operations)
annotations:
centry.api/serve: "true"

- name: dev
type: select
env_name: CONTEXT
required: true
description: Set the context to dev (development)
annotations:
centry.api/serve: "false"

- name: qa
type: select
env_name: CONTEXT
required: true
description: Set the context to qa (quality assurance)

- name: prod
type: select
required: true
env_name: CONTEXT
description: Set the context to prod (production)

Expand All @@ -78,7 +82,7 @@ config:
version: 1.0.0 # NOTE: This can also be set from an environment variable
log:
level: info
prefix: '[centry] '
prefix: "[centry] "
# environmentPrefix: MY_PREFIX_ # NOTE: This can be set to alter the naming of environment variables (internal environment variables stay the same)
hideInternalCommands: true # default: true
hideInternalOptions: true # default: true
10 changes: 10 additions & 0 deletions examples/centry/commands/get.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,13 @@ get:required() {
echo "This subcommand has a required option"
env | sort
}

# centry.cmd[get:selected].option[abc]/type=select
# centry.cmd[get:selected].option[abc]/envName=SELECTED
# centry.cmd[get:selected].option[abc]/required=true
# centry.cmd[get:selected].option[def]/type=select
# centry.cmd[get:selected].option[def]/envName=SELECTED
# centry.cmd[get:selected].option[def]/required=true
get:selected() {
echo "The selected value was ${SELECTED:?}"
}
12 changes: 10 additions & 2 deletions test/data/commands/option_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,16 @@ optiontest:noop() {
return 0
}

# centry.cmd[optiontest:required].option[abc]/required=true
# centry.cmd[optiontest:required].option[def]/required=false
# centry.cmd[optiontest:required].option[stringopt]/required=true
# centry.cmd[optiontest:required].option[boolopt]/type=bool
# centry.cmd[optiontest:required].option[boolopt]/required=true
# centry.cmd[optiontest:required].option[selectopt1]/required=true
# centry.cmd[optiontest:required].option[selectopt1]/type=select
# centry.cmd[optiontest:required].option[selectopt1]/envName=SELECT
# centry.cmd[optiontest:required].option[selectopt2]/type=bool
# centry.cmd[optiontest:required].option[selectopt2]/type=select
# centry.cmd[optiontest:required].option[selectopt2]/envName=SELECT
# centry.cmd[optiontest:required].option[notrequired]/required=false
optiontest:required() {
echo "This command should not run without required options specified..."
env | sort
Expand Down

0 comments on commit 29b764f

Please sign in to comment.