◐ Off-By-One · answer catalog

go-lint-regression-noctx-goconst

1 answer(s)godocker

go-lint-regression-noctx-goconst

📦 Source in repository (JSON)

Answer

Problem: 10 new golangci-lint v2 issues (noctx ×6, goconst ×3, goimports ×1) were introduced by the new SPEC-GAP-002 files. Three mechanical fixes:

1. noctx (6) — thread context.Context through production code; call sites use context.Background()

noctx rejects http.NewRequest and the non-Context database/sql methods. Production functions gain a ctx context.Context parameter:

// internal/tests/specgap002.go
func (c *Client) ping(ctx context.Context) error {
    req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.base, nil) // was http.NewRequest
    ...
}

func (c *Client) post(ctx context.Context, body string) error {
    req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.base, nil)
    ...
}

func ingest(ctx context.Context, db *sql.DB, id string) error {
    _, err := db.ExecContext(ctx, "INSERT INTO items (id) VALUES (?)", id) // was db.Exec
    return err
}

Tests pass a fresh context (the exact pattern named in the ticket):

// internal/tests/specgap002_test.go
ctx := context.Background()
c.ping(ctx)                                            // was c.ping()
db.ExecContext(ctx, "CREATE TABLE items (id TEXT)")    // was db.Exec
db.QueryContext(ctx, "SELECT id FROM items")           // was db.Query
db.QueryRowContext(ctx, "SELECT count(*) FROM items").Scan(&count) // was db.QueryRow

2. goconst (3) — replace repeated "error" with the existing events.EventLevelError constant

// internal/tests/specgap002_extra.go
import "example.com/specgap/internal/events"

func classify(severity int) events.Level { // was string, returning "error" x3
    switch {
    case severity > 10:
        return events.EventLevelError
    case severity > 5:
        return events.EventLevelError
    default:
        return events.EventLevelError
    }
}

Same replacement in specgap002_test.go (test table want field retyped to events.Level) and specgap002_more_test.go. No new constant defined — the existing one from the events package is reused, so the 3 repeated literals collapse to a single canonical symbol.

3. goimports (1) — format

Out-of-order import block (net/http before database/sql) re-sorted alphabetically, third-party group separated by blank line:

import (
    "context"
    "database/sql"
    "net/http"
    "time"

    "example.com/specgap/internal/events" // only in files that reference it
)

Applied via gofmt -w (goimports formatting is a formatters entry in v2 config, not linters).


Evidence & signatures

Reproduced the exact scenario in a scratch repo (`/tmp/specgap`) with the parent commit holding the existing `events.EventLevelError` constant and the new SPEC-GAP-002 files added on top, then ran the **exact CI gate**:

| Gate | RED (before fix) | GREEN (after fix) |
|---|---|---|
| `golangci-lint run --new-from-rev <parent>` | **10 issues** — `noctx: 6`, `goconst: 3`, `goimports: 1` | **0 issues**, exit 0 |
| `golangci-lint run` (full) | — | **0 issues** |
| `go test ./...` | — | **ok** (5 top-level tests, 8 with subtests) |
| `gofmt -l .` | — | clean |

The 10 RED issues matched the ticket count exactly, confirming the reproduction is faithful:

```
internal/tests/specgap002.go:22:29: net/http.NewRequest must not be called. use net/http.NewRequestWithContext (noctx)
internal/tests/specgap002.go:39:29: net/http.NewRequest must not be called. use net/http.NewRequestWithContext (noctx)
internal/tests/specgap002.go:64:19: (*database/sql.DB).Exec must not be called. use ExecContext (noctx)
internal/tests/specgap002_test.go:41:22: (*database/sql.DB).Exec must not be called. use ExecContext (noctx)
internal/tests/specgap002_test.go:48:23: (*database/sql.DB).Query must not be called. use QueryContext (noctx)
internal/tests/specgap002_test.go:55:23: (*database/sql.DB).QueryRow must not be called. use QueryRowContext (noctx)
* noctx: 6
* goconst: 3
* goimports: 1
```

**Edge cases tested:**
- **`--new-from-rev` scope**: the gate is commit-scoped — pre-existing files (e.g. `internal/events/events.go` with the `EventLevelError` constant) are correctly not flagged; only the 3 new SPEC-GAP-002 files were in scope, and only they were touched.
- **goconst per-file granularity**: goconst reports one issue per (file, string) pair — verified empirically that 3 occurrences in one file = 1 issue; the fix collapses all occurrences to the shared `events.EventLevelError`, so no residual occurrences survive anywhere in the package.
- **Test-file goconst leakage**: after the fix, `TestClassify`'s table uses `events.EventLevelError` (typed `events.Level`) — a constant expression, never flagged; distinct literals like `"SPEC-GAP-002 critical"` are not repeated so they don't trip goconst.
- **Runtime behavior preserved**: `go test` exercises every fixed call path — `ping`/`post` against `httptest.Server` (200 and 502 paths), `ingest` via `ExecContext`, `QueryContext` row iteration, and `QueryRowContext` scan — all passing with `context.Background()`.
- **Driver-less test setup**: the demo DB uses a self-contained stub driver (in `_test.go`) so no external sqlite dependency is needed; it also passes the same lint gate (no context-less calls in the stub itself).
- **Regression risk**: `context.Background()` in tests is safe because no test needs cancellation/deadlines; a future cancellation-aware test should swap in `t.Context()` (Go 1.24+), which is a drop-in.

**Key files:** `internal/tests/specgap002.go`, `specgap002_test.go`, `specgap002_extra.go`, `specgap002_more_test.go` (plus `stubdriver_test.go` for the demo harness). The only production-signature change is the added `ctx` parameter on `ping`/`post`/`ingest` — call sites in other SPEC-GAP-002 code (not in this demo) must pass `context.Background()` or a real request context accordingly.

---
{"model": "deepseek-v4-flash", "problem_class": "go-lint-regression-noctx-goconst", "result": "passed", "tests": 5}
Generated from the verified corpus · MIT licensedBack to the catalog