chore: release v1.0.2 with critical fixes and test improvements

- fix: add missing ClickHouse driver dependency
- fix: resolve race condition in orchestrator (single goroutine per source)
- feat: add explicit source_type config for Unix socket sources
- test: improve coverage from 50.6% to 62.0%
- docs: add CHANGELOG.md with release notes
- build: update version to 1.0.2 in build scripts and Dockerfiles

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
Jacquin Antoine
2026-02-28 21:45:00 +01:00
parent 5f97af3627
commit 180c57c35b
15 changed files with 1178 additions and 30 deletions

View File

@ -30,9 +30,10 @@ type InputsConfig struct {
// UnixSocketConfig holds a Unix socket source configuration.
type UnixSocketConfig struct {
Name string `yaml:"name"`
Path string `yaml:"path"`
Format string `yaml:"format"`
Name string `yaml:"name"`
Path string `yaml:"path"`
Format string `yaml:"format"`
SourceType string `yaml:"source_type"` // "A" for Apache/HTTP, "B" for Network
}
// OutputsConfig holds output sinks configuration.

View File

@ -207,3 +207,279 @@ func TestGetTimeWindow(t *testing.T) {
})
}
}
func TestValidate_DuplicateNames(t *testing.T) {
cfg := &Config{
Inputs: InputsConfig{
UnixSockets: []UnixSocketConfig{
{Name: "same", Path: "/tmp/a.sock"},
{Name: "same", Path: "/tmp/b.sock"},
},
},
Outputs: OutputsConfig{
File: FileOutputConfig{Enabled: true},
},
}
err := cfg.Validate()
if err == nil {
t.Error("expected error for duplicate names")
}
}
func TestValidate_DuplicatePaths(t *testing.T) {
cfg := &Config{
Inputs: InputsConfig{
UnixSockets: []UnixSocketConfig{
{Name: "a", Path: "/tmp/same.sock"},
{Name: "b", Path: "/tmp/same.sock"},
},
},
Outputs: OutputsConfig{
File: FileOutputConfig{Enabled: true},
},
}
err := cfg.Validate()
if err == nil {
t.Error("expected error for duplicate paths")
}
}
func TestValidate_EmptyName(t *testing.T) {
cfg := &Config{
Inputs: InputsConfig{
UnixSockets: []UnixSocketConfig{
{Name: "", Path: "/tmp/a.sock"},
{Name: "b", Path: "/tmp/b.sock"},
},
},
Outputs: OutputsConfig{
File: FileOutputConfig{Enabled: true},
},
}
err := cfg.Validate()
if err == nil {
t.Error("expected error for empty name")
}
}
func TestValidate_EmptyPath(t *testing.T) {
cfg := &Config{
Inputs: InputsConfig{
UnixSockets: []UnixSocketConfig{
{Name: "a", Path: ""},
{Name: "b", Path: "/tmp/b.sock"},
},
},
Outputs: OutputsConfig{
File: FileOutputConfig{Enabled: true},
},
}
err := cfg.Validate()
if err == nil {
t.Error("expected error for empty path")
}
}
func TestValidate_EmptyFilePath(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: true, Path: ""},
},
}
err := cfg.Validate()
if err == nil {
t.Error("expected error for empty file path")
}
}
func TestValidate_ClickHouseMissingDSN(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: true,
DSN: "",
Table: "test",
},
},
}
err := cfg.Validate()
if err == nil {
t.Error("expected error for missing ClickHouse DSN")
}
}
func TestValidate_ClickHouseMissingTable(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: true,
DSN: "clickhouse://localhost:9000/db",
Table: "",
},
},
}
err := cfg.Validate()
if err == nil {
t.Error("expected error for missing ClickHouse table")
}
}
func TestValidate_ClickHouseInvalidBatchSize(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: true,
DSN: "clickhouse://localhost:9000/db",
Table: "test",
BatchSize: 0,
},
},
}
err := cfg.Validate()
if err == nil {
t.Error("expected error for invalid batch size")
}
}
func TestValidate_ClickHouseInvalidMaxBufferSize(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: true,
DSN: "clickhouse://localhost:9000/db",
Table: "test",
BatchSize: 100,
MaxBufferSize: 0,
},
},
}
err := cfg.Validate()
if err == nil {
t.Error("expected error for invalid max buffer size")
}
}
func TestValidate_ClickHouseInvalidTimeout(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: true,
DSN: "clickhouse://localhost:9000/db",
Table: "test",
BatchSize: 100,
TimeoutMs: 0,
},
},
}
err := cfg.Validate()
if err == nil {
t.Error("expected error for invalid timeout")
}
}
func TestValidate_EmptyCorrelationKey(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: true},
},
Correlation: CorrelationConfig{
Key: []string{},
},
}
err := cfg.Validate()
if err == nil {
t.Error("expected error for empty correlation key")
}
}
func TestValidate_InvalidTimeWindow(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: true},
},
Correlation: CorrelationConfig{
Key: []string{"src_ip", "src_port"},
TimeWindow: TimeWindowConfig{Value: 0},
},
}
err := cfg.Validate()
if err == nil {
t.Error("expected error for invalid time window")
}
}
func TestGetTimeWindow_UnknownUnit(t *testing.T) {
config := CorrelationConfig{
TimeWindow: TimeWindowConfig{Value: 5, Unit: "unknown"},
}
result := config.GetTimeWindow()
expected := 5 * time.Second // Should default to seconds
if result != expected {
t.Errorf("expected %v, got %v", expected, result)
}
}