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¶m2=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¶m2=value2#fragment") assert.Equal(t, parsedURL, config.URL) assert.Equal(t, "param1=value1¶m2=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) }) }