Skip to content

chore(lint): enable testifylint and thelper, and fix what they found - #6984

Merged
otavio merged 3 commits into
masterfrom
chore/lint-testify
Aug 28, 2026
Merged

chore(lint): enable testifylint and thelper, and fix what they found#6984
otavio merged 3 commits into
masterfrom
chore/lint-testify

Conversation

@otavio

@otavio otavio commented Aug 28, 2026

Copy link
Copy Markdown
Member

What

Enables testifylint and thelper, and fixes the 216 findings they report across 63 files. All of it is test code.

Why

Fourth step of the lint expansion, after #6980, #6981 and #6982. These two are what make a failing test say the right thing: the assertion that failed, at the caller's line, instead of a nil panic three lines later.

Changes

One real bug. Two httptest handlers called require.NoError — in pkg/wsconnadapter and server/ssh/pkg/dialer. require reaches t.FailNow, which is only defined on the test's own goroutine. A failed WebSocket upgrade in those handlers would not have stopped the test the way the code reads. They now assert and return.

125 require-error. The recurring shape is an error asserted with assert, then the value it produced dereferenced on the next line. When the assertion fails the test keeps running and panics on nil, so the report is a crash rather than the assertion that actually failed. require stops there instead.

tests/environment had this hand-rolled seven times as if !assert.NoError(t, err) { assert.FailNow(t, err.Error()) }, which is require.NoError written out longhand. Those collapse to the real thing.

91 thelper. Helpers that take a *testing.T now declare it, so a failure points at the caller rather than at a line inside the helper. The storetest sub-suite closures took *testing.T second, which thelper reads as a helper with its parameters the wrong way round — they take it first now and runSubSuite's signature follows.

How

The rewrites were driven from the linter's own file:line output rather than typed, then the full suite was run to confirm no assertion changed a result. Two mechanical slips the process caught and I fixed: a t.Helper() insertion that landed in a composite literal because the target was a single-line empty closure, and 58 duplicated testify imports from the import pass.

Testing

Under golang:1.26.7-alpine3.24:

  • golangci-lint run ./... reports 0 issues for all six modules, and for agent under no tags, -tags docker and -tags native
  • go build ./... clean everywhere
  • go test ./... passes for root, server, and agent under both docker and native
  • go vet ./... clean; go mod tidy a no-op

https://claude.ai/code/session_01D4BSojj4fmh3ZQSZWkGbD8

216 findings, and one of them was a bug. Two httptest handlers called require.NoError, which
reaches t.FailNow from a goroutine that is not the test's. FailNow is only defined on the test
goroutine, so a failed upgrade there would not have stopped the test the way the code reads —
it now asserts and returns instead.

The other 125 are the same shape: an error asserted with assert, then the value it produced
dereferenced on the next line. When the assertion fails the test keeps going and panics on nil,
reporting a crash instead of the assertion that actually failed. require stops at the assertion.

thelper accounts for 91. Helpers that take a *testing.T now say so, which makes a failure point
at the caller rather than at the line inside the helper. The storetest sub-suite closures took
*testing.T second; they take it first now, and runSubSuite's signature follows.

The rewrites came from the linter's own file:line output rather than by hand, then the whole
suite was run to confirm no assertion changed a result.
@otavio
otavio requested review from a team as code owners August 28, 2026 20:50
@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown

Claude finished @otavio's task in 4m 59s —— View job


Code Review Complete

Reviewed 63 files across code quality, security, testing, language patterns, and architecture — no new issues found. The code looks good as-is.

The change is mechanical and test-only. I verified the parts that carry actual semantic weight:

  • The two goroutine-safety fixes (pkg/wsconnadapter/wsconnadapter_test.go, server/ssh/pkg/dialer/manager_test.go) — converting the in-handler require.NoError to if !assert.NoError(t, err) { return } is the correct direction, and the early return safely skips the nil-conn wrap and channel send. No reachable deadlock, since the client-side dial guards with require.NoError on the test goroutine first.
  • runSubSuite signature swap (server/api/store/pg/store_test.go) — definition, function type, invocation (testFunc(t, suite)), and all call-site closures updated consistently to (t *testing.T, suite *storetest.Suite); nothing left with the old order.
  • testifylint rewrites — argument reorders (Equal(t, 4, read)), substitutions (Len, Positive, ErrorIs/NotErrorAs, JSONEq, Equal for same-typed State) are all semantically equivalent; no operands swapped incorrectly.
  • thelper insertions — every t.Helper() lands as the first statement of a real function body receiving *testing.T; the composite-literal slip noted in the PR description does not recur.
  • Imports & config — no missing, duplicate, or now-unused imports in the changed files; the .golangci.yaml additions are correctly indented, unique, and spelled.

To request another review round, comment /review.
· chore/lint-testify

Comment thread pkg/dockerutils/utils_test.go Outdated
otavio added 2 commits August 28, 2026 18:10
The script that added the require imports appended each one as its own
blank-line-separated group instead of joining the existing testify group.
goimports preserves the grouping it is given, so nothing flagged it.
goimports preserves whatever grouping it is handed, so an import added in a block of
its own stays there and reads as a deliberate section. That is how the stray testify
groups in this branch survived a clean lint, a clean vet and a full test run, and it
took a human reading the diff to catch them.

gci decides the grouping instead of accepting it: one standard block, one for
everything else. That also flattens the two blocks this repo had split on purpose,
including the caddy-dns providers in the gateway, whose comment now names the imports
it describes rather than relying on the blank line above them.
@otavio
otavio merged commit dea071d into master Aug 28, 2026
47 checks passed
otavio added a commit that referenced this pull request Aug 28, 2026
The script that added the require imports appended each one as its own
blank-line-separated group instead of joining the existing testify group.
goimports preserves the grouping it is given, so nothing flagged it.
@otavio
otavio deleted the chore/lint-testify branch August 28, 2026 21:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant