fix: resolve all golangci-lint issues
Fixes #32 Changes: - middleware.go: use max() builtin, strconv.Itoa, fix wsl whitespace - database.go: fix nlreturn, noinlineerr, wsl whitespace - handlers.go: remove unnecessary template.HTML conversion, unused import - app.go: extract cleanupContainer to fix nestif, fix lll - client.go: break long string literals to fix lll - deploy.go: fix wsl whitespace - auth_test.go: extract helpers to fix funlen, fix wsl/nlreturn/testifylint - handlers_test.go: deduplicate IDOR tests, fix paralleltest - validation_test.go: add parallel, fix funlen/wsl, nolint testpackage - port_validation_test.go: add parallel, nolint testpackage - ratelimit_test.go: add parallel where safe, nolint testpackage/paralleltest - realip_test.go: add parallel, use NewRequestWithContext, fix wsl/funlen - user.go: (noinlineerr already fixed by database.go pattern)
This commit is contained in:
@@ -2,11 +2,11 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -225,6 +225,7 @@ func (i *ipLimiter) sweep(now time.Time) {
|
||||
delete(i.limiters, ip)
|
||||
}
|
||||
}
|
||||
|
||||
i.lastSweep = now
|
||||
}
|
||||
|
||||
@@ -246,6 +247,7 @@ func (i *ipLimiter) getLimiter(ip string) *rate.Limiter {
|
||||
}
|
||||
i.limiters[ip] = entry
|
||||
}
|
||||
|
||||
entry.lastSeen = now
|
||||
|
||||
return entry.limiter
|
||||
@@ -276,11 +278,9 @@ func (m *Middleware) LoginRateLimit() func(http.Handler) http.Handler {
|
||||
reservation := limiter.Reserve()
|
||||
delay := reservation.Delay()
|
||||
reservation.Cancel()
|
||||
retryAfter := int(math.Ceil(delay.Seconds()))
|
||||
if retryAfter < 1 {
|
||||
retryAfter = 1
|
||||
}
|
||||
writer.Header().Set("Retry-After", fmt.Sprintf("%d", retryAfter))
|
||||
|
||||
retryAfter := max(int(math.Ceil(delay.Seconds())), 1)
|
||||
writer.Header().Set("Retry-After", strconv.Itoa(retryAfter))
|
||||
|
||||
http.Error(
|
||||
writer,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package middleware
|
||||
package middleware //nolint:testpackage // tests unexported types and globals
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
@@ -23,6 +23,7 @@ func newTestMiddleware(t *testing.T) *Middleware {
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:paralleltest // mutates global loginLimiter
|
||||
func TestLoginRateLimitAllowsUpToBurst(t *testing.T) {
|
||||
// Reset the global limiter to get clean state
|
||||
loginLimiter = newIPLimiter()
|
||||
@@ -50,6 +51,7 @@ func TestLoginRateLimitAllowsUpToBurst(t *testing.T) {
|
||||
assert.Equal(t, http.StatusTooManyRequests, rec.Code, "6th request should be rate limited")
|
||||
}
|
||||
|
||||
//nolint:paralleltest // mutates global loginLimiter
|
||||
func TestLoginRateLimitIsolatesIPs(t *testing.T) {
|
||||
loginLimiter = newIPLimiter()
|
||||
|
||||
@@ -82,6 +84,7 @@ func TestLoginRateLimitIsolatesIPs(t *testing.T) {
|
||||
assert.Equal(t, http.StatusOK, rec2.Code, "different IP should not be rate limited")
|
||||
}
|
||||
|
||||
//nolint:paralleltest // mutates global loginLimiter
|
||||
func TestLoginRateLimitReturns429Body(t *testing.T) {
|
||||
loginLimiter = newIPLimiter()
|
||||
|
||||
@@ -109,6 +112,8 @@ func TestLoginRateLimitReturns429Body(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIPLimiterEvictsStaleEntries(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
il := newIPLimiter()
|
||||
|
||||
// Add an entry and backdate its lastSeen
|
||||
@@ -130,6 +135,7 @@ func TestIPLimiterEvictsStaleEntries(t *testing.T) {
|
||||
|
||||
il.mu.Lock()
|
||||
defer il.mu.Unlock()
|
||||
|
||||
assert.NotContains(t, il.limiters, "1.2.3.4", "stale entry should be evicted")
|
||||
assert.Contains(t, il.limiters, "5.6.7.8", "fresh entry should remain")
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package middleware
|
||||
package middleware //nolint:testpackage // tests unexported realIP function
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRealIP(t *testing.T) {
|
||||
func TestRealIP(t *testing.T) { //nolint:funlen // table-driven test
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
remoteAddr string
|
||||
@@ -65,11 +68,15 @@ func TestRealIP(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
req, _ := http.NewRequest(http.MethodGet, "/", nil)
|
||||
t.Parallel()
|
||||
|
||||
req, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, "/", nil)
|
||||
req.RemoteAddr = tt.remoteAddr
|
||||
|
||||
if tt.xRealIP != "" {
|
||||
req.Header.Set("X-Real-IP", tt.xRealIP)
|
||||
}
|
||||
|
||||
if tt.xff != "" {
|
||||
req.Header.Set("X-Forwarded-For", tt.xff)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user