package config import ( "os" "path/filepath" "testing" "time" ) func TestLoad_ValidConfig(t *testing.T) { content := ` service: name: logcorrelator language: go inputs: unix_sockets: - name: apache_source path: /var/run/logcorrelator/apache.sock format: json - name: network_source path: /var/run/logcorrelator/network.sock format: json outputs: file: enabled: true path: /var/log/logcorrelator/correlated.log clickhouse: enabled: false dsn: clickhouse://user:pass@localhost:9000/db table: correlated_logs correlation: key: - src_ip - src_port time_window: value: 1 unit: s orphan_policy: apache_always_emit: true network_emit: false ` tmpDir := t.TempDir() configPath := filepath.Join(tmpDir, "config.yml") 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.yml") if err == nil { t.Error("expected error for nonexistent path") } } func TestLoad_InvalidYAML(t *testing.T) { tmpDir := t.TempDir() configPath := filepath.Join(tmpDir, "config.yml") content := `invalid: yaml: content: [` 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 YAML") } } func TestLoad_DefaultValues(t *testing.T) { tmpDir := t.TempDir() configPath := filepath.Join(tmpDir, "config.yml") content := ` service: name: test-service inputs: unix_sockets: - name: a path: /tmp/a.sock - name: b path: /tmp/b.sock outputs: 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 != "test-service" { t.Errorf("expected service name test-service, got %s", cfg.Service.Name) } // Check defaults if cfg.Correlation.TimeWindow.Value != 1 { t.Errorf("expected default time window value 1, got %d", cfg.Correlation.TimeWindow.Value) } if cfg.Correlation.OrphanPolicy.ApacheAlwaysEmit != true { t.Error("expected default apache_always_emit to be true") } } 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) } }) } }