mirror of
https://github.com/aykhans/dodo.git
synced 2025-09-01 00:53:34 +00:00
Add 'Files' field to the 'Config'. Add 'Value' field to the 'FieldParseError'
This commit is contained in:
@@ -40,6 +40,9 @@ func main() {
|
|||||||
|
|
||||||
func printValidationErrors(parserName string, errors ...types.FieldParseError) {
|
func printValidationErrors(parserName string, errors ...types.FieldParseError) {
|
||||||
for _, fieldErr := range errors {
|
for _, fieldErr := range errors {
|
||||||
utils.PrintErr(text.FgRed, "[%s] Field '%s': %v", parserName, fieldErr.Field, fieldErr.Err)
|
if fieldErr.Value == "" {
|
||||||
|
utils.PrintErr(text.FgYellow, "[%s] Field '%s': %v", parserName, fieldErr.Field, fieldErr.Err)
|
||||||
|
}
|
||||||
|
utils.PrintErr(text.FgYellow, "[%s] Field '%s' (%s): %v", parserName, fieldErr.Field, fieldErr.Value, fieldErr.Err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -53,8 +53,7 @@ Flags:
|
|||||||
-skip-verify bool Skip SSL/TLS certificate verification (default %v)`
|
-skip-verify bool Skip SSL/TLS certificate verification (default %v)`
|
||||||
|
|
||||||
type ConfigCLIParser struct {
|
type ConfigCLIParser struct {
|
||||||
args []string
|
args []string
|
||||||
configFile *types.ConfigFile
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfigCLIParser(args []string) *ConfigCLIParser {
|
func NewConfigCLIParser(args []string) *ConfigCLIParser {
|
||||||
@@ -64,10 +63,6 @@ func NewConfigCLIParser(args []string) *ConfigCLIParser {
|
|||||||
return &ConfigCLIParser{args: args}
|
return &ConfigCLIParser{args: args}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser ConfigCLIParser) GetConfigFile() *types.ConfigFile {
|
|
||||||
return parser.configFile
|
|
||||||
}
|
|
||||||
|
|
||||||
type stringSliceArg []string
|
type stringSliceArg []string
|
||||||
|
|
||||||
func (arg *stringSliceArg) String() string {
|
func (arg *stringSliceArg) String() string {
|
||||||
@@ -91,7 +86,7 @@ func (parser *ConfigCLIParser) Parse() (*Config, error) {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
config = &Config{}
|
config = &Config{}
|
||||||
configFile string
|
configFiles = stringSliceArg{}
|
||||||
yes bool
|
yes bool
|
||||||
skipVerify bool
|
skipVerify bool
|
||||||
method string
|
method string
|
||||||
@@ -108,8 +103,8 @@ func (parser *ConfigCLIParser) Parse() (*Config, error) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
{
|
{
|
||||||
flagSet.StringVar(&configFile, "config-file", "", "Config file")
|
flagSet.Var(&configFiles, "config-file", "Config file")
|
||||||
flagSet.StringVar(&configFile, "f", "", "Config file")
|
flagSet.Var(&configFiles, "f", "Config file")
|
||||||
|
|
||||||
flagSet.BoolVar(&yes, "yes", false, "Answer yes to all questions")
|
flagSet.BoolVar(&yes, "yes", false, "Answer yes to all questions")
|
||||||
flagSet.BoolVar(&yes, "y", false, "Answer yes to all questions")
|
flagSet.BoolVar(&yes, "y", false, "Answer yes to all questions")
|
||||||
@@ -171,28 +166,50 @@ func (parser *ConfigCLIParser) Parse() (*Config, error) {
|
|||||||
flagSet.Visit(func(flagVar *flag.Flag) {
|
flagSet.Visit(func(flagVar *flag.Flag) {
|
||||||
switch flagVar.Name {
|
switch flagVar.Name {
|
||||||
case "config-file", "f":
|
case "config-file", "f":
|
||||||
var err error
|
for i, configFile := range configFiles {
|
||||||
parser.configFile, err = types.ParseConfigFile(configFile)
|
configFileParsed, err := types.ParseConfigFile(configFile)
|
||||||
_ = utils.HandleErrorOrDie(err,
|
|
||||||
utils.OnSentinelError(types.ErrConfigFileExtensionNotFound, func(err error) error {
|
_ = utils.HandleErrorOrDie(err,
|
||||||
fieldParseErrors = append(fieldParseErrors, *types.NewFieldParseError("config-file", errors.New("file extension not found")))
|
utils.OnSentinelError(types.ErrConfigFileExtensionNotFound, func(err error) error {
|
||||||
return nil
|
fieldParseErrors = append(
|
||||||
}),
|
fieldParseErrors,
|
||||||
utils.OnCustomError(func(err types.RemoteConfigFileParseError) error {
|
*types.NewFieldParseError(
|
||||||
fieldParseErrors = append(fieldParseErrors, *types.NewFieldParseError("config-file", fmt.Errorf("parse error: %w", err)))
|
fmt.Sprintf("config-file[%d]", i),
|
||||||
return nil
|
configFile,
|
||||||
}),
|
errors.New("file extension not found"),
|
||||||
utils.OnCustomError(func(err types.UnknownConfigFileTypeError) error {
|
),
|
||||||
fieldParseErrors = append(
|
)
|
||||||
fieldParseErrors,
|
return nil
|
||||||
*types.NewFieldParseError(
|
}),
|
||||||
"config-file",
|
utils.OnCustomError(func(err types.RemoteConfigFileParseError) error {
|
||||||
fmt.Errorf("file type '%s' not supported (supported types: %s)", err.Type, types.ConfigFileTypeYAML),
|
fieldParseErrors = append(
|
||||||
),
|
fieldParseErrors,
|
||||||
)
|
*types.NewFieldParseError(
|
||||||
return nil
|
fmt.Sprintf("config-file[%d]", i),
|
||||||
}),
|
configFile,
|
||||||
)
|
fmt.Errorf("parse error: %w", err),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return nil
|
||||||
|
}),
|
||||||
|
utils.OnCustomError(func(err types.UnknownConfigFileTypeError) error {
|
||||||
|
fieldParseErrors = append(
|
||||||
|
fieldParseErrors,
|
||||||
|
*types.NewFieldParseError(
|
||||||
|
fmt.Sprintf("config-file[%d]", i),
|
||||||
|
configFile,
|
||||||
|
fmt.Errorf("file type '%s' not supported (supported types: %s)", err.Type, types.ConfigFileTypeYAML),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return nil
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
config.Files = append(config.Files, *configFileParsed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case "yes", "y":
|
case "yes", "y":
|
||||||
config.Yes = utils.ToPtr(yes)
|
config.Yes = utils.ToPtr(yes)
|
||||||
case "skip-verify":
|
case "skip-verify":
|
||||||
@@ -202,7 +219,7 @@ func (parser *ConfigCLIParser) Parse() (*Config, error) {
|
|||||||
case "url", "u":
|
case "url", "u":
|
||||||
urlParsed, err := url.Parse(urlInput)
|
urlParsed, err := url.Parse(urlInput)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fieldParseErrors = append(fieldParseErrors, *types.NewFieldParseError("url", err))
|
fieldParseErrors = append(fieldParseErrors, *types.NewFieldParseError("url", urlInput, err))
|
||||||
} else {
|
} else {
|
||||||
config.URL = urlParsed
|
config.URL = urlParsed
|
||||||
}
|
}
|
||||||
@@ -228,7 +245,7 @@ func (parser *ConfigCLIParser) Parse() (*Config, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
fieldParseErrors = append(
|
fieldParseErrors = append(
|
||||||
fieldParseErrors,
|
fieldParseErrors,
|
||||||
*types.NewFieldParseError(fmt.Sprintf("proxy[%d]", i), err),
|
*types.NewFieldParseError(fmt.Sprintf("proxy[%d]", i), proxy, err),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,6 @@ func TestNewConfigCLIParser(t *testing.T) {
|
|||||||
|
|
||||||
require.NotNil(t, parser)
|
require.NotNil(t, parser)
|
||||||
assert.Equal(t, args, parser.args)
|
assert.Equal(t, args, parser.args)
|
||||||
assert.Nil(t, parser.configFile)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("NewConfigCLIParser with nil args", func(t *testing.T) {
|
t.Run("NewConfigCLIParser with nil args", func(t *testing.T) {
|
||||||
@@ -28,7 +27,6 @@ func TestNewConfigCLIParser(t *testing.T) {
|
|||||||
|
|
||||||
require.NotNil(t, parser)
|
require.NotNil(t, parser)
|
||||||
assert.Equal(t, []string{}, parser.args)
|
assert.Equal(t, []string{}, parser.args)
|
||||||
assert.Nil(t, parser.configFile)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("NewConfigCLIParser with empty args", func(t *testing.T) {
|
t.Run("NewConfigCLIParser with empty args", func(t *testing.T) {
|
||||||
@@ -37,21 +35,6 @@ func TestNewConfigCLIParser(t *testing.T) {
|
|||||||
|
|
||||||
require.NotNil(t, parser)
|
require.NotNil(t, parser)
|
||||||
assert.Equal(t, args, parser.args)
|
assert.Equal(t, args, parser.args)
|
||||||
assert.Nil(t, parser.configFile)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfigCLIParser_GetConfigFile(t *testing.T) {
|
|
||||||
t.Run("GetConfigFile returns nil when no config file is set", func(t *testing.T) {
|
|
||||||
parser := NewConfigCLIParser([]string{"dodo"})
|
|
||||||
assert.Nil(t, parser.GetConfigFile())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("GetConfigFile returns config file when set", func(t *testing.T) {
|
|
||||||
parser := NewConfigCLIParser([]string{"dodo"})
|
|
||||||
expectedConfigFile := &types.ConfigFile{}
|
|
||||||
parser.configFile = expectedConfigFile
|
|
||||||
assert.Equal(t, expectedConfigFile, parser.GetConfigFile())
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,6 +113,7 @@ func TestConfigCLIParser_Parse(t *testing.T) {
|
|||||||
require.ErrorAs(t, err, &fieldErr)
|
require.ErrorAs(t, err, &fieldErr)
|
||||||
assert.Len(t, fieldErr.Errors, 1)
|
assert.Len(t, fieldErr.Errors, 1)
|
||||||
assert.Equal(t, "url", fieldErr.Errors[0].Field)
|
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) {
|
t.Run("Parse with method flag", func(t *testing.T) {
|
||||||
@@ -272,6 +256,7 @@ func TestConfigCLIParser_Parse(t *testing.T) {
|
|||||||
require.ErrorAs(t, err, &fieldErr)
|
require.ErrorAs(t, err, &fieldErr)
|
||||||
assert.Len(t, fieldErr.Errors, 1)
|
assert.Len(t, fieldErr.Errors, 1)
|
||||||
assert.Equal(t, "proxy[0]", fieldErr.Errors[0].Field)
|
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) {
|
t.Run("Parse with mixed valid and invalid proxies", func(t *testing.T) {
|
||||||
@@ -283,6 +268,7 @@ func TestConfigCLIParser_Parse(t *testing.T) {
|
|||||||
require.ErrorAs(t, err, &fieldErr)
|
require.ErrorAs(t, err, &fieldErr)
|
||||||
assert.Len(t, fieldErr.Errors, 1)
|
assert.Len(t, fieldErr.Errors, 1)
|
||||||
assert.Equal(t, "proxy[1]", fieldErr.Errors[0].Field)
|
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) {
|
t.Run("Parse with long flag names", func(t *testing.T) {
|
||||||
@@ -318,7 +304,7 @@ func TestConfigCLIParser_Parse(t *testing.T) {
|
|||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, config)
|
require.NotNil(t, config)
|
||||||
assert.NotNil(t, parser.GetConfigFile())
|
assert.Len(t, config.Files, 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Parse with config-file flag using long form", func(t *testing.T) {
|
t.Run("Parse with config-file flag using long form", func(t *testing.T) {
|
||||||
@@ -327,7 +313,7 @@ func TestConfigCLIParser_Parse(t *testing.T) {
|
|||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, config)
|
require.NotNil(t, config)
|
||||||
assert.NotNil(t, parser.GetConfigFile())
|
assert.Len(t, config.Files, 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Parse with config-file flag invalid extension", func(t *testing.T) {
|
t.Run("Parse with config-file flag invalid extension", func(t *testing.T) {
|
||||||
@@ -338,7 +324,8 @@ func TestConfigCLIParser_Parse(t *testing.T) {
|
|||||||
var fieldErr types.FieldParseErrors
|
var fieldErr types.FieldParseErrors
|
||||||
require.ErrorAs(t, err, &fieldErr)
|
require.ErrorAs(t, err, &fieldErr)
|
||||||
assert.Len(t, fieldErr.Errors, 1)
|
assert.Len(t, fieldErr.Errors, 1)
|
||||||
assert.Equal(t, "config-file", fieldErr.Errors[0].Field)
|
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")
|
assert.Contains(t, fieldErr.Errors[0].Err.Error(), "file extension not found")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -350,7 +337,8 @@ func TestConfigCLIParser_Parse(t *testing.T) {
|
|||||||
var fieldErr types.FieldParseErrors
|
var fieldErr types.FieldParseErrors
|
||||||
require.ErrorAs(t, err, &fieldErr)
|
require.ErrorAs(t, err, &fieldErr)
|
||||||
assert.Len(t, fieldErr.Errors, 1)
|
assert.Len(t, fieldErr.Errors, 1)
|
||||||
assert.Equal(t, "config-file", fieldErr.Errors[0].Field)
|
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(), "file type")
|
||||||
assert.Contains(t, fieldErr.Errors[0].Err.Error(), "not supported")
|
assert.Contains(t, fieldErr.Errors[0].Err.Error(), "not supported")
|
||||||
})
|
})
|
||||||
@@ -361,7 +349,16 @@ func TestConfigCLIParser_Parse(t *testing.T) {
|
|||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, config)
|
require.NotNil(t, config)
|
||||||
assert.NotNil(t, parser.GetConfigFile())
|
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) {
|
t.Run("Parse with all flags combined", func(t *testing.T) {
|
||||||
@@ -412,7 +409,7 @@ func TestConfigCLIParser_Parse(t *testing.T) {
|
|||||||
assert.Len(t, config.Proxies, 1)
|
assert.Len(t, config.Proxies, 1)
|
||||||
assert.Equal(t, "http://proxy.example.com:3128", config.Proxies[0].String())
|
assert.Equal(t, "http://proxy.example.com:3128", config.Proxies[0].String())
|
||||||
|
|
||||||
assert.NotNil(t, parser.GetConfigFile())
|
assert.Len(t, config.Files, 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Parse with multiple field parse errors", func(t *testing.T) {
|
t.Run("Parse with multiple field parse errors", func(t *testing.T) {
|
||||||
@@ -437,6 +434,15 @@ func TestConfigCLIParser_Parse(t *testing.T) {
|
|||||||
assert.True(t, fields["url"])
|
assert.True(t, fields["url"])
|
||||||
assert.True(t, fields["proxy[0]"])
|
assert.True(t, fields["proxy[0]"])
|
||||||
assert.True(t, fields["proxy[1]"])
|
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]"])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -29,6 +29,7 @@ var Defaults = struct {
|
|||||||
var SupportedProxySchemes []string = []string{"http", "socks5", "socks5h"}
|
var SupportedProxySchemes []string = []string{"http", "socks5", "socks5h"}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
Files []types.ConfigFile
|
||||||
Method *string
|
Method *string
|
||||||
URL *url.URL
|
URL *url.URL
|
||||||
Timeout *time.Duration
|
Timeout *time.Duration
|
||||||
@@ -49,6 +50,7 @@ func NewConfig() *Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (config *Config) MergeConfig(newConfig *Config) {
|
func (config *Config) MergeConfig(newConfig *Config) {
|
||||||
|
config.Files = append(config.Files, newConfig.Files...)
|
||||||
if newConfig.Method != nil {
|
if newConfig.Method != nil {
|
||||||
config.Method = newConfig.Method
|
config.Method = newConfig.Method
|
||||||
}
|
}
|
||||||
|
@@ -23,6 +23,7 @@ func TestMergeConfig(t *testing.T) {
|
|||||||
newDuration := 2 * time.Minute
|
newDuration := 2 * time.Minute
|
||||||
|
|
||||||
config := &Config{
|
config := &Config{
|
||||||
|
Files: []types.ConfigFile{},
|
||||||
Method: utils.ToPtr("GET"),
|
Method: utils.ToPtr("GET"),
|
||||||
URL: originalURL,
|
URL: originalURL,
|
||||||
Timeout: &originalTimeout,
|
Timeout: &originalTimeout,
|
||||||
@@ -39,6 +40,7 @@ func TestMergeConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
newConfig := &Config{
|
newConfig := &Config{
|
||||||
|
Files: []types.ConfigFile{},
|
||||||
Method: utils.ToPtr("POST"),
|
Method: utils.ToPtr("POST"),
|
||||||
URL: newURL,
|
URL: newURL,
|
||||||
Timeout: &newTimeout,
|
Timeout: &newTimeout,
|
||||||
@@ -76,6 +78,7 @@ func TestMergeConfig(t *testing.T) {
|
|||||||
originalTimeout := 5 * time.Second
|
originalTimeout := 5 * time.Second
|
||||||
|
|
||||||
config := &Config{
|
config := &Config{
|
||||||
|
Files: []types.ConfigFile{},
|
||||||
Method: utils.ToPtr("GET"),
|
Method: utils.ToPtr("GET"),
|
||||||
URL: originalURL,
|
URL: originalURL,
|
||||||
Timeout: &originalTimeout,
|
Timeout: &originalTimeout,
|
||||||
@@ -85,6 +88,7 @@ func TestMergeConfig(t *testing.T) {
|
|||||||
|
|
||||||
newURL, _ := url.Parse("https://new.example.com")
|
newURL, _ := url.Parse("https://new.example.com")
|
||||||
newConfig := &Config{
|
newConfig := &Config{
|
||||||
|
Files: []types.ConfigFile{},
|
||||||
URL: newURL,
|
URL: newURL,
|
||||||
DodosCount: utils.ToPtr(uint(10)),
|
DodosCount: utils.ToPtr(uint(10)),
|
||||||
}
|
}
|
||||||
@@ -103,6 +107,7 @@ func TestMergeConfig(t *testing.T) {
|
|||||||
originalTimeout := 5 * time.Second
|
originalTimeout := 5 * time.Second
|
||||||
|
|
||||||
config := &Config{
|
config := &Config{
|
||||||
|
Files: []types.ConfigFile{},
|
||||||
Method: utils.ToPtr("GET"),
|
Method: utils.ToPtr("GET"),
|
||||||
URL: originalURL,
|
URL: originalURL,
|
||||||
Timeout: &originalTimeout,
|
Timeout: &originalTimeout,
|
||||||
@@ -112,6 +117,7 @@ func TestMergeConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
newConfig := &Config{
|
newConfig := &Config{
|
||||||
|
Files: []types.ConfigFile{},
|
||||||
Method: nil,
|
Method: nil,
|
||||||
URL: nil,
|
URL: nil,
|
||||||
Timeout: nil,
|
Timeout: nil,
|
||||||
@@ -132,7 +138,9 @@ func TestMergeConfig(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("MergeConfig with empty slices", func(t *testing.T) {
|
t.Run("MergeConfig with empty slices", func(t *testing.T) {
|
||||||
|
configFile, _ := types.ParseConfigFile("original.yml")
|
||||||
config := &Config{
|
config := &Config{
|
||||||
|
Files: []types.ConfigFile{*configFile},
|
||||||
Params: types.Params{{Key: "original", Value: []string{"value"}}},
|
Params: types.Params{{Key: "original", Value: []string{"value"}}},
|
||||||
Headers: types.Headers{{Key: "Original-Header", Value: []string{"original"}}},
|
Headers: types.Headers{{Key: "Original-Header", Value: []string{"original"}}},
|
||||||
Cookies: types.Cookies{{Key: "originalCookie", Value: []string{"originalValue"}}},
|
Cookies: types.Cookies{{Key: "originalCookie", Value: []string{"originalValue"}}},
|
||||||
@@ -141,6 +149,7 @@ func TestMergeConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
newConfig := &Config{
|
newConfig := &Config{
|
||||||
|
Files: []types.ConfigFile{},
|
||||||
Params: types.Params{},
|
Params: types.Params{},
|
||||||
Headers: types.Headers{},
|
Headers: types.Headers{},
|
||||||
Cookies: types.Cookies{},
|
Cookies: types.Cookies{},
|
||||||
@@ -150,6 +159,7 @@ func TestMergeConfig(t *testing.T) {
|
|||||||
|
|
||||||
config.MergeConfig(newConfig)
|
config.MergeConfig(newConfig)
|
||||||
|
|
||||||
|
assert.Equal(t, []types.ConfigFile{*configFile}, config.Files, "Empty Files should not override")
|
||||||
assert.Equal(t, types.Params{{Key: "original", Value: []string{"value"}}}, config.Params, "Empty Params should not override")
|
assert.Equal(t, types.Params{{Key: "original", Value: []string{"value"}}}, config.Params, "Empty Params should not override")
|
||||||
assert.Equal(t, types.Headers{{Key: "Original-Header", Value: []string{"original"}}}, config.Headers, "Empty Headers should not override")
|
assert.Equal(t, types.Headers{{Key: "Original-Header", Value: []string{"original"}}}, config.Headers, "Empty Headers should not override")
|
||||||
assert.Equal(t, types.Cookies{{Key: "originalCookie", Value: []string{"originalValue"}}}, config.Cookies, "Empty Cookies should not override")
|
assert.Equal(t, types.Cookies{{Key: "originalCookie", Value: []string{"originalValue"}}}, config.Cookies, "Empty Cookies should not override")
|
||||||
@@ -157,6 +167,28 @@ func TestMergeConfig(t *testing.T) {
|
|||||||
assert.Equal(t, types.Proxies{}, config.Proxies, "Empty Proxies should not override")
|
assert.Equal(t, types.Proxies{}, config.Proxies, "Empty Proxies should not override")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("MergeConfig with Files field", func(t *testing.T) {
|
||||||
|
configFile1, _ := types.ParseConfigFile("config1.yml")
|
||||||
|
configFile2, _ := types.ParseConfigFile("config2.yaml")
|
||||||
|
|
||||||
|
config := &Config{
|
||||||
|
Files: []types.ConfigFile{*configFile1},
|
||||||
|
Method: utils.ToPtr("GET"),
|
||||||
|
Headers: types.Headers{{Key: "Original-Header", Value: []string{"original"}}},
|
||||||
|
}
|
||||||
|
|
||||||
|
newConfig := &Config{
|
||||||
|
Files: []types.ConfigFile{*configFile2},
|
||||||
|
Method: utils.ToPtr("POST"),
|
||||||
|
}
|
||||||
|
|
||||||
|
config.MergeConfig(newConfig)
|
||||||
|
|
||||||
|
assert.Equal(t, "POST", *config.Method, "Method should be updated")
|
||||||
|
assert.Equal(t, []types.ConfigFile{*configFile1, *configFile2}, config.Files, "Files should be appended")
|
||||||
|
assert.Equal(t, types.Headers{{Key: "Original-Header", Value: []string{"original"}}}, config.Headers, "Headers should remain unchanged")
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("MergeConfig on empty original config", func(t *testing.T) {
|
t.Run("MergeConfig on empty original config", func(t *testing.T) {
|
||||||
config := &Config{}
|
config := &Config{}
|
||||||
|
|
||||||
@@ -165,6 +197,7 @@ func TestMergeConfig(t *testing.T) {
|
|||||||
newDuration := 2 * time.Minute
|
newDuration := 2 * time.Minute
|
||||||
|
|
||||||
newConfig := &Config{
|
newConfig := &Config{
|
||||||
|
Files: []types.ConfigFile{},
|
||||||
Method: utils.ToPtr("POST"),
|
Method: utils.ToPtr("POST"),
|
||||||
URL: newURL,
|
URL: newURL,
|
||||||
Timeout: &newTimeout,
|
Timeout: &newTimeout,
|
||||||
@@ -246,6 +279,7 @@ func TestSetDefaults(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("SetDefaults adds User-Agent when missing", func(t *testing.T) {
|
t.Run("SetDefaults adds User-Agent when missing", func(t *testing.T) {
|
||||||
config := &Config{
|
config := &Config{
|
||||||
|
Files: []types.ConfigFile{},
|
||||||
Headers: types.Headers{{Key: "Content-Type", Value: []string{"application/json"}}},
|
Headers: types.Headers{{Key: "Content-Type", Value: []string{"application/json"}}},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,6 +302,7 @@ func TestSetDefaults(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("SetDefaults with partial config", func(t *testing.T) {
|
t.Run("SetDefaults with partial config", func(t *testing.T) {
|
||||||
config := &Config{
|
config := &Config{
|
||||||
|
Files: []types.ConfigFile{},
|
||||||
Method: utils.ToPtr("PUT"),
|
Method: utils.ToPtr("PUT"),
|
||||||
Yes: utils.ToPtr(true),
|
Yes: utils.ToPtr(true),
|
||||||
}
|
}
|
||||||
@@ -306,6 +341,7 @@ func TestSetDefaults(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("SetDefaults with empty Headers initializes correctly", func(t *testing.T) {
|
t.Run("SetDefaults with empty Headers initializes correctly", func(t *testing.T) {
|
||||||
config := &Config{
|
config := &Config{
|
||||||
|
Files: []types.ConfigFile{},
|
||||||
Headers: types.Headers{},
|
Headers: types.Headers{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -22,14 +22,15 @@ var (
|
|||||||
|
|
||||||
type FieldParseError struct {
|
type FieldParseError struct {
|
||||||
Field string
|
Field string
|
||||||
|
Value string
|
||||||
Err error
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFieldParseError(field string, err error) *FieldParseError {
|
func NewFieldParseError(field string, value string, err error) *FieldParseError {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = ErrNoError
|
err = ErrNoError
|
||||||
}
|
}
|
||||||
return &FieldParseError{field, err}
|
return &FieldParseError{field, value, err}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e FieldParseError) Error() string {
|
func (e FieldParseError) Error() string {
|
||||||
|
@@ -10,7 +10,7 @@ import (
|
|||||||
func TestFieldParseError_Error(t *testing.T) {
|
func TestFieldParseError_Error(t *testing.T) {
|
||||||
t.Run("Error returns formatted message", func(t *testing.T) {
|
t.Run("Error returns formatted message", func(t *testing.T) {
|
||||||
originalErr := errors.New("invalid value")
|
originalErr := errors.New("invalid value")
|
||||||
fieldErr := NewFieldParseError("username", originalErr)
|
fieldErr := NewFieldParseError("username", "testuser", originalErr)
|
||||||
|
|
||||||
expected := "Field 'username' parse failed: invalid value"
|
expected := "Field 'username' parse failed: invalid value"
|
||||||
assert.Equal(t, expected, fieldErr.Error())
|
assert.Equal(t, expected, fieldErr.Error())
|
||||||
@@ -18,14 +18,14 @@ func TestFieldParseError_Error(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("Error with empty field name", func(t *testing.T) {
|
t.Run("Error with empty field name", func(t *testing.T) {
|
||||||
originalErr := errors.New("test error")
|
originalErr := errors.New("test error")
|
||||||
fieldErr := NewFieldParseError("", originalErr)
|
fieldErr := NewFieldParseError("", "somevalue", originalErr)
|
||||||
|
|
||||||
expected := "Field '' parse failed: test error"
|
expected := "Field '' parse failed: test error"
|
||||||
assert.Equal(t, expected, fieldErr.Error())
|
assert.Equal(t, expected, fieldErr.Error())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Error with nil underlying error", func(t *testing.T) {
|
t.Run("Error with nil underlying error", func(t *testing.T) {
|
||||||
fieldErr := NewFieldParseError("field", nil)
|
fieldErr := NewFieldParseError("field", "value123", nil)
|
||||||
|
|
||||||
expected := "Field 'field' parse failed: no error (internal)"
|
expected := "Field 'field' parse failed: no error (internal)"
|
||||||
assert.Equal(t, expected, fieldErr.Error())
|
assert.Equal(t, expected, fieldErr.Error())
|
||||||
@@ -35,13 +35,13 @@ func TestFieldParseError_Error(t *testing.T) {
|
|||||||
func TestFieldParseError_Unwrap(t *testing.T) {
|
func TestFieldParseError_Unwrap(t *testing.T) {
|
||||||
t.Run("Unwrap returns original error", func(t *testing.T) {
|
t.Run("Unwrap returns original error", func(t *testing.T) {
|
||||||
originalErr := errors.New("original error")
|
originalErr := errors.New("original error")
|
||||||
fieldErr := NewFieldParseError("field", originalErr)
|
fieldErr := NewFieldParseError("field", "value", originalErr)
|
||||||
|
|
||||||
assert.Equal(t, originalErr, fieldErr.Unwrap())
|
assert.Equal(t, originalErr, fieldErr.Unwrap())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Unwrap with nil error", func(t *testing.T) {
|
t.Run("Unwrap with nil error", func(t *testing.T) {
|
||||||
fieldErr := NewFieldParseError("field", nil)
|
fieldErr := NewFieldParseError("field", "value", nil)
|
||||||
|
|
||||||
assert.Equal(t, ErrNoError, fieldErr.Unwrap())
|
assert.Equal(t, ErrNoError, fieldErr.Unwrap())
|
||||||
})
|
})
|
||||||
@@ -50,16 +50,18 @@ func TestFieldParseError_Unwrap(t *testing.T) {
|
|||||||
func TestNewFieldParseError(t *testing.T) {
|
func TestNewFieldParseError(t *testing.T) {
|
||||||
t.Run("Creates FieldParseError with correct values", func(t *testing.T) {
|
t.Run("Creates FieldParseError with correct values", func(t *testing.T) {
|
||||||
originalErr := errors.New("test error")
|
originalErr := errors.New("test error")
|
||||||
fieldErr := NewFieldParseError("testField", originalErr)
|
fieldErr := NewFieldParseError("testField", "testValue", originalErr)
|
||||||
|
|
||||||
assert.Equal(t, "testField", fieldErr.Field)
|
assert.Equal(t, "testField", fieldErr.Field)
|
||||||
|
assert.Equal(t, "testValue", fieldErr.Value)
|
||||||
assert.Equal(t, originalErr, fieldErr.Err)
|
assert.Equal(t, originalErr, fieldErr.Err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Creates FieldParseError with ErrNoError when nil passed", func(t *testing.T) {
|
t.Run("Creates FieldParseError with ErrNoError when nil passed", func(t *testing.T) {
|
||||||
fieldErr := NewFieldParseError("testField", nil)
|
fieldErr := NewFieldParseError("testField", "testValue", nil)
|
||||||
|
|
||||||
assert.Equal(t, "testField", fieldErr.Field)
|
assert.Equal(t, "testField", fieldErr.Field)
|
||||||
|
assert.Equal(t, "testValue", fieldErr.Value)
|
||||||
assert.Equal(t, ErrNoError, fieldErr.Err)
|
assert.Equal(t, ErrNoError, fieldErr.Err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -72,7 +74,7 @@ func TestFieldParseErrors_Error(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Error with single error returns single error message", func(t *testing.T) {
|
t.Run("Error with single error returns single error message", func(t *testing.T) {
|
||||||
fieldErr := *NewFieldParseError("field1", errors.New("error1"))
|
fieldErr := *NewFieldParseError("field1", "value1", errors.New("error1"))
|
||||||
fieldErrors := NewFieldParseErrors([]FieldParseError{fieldErr})
|
fieldErrors := NewFieldParseErrors([]FieldParseError{fieldErr})
|
||||||
|
|
||||||
expected := "Field 'field1' parse failed: error1"
|
expected := "Field 'field1' parse failed: error1"
|
||||||
@@ -80,9 +82,9 @@ func TestFieldParseErrors_Error(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Error with multiple errors returns concatenated messages", func(t *testing.T) {
|
t.Run("Error with multiple errors returns concatenated messages", func(t *testing.T) {
|
||||||
fieldErr1 := *NewFieldParseError("field1", errors.New("error1"))
|
fieldErr1 := *NewFieldParseError("field1", "value1", errors.New("error1"))
|
||||||
fieldErr2 := *NewFieldParseError("field2", errors.New("error2"))
|
fieldErr2 := *NewFieldParseError("field2", "value2", errors.New("error2"))
|
||||||
fieldErr3 := *NewFieldParseError("field3", errors.New("error3"))
|
fieldErr3 := *NewFieldParseError("field3", "value3", errors.New("error3"))
|
||||||
fieldErrors := NewFieldParseErrors([]FieldParseError{fieldErr1, fieldErr2, fieldErr3})
|
fieldErrors := NewFieldParseErrors([]FieldParseError{fieldErr1, fieldErr2, fieldErr3})
|
||||||
|
|
||||||
expected := "Field 'field1' parse failed: error1\nField 'field2' parse failed: error2\nField 'field3' parse failed: error3"
|
expected := "Field 'field1' parse failed: error1\nField 'field2' parse failed: error2\nField 'field3' parse failed: error3"
|
||||||
@@ -90,8 +92,8 @@ func TestFieldParseErrors_Error(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Error with two errors", func(t *testing.T) {
|
t.Run("Error with two errors", func(t *testing.T) {
|
||||||
fieldErr1 := *NewFieldParseError("username", errors.New("too short"))
|
fieldErr1 := *NewFieldParseError("username", "john", errors.New("too short"))
|
||||||
fieldErr2 := *NewFieldParseError("email", errors.New("invalid format"))
|
fieldErr2 := *NewFieldParseError("email", "invalid", errors.New("invalid format"))
|
||||||
fieldErrors := NewFieldParseErrors([]FieldParseError{fieldErr1, fieldErr2})
|
fieldErrors := NewFieldParseErrors([]FieldParseError{fieldErr1, fieldErr2})
|
||||||
|
|
||||||
expected := "Field 'username' parse failed: too short\nField 'email' parse failed: invalid format"
|
expected := "Field 'username' parse failed: too short\nField 'email' parse failed: invalid format"
|
||||||
@@ -101,8 +103,8 @@ func TestFieldParseErrors_Error(t *testing.T) {
|
|||||||
|
|
||||||
func TestNewFieldParseErrors(t *testing.T) {
|
func TestNewFieldParseErrors(t *testing.T) {
|
||||||
t.Run("Creates FieldParseErrors with correct values", func(t *testing.T) {
|
t.Run("Creates FieldParseErrors with correct values", func(t *testing.T) {
|
||||||
fieldErr1 := *NewFieldParseError("field1", errors.New("error1"))
|
fieldErr1 := *NewFieldParseError("field1", "value1", errors.New("error1"))
|
||||||
fieldErr2 := *NewFieldParseError("field2", errors.New("error2"))
|
fieldErr2 := *NewFieldParseError("field2", "value2", errors.New("error2"))
|
||||||
fieldErrors := NewFieldParseErrors([]FieldParseError{fieldErr1, fieldErr2})
|
fieldErrors := NewFieldParseErrors([]FieldParseError{fieldErr1, fieldErr2})
|
||||||
|
|
||||||
assert.Len(t, fieldErrors.Errors, 2)
|
assert.Len(t, fieldErrors.Errors, 2)
|
||||||
@@ -258,7 +260,7 @@ func TestErrorConstants(t *testing.T) {
|
|||||||
|
|
||||||
func TestErrorImplementsErrorInterface(t *testing.T) {
|
func TestErrorImplementsErrorInterface(t *testing.T) {
|
||||||
t.Run("FieldParseError implements error interface", func(t *testing.T) {
|
t.Run("FieldParseError implements error interface", func(t *testing.T) {
|
||||||
var err error = NewFieldParseError("field", errors.New("test"))
|
var err error = NewFieldParseError("field", "value", errors.New("test"))
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user