Files
dodo/pkg/config/file_test.go

1061 lines
30 KiB
Go

package config
import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"testing"
"time"
"github.com/aykhans/dodo/pkg/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.yaml.in/yaml/v4"
)
func TestNewConfigFileParser(t *testing.T) {
t.Run("NewConfigFileParser with valid config file", func(t *testing.T) {
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
require.NotNil(t, parser)
assert.Equal(t, *configFile, parser.configFile)
})
t.Run("NewConfigFileParser with remote config file", func(t *testing.T) {
configFile, _ := types.ParseConfigFile("https://example.com/config.yaml")
parser := NewConfigFileParser(*configFile)
require.NotNil(t, parser)
assert.Equal(t, *configFile, parser.configFile)
assert.Equal(t, types.ConfigFileLocationRemote, parser.configFile.LocationType())
})
}
func TestStringOrSliceField_UnmarshalYAML(t *testing.T) {
t.Run("UnmarshalYAML with single string", func(t *testing.T) {
yamlData := `value: single_string`
type testStruct struct {
Value stringOrSliceField `yaml:"value"`
}
var ts testStruct
err := yaml.Unmarshal([]byte(yamlData), &ts)
require.NoError(t, err)
assert.Equal(t, stringOrSliceField{"single_string"}, ts.Value)
})
t.Run("UnmarshalYAML with array of strings", func(t *testing.T) {
yamlData := `value:
- first
- second
- third`
type testStruct struct {
Value stringOrSliceField `yaml:"value"`
}
var ts testStruct
err := yaml.Unmarshal([]byte(yamlData), &ts)
require.NoError(t, err)
assert.Equal(t, stringOrSliceField{"first", "second", "third"}, ts.Value)
})
t.Run("UnmarshalYAML with empty array", func(t *testing.T) {
yamlData := `value: []`
type testStruct struct {
Value stringOrSliceField `yaml:"value"`
}
var ts testStruct
err := yaml.Unmarshal([]byte(yamlData), &ts)
require.NoError(t, err)
assert.Empty(t, ts.Value)
})
t.Run("UnmarshalYAML with invalid type", func(t *testing.T) {
yamlData := `value:
key: value`
type testStruct struct {
Value stringOrSliceField `yaml:"value"`
}
var ts testStruct
err := yaml.Unmarshal([]byte(yamlData), &ts)
require.Error(t, err)
assert.Contains(t, err.Error(), "expected a string or a sequence of strings")
})
}
func TestConfigFileParser_ParseYAML(t *testing.T) {
t.Run("ParseYAML with all fields", func(t *testing.T) {
yamlData := `
method: POST
url: https://api.example.com/endpoint
timeout: 30s
dodos: 10
requests: 1000
duration: 5m
yes: true
skipVerify: true
params:
- key1=value1
- key2=value2
headers:
- "Content-Type: application/json"
- "Authorization: Bearer token"
cookies:
- session=abc123
- user=john
body:
- '{"data": "test1"}'
- '{"data": "test2"}'
proxy:
- http://proxy1.example.com:8080
- socks5://proxy2.example.com:1080
files:
- config1.yaml
- config2.yml`
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
assert.Equal(t, "POST", *config.Method)
assert.Equal(t, "https://api.example.com/endpoint", config.URL.String())
assert.Equal(t, 30*time.Second, *config.Timeout)
assert.Equal(t, uint(10), *config.DodosCount)
assert.Equal(t, uint(1000), *config.RequestCount)
assert.Equal(t, 5*time.Minute, *config.Duration)
assert.True(t, *config.Yes)
assert.True(t, *config.SkipVerify)
assert.Len(t, config.Params, 2)
assert.Equal(t, "key1", config.Params[0].Key)
assert.Equal(t, []string{"value1"}, config.Params[0].Value)
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.Len(t, config.Cookies, 2)
assert.Equal(t, "session", config.Cookies[0].Key)
assert.Len(t, config.Bodies, 2)
assert.Equal(t, types.Body(`{"data": "test1"}`), config.Bodies[0]) //nolint:testifylint
assert.Len(t, config.Proxies, 2)
assert.Equal(t, "http://proxy1.example.com:8080", config.Proxies[0].String())
assert.Len(t, config.Files, 2)
})
t.Run("ParseYAML with single value fields as strings", func(t *testing.T) {
yamlData := `
params: key=value
headers: "Content-Type: application/json"
cookies: session=token
body: '{"data": "test"}'
proxy: http://proxy.example.com:8080
files: config.yaml`
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
assert.Len(t, config.Params, 1)
assert.Equal(t, "key", 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:8080", config.Proxies[0].String())
assert.Len(t, config.Files, 1)
})
t.Run("ParseYAML with minimal fields", func(t *testing.T) {
yamlData := `
method: GET
url: https://example.com`
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
assert.Equal(t, "GET", *config.Method)
assert.Equal(t, "https://example.com", config.URL.String())
// Check other fields are nil or empty
assert.Nil(t, config.Timeout)
assert.Nil(t, config.DodosCount)
assert.Nil(t, config.RequestCount)
assert.Nil(t, config.Duration)
assert.Nil(t, config.Yes)
assert.Nil(t, config.SkipVerify)
assert.Empty(t, config.Params)
assert.Empty(t, config.Headers)
assert.Empty(t, config.Cookies)
assert.Empty(t, config.Bodies)
assert.Empty(t, config.Proxies)
assert.Empty(t, config.Files)
})
t.Run("ParseYAML with invalid URL", func(t *testing.T) {
yamlData := `
url: "://invalid-url"`
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
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("ParseYAML with invalid proxy", func(t *testing.T) {
yamlData := `
proxy:
- http://valid-proxy.com:8080
- "://invalid-proxy"`
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
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-proxy", fieldErr.Errors[0].Value)
})
t.Run("ParseYAML with invalid YAML", func(t *testing.T) {
yamlData := `
method: POST
invalid yaml structure:
nested: improperly
url: https://example.com`
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
// The YAML parser may handle this differently, so let's check if we get any error
if err == nil {
// If no error, check if the config has expected values
require.NotNil(t, config)
require.NotNil(t, config.Method)
assert.Equal(t, "POST", *config.Method)
} else {
// If error, it should be an UnmarshalError
var unmarshalErr types.UnmarshalError
require.ErrorAs(t, err, &unmarshalErr)
}
})
t.Run("ParseYAML with empty data", func(t *testing.T) {
yamlData := ``
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
// All fields should be nil or empty
assert.Nil(t, config.Method)
assert.Nil(t, config.URL)
assert.Nil(t, config.Timeout)
assert.Nil(t, config.DodosCount)
assert.Nil(t, config.RequestCount)
assert.Nil(t, config.Duration)
assert.Nil(t, config.Yes)
assert.Nil(t, config.SkipVerify)
assert.Empty(t, config.Params)
assert.Empty(t, config.Headers)
assert.Empty(t, config.Cookies)
assert.Empty(t, config.Bodies)
assert.Empty(t, config.Proxies)
assert.Empty(t, config.Files)
})
t.Run("ParseYAML with boolean fields", func(t *testing.T) {
yamlData := `
yes: false
skipVerify: false`
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
require.NotNil(t, config.Yes)
assert.False(t, *config.Yes)
require.NotNil(t, config.SkipVerify)
assert.False(t, *config.SkipVerify)
})
t.Run("ParseYAML with zero values", func(t *testing.T) {
yamlData := `
timeout: 0s
dodos: 0
requests: 0
duration: 0s`
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
assert.Equal(t, time.Duration(0), *config.Timeout)
assert.Equal(t, uint(0), *config.DodosCount)
assert.Equal(t, uint(0), *config.RequestCount)
assert.Equal(t, time.Duration(0), *config.Duration)
})
t.Run("ParseYAML with nested config files", func(t *testing.T) {
yamlData := `
files:
- /path/to/config1.yaml
- https://example.com/config2.yml
- relative/config3.yaml`
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
assert.Len(t, config.Files, 3)
// Check file paths
assert.Equal(t, "/path/to/config1.yaml", config.Files[0].Path())
assert.Equal(t, "https://example.com/config2.yml", config.Files[1].Path())
assert.Equal(t, "relative/config3.yaml", config.Files[2].Path())
// Check file types
assert.Equal(t, types.ConfigFileTypeYAML, config.Files[0].Type())
assert.Equal(t, types.ConfigFileTypeYAML, config.Files[1].Type())
assert.Equal(t, types.ConfigFileTypeYAML, config.Files[2].Type())
// Check location types
assert.Equal(t, types.ConfigFileLocationLocal, config.Files[0].LocationType())
assert.Equal(t, types.ConfigFileLocationRemote, config.Files[1].LocationType())
assert.Equal(t, types.ConfigFileLocationLocal, config.Files[2].LocationType())
})
t.Run("ParseYAML with complex durations", func(t *testing.T) {
yamlData := `
timeout: 1h30m45s
duration: 2h15m`
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
expectedTimeout := time.Hour + 30*time.Minute + 45*time.Second
expectedDuration := 2*time.Hour + 15*time.Minute
assert.Equal(t, expectedTimeout, *config.Timeout)
assert.Equal(t, expectedDuration, *config.Duration)
})
t.Run("ParseYAML with multiple headers with same key", func(t *testing.T) {
yamlData := `
headers:
- "Accept: text/html"
- "Accept: application/json"
- "Content-Type: application/json"`
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
assert.Len(t, config.Headers, 2)
// Check that Accept header has both values
acceptValue := config.Headers.GetValue("Accept")
require.NotNil(t, acceptValue)
assert.Equal(t, []string{"text/html", "application/json"}, *acceptValue)
})
t.Run("ParseYAML with multiple params with same key", func(t *testing.T) {
yamlData := `
params:
- filter=active
- filter=verified
- limit=10`
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
assert.Len(t, config.Params, 2)
// Check that filter param has both values
filterValue := config.Params.GetValue("filter")
require.NotNil(t, filterValue)
assert.Equal(t, []string{"active", "verified"}, *filterValue)
})
t.Run("ParseYAML with special characters in values", func(t *testing.T) {
yamlData := `
headers:
- "X-Special: !@#$%^&*()"
body:
- '{"special": "characters: !@#$%^&*()"}'
cookies:
- "session=abc123!@#"`
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
assert.Len(t, config.Headers, 1)
assert.Contains(t, config.Headers[0].Value[0], "!@#$%^&*()")
assert.Len(t, config.Bodies, 1)
assert.Contains(t, string(config.Bodies[0]), "!@#$%^&*()")
assert.Len(t, config.Cookies, 1)
assert.Equal(t, "abc123!@#", config.Cookies[0].Value[0])
})
}
func TestConfigFileParser_Parse_LocalFile(t *testing.T) {
t.Run("Parse local YAML file with valid content", func(t *testing.T) {
// Create a temporary file
tmpDir := t.TempDir()
tmpFile := filepath.Join(tmpDir, "config.yaml")
yamlContent := `
method: POST
url: https://api.example.com
dodos: 5
yes: true`
err := os.WriteFile(tmpFile, []byte(yamlContent), 0644)
require.NoError(t, err)
configFile, _ := types.ParseConfigFile(tmpFile)
parser := NewConfigFileParser(*configFile)
config, err := parser.Parse()
require.NoError(t, err)
require.NotNil(t, config)
assert.Equal(t, "POST", *config.Method)
assert.Equal(t, "https://api.example.com", config.URL.String())
assert.Equal(t, uint(5), *config.DodosCount)
assert.True(t, *config.Yes)
})
t.Run("Parse local file that doesn't exist", func(t *testing.T) {
configFile, _ := types.ParseConfigFile("/nonexistent/file.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.Parse()
assert.Nil(t, config)
var readErr types.ConfigFileReadError
require.ErrorAs(t, err, &readErr)
})
t.Run("Parse local file with invalid YAML", func(t *testing.T) {
tmpDir := t.TempDir()
tmpFile := filepath.Join(tmpDir, "invalid.yaml")
invalidYAML := `
method: POST
invalid indentation here:
nested: improperly
url: https://example.com`
err := os.WriteFile(tmpFile, []byte(invalidYAML), 0644)
require.NoError(t, err)
configFile, _ := types.ParseConfigFile(tmpFile)
parser := NewConfigFileParser(*configFile)
config, err := parser.Parse()
// The YAML parser may handle this differently
if err == nil {
// If no error, check if the config has expected values
require.NotNil(t, config)
require.NotNil(t, config.Method)
assert.Equal(t, "POST", *config.Method)
} else {
// If error, it should be an UnmarshalError
var unmarshalErr types.UnmarshalError
require.ErrorAs(t, err, &unmarshalErr)
}
})
t.Run("Parse local file with unknown extension", func(t *testing.T) {
tmpDir := t.TempDir()
tmpFile := filepath.Join(tmpDir, "config.unknown")
yamlContent := `
method: GET
url: https://example.com`
err := os.WriteFile(tmpFile, []byte(yamlContent), 0644)
require.NoError(t, err)
configFile, _ := types.ParseConfigFile(tmpFile)
assert.Equal(t, types.ConfigFileTypeUnknown, configFile.Type())
parser := NewConfigFileParser(*configFile)
config, err := parser.Parse()
// Should still parse as YAML for unknown types
require.NoError(t, err)
require.NotNil(t, config)
assert.Equal(t, "GET", *config.Method)
})
t.Run("Parse local file with .yml extension", func(t *testing.T) {
tmpDir := t.TempDir()
tmpFile := filepath.Join(tmpDir, "config.yml")
yamlContent := `
method: DELETE
url: https://api.example.com/resource`
err := os.WriteFile(tmpFile, []byte(yamlContent), 0644)
require.NoError(t, err)
configFile, _ := types.ParseConfigFile(tmpFile)
assert.Equal(t, types.ConfigFileTypeYAML, configFile.Type())
parser := NewConfigFileParser(*configFile)
config, err := parser.Parse()
require.NoError(t, err)
require.NotNil(t, config)
assert.Equal(t, "DELETE", *config.Method)
})
}
func TestConfigFileParser_Parse_RemoteFile(t *testing.T) {
t.Run("Parse remote YAML file with valid content", func(t *testing.T) {
yamlContent := `
method: PUT
url: https://api.example.com/update
timeout: 15s
dodos: 3`
// Create test HTTP server
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(yamlContent))
}))
defer server.Close()
configFile, _ := types.ParseConfigFile(server.URL + "/config.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.Parse()
require.NoError(t, err)
require.NotNil(t, config)
assert.Equal(t, "PUT", *config.Method)
assert.Equal(t, "https://api.example.com/update", config.URL.String())
assert.Equal(t, 15*time.Second, *config.Timeout)
assert.Equal(t, uint(3), *config.DodosCount)
})
t.Run("Parse remote file with 404 status", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("Not Found"))
}))
defer server.Close()
configFile, _ := types.ParseConfigFile(server.URL + "/missing.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.Parse()
assert.Nil(t, config)
var readErr types.ConfigFileReadError
require.ErrorAs(t, err, &readErr)
assert.Contains(t, err.Error(), "404")
})
t.Run("Parse remote file with server error", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Internal Server Error"))
}))
defer server.Close()
configFile, _ := types.ParseConfigFile(server.URL + "/error.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.Parse()
assert.Nil(t, config)
var readErr types.ConfigFileReadError
require.ErrorAs(t, err, &readErr)
assert.Contains(t, err.Error(), "500")
})
t.Run("Parse remote file with invalid YAML", func(t *testing.T) {
invalidYAML := `
method: POST
bad indentation:
nested: improperly
url: https://example.com`
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(invalidYAML))
}))
defer server.Close()
configFile, _ := types.ParseConfigFile(server.URL + "/invalid.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.Parse()
// The YAML parser may handle this differently
if err == nil {
// If no error, check if the config has expected values
require.NotNil(t, config)
require.NotNil(t, config.Method)
assert.Equal(t, "POST", *config.Method)
} else {
// If error, it should be an UnmarshalError
var unmarshalErr types.UnmarshalError
require.ErrorAs(t, err, &unmarshalErr)
}
})
t.Run("Parse remote file with network error", func(t *testing.T) {
// Use an invalid URL that will cause a network error
configFile, _ := types.ParseConfigFile("http://invalid-domain-that-does-not-exist-12345.com/config.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.Parse()
assert.Nil(t, config)
var readErr types.ConfigFileReadError
require.ErrorAs(t, err, &readErr)
})
t.Run("Parse remote file with redirect", func(t *testing.T) {
yamlContent := `
method: GET
url: https://redirected.example.com`
redirectCount := 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if redirectCount < 1 {
redirectCount++
http.Redirect(w, r, "/final", http.StatusMovedPermanently)
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte(yamlContent))
}))
defer server.Close()
configFile, _ := types.ParseConfigFile(server.URL + "/redirect.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.Parse()
require.NoError(t, err)
require.NotNil(t, config)
assert.Equal(t, "GET", *config.Method)
assert.Equal(t, "https://redirected.example.com", config.URL.String())
})
}
func TestConfigFileParser_InterfaceConformance(t *testing.T) {
t.Run("ConfigFileParser implements IParser interface", func(t *testing.T) {
configFile, _ := types.ParseConfigFile("test.yaml")
parser := ConfigFileParser{configFile: *configFile}
// This test verifies that ConfigFileParser implements the IParser interface
var _ IParser = parser
// Also test that the pointer type implements the interface
var _ IParser = &parser
})
}
func TestConfigFileParser_EdgeCases(t *testing.T) {
t.Run("ParseYAML with very large numbers", func(t *testing.T) {
yamlData := `
dodos: 999999999
requests: 999999999`
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
assert.Equal(t, uint(999999999), *config.DodosCount)
assert.Equal(t, uint(999999999), *config.RequestCount)
})
t.Run("ParseYAML with empty arrays", func(t *testing.T) {
yamlData := `
params: []
headers: []
cookies: []
body: []
proxy: []
files: []`
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
assert.Empty(t, config.Params)
assert.Empty(t, config.Headers)
assert.Empty(t, config.Cookies)
assert.Empty(t, config.Bodies)
assert.Empty(t, config.Proxies)
assert.Empty(t, config.Files)
})
t.Run("ParseYAML with multiline body", func(t *testing.T) {
yamlData := `
body:
- |
This is a
multiline
body content
- "Single line body"`
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
assert.Len(t, config.Bodies, 2)
assert.Contains(t, string(config.Bodies[0]), "multiline")
assert.Equal(t, types.Body("Single line body"), config.Bodies[1])
})
t.Run("ParseYAML with URL containing query parameters", func(t *testing.T) {
yamlData := `
url: "https://api.example.com/endpoint?param1=value1&param2=value2#fragment"`
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
require.NotNil(t, config.URL)
parsedURL, _ := url.Parse("https://api.example.com/endpoint?param1=value1&param2=value2#fragment")
assert.Equal(t, parsedURL, config.URL)
assert.Equal(t, "param1=value1&param2=value2", config.URL.RawQuery)
assert.Equal(t, "fragment", config.URL.Fragment)
})
t.Run("ParseYAML with headers containing colons in values", func(t *testing.T) {
yamlData := `
headers:
- "Timestamp: 2024-01-01T10:30:00Z"
- "Ratio: 16:9"`
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
assert.Len(t, config.Headers, 2)
assert.Equal(t, "Timestamp", config.Headers[0].Key)
assert.Equal(t, []string{"2024-01-01T10:30:00Z"}, config.Headers[0].Value)
assert.Equal(t, "Ratio", config.Headers[1].Key)
assert.Equal(t, []string{"16:9"}, config.Headers[1].Value)
})
t.Run("ParseYAML with params containing equals in values", func(t *testing.T) {
yamlData := `
params:
- "equation=a=b+c"
- "formula=x==y"`
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
assert.Len(t, config.Params, 2)
assert.Equal(t, "equation", config.Params[0].Key)
assert.Equal(t, []string{"a=b+c"}, config.Params[0].Value)
assert.Equal(t, "formula", config.Params[1].Key)
assert.Equal(t, []string{"x==y"}, config.Params[1].Value)
})
t.Run("ParseYAML with null values", func(t *testing.T) {
yamlData := `
method: null
url: null
timeout: null
dodos: null
requests: null
duration: null
yes: null
skipVerify: null`
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
assert.Nil(t, config.Method)
assert.Nil(t, config.URL)
assert.Nil(t, config.Timeout)
assert.Nil(t, config.DodosCount)
assert.Nil(t, config.RequestCount)
assert.Nil(t, config.Duration)
assert.Nil(t, config.Yes)
assert.Nil(t, config.SkipVerify)
})
t.Run("Parse with empty file content", func(t *testing.T) {
tmpDir := t.TempDir()
tmpFile := filepath.Join(tmpDir, "empty.yaml")
err := os.WriteFile(tmpFile, []byte(""), 0644)
require.NoError(t, err)
configFile, _ := types.ParseConfigFile(tmpFile)
parser := NewConfigFileParser(*configFile)
config, err := parser.Parse()
require.NoError(t, err)
require.NotNil(t, config)
// Should return empty config without error
assert.Nil(t, config.Method)
assert.Nil(t, config.URL)
})
t.Run("ParseYAML with comments", func(t *testing.T) {
yamlData := `
# This is a comment
method: POST # Inline comment
url: https://example.com
# Another comment
dodos: 5`
configFile, _ := types.ParseConfigFile("test.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
assert.Equal(t, "POST", *config.Method)
assert.Equal(t, uint(5), *config.DodosCount)
})
}
func TestConfigFileParser_ComplexScenarios(t *testing.T) {
t.Run("Parse complete production configuration", func(t *testing.T) {
yamlData := `
# Production configuration
method: POST
url: https://api.production.com/v2/endpoint
timeout: 60s
dodos: 100
requests: 10000
duration: 1h
yes: false
skipVerify: false
# Authentication
headers:
- "Authorization: Bearer production-token-xyz123"
- "Content-Type: application/json"
- "Accept: application/json"
- "X-API-Version: 2.0"
# Request parameters
params:
- page=1
- limit=100
- filter=active
- sort=created_desc
# Session management
cookies:
- session=prod-session-abc123
- preferences=production
- tracking=disabled
# Request bodies for testing different scenarios
body:
- '{"action": "create", "resource": "user"}'
- '{"action": "update", "resource": "profile"}'
- '{"action": "delete", "resource": "session"}'
# Corporate proxy
proxy:
- http://corporate-proxy.example.com:8080
# Additional configuration files
files:
- /configs/shared/common.yaml
- https://config-server.example.com/production.yaml`
configFile, _ := types.ParseConfigFile("production.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
// Verify all production settings
assert.Equal(t, "POST", *config.Method)
assert.Equal(t, "https://api.production.com/v2/endpoint", config.URL.String())
assert.Equal(t, 60*time.Second, *config.Timeout)
assert.Equal(t, uint(100), *config.DodosCount)
assert.Equal(t, uint(10000), *config.RequestCount)
assert.Equal(t, time.Hour, *config.Duration)
assert.False(t, *config.Yes)
assert.False(t, *config.SkipVerify)
assert.Len(t, config.Headers, 4)
assert.Len(t, config.Params, 4)
assert.Len(t, config.Cookies, 3)
assert.Len(t, config.Bodies, 3)
assert.Len(t, config.Proxies, 1)
assert.Len(t, config.Files, 2)
})
t.Run("Parse development configuration with overrides", func(t *testing.T) {
yamlData := `
# Development configuration with testing overrides
method: GET
url: http://localhost:8080/api/test
timeout: 5s
dodos: 1
requests: 10
duration: 30s
yes: true # Auto-confirm for development
skipVerify: true # Skip certificate verification for local testing
headers:
- "X-Debug: true"
- "X-Test-Mode: enabled"
params:
- debug=true
- verbose=true
body:
- "test data"`
configFile, _ := types.ParseConfigFile("dev.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
// Verify development settings
assert.Equal(t, "GET", *config.Method)
assert.Equal(t, "http://localhost:8080/api/test", config.URL.String())
assert.Equal(t, 5*time.Second, *config.Timeout)
assert.Equal(t, uint(1), *config.DodosCount)
assert.True(t, *config.Yes)
assert.True(t, *config.SkipVerify)
})
}
func TestConfigFileParser_RealWorldFiles(t *testing.T) {
t.Run("Parse file with permission issues", func(t *testing.T) {
if os.Geteuid() == 0 {
t.Skip("Cannot test permission issues as root")
}
tmpDir := t.TempDir()
tmpFile := filepath.Join(tmpDir, "no-read.yaml")
err := os.WriteFile(tmpFile, []byte("method: GET"), 0000)
require.NoError(t, err)
configFile, _ := types.ParseConfigFile(tmpFile)
parser := NewConfigFileParser(*configFile)
config, err := parser.Parse()
assert.Nil(t, config)
var readErr types.ConfigFileReadError
require.ErrorAs(t, err, &readErr)
})
t.Run("Parse very large configuration file", func(t *testing.T) {
// Create a large YAML with many entries
yamlData := `
method: POST
url: https://example.com
headers:`
// Add 1000 headers
for i := range 1000 {
yamlData += fmt.Sprintf("\n - \"Header-%d: value-%d\"", i, i)
}
yamlData += "\nparams:"
// Add 1000 params
for i := range 1000 {
yamlData += fmt.Sprintf("\n - param%d=value%d", i, i)
}
configFile, _ := types.ParseConfigFile("large.yaml")
parser := NewConfigFileParser(*configFile)
config, err := parser.ParseYAML([]byte(yamlData))
require.NoError(t, err)
require.NotNil(t, config)
assert.Len(t, config.Headers, 1000)
assert.Len(t, config.Params, 1000)
})
}