refactor: use pinned golangci-lint Docker image for linting
All checks were successful
check / check (push) Successful in 1m37s
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:
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user