Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,7 @@ claudedocs/
# Mutation testing reports (generated by Gremlins)
mutation-report.json
mutation-report.html
mutation-output.txt
mutation-output.txt
# Generated test artifacts
cover.out
test_cron_compat
117 changes: 116 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
version: "2"
run:
tests: false
tests: true
linters:
default: none
enable:
Expand All @@ -11,17 +11,21 @@ linters:
- copyloopvar
- depguard
- dupl
- dupword
- durationcheck
- err113
- errcheck
- errname
- errorlint
- exhaustive
- fatcontext
- forbidigo
- goconst
- gocritic
- gocyclo
- govet
- ineffassign
- intrange
- misspell
- nilerr
- nilnil
Expand All @@ -34,10 +38,12 @@ linters:
- revive
- staticcheck
- tagliatelle
- testifylint
- tparallel
- unconvert
- unparam
- unused
- usestdlibvars
- wrapcheck
settings:
depguard:
Expand All @@ -49,6 +55,8 @@ linters:
forbidigo:
forbid:
- pattern: ^fmt\.Print.*$
goconst:
min-occurrences: 6
gocyclo:
min-complexity: 15
misspell:
Expand Down Expand Up @@ -81,11 +89,40 @@ linters:
- legacy
- std-error-handling
rules:
# Test files have relaxed linting for practical reasons
- linters:
- gocyclo
- revive
- wrapcheck
- goconst
- nilnil
- err113
- errcheck
- dupl
- errorlint
- gocritic
- predeclared
- staticcheck
- unparam
path: _test\.go
# User-facing validation errors need dynamic context for helpful messages
- linters:
- err113
path: cli/(init|hashpw|daemon|doctor|config_webhook|docker_config_handler)\.go
- linters:
- err113
path: middlewares/(preset|webhook|sanitize).*\.go
- linters:
- err113
path: config/(command_validator|sanitizer)\.go
- linters:
- err113
path: web/server\.go
# Empty branches in test code - often used for pattern matching
- linters:
- staticcheck
path: _test\.go
text: "SA9003"
- linters:
- govet
path: "_test"
Expand All @@ -106,9 +143,87 @@ linters:
- linters:
- staticcheck
text: "ST1018:"
# Docker SDK deprecated types - will update when SDK breaks
- linters:
- staticcheck
path: core/adapters/docker/convert_test\.go
text: "SA1019:"
- linters:
- noctx
path: cli/daemon\.go
# Test files don't need context propagation
- linters:
- noctx
path: _test\.go
# Non-parallel tests - intentionally modify global state
- linters:
- paralleltest
- tparallel
path: cli/daemon_boot_test\.go
- linters:
- paralleltest
- tparallel
path: cli/config_initialize_test\.go
- linters:
- paralleltest
- tparallel
path: middlewares/webhook_test\.go
text: "TestWebhook_SendsRequest|TestWebhook_Retry"
- linters:
- paralleltest
- tparallel
path: middlewares/webhook_security_test\.go
text: "TestSetGlobalSecurityConfig"
# Benchmark tests don't need t.Parallel()
- linters:
- paralleltest
- tparallel
path: _benchmark_test\.go
# Integration tests often share state
- linters:
- paralleltest
- tparallel
path: _integration_test\.go
# Mutation tests - generated structure, TODO: add t.Parallel() later
- linters:
- paralleltest
- tparallel
path: _mutation_test\.go
# Core tests - TODO: add t.Parallel() incrementally
- linters:
- paralleltest
- tparallel
path: core/.*_test\.go
# CLI tests - many are lifecycle/integration tests
- linters:
- paralleltest
- tparallel
path: cli/.*_test\.go
# Config tests - TODO: add t.Parallel() incrementally
- linters:
- paralleltest
- tparallel
path: config/.*_test\.go
# Logging tests - TODO: add t.Parallel() incrementally
- linters:
- paralleltest
- tparallel
path: logging/.*_test\.go
# Web tests - TODO: add t.Parallel() incrementally
- linters:
- paralleltest
- tparallel
path: web/.*_test\.go
# Metrics tests - TODO: add t.Parallel() incrementally
- linters:
- paralleltest
- tparallel
path: metrics/.*_test\.go
# Middlewares tests - TODO: add t.Parallel() incrementally
- linters:
- paralleltest
- tparallel
path: middlewares/.*_test\.go
paths:
- third_party$
- builtin$
Expand Down
6 changes: 3 additions & 3 deletions cli/config_dependencies_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,13 +268,13 @@ command = echo standalone
}

// All dependency-related fields should be nil or empty
if job.Dependencies != nil && len(job.Dependencies) != 0 {
if len(job.Dependencies) != 0 {
t.Errorf("Expected no dependencies, got %v", job.Dependencies)
}
if job.OnSuccess != nil && len(job.OnSuccess) != 0 {
if len(job.OnSuccess) != 0 {
t.Errorf("Expected no on-success triggers, got %v", job.OnSuccess)
}
if job.OnFailure != nil && len(job.OnFailure) != 0 {
if len(job.OnFailure) != 0 {
t.Errorf("Expected no on-failure triggers, got %v", job.OnFailure)
}
}
Expand Down
2 changes: 1 addition & 1 deletion cli/config_execjob_init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func TestExecJobInit_FromINIConfig(t *testing.T) {
assert.NotNil(t, job)

// Verify job fields are set from config
assert.Equal(t, "", job.GetName()) // Name not set yet (set during registration)
assert.Empty(t, job.GetName()) // Name not set yet (set during registration)
assert.Equal(t, "@every 1h", job.GetSchedule())
assert.Equal(t, `echo "test"`, job.GetCommand())
assert.Equal(t, "test-container", job.Container)
Expand Down
40 changes: 20 additions & 20 deletions cli/config_extra_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ const (
func TestBuildFromStringInvalidIni(t *testing.T) {
t.Parallel()
_, err := BuildFromString("this is not ini", test.NewTestLogger())
assert.NotNil(t, err)
assert.Error(t, err)
}

// Test error path of BuildFromFile for non-existent or invalid file
func TestBuildFromFileError(t *testing.T) {
t.Parallel()
// Non-existent file
_, err := BuildFromFile("nonexistent_file.ini", test.NewTestLogger())
assert.NotNil(t, err)
require.Error(t, err)

// Invalid content
tmpFile, err := os.CreateTemp("", "config_test")
Expand All @@ -50,7 +50,7 @@ func TestBuildFromFileError(t *testing.T) {
tmpFile.Close()

_, err = BuildFromFile(tmpFile.Name(), test.NewTestLogger())
assert.NotNil(t, err)
assert.Error(t, err)
}

// Test InitializeApp returns error when Docker handler factory fails
Expand All @@ -65,7 +65,7 @@ func TestInitializeAppErrorDockerHandler(t *testing.T) {

cfg := NewConfig(test.NewTestLogger())
err := cfg.InitializeApp()
require.NotNil(t, err)
require.Error(t, err)
assert.Equal(t, "factory error", err.Error())
}

Expand All @@ -88,7 +88,7 @@ func TestDockerLabelsUpdateExecJobs(t *testing.T) {
},
}
cfg.dockerLabelsUpdate(labelsAdd)
assert.Equal(t, 1, len(cfg.ExecJobs))
assert.Len(t, cfg.ExecJobs, 1)
j := cfg.ExecJobs["container1.foo"]
assert.Equal(t, JobSourceLabel, j.JobSource)
// Verify schedule and command set
Expand All @@ -97,7 +97,7 @@ func TestDockerLabelsUpdateExecJobs(t *testing.T) {

// Inspect cron entries count
entries := cfg.sh.Entries()
assert.Equal(t, 1, len(entries))
assert.Len(t, entries, 1)

// 2) Change schedule (should restart job)
labelsChange := map[string]map[string]string{
Expand All @@ -107,17 +107,17 @@ func TestDockerLabelsUpdateExecJobs(t *testing.T) {
},
}
cfg.dockerLabelsUpdate(labelsChange)
assert.Equal(t, 1, len(cfg.ExecJobs))
assert.Len(t, cfg.ExecJobs, 1)
j2 := cfg.ExecJobs["container1.foo"]
assert.Equal(t, "@every 10s", j2.GetSchedule())
entries = cfg.sh.Entries()
assert.Equal(t, 1, len(entries))
assert.Len(t, entries, 1)

// 3) Removal of job
cfg.dockerLabelsUpdate(map[string]map[string]string{})
assert.Equal(t, 0, len(cfg.ExecJobs))
assert.Empty(t, cfg.ExecJobs)
entries = cfg.sh.Entries()
assert.Equal(t, 0, len(entries))
assert.Empty(t, entries)
}

// Test dockerLabelsUpdate blocks host jobs when security policy is disabled.
Expand Down Expand Up @@ -147,8 +147,8 @@ func TestDockerLabelsSecurityPolicyViolation(t *testing.T) {
cfg.dockerLabelsUpdate(labels)

// Verify security policy blocked the jobs
assert.Len(t, cfg.LocalJobs, 0, "Local jobs should be blocked by security policy")
assert.Len(t, cfg.ComposeJobs, 0, "Compose jobs should be blocked by security policy")
assert.Empty(t, cfg.LocalJobs, "Local jobs should be blocked by security policy")
assert.Empty(t, cfg.ComposeJobs, "Compose jobs should be blocked by security policy")

// Verify error logs were generated
assert.Equal(t, 2, logger.ErrorCount(), "Expected 2 error logs (1 for local, 1 for compose)")
Expand Down Expand Up @@ -190,8 +190,8 @@ func TestDockerLabelsUpdateStaleJobs(t *testing.T) {
assert.Len(t, cfg.ServiceJobs, 1)

cfg.dockerLabelsUpdate(map[string]map[string]string{})
assert.Len(t, cfg.LocalJobs, 0)
assert.Len(t, cfg.ServiceJobs, 0)
assert.Empty(t, cfg.LocalJobs)
assert.Empty(t, cfg.ServiceJobs)
}

// Test iniConfigUpdate reloads jobs from the INI file
Expand Down Expand Up @@ -221,7 +221,7 @@ func TestIniConfigUpdate(t *testing.T) {
_ = cfg.sh.AddJob(j)
}

assert.Equal(t, 1, len(cfg.RunJobs))
assert.Len(t, cfg.RunJobs, 1)
assert.Equal(t, "@every 5s", cfg.RunJobs["foo"].GetSchedule())

// modify ini: change schedule and add new job
Expand All @@ -233,7 +233,7 @@ func TestIniConfigUpdate(t *testing.T) {

err = cfg.iniConfigUpdate()
require.NoError(t, err)
assert.Equal(t, 2, len(cfg.RunJobs))
assert.Len(t, cfg.RunJobs, 2)
assert.Equal(t, "@every 10s", cfg.RunJobs["foo"].GetSchedule())

// modify ini: remove foo
Expand All @@ -245,7 +245,7 @@ func TestIniConfigUpdate(t *testing.T) {

err = cfg.iniConfigUpdate()
require.NoError(t, err)
assert.Equal(t, 1, len(cfg.RunJobs))
assert.Len(t, cfg.RunJobs, 1)
_, ok := cfg.RunJobs["foo"]
assert.False(t, ok)
}
Expand Down Expand Up @@ -323,7 +323,7 @@ func TestIniConfigUpdateNoReload(t *testing.T) {
err = cfg.iniConfigUpdate()
require.NoError(t, err)
assert.Equal(t, oldTime, cfg.configModTime)
assert.Equal(t, 1, len(cfg.RunJobs))
assert.Len(t, cfg.RunJobs, 1)
}

// TestIniConfigUpdateLabelConflict verifies INI jobs override label jobs on reload.
Expand Down Expand Up @@ -399,7 +399,7 @@ func TestIniConfigUpdateGlob(t *testing.T) {
_ = cfg.sh.AddJob(j)
}

assert.Equal(t, 2, len(cfg.RunJobs))
assert.Len(t, cfg.RunJobs, 2)
assert.Equal(t, "@every 5s", cfg.RunJobs["foo"].GetSchedule())

oldTime := cfg.configModTime
Expand All @@ -409,7 +409,7 @@ func TestIniConfigUpdateGlob(t *testing.T) {

err = cfg.iniConfigUpdate()
require.NoError(t, err)
assert.Equal(t, 2, len(cfg.RunJobs))
assert.Len(t, cfg.RunJobs, 2)
assert.Equal(t, "@every 10s", cfg.RunJobs["foo"].GetSchedule())
}

Expand Down
6 changes: 2 additions & 4 deletions cli/config_parsing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,10 +297,8 @@ command = echo hello
if tt.errSubstr != "" && !strings.Contains(err.Error(), tt.errSubstr) {
t.Errorf("Expected error to contain %q, got %q", tt.errSubstr, err.Error())
}
} else {
if err != nil {
t.Errorf("Expected no error but got: %v", err)
}
} else if err != nil {
t.Errorf("Expected no error but got: %v", err)
}
})
}
Expand Down
Loading