package config import ( "os" "path/filepath" "testing" "time" ) func TestLoad_ValidConfig(t *testing.T) { content := ` # Test configuration service.name logcorrelator service.language go input.unix_socket apache_source /var/run/logcorrelator/apache.sock json input.unix_socket network_source /var/run/logcorrelator/network.sock json output.file.enabled true output.file.path /var/log/logcorrelator/correlated.log output.clickhouse.enabled false output.clickhouse.dsn clickhouse://user:pass@localhost:9000/db output.clickhouse.table correlated_logs correlation.key src_ip,src_port correlation.time_window.value 1 correlation.time_window.unit s correlation.orphan_policy.apache_always_emit true correlation.orphan_policy.network_emit false ` tmpDir := t.TempDir() configPath := filepath.Join(tmpDir, "config.conf") if err := os.WriteFile(configPath, []byte(content), 0644); err != nil { t.Fatalf("failed to write config: %v", err) } cfg, err := Load(configPath) if err != nil { t.Fatalf("unexpected error: %v", err) } if cfg.Service.Name != "logcorrelator" { t.Errorf("expected service name logcorrelator, got %s", cfg.Service.Name) } if len(cfg.Inputs.UnixSockets) != 2 { t.Errorf("expected 2 unix sockets, got %d", len(cfg.Inputs.UnixSockets)) } if !cfg.Outputs.File.Enabled { t.Error("expected file output enabled") } } func TestLoad_InvalidPath(t *testing.T) { _, err := Load("/nonexistent/path/config.conf") if err == nil { t.Error("expected error for nonexistent path") } } func TestLoad_InvalidDirective(t *testing.T) { tmpDir := t.TempDir() configPath := filepath.Join(tmpDir, "config.conf") content := `invalid.directive value` if err := os.WriteFile(configPath, []byte(content), 0644); err != nil { t.Fatalf("failed to write config: %v", err) } _, err := Load(configPath) if err == nil { t.Error("expected error for invalid directive") } } func TestLoad_Comments(t *testing.T) { tmpDir := t.TempDir() configPath := filepath.Join(tmpDir, "config.conf") content := ` # This is a comment service.name logcorrelator # Another comment input.unix_socket test /tmp/test.sock json input.unix_socket test2 /tmp/test2.sock json output.file.enabled true ` if err := os.WriteFile(configPath, []byte(content), 0644); err != nil { t.Fatalf("failed to write config: %v", err) } cfg, err := Load(configPath) if err != nil { t.Fatalf("unexpected error: %v", err) } if cfg.Service.Name != "logcorrelator" { t.Errorf("expected service name logcorrelator, got %s", cfg.Service.Name) } } func TestValidate_MinimumInputs(t *testing.T) { cfg := &Config{ Inputs: InputsConfig{ UnixSockets: []UnixSocketConfig{ {Name: "only_one", Path: "/tmp/test.sock"}, }, }, Outputs: OutputsConfig{ File: FileOutputConfig{Enabled: true}, }, } err := cfg.Validate() if err == nil { t.Error("expected error for less than 2 inputs") } } func TestValidate_AtLeastOneOutput(t *testing.T) { cfg := &Config{ Inputs: InputsConfig{ UnixSockets: []UnixSocketConfig{ {Name: "a", Path: "/tmp/a.sock"}, {Name: "b", Path: "/tmp/b.sock"}, }, }, Outputs: OutputsConfig{ File: FileOutputConfig{Enabled: false}, ClickHouse: ClickHouseOutputConfig{Enabled: false}, Stdout: StdoutOutputConfig{Enabled: false}, }, } err := cfg.Validate() if err == nil { t.Error("expected error for no outputs enabled") } } func TestGetTimeWindow(t *testing.T) { tests := []struct { name string config CorrelationConfig expected time.Duration }{ { name: "seconds", config: CorrelationConfig{ TimeWindow: TimeWindowConfig{Value: 1, Unit: "s"}, }, expected: time.Second, }, { name: "milliseconds", config: CorrelationConfig{ TimeWindow: TimeWindowConfig{Value: 500, Unit: "ms"}, }, expected: 500 * time.Millisecond, }, { name: "minutes", config: CorrelationConfig{ TimeWindow: TimeWindowConfig{Value: 2, Unit: "m"}, }, expected: 2 * time.Minute, }, { name: "default", config: CorrelationConfig{ TimeWindow: TimeWindowConfig{}, }, expected: time.Second, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := tt.config.GetTimeWindow() if result != tt.expected { t.Errorf("expected %v, got %v", tt.expected, result) } }) } } func TestParseBool(t *testing.T) { tests := []struct { input string expected bool hasError bool }{ {"true", true, false}, {"True", true, false}, {"TRUE", true, false}, {"yes", true, false}, {"1", true, false}, {"on", true, false}, {"false", false, false}, {"False", false, false}, {"no", false, false}, {"0", false, false}, {"off", false, false}, {"invalid", false, true}, } for _, tt := range tests { t.Run(tt.input, func(t *testing.T) { result, err := parseBool(tt.input) if tt.hasError { if err == nil { t.Error("expected error, got nil") } } else { if err != nil { t.Errorf("unexpected error: %v", err) } if result != tt.expected { t.Errorf("expected %v, got %v", tt.expected, result) } } }) } }