refactor: use pinned golangci-lint Docker image for linting
All checks were successful
check / check (push) Successful in 1m37s

Refactor Dockerfile to use a separate lint stage with a pinned
golangci-lint v2.11.3 Docker image instead of installing
golangci-lint via curl in the builder stage. This follows the
pattern used by sneak/pixa.

Changes:
- Dockerfile: separate lint stage using golangci/golangci-lint:v2.11.3
  (Debian-based, pinned by sha256) with COPY --from=lint dependency
- Bump Go from 1.24 to 1.26.1 (golang:1.26.1-bookworm, pinned)
- Bump golangci-lint from v1.64.8 to v2.11.3
- Migrate .golangci.yml from v1 to v2 format (same linters, format only)
- All Docker images pinned by sha256 digest
- Fix all lint issues from the v2 linter upgrade:
  - Add package comments to all packages
  - Add doc comments to all exported types, functions, and methods
  - Fix unchecked errors (errcheck)
  - Fix unused parameters (revive)
  - Fix gosec warnings (MaxBytesReader for form parsing)
  - Fix staticcheck suggestions (fmt.Fprintf instead of WriteString)
  - Rename DeliveryTask to Task to avoid stutter (delivery.Task)
  - Rename shadowed builtin 'max' parameter
- Update README.md version requirements
This commit is contained in:
clawbot
2026-03-17 05:46:03 -07:00
parent d771fe14df
commit 32a9170428
59 changed files with 7792 additions and 4282 deletions

View File

@@ -1,32 +1,36 @@
package handlers
package handlers_test
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/fx"
"go.uber.org/fx/fxtest"
"sneak.berlin/go/webhooker/internal/config"
"sneak.berlin/go/webhooker/internal/database"
"sneak.berlin/go/webhooker/internal/delivery"
"sneak.berlin/go/webhooker/internal/globals"
"sneak.berlin/go/webhooker/internal/handlers"
"sneak.berlin/go/webhooker/internal/healthcheck"
"sneak.berlin/go/webhooker/internal/logger"
"sneak.berlin/go/webhooker/internal/session"
)
// noopNotifier is a no-op delivery.Notifier for tests.
type noopNotifier struct{}
func (n *noopNotifier) Notify([]delivery.DeliveryTask) {}
func (n *noopNotifier) Notify([]delivery.Task) {}
func TestHandleIndex(t *testing.T) {
var h *Handlers
var sess *session.Session
func newTestApp(
t *testing.T,
targets ...any,
) *fxtest.App {
t.Helper()
app := fxtest.New(
return fxtest.New(
t,
fx.Provide(
globals.New,
@@ -40,90 +44,99 @@ func TestHandleIndex(t *testing.T) {
database.NewWebhookDBManager,
healthcheck.New,
session.New,
func() delivery.Notifier { return &noopNotifier{} },
New,
func() delivery.Notifier {
return &noopNotifier{}
},
handlers.New,
),
fx.Populate(&h, &sess),
fx.Populate(targets...),
)
}
func TestHandleIndex_Unauthenticated(t *testing.T) {
t.Parallel()
var h *handlers.Handlers
app := newTestApp(t, &h)
app.RequireStart()
defer app.RequireStop()
t.Run("unauthenticated redirects to login", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
w := httptest.NewRecorder()
t.Cleanup(app.RequireStop)
handler := h.HandleIndex()
handler.ServeHTTP(w, req)
req := httptest.NewRequestWithContext(
context.Background(), http.MethodGet, "/", nil)
w := httptest.NewRecorder()
assert.Equal(t, http.StatusSeeOther, w.Code)
assert.Equal(t, "/pages/login", w.Header().Get("Location"))
})
handler := h.HandleIndex()
handler.ServeHTTP(w, req)
t.Run("authenticated redirects to sources", func(t *testing.T) {
// Create a request, set up an authenticated session, then test
req := httptest.NewRequest(http.MethodGet, "/", nil)
w := httptest.NewRecorder()
assert.Equal(t, http.StatusSeeOther, w.Code)
assert.Equal(
t, "/pages/login", w.Header().Get("Location"),
)
}
// Get a session and mark it as authenticated
s, err := sess.Get(req)
assert.NoError(t, err)
sess.SetUser(s, "test-user-id", "testuser")
err = sess.Save(req, w, s)
assert.NoError(t, err)
func TestHandleIndex_Authenticated(t *testing.T) {
t.Parallel()
// Build a new request with the session cookie from the response
req2 := httptest.NewRequest(http.MethodGet, "/", nil)
for _, cookie := range w.Result().Cookies() {
req2.AddCookie(cookie)
}
w2 := httptest.NewRecorder()
var h *handlers.Handlers
handler := h.HandleIndex()
handler.ServeHTTP(w2, req2)
var sess *session.Session
assert.Equal(t, http.StatusSeeOther, w2.Code)
assert.Equal(t, "/sources", w2.Header().Get("Location"))
})
app := newTestApp(t, &h, &sess)
app.RequireStart()
t.Cleanup(app.RequireStop)
req := httptest.NewRequestWithContext(
context.Background(), http.MethodGet, "/", nil)
w := httptest.NewRecorder()
s, err := sess.Get(req)
require.NoError(t, err)
sess.SetUser(s, "test-user-id", "testuser")
err = sess.Save(req, w, s)
require.NoError(t, err)
req2 := httptest.NewRequestWithContext(
context.Background(), http.MethodGet, "/", nil)
for _, cookie := range w.Result().Cookies() {
req2.AddCookie(cookie)
}
w2 := httptest.NewRecorder()
h.HandleIndex().ServeHTTP(w2, req2)
assert.Equal(t, http.StatusSeeOther, w2.Code)
assert.Equal(
t, "/sources", w2.Header().Get("Location"),
)
}
func TestRenderTemplate(t *testing.T) {
var h *Handlers
t.Parallel()
app := fxtest.New(
t,
fx.Provide(
globals.New,
logger.New,
func() *config.Config {
return &config.Config{
DataDir: t.TempDir(),
}
},
database.New,
database.NewWebhookDBManager,
healthcheck.New,
session.New,
func() delivery.Notifier { return &noopNotifier{} },
New,
),
fx.Populate(&h),
)
var h *handlers.Handlers
app := newTestApp(t, &h)
app.RequireStart()
defer app.RequireStop()
t.Run("handles missing templates gracefully", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
w := httptest.NewRecorder()
t.Cleanup(app.RequireStop)
data := map[string]interface{}{
"Version": "1.0.0",
}
req := httptest.NewRequestWithContext(
context.Background(), http.MethodGet, "/", nil)
w := httptest.NewRecorder()
// When a non-existent template name is requested, renderTemplate
// should return an internal server error
h.renderTemplate(w, req, "nonexistent.html", data)
data := map[string]any{"Version": "1.0.0"}
// Should return internal server error when template is not found
assert.Equal(t, http.StatusInternalServerError, w.Code)
})
h.RenderTemplateForTest(
w, req, "nonexistent.html", data,
)
assert.Equal(
t, http.StatusInternalServerError, w.Code,
)
}