feat: Add reverse proxy functionality with enhanced routing capabilities
- Introduced IgnoreRequestPath option in proxy configuration to allow exact match routing. - Implemented proxy_pass directive in routing extension to handle backend requests. - Enhanced error handling for backend unavailability and timeouts. - Added integration tests for reverse proxy, including basic requests, exact match routes, regex routes, header forwarding, and query string preservation. - Created helper functions for setting up test servers and backends, along with assertion utilities for response validation. - Updated server initialization to support extension management and middleware chaining. - Improved logging for debugging purposes during request handling.
This commit is contained in:
@@ -2,6 +2,8 @@ package logging
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/konduktor/konduktor/internal/config"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
@@ -15,71 +17,84 @@ func TestNew(t *testing.T) {
|
||||
t.Fatal("Expected logger, got nil")
|
||||
}
|
||||
|
||||
if logger.level != "INFO" {
|
||||
t.Errorf("Expected level INFO, got %s", logger.level)
|
||||
if logger.name != "konduktor" {
|
||||
t.Errorf("Expected name konduktor, got %s", logger.name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNew_DefaultTimestampFormat(t *testing.T) {
|
||||
logger, _ := New(Config{Level: "DEBUG"})
|
||||
logger, err := New(Config{Level: "DEBUG"})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if logger.timestampFormat != "2006-01-02 15:04:05" {
|
||||
t.Errorf("Expected default timestamp format, got %s", logger.timestampFormat)
|
||||
// Logger should be created successfully
|
||||
if logger == nil {
|
||||
t.Fatal("Expected logger, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNew_CustomTimestampFormat(t *testing.T) {
|
||||
logger, _ := New(Config{
|
||||
logger, err := New(Config{
|
||||
Level: "DEBUG",
|
||||
TimestampFormat: "15:04:05",
|
||||
})
|
||||
|
||||
if logger.timestampFormat != "15:04:05" {
|
||||
t.Errorf("Expected custom timestamp format, got %s", logger.timestampFormat)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if logger == nil {
|
||||
t.Fatal("Expected logger, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogger_ShouldLog(t *testing.T) {
|
||||
tests := []struct {
|
||||
loggerLevel string
|
||||
msgLevel string
|
||||
shouldLog bool
|
||||
}{
|
||||
{"DEBUG", "DEBUG", true},
|
||||
{"DEBUG", "INFO", true},
|
||||
{"DEBUG", "WARN", true},
|
||||
{"DEBUG", "ERROR", true},
|
||||
{"INFO", "DEBUG", false},
|
||||
{"INFO", "INFO", true},
|
||||
{"INFO", "WARN", true},
|
||||
{"INFO", "ERROR", true},
|
||||
{"WARN", "DEBUG", false},
|
||||
{"WARN", "INFO", false},
|
||||
{"WARN", "WARN", true},
|
||||
{"WARN", "ERROR", true},
|
||||
{"ERROR", "DEBUG", false},
|
||||
{"ERROR", "INFO", false},
|
||||
{"ERROR", "WARN", false},
|
||||
{"ERROR", "ERROR", true},
|
||||
func TestNewFromConfig(t *testing.T) {
|
||||
cfg := config.LoggingConfig{
|
||||
Level: "DEBUG",
|
||||
ConsoleOutput: true,
|
||||
Format: config.LogFormatConfig{
|
||||
Type: "standard",
|
||||
UseColors: true,
|
||||
ShowModule: true,
|
||||
TimestampFormat: "2006-01-02 15:04:05",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.loggerLevel+"_"+tt.msgLevel, func(t *testing.T) {
|
||||
logger, _ := New(Config{Level: tt.loggerLevel})
|
||||
logger, err := NewFromConfig(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if got := logger.shouldLog(tt.msgLevel); got != tt.shouldLog {
|
||||
t.Errorf("shouldLog(%s) = %v, want %v", tt.msgLevel, got, tt.shouldLog)
|
||||
}
|
||||
})
|
||||
if logger == nil {
|
||||
t.Fatal("Expected logger, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogger_ShouldLog_InvalidLevel(t *testing.T) {
|
||||
logger, _ := New(Config{Level: "INVALID"})
|
||||
func TestNewFromConfig_WithConsole(t *testing.T) {
|
||||
cfg := config.LoggingConfig{
|
||||
Level: "INFO",
|
||||
ConsoleOutput: true,
|
||||
Format: config.LogFormatConfig{
|
||||
Type: "standard",
|
||||
UseColors: true,
|
||||
},
|
||||
Console: &config.ConsoleLogConfig{
|
||||
Level: "DEBUG",
|
||||
Format: config.LogFormatConfig{
|
||||
Type: "standard",
|
||||
UseColors: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Should default to INFO level
|
||||
if !logger.shouldLog("INFO") {
|
||||
t.Error("Invalid level should default to INFO")
|
||||
logger, err := NewFromConfig(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if logger == nil {
|
||||
t.Fatal("Expected logger, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,34 +126,65 @@ func TestLogger_Error(t *testing.T) {
|
||||
logger.Error("test message", "key", "value")
|
||||
}
|
||||
|
||||
func TestFormatValue(t *testing.T) {
|
||||
func TestLogger_Named(t *testing.T) {
|
||||
logger, _ := New(Config{Level: "INFO"})
|
||||
named := logger.Named("test.module")
|
||||
|
||||
if named == nil {
|
||||
t.Fatal("Expected named logger, got nil")
|
||||
}
|
||||
|
||||
if named.name != "test.module" {
|
||||
t.Errorf("Expected name 'test.module', got %s", named.name)
|
||||
}
|
||||
|
||||
// Should not panic
|
||||
named.Info("test from named logger")
|
||||
}
|
||||
|
||||
func TestLogger_With(t *testing.T) {
|
||||
logger, _ := New(Config{Level: "INFO"})
|
||||
withFields := logger.With("service", "test")
|
||||
|
||||
if withFields == nil {
|
||||
t.Fatal("Expected logger with fields, got nil")
|
||||
}
|
||||
|
||||
// Should not panic
|
||||
withFields.Info("test with fields")
|
||||
}
|
||||
|
||||
func TestLogger_Sync(t *testing.T) {
|
||||
logger, _ := New(Config{Level: "INFO"})
|
||||
|
||||
// Should not panic
|
||||
err := logger.Sync()
|
||||
// Sync may return an error for stdout on some systems, ignore it
|
||||
_ = err
|
||||
}
|
||||
|
||||
func TestParseLevel(t *testing.T) {
|
||||
tests := []struct {
|
||||
input interface{}
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"test", "test"},
|
||||
{42, "*"}, // int converts to rune
|
||||
{nil, ""},
|
||||
{"DEBUG", "debug"},
|
||||
{"INFO", "info"},
|
||||
{"WARN", "warn"},
|
||||
{"WARNING", "warn"},
|
||||
{"ERROR", "error"},
|
||||
{"CRITICAL", "fatal"},
|
||||
{"FATAL", "fatal"},
|
||||
{"invalid", "info"}, // defaults to INFO
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
got := formatValue(tt.input)
|
||||
// Just check it doesn't panic
|
||||
_ = got
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogger_FormatTime(t *testing.T) {
|
||||
logger, _ := New(Config{
|
||||
Level: "INFO",
|
||||
TimestampFormat: "2006-01-02",
|
||||
})
|
||||
|
||||
result := logger.formatTime()
|
||||
|
||||
// Should be in expected format (YYYY-MM-DD)
|
||||
if len(result) != 10 {
|
||||
t.Errorf("Expected date format YYYY-MM-DD, got %s", result)
|
||||
t.Run(tt.input, func(t *testing.T) {
|
||||
level := parseLevel(tt.input)
|
||||
if level.String() != tt.expected {
|
||||
t.Errorf("parseLevel(%s) = %s, want %s", tt.input, level.String(), tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,12 +207,3 @@ func BenchmarkLogger_Debug_Filtered(b *testing.B) {
|
||||
logger.Debug("test message", "key", "value")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLogger_ShouldLog(b *testing.B) {
|
||||
logger, _ := New(Config{Level: "INFO"})
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
logger.shouldLog("DEBUG")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user