package output import ( "bytes" "encoding/json" "os" "testing" "ja4sentinel/api" ) func TestStdoutWriter(t *testing.T) { // Capture stdout by replacing it temporarily oldStdout := os.Stdout r, w, _ := os.Pipe() os.Stdout = w writer := NewStdoutWriter() rec := api.LogRecord{ SrcIP: "192.168.1.1", SrcPort: 12345, DstIP: "10.0.0.1", DstPort: 443, JA4: "t12s0102ab_1234567890ab", } err := writer.Write(rec) if err != nil { t.Errorf("Write() error = %v", err) } w.Close() os.Stdout = oldStdout var buf bytes.Buffer buf.ReadFrom(r) output := buf.String() if output == "" { t.Error("Write() produced no output") } // Verify it's valid JSON var result api.LogRecord if err := json.Unmarshal([]byte(output), &result); err != nil { t.Errorf("Output is not valid JSON: %v", err) } } func TestFileWriter(t *testing.T) { // Create a temporary file tmpFile := "/tmp/ja4sentinel_test.log" defer os.Remove(tmpFile) writer, err := NewFileWriter(tmpFile) if err != nil { t.Fatalf("NewFileWriter() error = %v", err) } defer writer.Close() rec := api.LogRecord{ SrcIP: "192.168.1.1", SrcPort: 12345, DstIP: "10.0.0.1", DstPort: 443, JA4: "t12s0102ab_1234567890ab", } err = writer.Write(rec) if err != nil { t.Errorf("Write() error = %v", err) } // Read the file and verify data, err := os.ReadFile(tmpFile) if err != nil { t.Fatalf("Failed to read file: %v", err) } if len(data) == 0 { t.Error("Write() produced no output") } // Verify it's valid JSON var result api.LogRecord if err := json.Unmarshal(data, &result); err != nil { t.Errorf("Output is not valid JSON: %v", err) } } func TestMultiWriter(t *testing.T) { multiWriter := NewMultiWriter() // Create a temporary file writer tmpFile := "/tmp/ja4sentinel_multi_test.log" defer os.Remove(tmpFile) fileWriter, err := NewFileWriter(tmpFile) if err != nil { t.Fatalf("NewFileWriter() error = %v", err) } defer fileWriter.Close() multiWriter.Add(fileWriter) multiWriter.Add(NewStdoutWriter()) rec := api.LogRecord{ SrcIP: "192.168.1.1", SrcPort: 12345, DstIP: "10.0.0.1", DstPort: 443, JA4: "t12s0102ab_1234567890ab", } err = multiWriter.Write(rec) if err != nil { t.Errorf("Write() error = %v", err) } // Verify file output data, err := os.ReadFile(tmpFile) if err != nil { t.Fatalf("Failed to read file: %v", err) } if len(data) == 0 { t.Error("MultiWriter.Write() produced no file output") } } func TestBuilderNewFromConfig(t *testing.T) { builder := NewBuilder() tests := []struct { name string cfg api.AppConfig wantErr bool }{ { name: "stdout output", cfg: api.AppConfig{ Outputs: []api.OutputConfig{ {Type: "stdout", Enabled: true}, }, }, wantErr: false, }, { name: "file output", cfg: api.AppConfig{ Outputs: []api.OutputConfig{ { Type: "file", Enabled: true, Params: map[string]string{"path": "/tmp/ja4sentinel_builder_test.log"}, }, }, }, wantErr: false, }, { name: "file output without path", cfg: api.AppConfig{ Outputs: []api.OutputConfig{ {Type: "file", Enabled: true}, }, }, wantErr: true, }, { name: "unix socket output", cfg: api.AppConfig{ Outputs: []api.OutputConfig{ { Type: "unix_socket", Enabled: true, Params: map[string]string{"socket_path": "/tmp/ja4sentinel_test.sock"}, }, }, }, wantErr: false, }, { name: "unknown output type", cfg: api.AppConfig{ Outputs: []api.OutputConfig{ {Type: "unknown", Enabled: true}, }, }, wantErr: true, }, { name: "no outputs (should default to stdout)", cfg: api.AppConfig{}, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { writer, err := builder.NewFromConfig(tt.cfg) if (err != nil) != tt.wantErr { t.Errorf("NewFromConfig() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr && writer == nil { t.Error("NewFromConfig() returned nil writer") } }) } } func TestUnixSocketWriter(t *testing.T) { // Test creation without socket (should not fail) socketPath := "/tmp/ja4sentinel_nonexistent.sock" writer, err := NewUnixSocketWriter(socketPath) if err != nil { t.Fatalf("NewUnixSocketWriter() error = %v", err) } // Write should fail since socket doesn't exist rec := api.LogRecord{ SrcIP: "192.168.1.1", SrcPort: 12345, DstIP: "10.0.0.1", DstPort: 443, } err = writer.Write(rec) if err == nil { t.Error("Write() should fail for non-existent socket") } writer.Close() }