func main() { application := console.NewApplication("eidolon/console", "0.1.0") application.Logo = ` # ### ## ######## ### ####### ####### ### ####### ### ## ### ## ## ### ## #### ## ####### ### ### ## ## ## ### ## ## ####### ### ### ### ## ## ## ### ## ## ### ### ####### ### ###### ##### ####### ##### ### ## # ` application.AddCommand(console.Command{ Name: "greet:example", Description: "Greet's the given user, or the world.", Help: "You don't have to specify a name.", Configure: func(definition *console.Definition) { definition.AddOption( parameters.NewStringValue(&name), "-n, --name=NAME", "Provide a name for the greeting.", ) definition.AddArgument( parameters.NewIntValue(&favNum), "FAVOURITE_NUMBER", "Provide your favourite number.", ) }, Execute: func(input *console.Input, output *console.Output) error { output.Printf("Hello, %s!\n", name) output.Printf("Your favourite number is %d.\n", favNum) return nil }, }) code := application.Run(os.Args[1:]) os.Exit(code) }
func TestMapInput(t *testing.T) { createInput := func(params []string) *console.Input { return console.ParseInput(params) } t.Run("should map arguments to their reference values", func(t *testing.T) { var s1 string var s2 string assert.Equal(t, "", s1) assert.Equal(t, "", s2) input := createInput([]string{"hello", "world"}) definition := console.NewDefinition() definition.AddArgument(parameters.NewStringValue(&s1), "S1", "") definition.AddArgument(parameters.NewStringValue(&s2), "S2", "") err := console.MapInput(definition, input) assert.OK(t, err) assert.Equal(t, "hello", s1) assert.Equal(t, "world", s2) }) t.Run("should error parsing arguments with invalid values", func(t *testing.T) { var i1 int assert.Equal(t, 0, i1) input := createInput([]string{"foo"}) definition := console.NewDefinition() definition.AddArgument(parameters.NewIntValue(&i1), "I1", "") err := console.MapInput(definition, input) assert.NotOK(t, err) }) t.Run("should error when required arguments are missing from input", func(t *testing.T) { var s1 string var s2 string assert.Equal(t, "", s1) assert.Equal(t, "", s2) input := createInput([]string{"foo"}) definition := console.NewDefinition() definition.AddArgument(parameters.NewStringValue(&s1), "S1", "") definition.AddArgument(parameters.NewStringValue(&s1), "S2", "") err := console.MapInput(definition, input) assert.NotOK(t, err) }) t.Run("should not error when optional arguments are missing from input", func(t *testing.T) { var s1 string var s2 string assert.Equal(t, "", s1) assert.Equal(t, "", s2) input := createInput([]string{"foo"}) definition := console.NewDefinition() definition.AddArgument(parameters.NewStringValue(&s1), "S1", "") definition.AddArgument(parameters.NewStringValue(&s1), "[S2]", "") err := console.MapInput(definition, input) assert.OK(t, err) assert.Equal(t, "foo", s1) assert.Equal(t, "", s2) }) t.Run("should map short options to their reference values", func(t *testing.T) { var s1 string var s2 string assert.Equal(t, "", s1) assert.Equal(t, "", s2) input := createInput([]string{"-a=foo", "-b=bar"}) definition := console.NewDefinition() definition.AddOption(parameters.NewStringValue(&s1), "-a=S1", "") definition.AddOption(parameters.NewStringValue(&s2), "-b=S2", "") err := console.MapInput(definition, input) assert.OK(t, err) assert.Equal(t, "foo", s1) assert.Equal(t, "bar", s2) }) t.Run("should map long options to their reference values", func(t *testing.T) { var s1 string var s2 string assert.Equal(t, "", s1) assert.Equal(t, "", s2) input := createInput([]string{"--foo=bar", "--baz=qux"}) definition := console.NewDefinition() definition.AddOption(parameters.NewStringValue(&s1), "--foo=S1", "") definition.AddOption(parameters.NewStringValue(&s2), "--baz=S2", "") err := console.MapInput(definition, input) assert.OK(t, err) assert.Equal(t, "bar", s1) assert.Equal(t, "qux", s2) }) t.Run("should ignore options that don't exist in the definition", func(t *testing.T) { var s2 string input := createInput([]string{"--foo=bar"}) definition := console.NewDefinition() definition.AddOption(parameters.NewStringValue(&s2), "--baz=S2", "") err := console.MapInput(definition, input) assert.OK(t, err) }) t.Run("should error parsing an option that requires a value with no value", func(t *testing.T) { var s1 string input := createInput([]string{"--foo"}) definition := console.NewDefinition() definition.AddOption(parameters.NewStringValue(&s1), "--foo=s1", "") err := console.MapInput(definition, input) assert.NotOK(t, err) }) t.Run("should not error parsing an option that doesn't require a value", func(t *testing.T) { var s1 string input := createInput([]string{"--foo"}) definition := console.NewDefinition() definition.AddOption(parameters.NewStringValue(&s1), "--foo=s1", "") err := console.MapInput(definition, input) assert.NotOK(t, err) }) t.Run("should set flag option values where applicable", func(t *testing.T) { var b1 bool assert.Equal(t, false, b1) input := createInput([]string{"--foo"}) definition := console.NewDefinition() definition.AddOption(parameters.NewBoolValue(&b1), "--foo", "") err := console.MapInput(definition, input) assert.OK(t, err) assert.Equal(t, true, b1) }) t.Run("should error parsing options with invalid values", func(t *testing.T) { var i1 int assert.Equal(t, 0, i1) input := createInput([]string{"--foo=hello"}) definition := console.NewDefinition() definition.AddOption(parameters.NewIntValue(&i1), "--foo=I1", "") err := console.MapInput(definition, input) assert.NotOK(t, err) }) }
func TestIntValue(t *testing.T) { t.Run("NewIntValue()", func(t *testing.T) { intRef := 3 intValue := parameters.NewIntValue(&intRef) assert.Equal(t, "3", intValue.String()) }) t.Run("Set()", func(t *testing.T) { t.Run("should not error for valid values", func(t *testing.T) { var value parameters.Float64Value valid := []string{ "3", "10", "25", "100000", } for _, item := range valid { err := value.Set(item) assert.OK(t, err) } }) t.Run("should error for invalid values", func(t *testing.T) { var value parameters.IntValue invalid := []string{ "", "Hello, World!", "Three point one four", "92233720368547758070", } for _, item := range invalid { err := value.Set(item) assert.NotOK(t, err) } }) t.Run("should modify the int that it references", func(t *testing.T) { ref := 5 value := parameters.NewIntValue(&ref) assert.Equal(t, 5, ref) value.Set("10") assert.Equal(t, 10, ref) value.Set("25") assert.Equal(t, 25, ref) }) }) t.Run("String()", func(t *testing.T) { inOut := map[string]string{ "3": "3", "10": "10", "1000": "1000", } for in, expected := range inOut { value := new(parameters.IntValue) value.Set(in) actual := value.String() assert.Equal(t, expected, actual) } }) }
func TestApplication(t *testing.T) { createApplication := func(writer io.Writer) *console.Application { application := console.NewApplication("eidolon/console", "1.2.3.+testing") application.Writer = writer return application } createTestCommand := func(a *string, b *int) console.Command { return console.Command{ Name: "test", Configure: func(definition *console.Definition) { definition.AddArgument(parameters.NewStringValue(a), "STRINGARG", "") definition.AddOption(parameters.NewIntValue(b), "--int-opt=VALUE", "") }, Execute: func(input *console.Input, output *console.Output) error { output.Printf("STRINGARG = %s", *a) output.Printf("--int-opt = %v", *b) return nil }, } } t.Run("Run()", func(t *testing.T) { t.Run("should return exit code 2 if no command was asked for", func(t *testing.T) { writer := bytes.Buffer{} application := createApplication(&writer) code := application.Run([]string{}) assert.Equal(t, 100, code) }) t.Run("should return exit code 2 if no command was found", func(t *testing.T) { writer := bytes.Buffer{} application := createApplication(&writer) code := application.Run([]string{"foo"}) assert.Equal(t, 100, code) }) t.Run("should return exit code 100 if the help flag is set", func(t *testing.T) { writer := bytes.Buffer{} application := createApplication(&writer) code := application.Run([]string{"--help"}) assert.Equal(t, 100, code) }) t.Run("should show application help if the help flag is set", func(t *testing.T) { writer := bytes.Buffer{} application := createApplication(&writer) application.Run([]string{"--help"}) output := writer.String() containsUsage := strings.Contains(output, "USAGE:") containsArguments := strings.Contains(output, "ARGUMENTS:") containsOptions := strings.Contains(output, "OPTIONS:") containsHelp := containsUsage && containsOptions && !containsArguments assert.True(t, containsHelp, "Expected help output.") }) t.Run("should show command help if the help flag is set when running a command", func(t *testing.T) { var a string var b int writer := bytes.Buffer{} application := createApplication(&writer) application.AddCommand(createTestCommand(&a, &b)) application.Run([]string{"test", "--help"}) output := writer.String() containsUsage := strings.Contains(output, "USAGE:") containsArguments := strings.Contains(output, "ARGUMENTS:") containsOptions := strings.Contains(output, "OPTIONS:") containsHelp := containsUsage && containsOptions && containsArguments assert.True(t, containsHelp, "Expected help output.") }) t.Run("should return exit code 0 if a command was found, and ran OK", func(t *testing.T) { var a string var b int writer := bytes.Buffer{} application := createApplication(&writer) application.AddCommand(createTestCommand(&a, &b)) code := application.Run([]string{"test", "aval", "--int-opt=384"}) assert.Equal(t, 0, code) }) t.Run("should return exit code 101 if mapping input fails", func(t *testing.T) { var a string var b int writer := bytes.Buffer{} application := createApplication(&writer) application.AddCommand(createTestCommand(&a, &b)) code := application.Run([]string{"test", "aval", "--int-opt=hello"}) assert.Equal(t, 101, code) }) t.Run("should return exit code 102 if the command execution fails", func(t *testing.T) { writer := bytes.Buffer{} application := createApplication(&writer) application.AddCommand(console.Command{ Name: "test", Execute: func(input *console.Input, output *console.Output) error { return errors.New("Testing errors") }, }) code := application.Run([]string{"test", "aval", "--int-opt=hello"}) assert.Equal(t, 102, code) }) t.Run("should configure the application definition", func(t *testing.T) { var a string var b int var foo string writer := bytes.Buffer{} application := createApplication(&writer) application.Configure = func(definition *console.Definition) { definition.AddOption(parameters.NewStringValue(&foo), "--foo=FOO", "") } application.AddCommand(createTestCommand(&a, &b)) application.Run([]string{"test", "aval", "--foo=bar"}) assert.Equal(t, "bar", foo) }) }) t.Run("AddCommands()", func(t *testing.T) { t.Run("should work when adding 1 command", func(t *testing.T) { writer := bytes.Buffer{} application := createApplication(&writer) assert.Equal(t, 0, len(application.Commands)) application.AddCommands([]console.Command{ { Name: "test1", }, }) assert.Equal(t, 1, len(application.Commands)) }) t.Run("should work when adding no commands", func(t *testing.T) { writer := bytes.Buffer{} application := createApplication(&writer) assert.Equal(t, 0, len(application.Commands)) application.AddCommands([]console.Command{}) assert.Equal(t, 0, len(application.Commands)) }) t.Run("should work when adding more than 1 command", func(t *testing.T) { writer := bytes.Buffer{} application := createApplication(&writer) assert.Equal(t, 0, len(application.Commands)) application.AddCommands([]console.Command{ { Name: "test1", }, { Name: "test2", }, { Name: "test3", }, }) assert.Equal(t, 3, len(application.Commands)) }) }) t.Run("AddCommand()", func(t *testing.T) { writer := bytes.Buffer{} application := createApplication(&writer) assert.Equal(t, 0, len(application.Commands)) application.AddCommand(console.Command{ Name: "test1", }) assert.Equal(t, 1, len(application.Commands)) }) }