mirror of
https://github.com/aykhans/dodo.git
synced 2025-09-01 00:53:34 +00:00
759 lines
22 KiB
Go
759 lines
22 KiB
Go
package config
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"net/url"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/aykhans/dodo/pkg/types"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestNewConfigCLIParser(t *testing.T) {
|
|
t.Run("NewConfigCLIParser with valid args", func(t *testing.T) {
|
|
args := []string{"dodo", "-u", "https://example.com"}
|
|
parser := NewConfigCLIParser(args)
|
|
|
|
require.NotNil(t, parser)
|
|
assert.Equal(t, args, parser.args)
|
|
})
|
|
|
|
t.Run("NewConfigCLIParser with nil args", func(t *testing.T) {
|
|
parser := NewConfigCLIParser(nil)
|
|
|
|
require.NotNil(t, parser)
|
|
assert.Equal(t, []string{}, parser.args)
|
|
})
|
|
|
|
t.Run("NewConfigCLIParser with empty args", func(t *testing.T) {
|
|
args := []string{}
|
|
parser := NewConfigCLIParser(args)
|
|
|
|
require.NotNil(t, parser)
|
|
assert.Equal(t, args, parser.args)
|
|
})
|
|
}
|
|
|
|
func TestStringSliceArg(t *testing.T) {
|
|
t.Run("stringSliceArg String method", func(t *testing.T) {
|
|
arg := stringSliceArg{"value1", "value2", "value3"}
|
|
assert.Equal(t, "value1,value2,value3", arg.String())
|
|
})
|
|
|
|
t.Run("stringSliceArg String with empty slice", func(t *testing.T) {
|
|
arg := stringSliceArg{}
|
|
assert.Empty(t, arg.String())
|
|
})
|
|
|
|
t.Run("stringSliceArg String with single value", func(t *testing.T) {
|
|
arg := stringSliceArg{"single"}
|
|
assert.Equal(t, "single", arg.String())
|
|
})
|
|
|
|
t.Run("stringSliceArg Set method", func(t *testing.T) {
|
|
arg := &stringSliceArg{}
|
|
|
|
err := arg.Set("first")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, stringSliceArg{"first"}, *arg)
|
|
|
|
err = arg.Set("second")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, stringSliceArg{"first", "second"}, *arg)
|
|
})
|
|
|
|
t.Run("stringSliceArg Set with empty string", func(t *testing.T) {
|
|
arg := &stringSliceArg{}
|
|
|
|
err := arg.Set("")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, stringSliceArg{""}, *arg)
|
|
})
|
|
}
|
|
|
|
func TestConfigCLIParser_Parse(t *testing.T) {
|
|
t.Run("Parse with no arguments returns ErrCLINoArgs", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo"})
|
|
config, err := parser.Parse()
|
|
|
|
assert.Nil(t, config)
|
|
require.ErrorIs(t, err, types.ErrCLINoArgs)
|
|
})
|
|
|
|
t.Run("Parse with unexpected arguments returns CLIUnexpectedArgsError", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "unexpected", "args"})
|
|
config, err := parser.Parse()
|
|
|
|
assert.Nil(t, config)
|
|
var cliErr types.CLIUnexpectedArgsError
|
|
require.ErrorAs(t, err, &cliErr)
|
|
assert.Equal(t, []string{"unexpected", "args"}, cliErr.Args)
|
|
})
|
|
|
|
t.Run("Parse with valid URL", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-u", "https://example.com"})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
require.NotNil(t, config.URL)
|
|
assert.Equal(t, "https://example.com", config.URL.String())
|
|
})
|
|
|
|
t.Run("Parse with invalid URL returns FieldParseErrors", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-u", "://invalid-url"})
|
|
config, err := parser.Parse()
|
|
|
|
assert.Nil(t, config)
|
|
var fieldErr types.FieldParseErrors
|
|
require.ErrorAs(t, err, &fieldErr)
|
|
assert.Len(t, fieldErr.Errors, 1)
|
|
assert.Equal(t, "url", fieldErr.Errors[0].Field)
|
|
assert.Equal(t, "://invalid-url", fieldErr.Errors[0].Value)
|
|
})
|
|
|
|
t.Run("Parse with method flag", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-m", "POST"})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
require.NotNil(t, config.Method)
|
|
assert.Equal(t, "POST", *config.Method)
|
|
})
|
|
|
|
t.Run("Parse with yes flag", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-y"})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
require.NotNil(t, config.Yes)
|
|
assert.True(t, *config.Yes)
|
|
})
|
|
|
|
t.Run("Parse with skip-verify flag", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-skip-verify"})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
require.NotNil(t, config.SkipVerify)
|
|
assert.True(t, *config.SkipVerify)
|
|
})
|
|
|
|
t.Run("Parse with dodos count", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-d", "5"})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
require.NotNil(t, config.DodosCount)
|
|
assert.Equal(t, uint(5), *config.DodosCount)
|
|
})
|
|
|
|
t.Run("Parse with request count", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-r", "1000"})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
require.NotNil(t, config.RequestCount)
|
|
assert.Equal(t, uint(1000), *config.RequestCount)
|
|
})
|
|
|
|
t.Run("Parse with duration", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-o", "5m"})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
require.NotNil(t, config.Duration)
|
|
assert.Equal(t, 5*time.Minute, *config.Duration)
|
|
})
|
|
|
|
t.Run("Parse with timeout", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-t", "30s"})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
require.NotNil(t, config.Timeout)
|
|
assert.Equal(t, 30*time.Second, *config.Timeout)
|
|
})
|
|
|
|
t.Run("Parse with parameters", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-p", "key1=value1", "-p", "key2=value2"})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
assert.Len(t, config.Params, 2)
|
|
assert.Equal(t, "key1", config.Params[0].Key)
|
|
assert.Equal(t, []string{"value1"}, config.Params[0].Value)
|
|
assert.Equal(t, "key2", config.Params[1].Key)
|
|
assert.Equal(t, []string{"value2"}, config.Params[1].Value)
|
|
})
|
|
|
|
t.Run("Parse with headers", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-H", "Content-Type: application/json", "-H", "Authorization: Bearer token"})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
assert.Len(t, config.Headers, 2)
|
|
assert.Equal(t, "Content-Type", config.Headers[0].Key)
|
|
assert.Equal(t, []string{"application/json"}, config.Headers[0].Value)
|
|
assert.Equal(t, "Authorization", config.Headers[1].Key)
|
|
assert.Equal(t, []string{"Bearer token"}, config.Headers[1].Value)
|
|
})
|
|
|
|
t.Run("Parse with cookies", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-c", "session=abc123", "-c", "user=john"})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
assert.Len(t, config.Cookies, 2)
|
|
assert.Equal(t, "session", config.Cookies[0].Key)
|
|
assert.Equal(t, []string{"abc123"}, config.Cookies[0].Value)
|
|
assert.Equal(t, "user", config.Cookies[1].Key)
|
|
assert.Equal(t, []string{"john"}, config.Cookies[1].Value)
|
|
})
|
|
|
|
t.Run("Parse with bodies", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-b", "body1", "-b", "body2"})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
assert.Len(t, config.Bodies, 2)
|
|
assert.Equal(t, types.Body("body1"), config.Bodies[0])
|
|
assert.Equal(t, types.Body("body2"), config.Bodies[1])
|
|
})
|
|
|
|
t.Run("Parse with valid proxies", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-x", "http://proxy1.example.com:8080", "-x", "socks5://proxy2.example.com:1080"})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
assert.Len(t, config.Proxies, 2)
|
|
assert.Equal(t, "http://proxy1.example.com:8080", config.Proxies[0].String())
|
|
assert.Equal(t, "socks5://proxy2.example.com:1080", config.Proxies[1].String())
|
|
})
|
|
|
|
t.Run("Parse with invalid proxy returns FieldParseErrors", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-x", "://invalid-proxy"})
|
|
config, err := parser.Parse()
|
|
|
|
assert.Nil(t, config)
|
|
var fieldErr types.FieldParseErrors
|
|
require.ErrorAs(t, err, &fieldErr)
|
|
assert.Len(t, fieldErr.Errors, 1)
|
|
assert.Equal(t, "proxy[0]", fieldErr.Errors[0].Field)
|
|
assert.Equal(t, "://invalid-proxy", fieldErr.Errors[0].Value)
|
|
})
|
|
|
|
t.Run("Parse with mixed valid and invalid proxies", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-x", "http://valid.example.com:8080", "-x", "://invalid"})
|
|
config, err := parser.Parse()
|
|
|
|
assert.Nil(t, config)
|
|
var fieldErr types.FieldParseErrors
|
|
require.ErrorAs(t, err, &fieldErr)
|
|
assert.Len(t, fieldErr.Errors, 1)
|
|
assert.Equal(t, "proxy[1]", fieldErr.Errors[0].Field)
|
|
assert.Equal(t, "://invalid", fieldErr.Errors[0].Value)
|
|
})
|
|
|
|
t.Run("Parse with long flag names", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{
|
|
"dodo",
|
|
"--url", "https://example.com",
|
|
"--method", "POST",
|
|
"--yes",
|
|
"--skip-verify",
|
|
"--dodos", "3",
|
|
"--requests", "500",
|
|
"--duration", "1m",
|
|
"--timeout", "10s",
|
|
})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
|
|
assert.Equal(t, "https://example.com", config.URL.String())
|
|
assert.Equal(t, "POST", *config.Method)
|
|
assert.True(t, *config.Yes)
|
|
assert.True(t, *config.SkipVerify)
|
|
assert.Equal(t, uint(3), *config.DodosCount)
|
|
assert.Equal(t, uint(500), *config.RequestCount)
|
|
assert.Equal(t, time.Minute, *config.Duration)
|
|
assert.Equal(t, 10*time.Second, *config.Timeout)
|
|
})
|
|
|
|
t.Run("Parse with config-file flag valid YAML", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-f", "/path/to/config.yaml"})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
assert.Len(t, config.Files, 1)
|
|
})
|
|
|
|
t.Run("Parse with config-file flag using long form", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "--config-file", "/path/to/config.yml"})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
assert.Len(t, config.Files, 1)
|
|
})
|
|
|
|
t.Run("Parse with config-file flag invalid extension", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-f", "/path/to/config"})
|
|
config, err := parser.Parse()
|
|
|
|
assert.Nil(t, config)
|
|
var fieldErr types.FieldParseErrors
|
|
require.ErrorAs(t, err, &fieldErr)
|
|
assert.Len(t, fieldErr.Errors, 1)
|
|
assert.Equal(t, "config-file[0]", fieldErr.Errors[0].Field)
|
|
assert.Equal(t, "/path/to/config", fieldErr.Errors[0].Value)
|
|
assert.Contains(t, fieldErr.Errors[0].Err.Error(), "file extension not found")
|
|
})
|
|
|
|
t.Run("Parse with config-file flag unsupported file type", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-f", "/path/to/config.json"})
|
|
config, err := parser.Parse()
|
|
|
|
assert.Nil(t, config)
|
|
var fieldErr types.FieldParseErrors
|
|
require.ErrorAs(t, err, &fieldErr)
|
|
assert.Len(t, fieldErr.Errors, 1)
|
|
assert.Equal(t, "config-file[0]", fieldErr.Errors[0].Field)
|
|
assert.Equal(t, "/path/to/config.json", fieldErr.Errors[0].Value)
|
|
assert.Contains(t, fieldErr.Errors[0].Err.Error(), "file type")
|
|
assert.Contains(t, fieldErr.Errors[0].Err.Error(), "not supported")
|
|
})
|
|
|
|
t.Run("Parse with config-file flag remote URL", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-f", "https://example.com/config.yaml"})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
assert.Len(t, config.Files, 1)
|
|
})
|
|
|
|
t.Run("Parse with multiple config files", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-f", "/path/config1.yaml", "-f", "/path/config2.yml"})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
assert.Len(t, config.Files, 2)
|
|
})
|
|
|
|
t.Run("Parse with all flags combined", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{
|
|
"dodo",
|
|
"-u", "https://api.example.com/test",
|
|
"-m", "PUT",
|
|
"-y",
|
|
"-skip-verify",
|
|
"-d", "10",
|
|
"-r", "2000",
|
|
"-o", "30m",
|
|
"-t", "5s",
|
|
"-p", "apikey=123",
|
|
"-H", "Content-Type: application/json",
|
|
"-c", "session=token123",
|
|
"-b", `{"data": "test"}`,
|
|
"-x", "http://proxy.example.com:3128",
|
|
"-f", "/path/to/config.yaml",
|
|
})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
|
|
// Verify all fields are set correctly
|
|
assert.Equal(t, "https://api.example.com/test", config.URL.String())
|
|
assert.Equal(t, "PUT", *config.Method)
|
|
assert.True(t, *config.Yes)
|
|
assert.True(t, *config.SkipVerify)
|
|
assert.Equal(t, uint(10), *config.DodosCount)
|
|
assert.Equal(t, uint(2000), *config.RequestCount)
|
|
assert.Equal(t, 30*time.Minute, *config.Duration)
|
|
assert.Equal(t, 5*time.Second, *config.Timeout)
|
|
|
|
assert.Len(t, config.Params, 1)
|
|
assert.Equal(t, "apikey", config.Params[0].Key)
|
|
|
|
assert.Len(t, config.Headers, 1)
|
|
assert.Equal(t, "Content-Type", config.Headers[0].Key)
|
|
|
|
assert.Len(t, config.Cookies, 1)
|
|
assert.Equal(t, "session", config.Cookies[0].Key)
|
|
|
|
assert.Len(t, config.Bodies, 1)
|
|
assert.Equal(t, types.Body(`{"data": "test"}`), config.Bodies[0]) //nolint:testifylint
|
|
|
|
assert.Len(t, config.Proxies, 1)
|
|
assert.Equal(t, "http://proxy.example.com:3128", config.Proxies[0].String())
|
|
|
|
assert.Len(t, config.Files, 1)
|
|
})
|
|
|
|
t.Run("Parse with multiple field parse errors", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{
|
|
"dodo",
|
|
"-u", "://invalid-url",
|
|
"-x", "://invalid-proxy1",
|
|
"-x", "://invalid-proxy2",
|
|
})
|
|
config, err := parser.Parse()
|
|
|
|
assert.Nil(t, config)
|
|
var fieldErr types.FieldParseErrors
|
|
require.ErrorAs(t, err, &fieldErr)
|
|
assert.Len(t, fieldErr.Errors, 3)
|
|
|
|
// Check error fields
|
|
fields := make(map[string]bool)
|
|
for _, parseErr := range fieldErr.Errors {
|
|
fields[parseErr.Field] = true
|
|
}
|
|
assert.True(t, fields["url"])
|
|
assert.True(t, fields["proxy[0]"])
|
|
assert.True(t, fields["proxy[1]"])
|
|
|
|
// Check error values
|
|
values := make(map[string]string)
|
|
for _, parseErr := range fieldErr.Errors {
|
|
values[parseErr.Field] = parseErr.Value
|
|
}
|
|
assert.Equal(t, "://invalid-url", values["url"])
|
|
assert.Equal(t, "://invalid-proxy1", values["proxy[0]"])
|
|
assert.Equal(t, "://invalid-proxy2", values["proxy[1]"])
|
|
})
|
|
}
|
|
|
|
func TestConfigCLIParser_PrintHelp(t *testing.T) {
|
|
t.Run("PrintHelp outputs expected content", func(t *testing.T) {
|
|
// Capture stdout
|
|
oldStdout := os.Stdout
|
|
reader, writer, _ := os.Pipe()
|
|
os.Stdout = writer
|
|
|
|
parser := NewConfigCLIParser([]string{"dodo"})
|
|
parser.PrintHelp()
|
|
|
|
// Restore stdout and read output
|
|
writer.Close()
|
|
os.Stdout = oldStdout
|
|
var buf bytes.Buffer
|
|
io.Copy(&buf, reader)
|
|
output := buf.String()
|
|
|
|
// Verify help text contains expected elements
|
|
assert.Contains(t, output, "Usage:")
|
|
assert.Contains(t, output, "dodo [flags]")
|
|
assert.Contains(t, output, "Examples:")
|
|
assert.Contains(t, output, "Flags:")
|
|
assert.Contains(t, output, "-h, -help")
|
|
assert.Contains(t, output, "-v, -version")
|
|
assert.Contains(t, output, "-u, -url")
|
|
assert.Contains(t, output, "-m, -method")
|
|
assert.Contains(t, output, "-d, -dodos")
|
|
assert.Contains(t, output, "-r, -requests")
|
|
assert.Contains(t, output, "-t, -timeout")
|
|
assert.Contains(t, output, "-b, -body")
|
|
assert.Contains(t, output, "-H, -header")
|
|
assert.Contains(t, output, "-p, -param")
|
|
assert.Contains(t, output, "-c, -cookie")
|
|
assert.Contains(t, output, "-x, -proxy")
|
|
assert.Contains(t, output, "-skip-verify")
|
|
assert.Contains(t, output, "-y, -yes")
|
|
assert.Contains(t, output, "-f, -config-file")
|
|
|
|
// Verify default values are included
|
|
assert.Contains(t, output, Defaults.Method)
|
|
assert.Contains(t, output, "1") // DodosCount default
|
|
assert.Contains(t, output, "10s") // RequestTimeout default
|
|
assert.Contains(t, output, "false") // Yes default
|
|
assert.Contains(t, output, "false") // SkipVerify default
|
|
})
|
|
}
|
|
|
|
func TestCLIYesOrNoReader(t *testing.T) {
|
|
t.Run("CLIYesOrNoReader with 'y' input returns true", func(t *testing.T) {
|
|
// Redirect stdin
|
|
oldStdin := os.Stdin
|
|
reader, writer, _ := os.Pipe()
|
|
os.Stdin = reader
|
|
|
|
// Write input and close writer
|
|
writer.WriteString("y\n")
|
|
writer.Close()
|
|
|
|
result := CLIYesOrNoReader("Test question", false)
|
|
|
|
// Restore stdin
|
|
os.Stdin = oldStdin
|
|
|
|
assert.True(t, result)
|
|
})
|
|
|
|
t.Run("CLIYesOrNoReader with 'Y' input returns true", func(t *testing.T) {
|
|
// Redirect stdin
|
|
oldStdin := os.Stdin
|
|
reader, writer, _ := os.Pipe()
|
|
os.Stdin = reader
|
|
|
|
// Write input and close writer
|
|
writer.WriteString("Y\n")
|
|
writer.Close()
|
|
|
|
result := CLIYesOrNoReader("Test question", false)
|
|
|
|
// Restore stdin
|
|
os.Stdin = oldStdin
|
|
|
|
assert.True(t, result)
|
|
})
|
|
|
|
t.Run("CLIYesOrNoReader with 'n' input returns false", func(t *testing.T) {
|
|
// Redirect stdin
|
|
oldStdin := os.Stdin
|
|
reader, writer, _ := os.Pipe()
|
|
os.Stdin = reader
|
|
|
|
// Write input and close writer
|
|
writer.WriteString("n\n")
|
|
writer.Close()
|
|
|
|
result := CLIYesOrNoReader("Test question", true)
|
|
|
|
// Restore stdin
|
|
os.Stdin = oldStdin
|
|
|
|
assert.False(t, result)
|
|
})
|
|
|
|
t.Run("CLIYesOrNoReader with empty input returns default", func(t *testing.T) {
|
|
// Redirect stdin
|
|
oldStdin := os.Stdin
|
|
reader, writer, _ := os.Pipe()
|
|
os.Stdin = reader
|
|
|
|
// Write just newline and close writer
|
|
writer.WriteString("\n")
|
|
writer.Close()
|
|
|
|
// Test with default true
|
|
result := CLIYesOrNoReader("Test question", true)
|
|
os.Stdin = oldStdin
|
|
assert.True(t, result)
|
|
})
|
|
|
|
t.Run("CLIYesOrNoReader with empty input returns default false", func(t *testing.T) {
|
|
// Redirect stdin
|
|
oldStdin := os.Stdin
|
|
reader, writer, _ := os.Pipe()
|
|
os.Stdin = reader
|
|
|
|
// Write just newline and close writer
|
|
writer.WriteString("\n")
|
|
writer.Close()
|
|
|
|
// Test with default false
|
|
result := CLIYesOrNoReader("Test question", false)
|
|
os.Stdin = oldStdin
|
|
assert.False(t, result)
|
|
})
|
|
|
|
t.Run("CLIYesOrNoReader with other input returns false", func(t *testing.T) {
|
|
// Redirect stdin
|
|
oldStdin := os.Stdin
|
|
reader, writer, _ := os.Pipe()
|
|
os.Stdin = reader
|
|
|
|
// Write other input and close writer
|
|
writer.WriteString("maybe\n")
|
|
writer.Close()
|
|
|
|
result := CLIYesOrNoReader("Test question", true)
|
|
|
|
// Restore stdin
|
|
os.Stdin = oldStdin
|
|
|
|
assert.False(t, result)
|
|
})
|
|
|
|
t.Run("CLIYesOrNoReader message format with default true", func(t *testing.T) {
|
|
// Capture stdout to verify message format
|
|
oldStdout := os.Stdout
|
|
stdoutReader, stdoutWriter, _ := os.Pipe()
|
|
os.Stdout = stdoutWriter
|
|
|
|
// Redirect stdin
|
|
oldStdin := os.Stdin
|
|
stdinReader, stdinWriter, _ := os.Pipe()
|
|
os.Stdin = stdinReader
|
|
|
|
// Write input and close writer
|
|
stdinWriter.WriteString("y\n")
|
|
stdinWriter.Close()
|
|
|
|
CLIYesOrNoReader("Continue?", true)
|
|
|
|
// Restore stdin and stdout
|
|
os.Stdin = oldStdin
|
|
stdoutWriter.Close()
|
|
os.Stdout = oldStdout
|
|
|
|
// Read output
|
|
var buf bytes.Buffer
|
|
io.Copy(&buf, stdoutReader)
|
|
output := buf.String()
|
|
|
|
assert.Contains(t, output, "Continue? [Y/n]:")
|
|
})
|
|
|
|
t.Run("CLIYesOrNoReader message format with default false", func(t *testing.T) {
|
|
// Capture stdout to verify message format
|
|
oldStdout := os.Stdout
|
|
stdoutReader, stdoutWriter, _ := os.Pipe()
|
|
os.Stdout = stdoutWriter
|
|
|
|
// Redirect stdin
|
|
oldStdin := os.Stdin
|
|
stdinReader, stdinWriter, _ := os.Pipe()
|
|
os.Stdin = stdinReader
|
|
|
|
// Write input and close writer
|
|
stdinWriter.WriteString("n\n")
|
|
stdinWriter.Close()
|
|
|
|
CLIYesOrNoReader("Delete files?", false)
|
|
|
|
// Restore stdin and stdout
|
|
os.Stdin = oldStdin
|
|
stdoutWriter.Close()
|
|
os.Stdout = oldStdout
|
|
|
|
// Read output
|
|
var buf bytes.Buffer
|
|
io.Copy(&buf, stdoutReader)
|
|
output := buf.String()
|
|
|
|
assert.Contains(t, output, "Delete files? [y/N]:")
|
|
})
|
|
}
|
|
|
|
func TestConfigCLIParser_EdgeCases(t *testing.T) {
|
|
t.Run("Parse with zero duration", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-o", "0s"})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
require.NotNil(t, config.Duration)
|
|
assert.Equal(t, time.Duration(0), *config.Duration)
|
|
})
|
|
|
|
t.Run("Parse with zero timeout", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-t", "0s"})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
require.NotNil(t, config.Timeout)
|
|
assert.Equal(t, time.Duration(0), *config.Timeout)
|
|
})
|
|
|
|
t.Run("Parse with zero dodos count", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-d", "0"})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
require.NotNil(t, config.DodosCount)
|
|
assert.Equal(t, uint(0), *config.DodosCount)
|
|
})
|
|
|
|
t.Run("Parse with zero request count", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{"dodo", "-r", "0"})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
require.NotNil(t, config.RequestCount)
|
|
assert.Equal(t, uint(0), *config.RequestCount)
|
|
})
|
|
|
|
t.Run("Parse with empty string values", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{
|
|
"dodo",
|
|
"-m", "",
|
|
"-p", "",
|
|
"-H", "",
|
|
"-c", "",
|
|
"-b", "",
|
|
})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
|
|
assert.Empty(t, *config.Method)
|
|
assert.Len(t, config.Params, 1)
|
|
assert.Empty(t, config.Params[0].Key)
|
|
assert.Len(t, config.Headers, 1)
|
|
assert.Empty(t, config.Headers[0].Key)
|
|
assert.Len(t, config.Cookies, 1)
|
|
assert.Empty(t, config.Cookies[0].Key)
|
|
assert.Len(t, config.Bodies, 1)
|
|
assert.Equal(t, types.Body(""), config.Bodies[0])
|
|
})
|
|
|
|
t.Run("Parse with complex URL", func(t *testing.T) {
|
|
complexURL := "https://user:pass@api.example.com:8080/v1/endpoint?param=value&other=test#fragment"
|
|
parser := NewConfigCLIParser([]string{"dodo", "-u", complexURL})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
require.NotNil(t, config.URL)
|
|
|
|
parsedURL, parseErr := url.Parse(complexURL)
|
|
require.NoError(t, parseErr)
|
|
assert.Equal(t, parsedURL, config.URL)
|
|
})
|
|
|
|
t.Run("Parse with repeated same flags overrides previous values", func(t *testing.T) {
|
|
parser := NewConfigCLIParser([]string{
|
|
"dodo",
|
|
"-m", "GET",
|
|
"-m", "POST", // This should override the previous
|
|
"-d", "1",
|
|
"-d", "5", // This should override the previous
|
|
})
|
|
config, err := parser.Parse()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
|
|
assert.Equal(t, "POST", *config.Method)
|
|
assert.Equal(t, uint(5), *config.DodosCount)
|
|
})
|
|
}
|