Pulse/cmd/pulse-docker-agent/main_test.go

394 lines
8.5 KiB
Go

package main
import (
"reflect"
"strings"
"testing"
"github.com/rcourtman/pulse-go-rewrite/internal/dockeragent"
"github.com/rs/zerolog"
)
func TestParseTargetSpec(t *testing.T) {
target, err := parseTargetSpec("https://pulse.example.com|abc123|true")
if err != nil {
t.Fatalf("parseTargetSpec returned error: %v", err)
}
if target.URL != "https://pulse.example.com" {
t.Fatalf("expected URL https://pulse.example.com, got %q", target.URL)
}
if target.Token != "abc123" {
t.Fatalf("expected token abc123, got %q", target.Token)
}
if !target.InsecureSkipVerify {
t.Fatalf("expected insecure flag true")
}
}
func TestParseTargetSpecDefaults(t *testing.T) {
target, err := parseTargetSpec(" https://pulse.example.com | token456 ")
if err != nil {
t.Fatalf("parseTargetSpec returned error: %v", err)
}
if target.URL != "https://pulse.example.com" {
t.Fatalf("expected URL https://pulse.example.com, got %q", target.URL)
}
if target.Token != "token456" {
t.Fatalf("expected token token456, got %q", target.Token)
}
if target.InsecureSkipVerify {
t.Fatalf("expected insecure flag false")
}
}
func TestParseTargetSpecInvalid(t *testing.T) {
if _, err := parseTargetSpec("https://pulse.example.com"); err == nil {
t.Fatalf("expected error for missing token")
}
if _, err := parseTargetSpec("https://pulse.example.com|token|maybe"); err == nil {
t.Fatalf("expected error for invalid insecure flag")
}
}
func TestParseTargetSpecsSkipsBlanks(t *testing.T) {
specs, err := parseTargetSpecs([]string{"https://a|tokenA", " ", "\n", "https://b|tokenB|true"})
if err != nil {
t.Fatalf("parseTargetSpecs returned error: %v", err)
}
if len(specs) != 2 {
t.Fatalf("expected 2 targets, got %d", len(specs))
}
expected := []dockeragent.TargetConfig{
{URL: "https://a", Token: "tokenA", InsecureSkipVerify: false},
{URL: "https://b", Token: "tokenB", InsecureSkipVerify: true},
}
for i, target := range specs {
if target != expected[i] {
t.Fatalf("target %d mismatch: expected %+v, got %+v", i, expected[i], target)
}
}
}
func TestSplitTargetSpecs(t *testing.T) {
values := splitTargetSpecs("https://a|tokenA;https://b|tokenB\nhttps://c|tokenC")
expected := []string{"https://a|tokenA", "https://b|tokenB", "https://c|tokenC"}
if len(values) != len(expected) {
t.Fatalf("expected %d values, got %d", len(expected), len(values))
}
for i, v := range values {
if v != expected[i] {
t.Fatalf("value %d mismatch: expected %q, got %q", i, expected[i], v)
}
}
}
func TestParseLogLevel(t *testing.T) {
tests := []struct {
name string
input string
wantLevel zerolog.Level
wantErr bool
errSubstr string
}{
// Valid levels
{
name: "debug level",
input: "debug",
wantLevel: zerolog.DebugLevel,
},
{
name: "info level",
input: "info",
wantLevel: zerolog.InfoLevel,
},
{
name: "warn level",
input: "warn",
wantLevel: zerolog.WarnLevel,
},
{
name: "error level",
input: "error",
wantLevel: zerolog.ErrorLevel,
},
{
name: "trace level",
input: "trace",
wantLevel: zerolog.TraceLevel,
},
// Case insensitivity
{
name: "uppercase DEBUG",
input: "DEBUG",
wantLevel: zerolog.DebugLevel,
},
{
name: "mixed case Info",
input: "Info",
wantLevel: zerolog.InfoLevel,
},
{
name: "uppercase WARN",
input: "WARN",
wantLevel: zerolog.WarnLevel,
},
// Whitespace handling
{
name: "leading whitespace",
input: " debug",
wantLevel: zerolog.DebugLevel,
},
{
name: "trailing whitespace",
input: "warn ",
wantLevel: zerolog.WarnLevel,
},
{
name: "both whitespace",
input: " error ",
wantLevel: zerolog.ErrorLevel,
},
{
name: "tabs",
input: "\tinfo\t",
wantLevel: zerolog.InfoLevel,
},
// Empty string defaults to info
{
name: "empty string defaults to info",
input: "",
wantLevel: zerolog.InfoLevel,
},
{
name: "whitespace only defaults to info",
input: " ",
wantLevel: zerolog.InfoLevel,
},
{
name: "tabs only defaults to info",
input: "\t\t",
wantLevel: zerolog.InfoLevel,
},
// Invalid levels
{
name: "invalid level returns error",
input: "invalid",
wantLevel: zerolog.InfoLevel,
wantErr: true,
errSubstr: "invalid log level",
},
{
name: "typo returns error",
input: "debuf",
wantLevel: zerolog.InfoLevel,
wantErr: true,
errSubstr: "must be debug, info, warn, or error",
},
{
name: "numeric 1 maps to info level",
input: "1",
wantLevel: zerolog.InfoLevel,
},
{
name: "numeric 0 maps to debug level",
input: "0",
wantLevel: zerolog.DebugLevel,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
level, err := parseLogLevel(tt.input)
if tt.wantErr {
if err == nil {
t.Fatalf("expected error, got nil")
}
if tt.errSubstr != "" && !strings.Contains(err.Error(), tt.errSubstr) {
t.Fatalf("expected error containing %q, got %q", tt.errSubstr, err.Error())
}
} else {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
if level != tt.wantLevel {
t.Fatalf("expected level %v, got %v", tt.wantLevel, level)
}
})
}
}
func TestSplitStringList(t *testing.T) {
tests := []struct {
name string
input string
want []string
}{
// Empty input
{
name: "empty string returns nil",
input: "",
want: nil,
},
// Single item
{
name: "single item",
input: "foo",
want: []string{"foo"},
},
{
name: "single item with whitespace",
input: " foo ",
want: []string{"foo"},
},
// Comma delimiter
{
name: "comma separated",
input: "foo,bar,baz",
want: []string{"foo", "bar", "baz"},
},
{
name: "comma with spaces",
input: "foo, bar, baz",
want: []string{"foo", "bar", "baz"},
},
{
name: "comma with extra spaces",
input: " foo , bar , baz ",
want: []string{"foo", "bar", "baz"},
},
// Semicolon delimiter
{
name: "semicolon separated",
input: "foo;bar;baz",
want: []string{"foo", "bar", "baz"},
},
{
name: "semicolon with spaces",
input: "foo; bar; baz",
want: []string{"foo", "bar", "baz"},
},
// Newline delimiter
{
name: "newline separated",
input: "foo\nbar\nbaz",
want: []string{"foo", "bar", "baz"},
},
{
name: "newline with spaces",
input: "foo \n bar \n baz",
want: []string{"foo", "bar", "baz"},
},
// Carriage return delimiter
{
name: "carriage return separated",
input: "foo\rbar\rbaz",
want: []string{"foo", "bar", "baz"},
},
{
name: "CRLF (Windows line ending)",
input: "foo\r\nbar\r\nbaz",
want: []string{"foo", "bar", "baz"},
},
// Mixed delimiters
{
name: "mixed comma and semicolon",
input: "foo,bar;baz",
want: []string{"foo", "bar", "baz"},
},
{
name: "mixed all delimiters",
input: "a,b;c\nd\re",
want: []string{"a", "b", "c", "d", "e"},
},
{
name: "mixed with spaces",
input: "a , b ; c \n d \r e",
want: []string{"a", "b", "c", "d", "e"},
},
// Consecutive delimiters (should be filtered)
{
name: "double comma",
input: "foo,,bar",
want: []string{"foo", "bar"},
},
{
name: "multiple consecutive delimiters",
input: "foo,,,bar;;;baz",
want: []string{"foo", "bar", "baz"},
},
{
name: "trailing delimiter",
input: "foo,bar,",
want: []string{"foo", "bar"},
},
{
name: "leading delimiter",
input: ",foo,bar",
want: []string{"foo", "bar"},
},
{
name: "only delimiters returns empty slice",
input: ",;,;",
want: []string{},
},
// Whitespace-only items filtered
{
name: "whitespace between delimiters filtered",
input: "foo, ,bar",
want: []string{"foo", "bar"},
},
{
name: "tabs between delimiters filtered",
input: "foo,\t\t,bar",
want: []string{"foo", "bar"},
},
// Real-world examples
{
name: "network names list",
input: "bridge, host, none",
want: []string{"bridge", "host", "none"},
},
{
name: "container IDs",
input: "abc123;def456;ghi789",
want: []string{"abc123", "def456", "ghi789"},
},
{
name: "multiline config",
input: "web\napi\nworker",
want: []string{"web", "api", "worker"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := splitStringList(tt.input)
if !reflect.DeepEqual(got, tt.want) {
t.Fatalf("expected %v, got %v", tt.want, got)
}
})
}
}