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,6 +1,7 @@
|
||||
package middleware
|
||||
package middleware_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
@@ -12,25 +13,37 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"sneak.berlin/go/webhooker/internal/config"
|
||||
"sneak.berlin/go/webhooker/internal/middleware"
|
||||
"sneak.berlin/go/webhooker/internal/session"
|
||||
)
|
||||
|
||||
// testMiddleware creates a Middleware with minimal dependencies for testing.
|
||||
// It uses a real session.Session backed by an in-memory cookie store.
|
||||
func testMiddleware(t *testing.T, env string) (*Middleware, *session.Session) {
|
||||
const testKeySize = 32
|
||||
|
||||
// testMiddleware creates a Middleware with minimal dependencies
|
||||
// for testing. It uses a real session.Session backed by an
|
||||
// in-memory cookie store.
|
||||
func testMiddleware(
|
||||
t *testing.T,
|
||||
env string,
|
||||
) (*middleware.Middleware, *session.Session) {
|
||||
t.Helper()
|
||||
|
||||
log := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug}))
|
||||
log := slog.New(slog.NewTextHandler(
|
||||
os.Stderr,
|
||||
&slog.HandlerOptions{Level: slog.LevelDebug},
|
||||
))
|
||||
|
||||
cfg := &config.Config{
|
||||
Environment: env,
|
||||
}
|
||||
|
||||
// Create a real session manager with a known key
|
||||
key := make([]byte, 32)
|
||||
key := make([]byte, testKeySize)
|
||||
|
||||
for i := range key {
|
||||
key[i] = byte(i)
|
||||
}
|
||||
|
||||
store := sessions.NewCookieStore(key)
|
||||
store.Options = &sessions.Options{
|
||||
Path: "/",
|
||||
@@ -40,40 +53,33 @@ func testMiddleware(t *testing.T, env string) (*Middleware, *session.Session) {
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
}
|
||||
|
||||
sessManager := newTestSession(t, store, cfg, log, key)
|
||||
sessManager := session.NewForTest(store, cfg, log, key)
|
||||
|
||||
m := &Middleware{
|
||||
log: log,
|
||||
params: &MiddlewareParams{
|
||||
Config: cfg,
|
||||
},
|
||||
session: sessManager,
|
||||
}
|
||||
m := middleware.NewForTest(log, cfg, sessManager)
|
||||
|
||||
return m, sessManager
|
||||
}
|
||||
|
||||
// newTestSession creates a session.Session with a pre-configured cookie store
|
||||
// for testing. This avoids needing the fx lifecycle and database.
|
||||
func newTestSession(t *testing.T, store *sessions.CookieStore, cfg *config.Config, log *slog.Logger, key []byte) *session.Session {
|
||||
t.Helper()
|
||||
return session.NewForTest(store, cfg, log, key)
|
||||
}
|
||||
|
||||
// --- Logging Middleware Tests ---
|
||||
|
||||
func TestLogging_SetsStatusCode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
m, _ := testMiddleware(t, config.EnvironmentDev)
|
||||
|
||||
handler := m.Logging()(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
if _, err := w.Write([]byte("created")); err != nil {
|
||||
return
|
||||
}
|
||||
}))
|
||||
handler := m.Logging()(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/test", nil)
|
||||
_, err := w.Write([]byte("created"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
},
|
||||
))
|
||||
|
||||
req := httptest.NewRequestWithContext(
|
||||
context.Background(), http.MethodGet, "/test", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(w, req)
|
||||
@@ -84,15 +90,20 @@ func TestLogging_SetsStatusCode(t *testing.T) {
|
||||
|
||||
func TestLogging_DefaultStatusOK(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
m, _ := testMiddleware(t, config.EnvironmentDev)
|
||||
|
||||
handler := m.Logging()(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
if _, err := w.Write([]byte("ok")); err != nil {
|
||||
return
|
||||
}
|
||||
}))
|
||||
handler := m.Logging()(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, _ *http.Request) {
|
||||
_, err := w.Write([]byte("ok"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
},
|
||||
))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
req := httptest.NewRequestWithContext(
|
||||
context.Background(), http.MethodGet, "/", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(w, req)
|
||||
@@ -103,20 +114,31 @@ func TestLogging_DefaultStatusOK(t *testing.T) {
|
||||
|
||||
func TestLogging_PassesThroughToNext(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
m, _ := testMiddleware(t, config.EnvironmentDev)
|
||||
|
||||
var called bool
|
||||
handler := m.Logging()(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
called = true
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/webhook", nil)
|
||||
handler := m.Logging()(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, _ *http.Request) {
|
||||
called = true
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
},
|
||||
))
|
||||
|
||||
req := httptest.NewRequestWithContext(
|
||||
context.Background(),
|
||||
http.MethodPost, "/api/webhook", nil,
|
||||
)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(w, req)
|
||||
|
||||
assert.True(t, called, "logging middleware should call the next handler")
|
||||
assert.True(
|
||||
t, called,
|
||||
"logging middleware should call the next handler",
|
||||
)
|
||||
}
|
||||
|
||||
// --- LoggingResponseWriter Tests ---
|
||||
@@ -125,24 +147,33 @@ func TestLoggingResponseWriter_CapturesStatusCode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
lrw := NewLoggingResponseWriter(w)
|
||||
lrw := middleware.NewLoggingResponseWriterForTest(w)
|
||||
|
||||
// Default should be 200
|
||||
assert.Equal(t, http.StatusOK, lrw.statusCode)
|
||||
assert.Equal(
|
||||
t, http.StatusOK,
|
||||
middleware.LoggingResponseWriterStatusCode(lrw),
|
||||
)
|
||||
|
||||
// WriteHeader should capture the status code
|
||||
lrw.WriteHeader(http.StatusNotFound)
|
||||
assert.Equal(t, http.StatusNotFound, lrw.statusCode)
|
||||
|
||||
assert.Equal(
|
||||
t, http.StatusNotFound,
|
||||
middleware.LoggingResponseWriterStatusCode(lrw),
|
||||
)
|
||||
|
||||
// Underlying writer should also get the status code
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
}
|
||||
|
||||
func TestLoggingResponseWriter_WriteDelegatesToUnderlying(t *testing.T) {
|
||||
func TestLoggingResponseWriter_WriteDelegatesToUnderlying(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
lrw := NewLoggingResponseWriter(w)
|
||||
lrw := middleware.NewLoggingResponseWriterForTest(w)
|
||||
|
||||
n, err := lrw.Write([]byte("hello world"))
|
||||
require.NoError(t, err)
|
||||
@@ -154,79 +185,124 @@ func TestLoggingResponseWriter_WriteDelegatesToUnderlying(t *testing.T) {
|
||||
|
||||
func TestCORS_DevMode_AllowsAnyOrigin(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
m, _ := testMiddleware(t, config.EnvironmentDev)
|
||||
|
||||
handler := m.CORS()(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
handler := m.CORS()(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
},
|
||||
))
|
||||
|
||||
// Preflight request
|
||||
req := httptest.NewRequest(http.MethodOptions, "/api/test", nil)
|
||||
req := httptest.NewRequestWithContext(
|
||||
context.Background(),
|
||||
http.MethodOptions, "/api/test", nil,
|
||||
)
|
||||
req.Header.Set("Origin", "http://localhost:3000")
|
||||
req.Header.Set("Access-Control-Request-Method", "POST")
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(w, req)
|
||||
|
||||
// In dev mode, CORS should allow any origin
|
||||
assert.Equal(t, "*", w.Header().Get("Access-Control-Allow-Origin"))
|
||||
assert.Equal(
|
||||
t, "*",
|
||||
w.Header().Get("Access-Control-Allow-Origin"),
|
||||
)
|
||||
}
|
||||
|
||||
func TestCORS_ProdMode_NoOp(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
m, _ := testMiddleware(t, config.EnvironmentProd)
|
||||
|
||||
var called bool
|
||||
handler := m.CORS()(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
called = true
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/test", nil)
|
||||
handler := m.CORS()(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, _ *http.Request) {
|
||||
called = true
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
},
|
||||
))
|
||||
|
||||
req := httptest.NewRequestWithContext(
|
||||
context.Background(),
|
||||
http.MethodGet, "/api/test", nil,
|
||||
)
|
||||
req.Header.Set("Origin", "http://evil.com")
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(w, req)
|
||||
|
||||
assert.True(t, called, "prod CORS middleware should pass through to handler")
|
||||
assert.True(
|
||||
t, called,
|
||||
"prod CORS middleware should pass through to handler",
|
||||
)
|
||||
// In prod, no CORS headers should be set (no-op middleware)
|
||||
assert.Empty(t, w.Header().Get("Access-Control-Allow-Origin"),
|
||||
"prod mode should not set CORS headers")
|
||||
assert.Empty(
|
||||
t,
|
||||
w.Header().Get("Access-Control-Allow-Origin"),
|
||||
"prod mode should not set CORS headers",
|
||||
)
|
||||
}
|
||||
|
||||
// --- RequireAuth Middleware Tests ---
|
||||
|
||||
func TestRequireAuth_NoSession_RedirectsToLogin(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
m, _ := testMiddleware(t, config.EnvironmentDev)
|
||||
|
||||
var called bool
|
||||
handler := m.RequireAuth()(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {
|
||||
called = true
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/dashboard", nil)
|
||||
handler := m.RequireAuth()(http.HandlerFunc(
|
||||
func(_ http.ResponseWriter, _ *http.Request) {
|
||||
called = true
|
||||
},
|
||||
))
|
||||
|
||||
req := httptest.NewRequestWithContext(
|
||||
context.Background(),
|
||||
http.MethodGet, "/dashboard", nil,
|
||||
)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(w, req)
|
||||
|
||||
assert.False(t, called, "handler should not be called for unauthenticated request")
|
||||
assert.False(
|
||||
t, called,
|
||||
"handler should not be called for "+
|
||||
"unauthenticated request",
|
||||
)
|
||||
assert.Equal(t, http.StatusSeeOther, w.Code)
|
||||
assert.Equal(t, "/pages/login", w.Header().Get("Location"))
|
||||
}
|
||||
|
||||
func TestRequireAuth_AuthenticatedSession_PassesThrough(t *testing.T) {
|
||||
func TestRequireAuth_AuthenticatedSession_PassesThrough(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
m, sessManager := testMiddleware(t, config.EnvironmentDev)
|
||||
|
||||
var called bool
|
||||
handler := m.RequireAuth()(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {
|
||||
called = true
|
||||
}))
|
||||
|
||||
// Create an authenticated session by making a request, setting session data,
|
||||
// and saving the session cookie
|
||||
setupReq := httptest.NewRequest(http.MethodGet, "/setup", nil)
|
||||
handler := m.RequireAuth()(http.HandlerFunc(
|
||||
func(_ http.ResponseWriter, _ *http.Request) {
|
||||
called = true
|
||||
},
|
||||
))
|
||||
|
||||
// Create an authenticated session by making a request,
|
||||
// setting session data, and saving the session cookie
|
||||
setupReq := httptest.NewRequestWithContext(
|
||||
context.Background(),
|
||||
http.MethodGet, "/setup", nil,
|
||||
)
|
||||
setupW := httptest.NewRecorder()
|
||||
|
||||
sess, err := sessManager.Get(setupReq)
|
||||
@@ -239,47 +315,74 @@ func TestRequireAuth_AuthenticatedSession_PassesThrough(t *testing.T) {
|
||||
require.NotEmpty(t, cookies, "session cookie should be set")
|
||||
|
||||
// Make the actual request with the session cookie
|
||||
req := httptest.NewRequest(http.MethodGet, "/dashboard", nil)
|
||||
req := httptest.NewRequestWithContext(
|
||||
context.Background(),
|
||||
http.MethodGet, "/dashboard", nil,
|
||||
)
|
||||
|
||||
for _, c := range cookies {
|
||||
req.AddCookie(c)
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(w, req)
|
||||
|
||||
assert.True(t, called, "handler should be called for authenticated request")
|
||||
assert.True(
|
||||
t, called,
|
||||
"handler should be called for authenticated request",
|
||||
)
|
||||
}
|
||||
|
||||
func TestRequireAuth_UnauthenticatedSession_RedirectsToLogin(t *testing.T) {
|
||||
func TestRequireAuth_UnauthenticatedSession_RedirectsToLogin(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
m, sessManager := testMiddleware(t, config.EnvironmentDev)
|
||||
|
||||
var called bool
|
||||
handler := m.RequireAuth()(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {
|
||||
called = true
|
||||
}))
|
||||
|
||||
handler := m.RequireAuth()(http.HandlerFunc(
|
||||
func(_ http.ResponseWriter, _ *http.Request) {
|
||||
called = true
|
||||
},
|
||||
))
|
||||
|
||||
// Create a session but don't authenticate it
|
||||
setupReq := httptest.NewRequest(http.MethodGet, "/setup", nil)
|
||||
setupReq := httptest.NewRequestWithContext(
|
||||
context.Background(),
|
||||
http.MethodGet, "/setup", nil,
|
||||
)
|
||||
setupW := httptest.NewRecorder()
|
||||
|
||||
sess, err := sessManager.Get(setupReq)
|
||||
require.NoError(t, err)
|
||||
// Don't call SetUser — session exists but is not authenticated
|
||||
// Don't call SetUser -- session exists but is not
|
||||
// authenticated
|
||||
require.NoError(t, sessManager.Save(setupReq, setupW, sess))
|
||||
|
||||
cookies := setupW.Result().Cookies()
|
||||
require.NotEmpty(t, cookies)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/dashboard", nil)
|
||||
req := httptest.NewRequestWithContext(
|
||||
context.Background(),
|
||||
http.MethodGet, "/dashboard", nil,
|
||||
)
|
||||
|
||||
for _, c := range cookies {
|
||||
req.AddCookie(c)
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(w, req)
|
||||
|
||||
assert.False(t, called, "handler should not be called for unauthenticated session")
|
||||
assert.False(
|
||||
t, called,
|
||||
"handler should not be called for "+
|
||||
"unauthenticated session",
|
||||
)
|
||||
assert.Equal(t, http.StatusSeeOther, w.Code)
|
||||
assert.Equal(t, "/pages/login", w.Header().Get("Location"))
|
||||
}
|
||||
@@ -304,7 +407,9 @@ func TestIpFromHostPort(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
result := ipFromHostPort(tt.input)
|
||||
|
||||
result := middleware.IPFromHostPort(tt.input)
|
||||
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
@@ -312,122 +417,124 @@ func TestIpFromHostPort(t *testing.T) {
|
||||
|
||||
// --- MetricsAuth Tests ---
|
||||
|
||||
func TestMetricsAuth_ValidCredentials(t *testing.T) {
|
||||
t.Parallel()
|
||||
// metricsAuthMiddleware creates a Middleware configured for
|
||||
// metrics auth testing. This helper de-duplicates the setup in
|
||||
// metrics auth test functions.
|
||||
func metricsAuthMiddleware(
|
||||
t *testing.T,
|
||||
) *middleware.Middleware {
|
||||
t.Helper()
|
||||
|
||||
log := slog.New(slog.NewTextHandler(
|
||||
os.Stderr,
|
||||
&slog.HandlerOptions{Level: slog.LevelDebug},
|
||||
))
|
||||
|
||||
log := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug}))
|
||||
cfg := &config.Config{
|
||||
Environment: config.EnvironmentDev,
|
||||
MetricsUsername: "admin",
|
||||
MetricsPassword: "secret",
|
||||
}
|
||||
|
||||
key := make([]byte, 32)
|
||||
key := make([]byte, testKeySize)
|
||||
store := sessions.NewCookieStore(key)
|
||||
store.Options = &sessions.Options{Path: "/", MaxAge: 86400}
|
||||
|
||||
sessManager := session.NewForTest(store, cfg, log, key)
|
||||
|
||||
m := &Middleware{
|
||||
log: log,
|
||||
params: &MiddlewareParams{
|
||||
Config: cfg,
|
||||
},
|
||||
session: sessManager,
|
||||
}
|
||||
return middleware.NewForTest(log, cfg, sessManager)
|
||||
}
|
||||
|
||||
func TestMetricsAuth_ValidCredentials(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
m := metricsAuthMiddleware(t)
|
||||
|
||||
var called bool
|
||||
handler := m.MetricsAuth()(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
called = true
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/metrics", nil)
|
||||
handler := m.MetricsAuth()(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, _ *http.Request) {
|
||||
called = true
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
},
|
||||
))
|
||||
|
||||
req := httptest.NewRequestWithContext(
|
||||
context.Background(),
|
||||
http.MethodGet, "/metrics", nil,
|
||||
)
|
||||
req.SetBasicAuth("admin", "secret")
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(w, req)
|
||||
|
||||
assert.True(t, called, "handler should be called with valid basic auth")
|
||||
assert.True(
|
||||
t, called,
|
||||
"handler should be called with valid basic auth",
|
||||
)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
func TestMetricsAuth_InvalidCredentials(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
log := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug}))
|
||||
cfg := &config.Config{
|
||||
Environment: config.EnvironmentDev,
|
||||
MetricsUsername: "admin",
|
||||
MetricsPassword: "secret",
|
||||
}
|
||||
|
||||
key := make([]byte, 32)
|
||||
store := sessions.NewCookieStore(key)
|
||||
store.Options = &sessions.Options{Path: "/", MaxAge: 86400}
|
||||
|
||||
sessManager := session.NewForTest(store, cfg, log, key)
|
||||
|
||||
m := &Middleware{
|
||||
log: log,
|
||||
params: &MiddlewareParams{
|
||||
Config: cfg,
|
||||
},
|
||||
session: sessManager,
|
||||
}
|
||||
m := metricsAuthMiddleware(t)
|
||||
|
||||
var called bool
|
||||
handler := m.MetricsAuth()(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
called = true
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/metrics", nil)
|
||||
handler := m.MetricsAuth()(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, _ *http.Request) {
|
||||
called = true
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
},
|
||||
))
|
||||
|
||||
req := httptest.NewRequestWithContext(
|
||||
context.Background(),
|
||||
http.MethodGet, "/metrics", nil,
|
||||
)
|
||||
req.SetBasicAuth("admin", "wrong-password")
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(w, req)
|
||||
|
||||
assert.False(t, called, "handler should not be called with invalid basic auth")
|
||||
assert.False(
|
||||
t, called,
|
||||
"handler should not be called with invalid basic auth",
|
||||
)
|
||||
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
||||
}
|
||||
|
||||
func TestMetricsAuth_NoCredentials(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
log := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug}))
|
||||
cfg := &config.Config{
|
||||
Environment: config.EnvironmentDev,
|
||||
MetricsUsername: "admin",
|
||||
MetricsPassword: "secret",
|
||||
}
|
||||
|
||||
key := make([]byte, 32)
|
||||
store := sessions.NewCookieStore(key)
|
||||
store.Options = &sessions.Options{Path: "/", MaxAge: 86400}
|
||||
|
||||
sessManager := session.NewForTest(store, cfg, log, key)
|
||||
|
||||
m := &Middleware{
|
||||
log: log,
|
||||
params: &MiddlewareParams{
|
||||
Config: cfg,
|
||||
},
|
||||
session: sessManager,
|
||||
}
|
||||
m := metricsAuthMiddleware(t)
|
||||
|
||||
var called bool
|
||||
handler := m.MetricsAuth()(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
called = true
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/metrics", nil)
|
||||
handler := m.MetricsAuth()(http.HandlerFunc(
|
||||
func(_ http.ResponseWriter, _ *http.Request) {
|
||||
called = true
|
||||
},
|
||||
))
|
||||
|
||||
req := httptest.NewRequestWithContext(
|
||||
context.Background(),
|
||||
http.MethodGet, "/metrics", nil,
|
||||
)
|
||||
// No basic auth header
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(w, req)
|
||||
|
||||
assert.False(t, called, "handler should not be called without credentials")
|
||||
assert.False(
|
||||
t, called,
|
||||
"handler should not be called without credentials",
|
||||
)
|
||||
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
||||
}
|
||||
|
||||
@@ -435,16 +542,23 @@ func TestMetricsAuth_NoCredentials(t *testing.T) {
|
||||
|
||||
func TestCORS_DevMode_AllowsMethods(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
m, _ := testMiddleware(t, config.EnvironmentDev)
|
||||
|
||||
handler := m.CORS()(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
handler := m.CORS()(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
},
|
||||
))
|
||||
|
||||
// Preflight for POST
|
||||
req := httptest.NewRequest(http.MethodOptions, "/api/webhooks", nil)
|
||||
req := httptest.NewRequestWithContext(
|
||||
context.Background(),
|
||||
http.MethodOptions, "/api/webhooks", nil,
|
||||
)
|
||||
req.Header.Set("Origin", "http://localhost:5173")
|
||||
req.Header.Set("Access-Control-Request-Method", "POST")
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(w, req)
|
||||
@@ -458,14 +572,17 @@ func TestCORS_DevMode_AllowsMethods(t *testing.T) {
|
||||
func TestSessionKeyFormat(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Verify that the session initialization correctly validates key format.
|
||||
// A proper 32-byte key encoded as base64 should work.
|
||||
key := make([]byte, 32)
|
||||
// Verify that the session initialization correctly validates
|
||||
// key format. A proper 32-byte key encoded as base64 should
|
||||
// work.
|
||||
key := make([]byte, testKeySize)
|
||||
|
||||
for i := range key {
|
||||
key[i] = byte(i + 1)
|
||||
}
|
||||
|
||||
encoded := base64.StdEncoding.EncodeToString(key)
|
||||
decoded, err := base64.StdEncoding.DecodeString(encoded)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, decoded, 32)
|
||||
assert.Len(t, decoded, testKeySize)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user