package config import ( "os" "strings" "testing" "ja4sentinel/api" ) func TestParsePorts(t *testing.T) { tests := []struct { name string input string want []uint16 }{ { name: "single port", input: "443", want: []uint16{443}, }, { name: "multiple ports", input: "443, 8443, 9443", want: []uint16{443, 8443, 9443}, }, { name: "empty string", input: "", want: nil, }, { name: "with spaces", input: " 443 , 8443 ", want: []uint16{443, 8443}, }, { name: "invalid port ignored", input: "443, invalid, 8443", want: []uint16{443, 8443}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := parsePorts(tt.input) if len(got) != len(tt.want) { t.Errorf("parsePorts() length = %v, want %v", len(got), len(tt.want)) return } for i, v := range got { if v != tt.want[i] { t.Errorf("parsePorts()[%d] = %v, want %v", i, v, tt.want[i]) } } }) } } func TestMergeConfigs(t *testing.T) { base := api.AppConfig{ Core: api.Config{ Interface: "eth0", ListenPorts: []uint16{443}, BPFFilter: "", }, Outputs: []api.OutputConfig{}, } override := api.AppConfig{ Core: api.Config{ Interface: "lo", ListenPorts: []uint16{8443}, BPFFilter: "tcp", }, Outputs: []api.OutputConfig{ {Type: "stdout", Enabled: true}, }, } result := mergeConfigs(base, override) if result.Core.Interface != "lo" { t.Errorf("Interface = %v, want lo", result.Core.Interface) } if len(result.Core.ListenPorts) != 1 || result.Core.ListenPorts[0] != 8443 { t.Errorf("ListenPorts = %v, want [8443]", result.Core.ListenPorts) } if result.Core.BPFFilter != "tcp" { t.Errorf("BPFFilter = %v, want tcp", result.Core.BPFFilter) } if len(result.Outputs) != 1 { t.Errorf("Outputs length = %v, want 1", len(result.Outputs)) } } func TestValidate(t *testing.T) { loader := &LoaderImpl{} tests := []struct { name string config api.AppConfig wantErr bool }{ { name: "valid config", config: api.AppConfig{ Core: api.Config{ Interface: "eth0", ListenPorts: []uint16{443}, }, Outputs: []api.OutputConfig{ {Type: "stdout", Enabled: true}, }, }, wantErr: false, }, { name: "empty interface", config: api.AppConfig{ Core: api.Config{ Interface: "", ListenPorts: []uint16{443}, }, }, wantErr: true, }, { name: "no listen ports", config: api.AppConfig{ Core: api.Config{ Interface: "eth0", ListenPorts: []uint16{}, }, }, wantErr: true, }, { name: "output with empty type", config: api.AppConfig{ Core: api.Config{ Interface: "eth0", ListenPorts: []uint16{443}, }, Outputs: []api.OutputConfig{ {Type: "", Enabled: true}, }, }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := loader.validate(tt.config) if (err != nil) != tt.wantErr { t.Errorf("validate() error = %v, wantErr %v", err, tt.wantErr) } }) } } func TestLoadFromEnv(t *testing.T) { // Save original env vars origInterface := os.Getenv("JA4SENTINEL_INTERFACE") origPorts := os.Getenv("JA4SENTINEL_PORTS") origFilter := os.Getenv("JA4SENTINEL_BPF_FILTER") defer func() { os.Setenv("JA4SENTINEL_INTERFACE", origInterface) os.Setenv("JA4SENTINEL_PORTS", origPorts) os.Setenv("JA4SENTINEL_BPF_FILTER", origFilter) }() // Set test env vars os.Setenv("JA4SENTINEL_INTERFACE", "lo") os.Setenv("JA4SENTINEL_PORTS", "8443,9443") os.Setenv("JA4SENTINEL_BPF_FILTER", "tcp port 8443") loader := &LoaderImpl{} config := api.DefaultConfig() result := loader.loadFromEnv(config) if result.Core.Interface != "lo" { t.Errorf("Interface = %v, want lo", result.Core.Interface) } if len(result.Core.ListenPorts) != 2 { t.Errorf("ListenPorts length = %v, want 2", len(result.Core.ListenPorts)) } if result.Core.BPFFilter != "tcp port 8443" { t.Errorf("BPFFilter = %v, want 'tcp port 8443'", result.Core.BPFFilter) } } func TestToJSON(t *testing.T) { config := api.AppConfig{ Core: api.Config{ Interface: "eth0", ListenPorts: []uint16{443, 8443}, BPFFilter: "tcp", }, Outputs: []api.OutputConfig{ {Type: "stdout", Enabled: true, Params: map[string]string{}}, }, } jsonStr := ToJSON(config) if jsonStr == "" { t.Error("ToJSON() returned empty string") } if !strings.Contains(jsonStr, "eth0") { t.Error("ToJSON() result doesn't contain 'eth0'") } }