add 'Validate' method to the 'Config'

This commit is contained in:
2025-09-07 01:59:23 +04:00
parent 896bb3ad2d
commit 8b8f32d58f
4 changed files with 601 additions and 12 deletions

View File

@@ -553,8 +553,8 @@ func TestParseConfigFile(t *testing.T) {
})
}
func TestPrintValidationErrors(t *testing.T) {
t.Run("printValidationErrors with empty value", func(t *testing.T) {
func TestPrintParseErrors(t *testing.T) {
t.Run("printParseErrors with empty value", func(t *testing.T) {
// This function prints to stdout, so we can't easily test its output
// But we can test that it doesn't panic
errors := []types.FieldParseError{
@@ -567,11 +567,11 @@ func TestPrintValidationErrors(t *testing.T) {
// Should not panic
assert.NotPanics(t, func() {
printValidationErrors("TEST", errors...)
printParseErrors("TEST", errors...)
})
})
t.Run("printValidationErrors with value", func(t *testing.T) {
t.Run("printParseErrors with value", func(t *testing.T) {
errors := []types.FieldParseError{
{
Field: "test_field",
@@ -582,11 +582,11 @@ func TestPrintValidationErrors(t *testing.T) {
// Should not panic
assert.NotPanics(t, func() {
printValidationErrors("TEST", errors...)
printParseErrors("TEST", errors...)
})
})
t.Run("printValidationErrors with multiple errors", func(t *testing.T) {
t.Run("printParseErrors with multiple errors", func(t *testing.T) {
errors := []types.FieldParseError{
{
Field: "field1",
@@ -602,7 +602,345 @@ func TestPrintValidationErrors(t *testing.T) {
// Should not panic
assert.NotPanics(t, func() {
printValidationErrors("TEST", errors...)
printParseErrors("TEST", errors...)
})
})
}
func TestValidate(t *testing.T) {
t.Run("Valid config returns no error", func(t *testing.T) {
testURL, _ := url.Parse("https://example.com")
timeout := 30 * time.Second
duration := 1 * time.Minute
config := Config{
Method: utils.ToPtr("GET"),
URL: testURL,
Timeout: &timeout,
DodosCount: utils.ToPtr(uint(5)),
RequestCount: utils.ToPtr(uint(100)),
Duration: &duration,
Yes: utils.ToPtr(true),
SkipVerify: utils.ToPtr(false),
}
err := config.Validate()
assert.NoError(t, err)
})
t.Run("Missing Method returns validation error", func(t *testing.T) {
testURL, _ := url.Parse("https://example.com")
config := Config{
URL: testURL,
DodosCount: utils.ToPtr(uint(5)),
Yes: utils.ToPtr(true),
SkipVerify: utils.ToPtr(false),
}
err := config.Validate()
require.Error(t, err)
var validationErr types.FieldValidationErrors
require.ErrorAs(t, err, &validationErr)
assert.Contains(t, err.Error(), "method is required")
})
t.Run("Missing URL returns validation error", func(t *testing.T) {
config := Config{
Method: utils.ToPtr("GET"),
DodosCount: utils.ToPtr(uint(5)),
Yes: utils.ToPtr(true),
SkipVerify: utils.ToPtr(false),
}
err := config.Validate()
require.Error(t, err)
var validationErr types.FieldValidationErrors
require.ErrorAs(t, err, &validationErr)
assert.Contains(t, err.Error(), "URL is required")
})
t.Run("Invalid URL scheme returns validation error", func(t *testing.T) {
testURL, _ := url.Parse("ftp://example.com")
config := Config{
Method: utils.ToPtr("GET"),
URL: testURL,
DodosCount: utils.ToPtr(uint(5)),
Yes: utils.ToPtr(true),
SkipVerify: utils.ToPtr(false),
}
err := config.Validate()
require.Error(t, err)
var validationErr types.FieldValidationErrors
require.ErrorAs(t, err, &validationErr)
assert.Contains(t, err.Error(), "URL scheme must be one of")
})
t.Run("Missing DodosCount returns validation error", func(t *testing.T) {
testURL, _ := url.Parse("https://example.com")
config := Config{
Method: utils.ToPtr("GET"),
URL: testURL,
Yes: utils.ToPtr(true),
SkipVerify: utils.ToPtr(false),
}
err := config.Validate()
require.Error(t, err)
var validationErr types.FieldValidationErrors
require.ErrorAs(t, err, &validationErr)
assert.Contains(t, err.Error(), "dodos count is required")
})
t.Run("Zero DodosCount returns validation error", func(t *testing.T) {
testURL, _ := url.Parse("https://example.com")
config := Config{
Method: utils.ToPtr("GET"),
URL: testURL,
DodosCount: utils.ToPtr(uint(0)),
Yes: utils.ToPtr(true),
SkipVerify: utils.ToPtr(false),
}
err := config.Validate()
require.Error(t, err)
var validationErr types.FieldValidationErrors
require.ErrorAs(t, err, &validationErr)
assert.Contains(t, err.Error(), "dodos count must be greater than 0")
})
t.Run("Missing both RequestCount and Duration returns validation error", func(t *testing.T) {
testURL, _ := url.Parse("https://example.com")
config := Config{
Method: utils.ToPtr("GET"),
URL: testURL,
DodosCount: utils.ToPtr(uint(5)),
Yes: utils.ToPtr(true),
SkipVerify: utils.ToPtr(false),
}
err := config.Validate()
require.Error(t, err)
var validationErr types.FieldValidationErrors
require.ErrorAs(t, err, &validationErr)
assert.Contains(t, err.Error(), "either request count or duration must be specified")
})
t.Run("Both RequestCount and Duration zero returns validation error", func(t *testing.T) {
testURL, _ := url.Parse("https://example.com")
duration := time.Duration(0)
config := Config{
Method: utils.ToPtr("GET"),
URL: testURL,
DodosCount: utils.ToPtr(uint(5)),
RequestCount: utils.ToPtr(uint(0)),
Duration: &duration,
Yes: utils.ToPtr(true),
SkipVerify: utils.ToPtr(false),
}
err := config.Validate()
require.Error(t, err)
var validationErr types.FieldValidationErrors
require.ErrorAs(t, err, &validationErr)
assert.Contains(t, err.Error(), "both request count and duration cannot be zero")
})
t.Run("Zero RequestCount only returns validation error", func(t *testing.T) {
testURL, _ := url.Parse("https://example.com")
config := Config{
Method: utils.ToPtr("GET"),
URL: testURL,
DodosCount: utils.ToPtr(uint(5)),
RequestCount: utils.ToPtr(uint(0)),
Yes: utils.ToPtr(true),
SkipVerify: utils.ToPtr(false),
}
err := config.Validate()
require.Error(t, err)
var validationErr types.FieldValidationErrors
require.ErrorAs(t, err, &validationErr)
assert.Contains(t, err.Error(), "request count must be greater than 0")
})
t.Run("Zero Duration only returns validation error", func(t *testing.T) {
testURL, _ := url.Parse("https://example.com")
duration := time.Duration(0)
config := Config{
Method: utils.ToPtr("GET"),
URL: testURL,
DodosCount: utils.ToPtr(uint(5)),
Duration: &duration,
Yes: utils.ToPtr(true),
SkipVerify: utils.ToPtr(false),
}
err := config.Validate()
require.Error(t, err)
var validationErr types.FieldValidationErrors
require.ErrorAs(t, err, &validationErr)
assert.Contains(t, err.Error(), "duration must be greater than 0")
})
t.Run("Missing Yes returns validation error", func(t *testing.T) {
testURL, _ := url.Parse("https://example.com")
config := Config{
Method: utils.ToPtr("GET"),
URL: testURL,
DodosCount: utils.ToPtr(uint(5)),
RequestCount: utils.ToPtr(uint(100)),
SkipVerify: utils.ToPtr(false),
}
err := config.Validate()
require.Error(t, err)
var validationErr types.FieldValidationErrors
require.ErrorAs(t, err, &validationErr)
assert.Contains(t, err.Error(), "yes field is required")
})
t.Run("Missing SkipVerify returns validation error", func(t *testing.T) {
testURL, _ := url.Parse("https://example.com")
config := Config{
Method: utils.ToPtr("GET"),
URL: testURL,
DodosCount: utils.ToPtr(uint(5)),
RequestCount: utils.ToPtr(uint(100)),
Yes: utils.ToPtr(true),
}
err := config.Validate()
require.Error(t, err)
var validationErr types.FieldValidationErrors
require.ErrorAs(t, err, &validationErr)
assert.Contains(t, err.Error(), "skip verify field is required")
})
t.Run("Invalid proxy scheme returns validation error", func(t *testing.T) {
testURL, _ := url.Parse("https://example.com")
proxyURL, _ := url.Parse("ftp://proxy.example.com:8080")
config := Config{
Method: utils.ToPtr("GET"),
URL: testURL,
DodosCount: utils.ToPtr(uint(5)),
RequestCount: utils.ToPtr(uint(100)),
Yes: utils.ToPtr(true),
SkipVerify: utils.ToPtr(false),
Proxies: types.Proxies{types.Proxy(*proxyURL)},
}
err := config.Validate()
require.Error(t, err)
var validationErr types.FieldValidationErrors
require.ErrorAs(t, err, &validationErr)
assert.Contains(t, err.Error(), "proxy scheme must be one of")
assert.Contains(t, err.Error(), "Proxy[0]")
})
t.Run("Multiple invalid proxies return validation errors", func(t *testing.T) {
testURL, _ := url.Parse("https://example.com")
proxyURL1, _ := url.Parse("ftp://proxy1.example.com:8080")
proxyURL2, _ := url.Parse("ldap://proxy2.example.com:389")
config := Config{
Method: utils.ToPtr("GET"),
URL: testURL,
DodosCount: utils.ToPtr(uint(5)),
RequestCount: utils.ToPtr(uint(100)),
Yes: utils.ToPtr(true),
SkipVerify: utils.ToPtr(false),
Proxies: types.Proxies{types.Proxy(*proxyURL1), types.Proxy(*proxyURL2)},
}
err := config.Validate()
require.Error(t, err)
var validationErr types.FieldValidationErrors
require.ErrorAs(t, err, &validationErr)
assert.Len(t, validationErr.Errors, 2)
assert.Contains(t, err.Error(), "Proxy[0]")
assert.Contains(t, err.Error(), "Proxy[1]")
})
t.Run("Valid proxy schemes pass validation", func(t *testing.T) {
testURL, _ := url.Parse("https://example.com")
proxyURL1, _ := url.Parse("http://proxy1.example.com:8080")
proxyURL2, _ := url.Parse("socks5://proxy2.example.com:1080")
proxyURL3, _ := url.Parse("socks5h://proxy3.example.com:1080")
config := Config{
Method: utils.ToPtr("GET"),
URL: testURL,
DodosCount: utils.ToPtr(uint(5)),
RequestCount: utils.ToPtr(uint(100)),
Yes: utils.ToPtr(true),
SkipVerify: utils.ToPtr(false),
Proxies: types.Proxies{types.Proxy(*proxyURL1), types.Proxy(*proxyURL2), types.Proxy(*proxyURL3)},
}
err := config.Validate()
assert.NoError(t, err)
})
t.Run("Multiple validation errors are collected", func(t *testing.T) {
config := Config{
// Missing Method, URL, DodosCount, Yes, SkipVerify
// Missing both RequestCount and Duration
}
err := config.Validate()
require.Error(t, err)
var validationErr types.FieldValidationErrors
require.ErrorAs(t, err, &validationErr)
assert.Len(t, validationErr.Errors, 6) // All required fields missing
assert.Contains(t, err.Error(), "method is required")
assert.Contains(t, err.Error(), "URL is required")
assert.Contains(t, err.Error(), "dodos count is required")
assert.Contains(t, err.Error(), "either request count or duration must be specified")
assert.Contains(t, err.Error(), "yes field is required")
assert.Contains(t, err.Error(), "skip verify field is required")
})
t.Run("Valid config with Duration only passes validation", func(t *testing.T) {
testURL, _ := url.Parse("https://example.com")
duration := 30 * time.Second
config := Config{
Method: utils.ToPtr("POST"),
URL: testURL,
DodosCount: utils.ToPtr(uint(10)),
Duration: &duration,
Yes: utils.ToPtr(false),
SkipVerify: utils.ToPtr(true),
}
err := config.Validate()
assert.NoError(t, err)
})
t.Run("Valid config with RequestCount only passes validation", func(t *testing.T) {
testURL, _ := url.Parse("https://example.com")
config := Config{
Method: utils.ToPtr("PUT"),
URL: testURL,
DodosCount: utils.ToPtr(uint(3)),
RequestCount: utils.ToPtr(uint(50)),
Yes: utils.ToPtr(true),
SkipVerify: utils.ToPtr(false),
}
err := config.Validate()
assert.NoError(t, err)
})
}