// FILE: lixenwraith/log/integration_test.go package log import ( "fmt" "os" "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // TestFullLifecycle performs an end-to-end test of creating, configuring, and using the logger func TestFullLifecycle(t *testing.T) { tmpDir := t.TempDir() // Create logger with builder using the new streamlined interface logger, err := NewBuilder(). Directory(tmpDir). LevelString("debug"). Format("json"). MaxSizeKB(1). BufferSize(1000). EnableConsole(false). HeartbeatLevel(1). HeartbeatIntervalS(2). Build() require.NoError(t, err, "Logger creation with builder should succeed") require.NotNil(t, logger) // Start the logger before use err = logger.Start() require.NoError(t, err) // Defer shutdown right after successful creation defer func() { err := logger.Shutdown(2 * time.Second) assert.NoError(t, err, "Logger shutdown should be clean") }() // Log at various levels logger.Debug("debug message") logger.Info("info message") logger.Warn("warning message") logger.Error("error message") // Structured logging logger.LogStructured(LevelInfo, "structured log", map[string]any{ "user_id": 123, "action": "login", "success": true, }) // Raw write logger.Write("raw data write") // Trace logging logger.InfoTrace(2, "trace info") // Apply runtime override err = logger.ApplyConfigString("enable_console=true", "console_target=stderr") require.NoError(t, err) // More logging after reconfiguration logger.Info("after reconfiguration") // Wait for heartbeat time.Sleep(2500 * time.Millisecond) // Flush and check err = logger.Flush(time.Second) assert.NoError(t, err) // Verify log content files, err := os.ReadDir(tmpDir) require.NoError(t, err) assert.GreaterOrEqual(t, len(files), 1, "At least one log file should be created") } // TestConcurrentOperations tests the logger's stability under concurrent logging and reconfigurations func TestConcurrentOperations(t *testing.T) { logger, _ := createTestLogger(t) defer logger.Shutdown() var wg sync.WaitGroup // Concurrent logging for i := 0; i < 5; i++ { wg.Add(1) go func(id int) { defer wg.Done() for j := 0; j < 20; j++ { logger.Info("worker", id, "log", j) } }(i) } // Concurrent configuration changes wg.Add(1) go func() { defer wg.Done() for i := 0; i < 3; i++ { err := logger.ApplyConfigString(fmt.Sprintf("trace_depth=%d", i)) assert.NoError(t, err) time.Sleep(50 * time.Millisecond) } }() // Concurrent flushes wg.Add(1) go func() { defer wg.Done() for i := 0; i < 5; i++ { err := logger.Flush(100 * time.Millisecond) assert.NoError(t, err) time.Sleep(30 * time.Millisecond) } }() wg.Wait() } // TestErrorRecovery tests the logger's behavior in failure scenarios func TestErrorRecovery(t *testing.T) { t.Run("invalid directory", func(t *testing.T) { // Use the builder to attempt creation with an invalid directory logger, err := NewBuilder(). Directory("/root/cannot_write_here_without_sudo"). Build() assert.Error(t, err, "Should get an error for an invalid directory") assert.Nil(t, logger, "Logger should be nil on creation failure") }) t.Run("disk full simulation", func(t *testing.T) { logger, _ := createTestLogger(t) defer logger.Shutdown() cfg := logger.GetConfig() cfg.MinDiskFreeKB = 9999999999 // A very large number to simulate a full disk err := logger.ApplyConfig(cfg) require.NoError(t, err) // Small delay to ensure the processor has time to react if needed time.Sleep(100 * time.Millisecond) // Should detect disk space issue during the check isOK := logger.performDiskCheck(true) assert.False(t, isOK, "Disk check should fail when min free space is not met") assert.False(t, logger.state.DiskStatusOK.Load(), "DiskStatusOK state should be false") // Small delay to ensure the processor has time to react if needed time.Sleep(100 * time.Millisecond) preDropped := logger.state.DroppedLogs.Load() logger.Info("this log entry should be dropped") var postDropped uint64 var success bool // Poll for up to 500ms for the async processor to update the state for i := 0; i < 50; i++ { postDropped = logger.state.DroppedLogs.Load() if postDropped > preDropped { success = true break } time.Sleep(10 * time.Millisecond) } require.True(t, success, "Dropped log count should have increased after logging with disk full") }) }