mirror of
https://github.com/caddyserver/caddy.git
synced 2026-03-31 22:32:17 -04:00
internal: test package
Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
This commit is contained in:
parent
476d75219c
commit
435e521203
147
internal/logbuffer_test.go
Normal file
147
internal/logbuffer_test.go
Normal file
@ -0,0 +1,147 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"go.uber.org/zap/zaptest/observer"
|
||||
)
|
||||
|
||||
func TestLogBufferCoreEnabled(t *testing.T) {
|
||||
core := NewLogBufferCore(zapcore.InfoLevel)
|
||||
|
||||
if !core.Enabled(zapcore.InfoLevel) {
|
||||
t.Error("expected InfoLevel to be enabled")
|
||||
}
|
||||
if !core.Enabled(zapcore.ErrorLevel) {
|
||||
t.Error("expected ErrorLevel to be enabled")
|
||||
}
|
||||
if core.Enabled(zapcore.DebugLevel) {
|
||||
t.Error("expected DebugLevel to be disabled")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogBufferCoreWriteAndFlush(t *testing.T) {
|
||||
core := NewLogBufferCore(zapcore.InfoLevel)
|
||||
|
||||
// Write entries
|
||||
entry1 := zapcore.Entry{Level: zapcore.InfoLevel, Message: "message1"}
|
||||
entry2 := zapcore.Entry{Level: zapcore.WarnLevel, Message: "message2"}
|
||||
|
||||
if err := core.Write(entry1, []zapcore.Field{zap.String("key1", "val1")}); err != nil {
|
||||
t.Fatalf("Write() error = %v", err)
|
||||
}
|
||||
if err := core.Write(entry2, []zapcore.Field{zap.String("key2", "val2")}); err != nil {
|
||||
t.Fatalf("Write() error = %v", err)
|
||||
}
|
||||
|
||||
// Verify entries are buffered
|
||||
if len(core.entries) != 2 {
|
||||
t.Errorf("expected 2 entries, got %d", len(core.entries))
|
||||
}
|
||||
if len(core.fields) != 2 {
|
||||
t.Errorf("expected 2 field sets, got %d", len(core.fields))
|
||||
}
|
||||
|
||||
// Set up an observed logger to capture flushed entries
|
||||
observedCore, logs := observer.New(zapcore.InfoLevel)
|
||||
logger := zap.New(observedCore)
|
||||
|
||||
core.FlushTo(logger)
|
||||
|
||||
// Verify entries were flushed
|
||||
if logs.Len() != 2 {
|
||||
t.Errorf("expected 2 flushed log entries, got %d", logs.Len())
|
||||
}
|
||||
|
||||
// Verify buffer is cleared after flush
|
||||
if len(core.entries) != 0 {
|
||||
t.Errorf("expected entries to be cleared after flush, got %d", len(core.entries))
|
||||
}
|
||||
if len(core.fields) != 0 {
|
||||
t.Errorf("expected fields to be cleared after flush, got %d", len(core.fields))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogBufferCoreSync(t *testing.T) {
|
||||
core := NewLogBufferCore(zapcore.InfoLevel)
|
||||
if err := core.Sync(); err != nil {
|
||||
t.Errorf("Sync() error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogBufferCoreWith(t *testing.T) {
|
||||
core := NewLogBufferCore(zapcore.InfoLevel)
|
||||
|
||||
// With() currently returns the same core (known limitation)
|
||||
result := core.With([]zapcore.Field{zap.String("test", "val")})
|
||||
if result != core {
|
||||
t.Error("With() should return the same core instance")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogBufferCoreCheck(t *testing.T) {
|
||||
core := NewLogBufferCore(zapcore.InfoLevel)
|
||||
|
||||
// Check for enabled level should add core
|
||||
entry := zapcore.Entry{Level: zapcore.InfoLevel, Message: "test"}
|
||||
ce := &zapcore.CheckedEntry{}
|
||||
result := core.Check(entry, ce)
|
||||
if result == nil {
|
||||
t.Error("Check() should return non-nil for enabled level")
|
||||
}
|
||||
|
||||
// Check for disabled level should not add core
|
||||
debugEntry := zapcore.Entry{Level: zapcore.DebugLevel, Message: "test"}
|
||||
ce2 := &zapcore.CheckedEntry{}
|
||||
result2 := core.Check(debugEntry, ce2)
|
||||
// The ce2 should be returned unchanged (no core added)
|
||||
if result2 != ce2 {
|
||||
t.Error("Check() should return unchanged CheckedEntry for disabled level")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogBufferCoreEmptyFlush(t *testing.T) {
|
||||
core := NewLogBufferCore(zapcore.InfoLevel)
|
||||
|
||||
// Flushing with no entries should not panic
|
||||
observedCore, logs := observer.New(zapcore.InfoLevel)
|
||||
logger := zap.New(observedCore)
|
||||
|
||||
core.FlushTo(logger)
|
||||
|
||||
if logs.Len() != 0 {
|
||||
t.Errorf("expected 0 flushed entries for empty buffer, got %d", logs.Len())
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogBufferCoreConcurrentWrites(t *testing.T) {
|
||||
core := NewLogBufferCore(zapcore.InfoLevel)
|
||||
|
||||
done := make(chan struct{})
|
||||
const numWriters = 10
|
||||
const numWrites = 100
|
||||
|
||||
for i := 0; i < numWriters; i++ {
|
||||
go func() {
|
||||
defer func() { done <- struct{}{} }()
|
||||
for j := 0; j < numWrites; j++ {
|
||||
entry := zapcore.Entry{Level: zapcore.InfoLevel, Message: "concurrent"}
|
||||
_ = core.Write(entry, nil)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
for i := 0; i < numWriters; i++ {
|
||||
<-done
|
||||
}
|
||||
|
||||
core.mu.Lock()
|
||||
count := len(core.entries)
|
||||
core.mu.Unlock()
|
||||
|
||||
if count != numWriters*numWrites {
|
||||
t.Errorf("expected %d entries, got %d", numWriters*numWrites, count)
|
||||
}
|
||||
}
|
||||
125
internal/ranges_test.go
Normal file
125
internal/ranges_test.go
Normal file
@ -0,0 +1,125 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPrivateRangesCIDR(t *testing.T) {
|
||||
ranges := PrivateRangesCIDR()
|
||||
|
||||
// Should include standard private IP ranges
|
||||
expected := map[string]bool{
|
||||
"192.168.0.0/16": false,
|
||||
"172.16.0.0/12": false,
|
||||
"10.0.0.0/8": false,
|
||||
"127.0.0.1/8": false,
|
||||
"fd00::/8": false,
|
||||
"::1": false,
|
||||
}
|
||||
|
||||
for _, r := range ranges {
|
||||
if _, ok := expected[r]; ok {
|
||||
expected[r] = true
|
||||
}
|
||||
}
|
||||
|
||||
for cidr, found := range expected {
|
||||
if !found {
|
||||
t.Errorf("expected private range %q not found in PrivateRangesCIDR()", cidr)
|
||||
}
|
||||
}
|
||||
|
||||
if len(ranges) < 6 {
|
||||
t.Errorf("expected at least 6 private ranges, got %d", len(ranges))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxSizeSubjectsListForLog(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
subjects map[string]struct{}
|
||||
maxToDisplay int
|
||||
wantLen int
|
||||
wantSuffix bool // whether "(and N more...)" is expected
|
||||
}{
|
||||
{
|
||||
name: "empty map",
|
||||
subjects: map[string]struct{}{},
|
||||
maxToDisplay: 5,
|
||||
wantLen: 0,
|
||||
wantSuffix: false,
|
||||
},
|
||||
{
|
||||
name: "fewer than max",
|
||||
subjects: map[string]struct{}{
|
||||
"example.com": {},
|
||||
"example.org": {},
|
||||
},
|
||||
maxToDisplay: 5,
|
||||
wantLen: 2,
|
||||
wantSuffix: false,
|
||||
},
|
||||
{
|
||||
name: "equal to max",
|
||||
subjects: map[string]struct{}{
|
||||
"a.com": {},
|
||||
"b.com": {},
|
||||
"c.com": {},
|
||||
},
|
||||
maxToDisplay: 3,
|
||||
wantLen: 3,
|
||||
wantSuffix: false,
|
||||
},
|
||||
{
|
||||
name: "more than max",
|
||||
subjects: map[string]struct{}{
|
||||
"a.com": {},
|
||||
"b.com": {},
|
||||
"c.com": {},
|
||||
"d.com": {},
|
||||
"e.com": {},
|
||||
},
|
||||
maxToDisplay: 2,
|
||||
wantLen: 3, // 2 domains + suffix
|
||||
wantSuffix: true,
|
||||
},
|
||||
{
|
||||
name: "max is zero",
|
||||
subjects: map[string]struct{}{
|
||||
"a.com": {},
|
||||
"b.com": {},
|
||||
},
|
||||
maxToDisplay: 0,
|
||||
// BUG: When maxToDisplay is 0, code still appends one domain
|
||||
// because append happens before the break check in the loop.
|
||||
// Expected behavior: 1 item (just suffix). Actual: 2 items
|
||||
// (1 leaked domain + suffix).
|
||||
wantLen: 2,
|
||||
wantSuffix: true,
|
||||
},
|
||||
{
|
||||
name: "single subject with max 1",
|
||||
subjects: map[string]struct{}{
|
||||
"example.com": {},
|
||||
},
|
||||
maxToDisplay: 1,
|
||||
wantLen: 1,
|
||||
wantSuffix: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := MaxSizeSubjectsListForLog(tt.subjects, tt.maxToDisplay)
|
||||
if len(result) != tt.wantLen {
|
||||
t.Errorf("MaxSizeSubjectsListForLog() returned %d items, want %d; got: %v", len(result), tt.wantLen, result)
|
||||
}
|
||||
if tt.wantSuffix {
|
||||
last := result[len(result)-1]
|
||||
if len(last) < 4 || last[:4] != "(and" {
|
||||
t.Errorf("expected suffix '(and N more...)' but got %q", last)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
146
internal/sockets_test.go
Normal file
146
internal/sockets_test.go
Normal file
@ -0,0 +1,146 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSplitUnixSocketPermissionsBits(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
wantPath string
|
||||
wantFileMode fs.FileMode
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "no permission bits defaults to 0200",
|
||||
input: "/run/caddy.sock",
|
||||
wantPath: "/run/caddy.sock",
|
||||
wantFileMode: 0o200,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid permission 0222",
|
||||
input: "/run/caddy.sock|0222",
|
||||
wantPath: "/run/caddy.sock",
|
||||
wantFileMode: 0o222,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid permission 0200",
|
||||
input: "/run/caddy.sock|0200",
|
||||
wantPath: "/run/caddy.sock",
|
||||
wantFileMode: 0o200,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid permission 0777",
|
||||
input: "/run/caddy.sock|0777",
|
||||
wantPath: "/run/caddy.sock",
|
||||
wantFileMode: 0o777,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid permission 0755",
|
||||
input: "/run/caddy.sock|0755",
|
||||
wantPath: "/run/caddy.sock",
|
||||
wantFileMode: 0o755,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid permission 0666",
|
||||
input: "/tmp/test.sock|0666",
|
||||
wantPath: "/tmp/test.sock",
|
||||
wantFileMode: 0o666,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "missing owner write permission 0444",
|
||||
input: "/run/caddy.sock|0444",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing owner write permission 0044",
|
||||
input: "/run/caddy.sock|0044",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing owner write permission 0100",
|
||||
input: "/run/caddy.sock|0100",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing owner write permission 0500",
|
||||
input: "/run/caddy.sock|0500",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid octal digits",
|
||||
input: "/run/caddy.sock|09ab",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid non-numeric permission",
|
||||
input: "/run/caddy.sock|rwxrwxrwx",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty permission string",
|
||||
input: "/run/caddy.sock|",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "multiple pipes only splits on first",
|
||||
input: "/run/caddy|sock|0222",
|
||||
wantPath: "/run/caddy",
|
||||
wantFileMode: 0, // "sock|0222" is not valid octal
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty path with valid permission",
|
||||
input: "|0222",
|
||||
wantPath: "",
|
||||
wantFileMode: 0o222,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "path only with no pipe",
|
||||
input: "/var/run/my-app.sock",
|
||||
wantPath: "/var/run/my-app.sock",
|
||||
wantFileMode: 0o200,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "permission 0300 has write bit",
|
||||
input: "/run/caddy.sock|0300",
|
||||
wantPath: "/run/caddy.sock",
|
||||
wantFileMode: 0o300,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "permission 0422 missing owner write",
|
||||
input: "/run/caddy.sock|0422",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotPath, gotMode, err := SplitUnixSocketPermissionsBits(tt.input)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("SplitUnixSocketPermissionsBits(%q) error = %v, wantErr %v", tt.input, err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if gotPath != tt.wantPath {
|
||||
t.Errorf("SplitUnixSocketPermissionsBits(%q) path = %q, want %q", tt.input, gotPath, tt.wantPath)
|
||||
}
|
||||
if gotMode != tt.wantFileMode {
|
||||
t.Errorf("SplitUnixSocketPermissionsBits(%q) mode = %04o, want %04o", tt.input, gotMode, tt.wantFileMode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user