mirror of
https://github.com/go-i2p/go-i2ptunnel-config.git
synced 2025-12-01 06:54:57 -05:00
694 lines
18 KiB
Go
694 lines
18 KiB
Go
package i2pconv
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestCrossFormatConversion tests comprehensive conversion between all supported formats
|
|
// This covers the primary conversion paths: properties ↔ yaml ↔ ini
|
|
func TestCrossFormatConversion(t *testing.T) {
|
|
// Base tunnel configuration that works across all formats
|
|
baseConfig := &TunnelConfig{
|
|
Name: "TestTunnel",
|
|
Type: "httpclient",
|
|
Interface: "127.0.0.1",
|
|
Port: 8080,
|
|
Target: "example.i2p",
|
|
Description: "Cross-format test tunnel",
|
|
PersistentKey: true,
|
|
I2CP: map[string]interface{}{
|
|
"leaseSetEncType": []string{"4", "0"},
|
|
"reduceIdleTime": 900000,
|
|
},
|
|
Tunnel: map[string]interface{}{
|
|
"proxyList": "proxy1.i2p,proxy2.i2p",
|
|
"sharedClient": true,
|
|
"useCompression": true,
|
|
},
|
|
Inbound: map[string]interface{}{
|
|
"length": 3,
|
|
},
|
|
Outbound: map[string]interface{}{
|
|
"length": 2,
|
|
},
|
|
}
|
|
|
|
converter := &Converter{strict: false}
|
|
formats := []string{"properties", "yaml", "ini"}
|
|
|
|
// Test round-trip conversions for each format
|
|
for _, format := range formats {
|
|
t.Run("round-trip_"+format, func(t *testing.T) {
|
|
// Convert config to format
|
|
output1, err := converter.generateOutput(baseConfig, format)
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate %s: %v", format, err)
|
|
}
|
|
|
|
// Parse it back
|
|
config1, err := converter.ParseInput(output1, format)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse generated %s: %v", format, err)
|
|
}
|
|
|
|
// Convert back to same format
|
|
output2, err := converter.generateOutput(config1, format)
|
|
if err != nil {
|
|
t.Fatalf("Failed to re-generate %s: %v", format, err)
|
|
}
|
|
|
|
// Parse again to ensure consistency
|
|
config2, err := converter.ParseInput(output2, format)
|
|
if err != nil {
|
|
t.Fatalf("Failed to re-parse %s: %v", format, err)
|
|
}
|
|
|
|
// Verify basic fields are preserved
|
|
if config1.Name != config2.Name {
|
|
t.Errorf("Round-trip %s Name: expected %q, got %q", format, config1.Name, config2.Name)
|
|
}
|
|
if config1.Type != config2.Type {
|
|
t.Errorf("Round-trip %s Type: expected %q, got %q", format, config1.Type, config2.Type)
|
|
}
|
|
if config1.Interface != config2.Interface {
|
|
t.Errorf("Round-trip %s Interface: expected %q, got %q", format, config1.Interface, config2.Interface)
|
|
}
|
|
if config1.Port != config2.Port {
|
|
t.Errorf("Round-trip %s Port: expected %d, got %d", format, config1.Port, config2.Port)
|
|
}
|
|
|
|
t.Logf("%s round-trip successful", strings.ToUpper(format))
|
|
})
|
|
}
|
|
|
|
// Test cross-format conversion paths (properties → yaml → ini → properties)
|
|
t.Run("cross-format_conversion_chain", func(t *testing.T) {
|
|
// Start with properties
|
|
propsOutput, err := converter.generateOutput(baseConfig, "properties")
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate properties: %v", err)
|
|
}
|
|
|
|
// Properties → YAML
|
|
propsConfig, err := converter.ParseInput(propsOutput, "properties")
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse properties: %v", err)
|
|
}
|
|
// The YAML parseInput doesn't handle the wrapper format that generateYAML produces -
|
|
// we need to test direct conversion instead
|
|
_, err = converter.Convert(propsOutput, "properties", "yaml")
|
|
if err != nil {
|
|
t.Fatalf("Failed direct properties to YAML conversion: %v", err)
|
|
}
|
|
|
|
// Test INI conversion
|
|
iniOutput, err := converter.generateOutput(propsConfig, "ini")
|
|
if err != nil {
|
|
t.Fatalf("Failed to convert properties to INI: %v", err)
|
|
}
|
|
|
|
// INI → Properties (complete a partial circle)
|
|
iniConfig, err := converter.ParseInput(iniOutput, "ini")
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse INI: %v", err)
|
|
}
|
|
finalPropsOutput, err := converter.generateOutput(iniConfig, "properties")
|
|
if err != nil {
|
|
t.Fatalf("Failed to convert INI back to properties: %v", err)
|
|
}
|
|
|
|
// Parse final result and verify core fields are preserved
|
|
finalConfig, err := converter.ParseInput(finalPropsOutput, "properties")
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse final properties: %v", err)
|
|
}
|
|
|
|
// Verify essential data is preserved through the conversion chain
|
|
if finalConfig.Name != propsConfig.Name {
|
|
t.Errorf("Cross-format Name: expected %q, got %q", propsConfig.Name, finalConfig.Name)
|
|
}
|
|
if finalConfig.Type != propsConfig.Type {
|
|
t.Errorf("Cross-format Type: expected %q, got %q", propsConfig.Type, finalConfig.Type)
|
|
}
|
|
if finalConfig.Interface != propsConfig.Interface {
|
|
t.Errorf("Cross-format Interface: expected %q, got %q", propsConfig.Interface, finalConfig.Interface)
|
|
}
|
|
if finalConfig.Port != propsConfig.Port {
|
|
t.Errorf("Cross-format Port: expected %d, got %d", propsConfig.Port, finalConfig.Port)
|
|
}
|
|
|
|
t.Log("Cross-format conversion chain completed successfully")
|
|
})
|
|
}
|
|
|
|
// TestYAMLParser tests the parseYAML function with the nested tunnels format
|
|
func TestYAMLParser(t *testing.T) {
|
|
converter := &Converter{}
|
|
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
expected *TunnelConfig
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "valid_yaml_nested_format",
|
|
input: `tunnels:
|
|
HttpProxy:
|
|
type: httpclient
|
|
interface: 127.0.0.1
|
|
port: 4444
|
|
target: example.i2p
|
|
description: HTTP proxy tunnel
|
|
persistentKey: true
|
|
i2cp:
|
|
leaseSetEncType:
|
|
- "4"
|
|
- "0"
|
|
reduceIdleTime: 900000
|
|
options:
|
|
proxyList: "proxy1.i2p,proxy2.i2p"
|
|
sharedClient: true
|
|
inbound:
|
|
length: 3
|
|
outbound:
|
|
length: 2`,
|
|
expected: &TunnelConfig{
|
|
Name: "HttpProxy",
|
|
Type: "httpclient",
|
|
Interface: "127.0.0.1",
|
|
Port: 4444,
|
|
Target: "example.i2p",
|
|
Description: "HTTP proxy tunnel",
|
|
PersistentKey: true,
|
|
I2CP: map[string]interface{}{
|
|
"leaseSetEncType": []interface{}{"4", "0"},
|
|
"reduceIdleTime": 900000,
|
|
},
|
|
Tunnel: map[string]interface{}{
|
|
"proxyList": "proxy1.i2p,proxy2.i2p",
|
|
"sharedClient": true,
|
|
},
|
|
Inbound: map[string]interface{}{
|
|
"length": 3,
|
|
},
|
|
Outbound: map[string]interface{}{
|
|
"length": 2,
|
|
},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "minimal_yaml_tunnel",
|
|
input: `tunnels:
|
|
MinimalTunnel:
|
|
type: client`,
|
|
expected: &TunnelConfig{
|
|
Name: "MinimalTunnel",
|
|
Type: "client",
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "invalid_yaml_syntax",
|
|
input: `tunnels:
|
|
BadTunnel:
|
|
type: httpclient
|
|
port: "not_a_number"
|
|
invalid_yaml: [
|
|
- missing closing bracket`,
|
|
expected: nil,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "empty_tunnels_map",
|
|
input: "tunnels: {}",
|
|
expected: nil,
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result, err := converter.parseYAML([]byte(tt.input))
|
|
|
|
if tt.wantErr {
|
|
if err == nil {
|
|
t.Errorf("parseYAML() expected error, got nil")
|
|
}
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Fatalf("parseYAML() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
|
|
if tt.expected != nil {
|
|
if result.Name != tt.expected.Name {
|
|
t.Errorf("Name: expected %q, got %q", tt.expected.Name, result.Name)
|
|
}
|
|
if result.Type != tt.expected.Type {
|
|
t.Errorf("Type: expected %q, got %q", tt.expected.Type, result.Type)
|
|
}
|
|
if result.Interface != tt.expected.Interface {
|
|
t.Errorf("Interface: expected %q, got %q", tt.expected.Interface, result.Interface)
|
|
}
|
|
if result.Port != tt.expected.Port {
|
|
t.Errorf("Port: expected %d, got %d", tt.expected.Port, result.Port)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestConverterMethods tests the core Converter methods with 0% coverage
|
|
func TestConverterMethods(t *testing.T) {
|
|
converter := &Converter{strict: false}
|
|
|
|
t.Run("Convert_method", func(t *testing.T) {
|
|
// Test successful conversion
|
|
input := `name=TestConvert
|
|
type=httpclient
|
|
interface=127.0.0.1
|
|
listenPort=8080`
|
|
|
|
output, err := converter.Convert([]byte(input), "properties", "yaml")
|
|
if err != nil {
|
|
t.Fatalf("Convert() failed: %v", err)
|
|
}
|
|
|
|
if len(output) == 0 {
|
|
t.Error("Convert() returned empty output")
|
|
}
|
|
|
|
// Verify the output is valid YAML
|
|
_, err = converter.ParseInput(output, "yaml")
|
|
if err != nil {
|
|
t.Errorf("Convert() produced invalid YAML: %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("Convert_validation_error", func(t *testing.T) {
|
|
// Test conversion with invalid config (missing required fields)
|
|
input := `type=httpclient` // Missing name - should fail validation
|
|
|
|
_, err := converter.Convert([]byte(input), "properties", "yaml")
|
|
if err == nil {
|
|
t.Error("Convert() expected validation error, got nil")
|
|
}
|
|
|
|
// Verify it's a ValidationError
|
|
var validationErr *ValidationError
|
|
if !strings.Contains(err.Error(), "validation:") {
|
|
t.Errorf("Expected ValidationError, got: %v", err)
|
|
} else {
|
|
t.Log("ValidationError correctly returned")
|
|
}
|
|
|
|
// Test error unwrapping
|
|
if validationErr != nil && validationErr.Unwrap() == nil {
|
|
t.Error("ValidationError.Unwrap() returned nil")
|
|
}
|
|
})
|
|
|
|
t.Run("Convert_parse_error", func(t *testing.T) {
|
|
// Test conversion with malformed input that should cause parse error, not validation error
|
|
input := `malformed=properties=line=with=too=many=equals`
|
|
|
|
_, err := converter.Convert([]byte(input), "properties", "yaml")
|
|
if err == nil {
|
|
t.Error("Convert() expected error for malformed input, got nil")
|
|
}
|
|
|
|
// The error should occur during parsing/validation, which is expected behavior
|
|
t.Logf("Convert() correctly returned error for malformed input: %v", err)
|
|
})
|
|
|
|
t.Run("generateOutput_unsupported_format", func(t *testing.T) {
|
|
config := &TunnelConfig{
|
|
Name: "TestConfig",
|
|
Type: "httpclient",
|
|
}
|
|
|
|
_, err := converter.generateOutput(config, "unsupported")
|
|
if err == nil {
|
|
t.Error("generateOutput() expected error for unsupported format, got nil")
|
|
}
|
|
|
|
expectedErrMsg := "unsupported output format: unsupported"
|
|
if err.Error() != expectedErrMsg {
|
|
t.Errorf("generateOutput() error = %q, want %q", err.Error(), expectedErrMsg)
|
|
}
|
|
})
|
|
}
|
|
|
|
// TestErrorTypes tests the error type methods with 0% coverage
|
|
func TestErrorTypes(t *testing.T) {
|
|
t.Run("ConversionError", func(t *testing.T) {
|
|
innerErr := errors.New("test inner error")
|
|
convErr := &ConversionError{
|
|
Op: "test_operation",
|
|
Err: innerErr,
|
|
}
|
|
|
|
// Test Error() method
|
|
expectedMsg := "test_operation: test inner error"
|
|
if convErr.Error() != expectedMsg {
|
|
t.Errorf("ConversionError.Error() = %q, want %q", convErr.Error(), expectedMsg)
|
|
}
|
|
|
|
// Test Unwrap() method
|
|
unwrapped := convErr.Unwrap()
|
|
if unwrapped != innerErr {
|
|
t.Errorf("ConversionError.Unwrap() = %v, want %v", unwrapped, innerErr)
|
|
}
|
|
})
|
|
|
|
t.Run("ValidationError", func(t *testing.T) {
|
|
config := &TunnelConfig{Name: "TestConfig"}
|
|
innerErr := errors.New("test validation error")
|
|
valErr := &ValidationError{
|
|
Config: config,
|
|
Err: innerErr,
|
|
}
|
|
|
|
// Test Error() method
|
|
expectedMsg := "validation: test validation error"
|
|
if valErr.Error() != expectedMsg {
|
|
t.Errorf("ValidationError.Error() = %q, want %q", valErr.Error(), expectedMsg)
|
|
}
|
|
|
|
// Test Unwrap() method
|
|
unwrapped := valErr.Unwrap()
|
|
if unwrapped != innerErr {
|
|
t.Errorf("ValidationError.Unwrap() = %v, want %v", unwrapped, innerErr)
|
|
}
|
|
})
|
|
}
|
|
|
|
// TestTunnelConfigMethods tests TunnelConfig methods with 0% coverage
|
|
func TestTunnelConfigMethods(t *testing.T) {
|
|
t.Run("LoadConfig", func(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
|
|
// Test loading properties file
|
|
propsFile := filepath.Join(tempDir, "test.properties")
|
|
propsContent := `name=LoadTest
|
|
type=httpclient
|
|
interface=127.0.0.1
|
|
listenPort=8080
|
|
description=Load test tunnel`
|
|
err := os.WriteFile(propsFile, []byte(propsContent), 0644)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create test properties file: %v", err)
|
|
}
|
|
|
|
config := &TunnelConfig{}
|
|
err = config.LoadConfig(propsFile)
|
|
if err != nil {
|
|
t.Fatalf("LoadConfig() failed: %v", err)
|
|
}
|
|
|
|
if config.Name != "LoadTest" {
|
|
t.Errorf("LoadConfig() Name = %q, want %q", config.Name, "LoadTest")
|
|
}
|
|
if config.Type != "httpclient" {
|
|
t.Errorf("LoadConfig() Type = %q, want %q", config.Type, "httpclient")
|
|
}
|
|
if config.Port != 8080 {
|
|
t.Errorf("LoadConfig() Port = %d, want %d", config.Port, 8080)
|
|
}
|
|
|
|
// Test loading YAML file (nested format with tunnels map)
|
|
yamlFile := filepath.Join(tempDir, "test.yaml")
|
|
yamlContent := `tunnels:
|
|
YamlTest:
|
|
type: server
|
|
port: 9090`
|
|
err = os.WriteFile(yamlFile, []byte(yamlContent), 0644)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create test YAML file: %v", err)
|
|
}
|
|
|
|
config2 := &TunnelConfig{}
|
|
err = config2.LoadConfig(yamlFile)
|
|
if err != nil {
|
|
t.Fatalf("LoadConfig() YAML failed: %v", err)
|
|
}
|
|
|
|
if config2.Name != "YamlTest" {
|
|
t.Errorf("LoadConfig() YAML Name = %q, want %q", config2.Name, "YamlTest")
|
|
}
|
|
|
|
// Test loading non-existent file
|
|
config3 := &TunnelConfig{}
|
|
err = config3.LoadConfig("/non/existent/file")
|
|
if err == nil {
|
|
t.Error("LoadConfig() expected error for non-existent file, got nil")
|
|
}
|
|
})
|
|
|
|
t.Run("Options_and_SetOptions", func(t *testing.T) {
|
|
config := &TunnelConfig{
|
|
Name: "OptionsTest",
|
|
Type: "httpclient",
|
|
Interface: "192.168.1.100",
|
|
Port: 8080,
|
|
Target: "example.i2p",
|
|
PersistentKey: true,
|
|
Description: "Options test tunnel",
|
|
I2CP: map[string]interface{}{
|
|
"leaseSetEncType": "4,0",
|
|
},
|
|
Tunnel: map[string]interface{}{
|
|
"proxyList": "proxy1.i2p",
|
|
},
|
|
Inbound: map[string]interface{}{
|
|
"length": 3,
|
|
},
|
|
Outbound: map[string]interface{}{
|
|
"length": 2,
|
|
},
|
|
}
|
|
|
|
// Test Options() method
|
|
options := config.Options()
|
|
|
|
expectedOptions := map[string]string{
|
|
"Name": "OptionsTest",
|
|
"Type": "httpclient",
|
|
"Interface": "192.168.1.100",
|
|
"Port": "8080",
|
|
"Target": "example.i2p",
|
|
"PersistentKey": "true",
|
|
"Description": "Options test tunnel",
|
|
"I2CP.leaseSetEncType": "4,0",
|
|
"Tunnel.proxyList": "proxy1.i2p",
|
|
"Inbound.length": "3",
|
|
"Outbound.length": "2",
|
|
}
|
|
|
|
for key, expectedValue := range expectedOptions {
|
|
actualValue, exists := options[key]
|
|
if !exists {
|
|
t.Errorf("Options() missing key %q", key)
|
|
} else if actualValue != expectedValue {
|
|
t.Errorf("Options()[%q] = %q, want %q", key, actualValue, expectedValue)
|
|
}
|
|
}
|
|
|
|
// Test SetOptions() method
|
|
newConfig := &TunnelConfig{}
|
|
err := newConfig.SetOptions(options)
|
|
if err != nil {
|
|
t.Fatalf("SetOptions() failed: %v", err)
|
|
}
|
|
|
|
// Verify basic fields were set correctly
|
|
if newConfig.Name != config.Name {
|
|
t.Errorf("SetOptions() Name = %q, want %q", newConfig.Name, config.Name)
|
|
}
|
|
if newConfig.Type != config.Type {
|
|
t.Errorf("SetOptions() Type = %q, want %q", newConfig.Type, config.Type)
|
|
}
|
|
if newConfig.Port != config.Port {
|
|
t.Errorf("SetOptions() Port = %d, want %d", newConfig.Port, config.Port)
|
|
}
|
|
if newConfig.PersistentKey != config.PersistentKey {
|
|
t.Errorf("SetOptions() PersistentKey = %t, want %t", newConfig.PersistentKey, config.PersistentKey)
|
|
}
|
|
|
|
// Test SetOptions() with invalid port
|
|
invalidOptions := map[string]string{
|
|
"Name": "InvalidTest",
|
|
"Type": "client",
|
|
"Port": "invalid_port",
|
|
}
|
|
|
|
err = newConfig.SetOptions(invalidOptions)
|
|
if err == nil {
|
|
t.Error("SetOptions() expected error for invalid port, got nil")
|
|
}
|
|
|
|
// Test SetOptions() with invalid PersistentKey
|
|
invalidOptions2 := map[string]string{
|
|
"Name": "InvalidTest2",
|
|
"Type": "client",
|
|
"PersistentKey": "invalid_bool",
|
|
}
|
|
|
|
err = newConfig.SetOptions(invalidOptions2)
|
|
if err == nil {
|
|
t.Error("SetOptions() expected error for invalid PersistentKey, got nil")
|
|
}
|
|
})
|
|
}
|
|
|
|
// TestFormatDetection tests comprehensive format detection scenarios
|
|
func TestFormatDetection(t *testing.T) {
|
|
converter := &Converter{}
|
|
|
|
tests := []struct {
|
|
name string
|
|
filename string
|
|
expectedFormat string
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "properties_extension",
|
|
filename: "tunnel.properties",
|
|
expectedFormat: "properties",
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "config_extension",
|
|
filename: "tunnel.config",
|
|
expectedFormat: "properties",
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "prop_extension",
|
|
filename: "tunnel.prop",
|
|
expectedFormat: "properties",
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "yaml_extension",
|
|
filename: "tunnel.yaml",
|
|
expectedFormat: "yaml",
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "yml_extension",
|
|
filename: "tunnel.yml",
|
|
expectedFormat: "yaml",
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "ini_extension",
|
|
filename: "tunnel.ini",
|
|
expectedFormat: "ini",
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "conf_extension",
|
|
filename: "tunnel.conf",
|
|
expectedFormat: "ini",
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "unsupported_extension",
|
|
filename: "tunnel.xml",
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "no_extension",
|
|
filename: "tunnel",
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
format, err := converter.DetectFormat(tt.filename)
|
|
|
|
if tt.expectError {
|
|
if err == nil {
|
|
t.Errorf("DetectFormat() expected error, got nil")
|
|
}
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Fatalf("DetectFormat() error = %v, expectError %v", err, tt.expectError)
|
|
}
|
|
|
|
if format != tt.expectedFormat {
|
|
t.Errorf("DetectFormat() format = %q, want %q", format, tt.expectedFormat)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestINIFormatValidation tests the validateINIFormat function (0% coverage)
|
|
func TestINIFormatValidation(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
config *TunnelConfig
|
|
strict bool
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "valid_ini_name",
|
|
config: &TunnelConfig{
|
|
Name: "ValidTunnel",
|
|
Type: "httpclient",
|
|
Port: 8080,
|
|
},
|
|
strict: true,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "ini_name_with_brackets_strict",
|
|
config: &TunnelConfig{
|
|
Name: "Bad[Tunnel]",
|
|
Type: "httpclient",
|
|
Port: 8080,
|
|
},
|
|
strict: true,
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "ini_name_with_brackets_non_strict",
|
|
config: &TunnelConfig{
|
|
Name: "Bad[Tunnel]",
|
|
Type: "httpclient",
|
|
Port: 8080,
|
|
},
|
|
strict: false,
|
|
expectError: true, // Basic validation should still catch this
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
validationCtx := NewValidationContext(tt.strict, "ini")
|
|
err := validationCtx.Validate(tt.config)
|
|
|
|
if tt.expectError {
|
|
if err == nil {
|
|
t.Errorf("Expected validation error for INI format, got nil")
|
|
}
|
|
} else {
|
|
if err != nil {
|
|
t.Errorf("Unexpected validation error: %v", err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|