Files
log/builder_test.go
T

141 lines
4.6 KiB
Go

package log
import (
"os"
"path/filepath"
"testing"
)
// TestBuilderBuild verifies configuration flows from the fluent API into the logger.
func TestBuilderBuild(t *testing.T) {
tmpDir := t.TempDir()
logger, err := NewBuilder().
Directory(tmpDir).
LevelString("debug").
Format("json").
BufferSize(2048).
EnableConsole(false).
EnableFile(true).
MaxSizeMB(10).
HeartbeatLevel(2).
Build()
mustNoErr(t, err, "Build")
if logger == nil {
t.Fatal("Build returned a nil logger without an error")
}
t.Cleanup(func() { _ = logger.Shutdown() })
cfg := logger.GetConfig()
equal(t, cfg.Directory, tmpDir, "Directory")
equal(t, cfg.Level, LevelDebug, "Level")
equal(t, cfg.Format, "json", "Format")
equal(t, cfg.BufferSize, int64(2048), "BufferSize")
isFalse(t, cfg.EnableConsole, "EnableConsole")
isTrue(t, cfg.EnableFile, "EnableFile")
equal(t, cfg.MaxSizeKB, int64(10*sizeMultiplier), "MaxSizeKB")
equal(t, cfg.HeartbeatLevel, int64(2), "HeartbeatLevel")
// Build applies but does not start the processor
isTrue(t, logger.state.IsInitialized.Load(), "IsInitialized")
isFalse(t, logger.state.Started.Load(), "Started")
}
// TestBuilderUnitConversion verifies KB/MB setter pairs share one field.
func TestBuilderUnitConversion(t *testing.T) {
tests := []struct {
name string
set func(*Builder) *Builder
get func(*Config) int64
want int64
}{
{"MaxSizeKB", func(b *Builder) *Builder { return b.MaxSizeKB(512) }, func(c *Config) int64 { return c.MaxSizeKB }, 512},
{"MaxSizeMB", func(b *Builder) *Builder { return b.MaxSizeMB(2) }, func(c *Config) int64 { return c.MaxSizeKB }, 2 * sizeMultiplier},
{"MaxTotalSizeKB", func(b *Builder) *Builder { return b.MaxTotalSizeKB(512) }, func(c *Config) int64 { return c.MaxTotalSizeKB }, 512},
{"MaxTotalSizeMB", func(b *Builder) *Builder { return b.MaxTotalSizeMB(3) }, func(c *Config) int64 { return c.MaxTotalSizeKB }, 3 * sizeMultiplier},
{"MinDiskFreeKB", func(b *Builder) *Builder { return b.MinDiskFreeKB(64) }, func(c *Config) int64 { return c.MinDiskFreeKB }, 64},
{"MinDiskFreeMB", func(b *Builder) *Builder { return b.MinDiskFreeMB(4) }, func(c *Config) int64 { return c.MinDiskFreeKB }, 4 * sizeMultiplier},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b := NewBuilder().Directory(t.TempDir()).EnableConsole(false)
logger, err := tt.set(b).Build()
mustNoErr(t, err, "Build")
t.Cleanup(func() { _ = logger.Shutdown() })
equal(t, tt.get(logger.GetConfig()), tt.want, tt.name)
})
}
}
// TestBuilderErrorAccumulation verifies a deferred error aborts Build.
func TestBuilderErrorAccumulation(t *testing.T) {
logger, err := NewBuilder().
LevelString("invalid-level-string").
Directory("/some/dir"). // must never be applied
Build()
errContains(t, err, "invalid level string", "Build")
if logger != nil {
t.Error("Build must return a nil logger on error")
}
// A subsequent setter must not clear the accumulated error
logger, err = NewBuilder().
LevelString("nonsense").
LevelString("info").
Build()
mustErr(t, err, "Build after error recovery attempt")
if logger != nil {
t.Error("Build must return a nil logger on error")
}
}
// TestBuilderValidationFailure verifies validation errors surface from ApplyConfig.
func TestBuilderValidationFailure(t *testing.T) {
t.Run("invalid format", func(t *testing.T) {
logger, err := NewBuilder().Format("yaml").Build()
errContains(t, err, "invalid format", "Build")
if logger != nil {
t.Error("Build must return a nil logger on error")
}
})
t.Run("unwritable directory", func(t *testing.T) {
// Directory mode is not enforced against uid 0
if os.Geteuid() == 0 {
t.Skip("running as root; directory permissions are not enforced")
}
parent := t.TempDir()
mustNoErr(t, os.Chmod(parent, 0o500), "chmod parent")
t.Cleanup(func() { _ = os.Chmod(parent, 0o700) })
logger, err := NewBuilder().
Directory(filepath.Join(parent, "nested")).
EnableFile(true).
Build()
errContains(t, err, "failed to create log directory", "Build")
if logger != nil {
t.Error("Build must return a nil logger on error")
}
})
}
// TestBuilderDefaults verifies an unconfigured builder yields the package defaults.
func TestBuilderDefaults(t *testing.T) {
logger, err := NewBuilder().EnableConsole(false).Build()
mustNoErr(t, err, "Build")
t.Cleanup(func() { _ = logger.Shutdown() })
cfg := logger.GetConfig()
def := DefaultConfig()
equal(t, cfg.Level, def.Level, "Level")
equal(t, cfg.Format, def.Format, "Format")
equal(t, cfg.Name, def.Name, "Name")
equal(t, cfg.BufferSize, def.BufferSize, "BufferSize")
equal(t, cfg.Sanitization, def.Sanitization, "Sanitization")
}