chore: add golangci-lint config and fix all lint issues
Enable 15 additional linters (gosec, errorlint, gocritic, modernize, misspell, bodyclose, sqlclosecheck, nilerr, unconvert, durationcheck, sloglint, wastedassign, usestdlibvars) with sensible exclusion rules. Fix all findings: errors.Is for error comparisons, run() pattern in main to avoid exitAfterDefer, ReadHeaderTimeout for Slowloris protection, bounds check in escape sequence reader, WaitGroup.Go, slices.Contains, range-over-int loops, and http.MethodGet constants. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
79
.golangci.yml
Normal file
79
.golangci.yml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
version: "2"
|
||||||
|
|
||||||
|
linters:
|
||||||
|
enable:
|
||||||
|
# Bug detectors.
|
||||||
|
- bodyclose
|
||||||
|
- durationcheck
|
||||||
|
- errorlint
|
||||||
|
- gocritic
|
||||||
|
- nilerr
|
||||||
|
- sqlclosecheck
|
||||||
|
|
||||||
|
# Security.
|
||||||
|
- gosec
|
||||||
|
|
||||||
|
# Style and modernization.
|
||||||
|
- misspell
|
||||||
|
- modernize
|
||||||
|
- unconvert
|
||||||
|
- usestdlibvars
|
||||||
|
|
||||||
|
# Logging.
|
||||||
|
- sloglint
|
||||||
|
|
||||||
|
# Dead code.
|
||||||
|
- wastedassign
|
||||||
|
|
||||||
|
settings:
|
||||||
|
errcheck:
|
||||||
|
exclude-functions:
|
||||||
|
# Terminal I/O writes (honeypot shell output).
|
||||||
|
- fmt.Fprint
|
||||||
|
- fmt.Fprintf
|
||||||
|
# Low-level byte I/O in shell readLine (escape sequences, echo).
|
||||||
|
- (io.ReadWriter).Read
|
||||||
|
- (io.ReadWriter).Write
|
||||||
|
- (io.ReadWriteCloser).Read
|
||||||
|
- (io.ReadWriteCloser).Write
|
||||||
|
- (io.Reader).Read
|
||||||
|
- (io.Writer).Write
|
||||||
|
|
||||||
|
gosec:
|
||||||
|
excludes:
|
||||||
|
# File reads from config paths — expected in a CLI tool.
|
||||||
|
- G304
|
||||||
|
# Weak RNG for shell selection — crypto/rand not needed.
|
||||||
|
- G404
|
||||||
|
|
||||||
|
exclusions:
|
||||||
|
rules:
|
||||||
|
# Ignore unchecked Close() — standard resource cleanup.
|
||||||
|
- linters: [errcheck]
|
||||||
|
text: "Error return value of .+\\.Close.+ is not checked"
|
||||||
|
|
||||||
|
# Ignore unchecked Rollback() — called in error paths before returning.
|
||||||
|
- linters: [errcheck]
|
||||||
|
text: "Error return value of .+\\.Rollback.+ is not checked"
|
||||||
|
|
||||||
|
# Ignore unchecked Reply/Reject — SSH protocol; nothing useful on failure.
|
||||||
|
- linters: [errcheck]
|
||||||
|
text: "Error return value of .+\\.(Reply|Reject).+ is not checked"
|
||||||
|
|
||||||
|
# Test files: allow unchecked errors.
|
||||||
|
- linters: [errcheck]
|
||||||
|
path: "_test\\.go"
|
||||||
|
|
||||||
|
# Test files: InsecureIgnoreHostKey, file permissions, unhandled errors are expected.
|
||||||
|
- linters: [gosec]
|
||||||
|
path: "_test\\.go"
|
||||||
|
|
||||||
|
# Unhandled errors for cleanup/protocol ops — mirrors errcheck exclusions.
|
||||||
|
- linters: [gosec]
|
||||||
|
text: "G104"
|
||||||
|
source: "\\.(Close|Rollback|Reject|Reply|Read|Write)\\("
|
||||||
|
|
||||||
|
# SQL with safe column interpolation from a fixed switch — not user input.
|
||||||
|
- linters: [gosec]
|
||||||
|
text: "G201"
|
||||||
|
path: "internal/storage/"
|
||||||
@@ -4,12 +4,14 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.t-juice.club/torjus/oubliette/internal/config"
|
"git.t-juice.club/torjus/oubliette/internal/config"
|
||||||
"git.t-juice.club/torjus/oubliette/internal/server"
|
"git.t-juice.club/torjus/oubliette/internal/server"
|
||||||
@@ -20,13 +22,19 @@ import (
|
|||||||
const Version = "0.2.0"
|
const Version = "0.2.0"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
if err := run(); err != nil {
|
||||||
|
slog.Error("fatal error", "err", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func run() error {
|
||||||
configPath := flag.String("config", "oubliette.toml", "path to config file")
|
configPath := flag.String("config", "oubliette.toml", "path to config file")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
cfg, err := config.Load(*configPath)
|
cfg, err := config.Load(*configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("failed to load config", "err", err)
|
return fmt.Errorf("load config: %w", err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
level := new(slog.LevelVar)
|
level := new(slog.LevelVar)
|
||||||
@@ -53,8 +61,7 @@ func main() {
|
|||||||
|
|
||||||
store, err := storage.NewSQLiteStore(cfg.Storage.DBPath)
|
store, err := storage.NewSQLiteStore(cfg.Storage.DBPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("failed to open database", "err", err)
|
return fmt.Errorf("open database: %w", err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
defer store.Close()
|
defer store.Close()
|
||||||
|
|
||||||
@@ -65,8 +72,7 @@ func main() {
|
|||||||
|
|
||||||
srv, err := server.New(*cfg, store, logger)
|
srv, err := server.New(*cfg, store, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("failed to create server", "err", err)
|
return fmt.Errorf("create server: %w", err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
@@ -75,23 +81,21 @@ func main() {
|
|||||||
if cfg.Web.Enabled {
|
if cfg.Web.Enabled {
|
||||||
webHandler, err := web.NewServer(store, logger.With("component", "web"))
|
webHandler, err := web.NewServer(store, logger.With("component", "web"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("failed to create web server", "err", err)
|
return fmt.Errorf("create web server: %w", err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
httpServer := &http.Server{
|
httpServer := &http.Server{
|
||||||
Addr: cfg.Web.ListenAddr,
|
Addr: cfg.Web.ListenAddr,
|
||||||
Handler: webHandler,
|
Handler: webHandler,
|
||||||
|
ReadHeaderTimeout: 10 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
logger.Info("web server listening", "addr", cfg.Web.ListenAddr)
|
logger.Info("web server listening", "addr", cfg.Web.ListenAddr)
|
||||||
if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
logger.Error("web server error", "err", err)
|
logger.Error("web server error", "err", err)
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
|
|
||||||
// Graceful shutdown on context cancellation.
|
// Graceful shutdown on context cancellation.
|
||||||
go func() {
|
go func() {
|
||||||
@@ -103,10 +107,10 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := srv.ListenAndServe(ctx); err != nil {
|
if err := srv.ListenAndServe(ctx); err != nil {
|
||||||
logger.Error("server error", "err", err)
|
return fmt.Errorf("server: %w", err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
logger.Info("server stopped")
|
logger.Info("server stopped")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func TestStaticCredentialsWrongPassword(t *testing.T) {
|
|||||||
|
|
||||||
func TestRejectionBeforeThreshold(t *testing.T) {
|
func TestRejectionBeforeThreshold(t *testing.T) {
|
||||||
a := newTestAuth(3, time.Hour)
|
a := newTestAuth(3, time.Hour)
|
||||||
for i := 0; i < 2; i++ {
|
for i := range 2 {
|
||||||
d := a.Authenticate("1.2.3.4", "user", "pass")
|
d := a.Authenticate("1.2.3.4", "user", "pass")
|
||||||
if d.Accepted {
|
if d.Accepted {
|
||||||
t.Fatalf("attempt %d should be rejected", i+1)
|
t.Fatalf("attempt %d should be rejected", i+1)
|
||||||
@@ -49,7 +49,7 @@ func TestRejectionBeforeThreshold(t *testing.T) {
|
|||||||
|
|
||||||
func TestThresholdAcceptance(t *testing.T) {
|
func TestThresholdAcceptance(t *testing.T) {
|
||||||
a := newTestAuth(3, time.Hour)
|
a := newTestAuth(3, time.Hour)
|
||||||
for i := 0; i < 2; i++ {
|
for i := range 2 {
|
||||||
d := a.Authenticate("1.2.3.4", "user", "pass")
|
d := a.Authenticate("1.2.3.4", "user", "pass")
|
||||||
if d.Accepted {
|
if d.Accepted {
|
||||||
t.Fatalf("attempt %d should be rejected", i+1)
|
t.Fatalf("attempt %d should be rejected", i+1)
|
||||||
@@ -65,7 +65,7 @@ func TestPerIPIsolation(t *testing.T) {
|
|||||||
a := newTestAuth(3, time.Hour)
|
a := newTestAuth(3, time.Hour)
|
||||||
|
|
||||||
// IP1 gets 2 failures.
|
// IP1 gets 2 failures.
|
||||||
for i := 0; i < 2; i++ {
|
for range 2 {
|
||||||
a.Authenticate("1.1.1.1", "user", "pass")
|
a.Authenticate("1.1.1.1", "user", "pass")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,12 +157,10 @@ func TestConcurrentAccess(t *testing.T) {
|
|||||||
a := newTestAuth(5, time.Hour)
|
a := newTestAuth(5, time.Hour)
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
for i := 0; i < 100; i++ {
|
for range 100 {
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
a.Authenticate("1.2.3.4", "user", "pass")
|
a.Authenticate("1.2.3.4", "user", "pass")
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ func TestScorer_OutputIgnored(t *testing.T) {
|
|||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
// Only output events — should not affect score.
|
// Only output events — should not affect score.
|
||||||
for i := 0; i < 100; i++ {
|
for range 100 {
|
||||||
s.RecordEvent(now, DirOutput, []byte("some output\n"))
|
s.RecordEvent(now, DirOutput, []byte("some output\n"))
|
||||||
now = now.Add(10 * time.Millisecond)
|
now = now.Add(10 * time.Millisecond)
|
||||||
}
|
}
|
||||||
@@ -103,25 +103,21 @@ func TestScorer_ThreadSafety(t *testing.T) {
|
|||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
for i := 0; i < 10; i++ {
|
for i := range 10 {
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func(offset int) {
|
for j := range 100 {
|
||||||
defer wg.Done()
|
ts := now.Add(time.Duration(i*100+j) * time.Millisecond)
|
||||||
for j := 0; j < 100; j++ {
|
|
||||||
ts := now.Add(time.Duration(offset*100+j) * time.Millisecond)
|
|
||||||
s.RecordEvent(ts, DirInput, []byte("a"))
|
s.RecordEvent(ts, DirInput, []byte("a"))
|
||||||
}
|
}
|
||||||
}(i)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Concurrently read score.
|
// Concurrently read score.
|
||||||
wg.Add(1)
|
wg.Go(func() {
|
||||||
go func() {
|
for range 50 {
|
||||||
defer wg.Done()
|
|
||||||
for i := 0; i < 50; i++ {
|
|
||||||
_ = s.Score()
|
_ = s.Score()
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -98,12 +99,7 @@ func (n *Notifier) shouldSend(wh config.WebhookNotifyConfig, eventType string) b
|
|||||||
if len(wh.Events) == 0 {
|
if len(wh.Events) == 0 {
|
||||||
return true // empty = all events
|
return true // empty = all events
|
||||||
}
|
}
|
||||||
for _, ev := range wh.Events {
|
return slices.Contains(wh.Events, eventType)
|
||||||
if ev == eventType {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Notifier) send(ctx context.Context, wh config.WebhookNotifyConfig, payload webhookPayload) {
|
func (n *Notifier) send(ctx context.Context, wh config.WebhookNotifyConfig, payload webhookPayload) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package bash
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -55,7 +56,7 @@ func (b *BashShell) Handle(ctx context.Context, sess *shell.SessionContext, rw i
|
|||||||
}
|
}
|
||||||
|
|
||||||
line, err := readLine(ctx, rw)
|
line, err := readLine(ctx, rw)
|
||||||
if err == io.EOF {
|
if errors.Is(err, io.EOF) {
|
||||||
fmt.Fprint(rw, "logout\r\n")
|
fmt.Fprint(rw, "logout\r\n")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -81,7 +82,9 @@ func (b *BashShell) Handle(ctx context.Context, sess *shell.SessionContext, rw i
|
|||||||
|
|
||||||
// Log command and output to store.
|
// Log command and output to store.
|
||||||
if sess.Store != nil {
|
if sess.Store != nil {
|
||||||
sess.Store.AppendSessionLog(ctx, sess.SessionID, trimmed, output)
|
if err := sess.Store.AppendSessionLog(ctx, sess.SessionID, trimmed, output); err != nil {
|
||||||
|
return fmt.Errorf("append session log: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.exit {
|
if result.exit {
|
||||||
@@ -145,8 +148,7 @@ func readLine(ctx context.Context, rw io.ReadWriter) (string, error) {
|
|||||||
// Read and discard the rest of the escape sequence.
|
// Read and discard the rest of the escape sequence.
|
||||||
// Most are 3 bytes: ESC [ X (arrow keys, etc.)
|
// Most are 3 bytes: ESC [ X (arrow keys, etc.)
|
||||||
next := make([]byte, 1)
|
next := make([]byte, 1)
|
||||||
rw.Read(next)
|
if n, _ := rw.Read(next); n > 0 && next[0] == '[' {
|
||||||
if next[0] == '[' {
|
|
||||||
rw.Read(next) // read the final byte
|
rw.Read(next) // read the final byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package bash
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -108,7 +109,7 @@ func TestReadLineCtrlD(t *testing.T) {
|
|||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
_, err := readLine(ctx, rw)
|
_, err := readLine(ctx, rw)
|
||||||
if err != io.EOF {
|
if !errors.Is(err, io.EOF) {
|
||||||
t.Fatalf("expected io.EOF, got %v", err)
|
t.Fatalf("expected io.EOF, got %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -205,11 +205,7 @@ func TestDeleteRecordsBefore(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNewSQLiteStoreCreatesFile(t *testing.T) {
|
func TestNewSQLiteStoreCreatesFile(t *testing.T) {
|
||||||
dbPath := filepath.Join(t.TempDir(), "subdir", "test.db")
|
dbPath := filepath.Join(t.TempDir(), "test.db")
|
||||||
// Parent directory doesn't exist yet; SQLite should create it.
|
|
||||||
// Actually, SQLite doesn't create parent dirs, but the file itself.
|
|
||||||
// Use a path in the temp dir directly.
|
|
||||||
dbPath = filepath.Join(t.TempDir(), "test.db")
|
|
||||||
store, err := NewSQLiteStore(dbPath)
|
store, err := NewSQLiteStore(dbPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("creating store: %v", err)
|
t.Fatalf("creating store: %v", err)
|
||||||
|
|||||||
@@ -37,17 +37,17 @@ func seedData(t *testing.T, store Store) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
// Login attempts: root/toor from two IPs, admin/admin from one IP.
|
// Login attempts: root/toor from two IPs, admin/admin from one IP.
|
||||||
for i := 0; i < 5; i++ {
|
for range 5 {
|
||||||
if err := store.RecordLoginAttempt(ctx, "root", "toor", "10.0.0.1"); err != nil {
|
if err := store.RecordLoginAttempt(ctx, "root", "toor", "10.0.0.1"); err != nil {
|
||||||
t.Fatalf("seeding attempt: %v", err)
|
t.Fatalf("seeding attempt: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := 0; i < 3; i++ {
|
for range 3 {
|
||||||
if err := store.RecordLoginAttempt(ctx, "root", "toor", "10.0.0.2"); err != nil {
|
if err := store.RecordLoginAttempt(ctx, "root", "toor", "10.0.0.2"); err != nil {
|
||||||
t.Fatalf("seeding attempt: %v", err)
|
t.Fatalf("seeding attempt: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := 0; i < 2; i++ {
|
for range 2 {
|
||||||
if err := store.RecordLoginAttempt(ctx, "admin", "admin", "10.0.0.1"); err != nil {
|
if err := store.RecordLoginAttempt(ctx, "admin", "admin", "10.0.0.1"); err != nil {
|
||||||
t.Fatalf("seeding attempt: %v", err)
|
t.Fatalf("seeding attempt: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func newSeededTestServer(t *testing.T) *Server {
|
|||||||
store := storage.NewMemoryStore()
|
store := storage.NewMemoryStore()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
for i := 0; i < 5; i++ {
|
for range 5 {
|
||||||
if err := store.RecordLoginAttempt(ctx, "root", "toor", "10.0.0.1"); err != nil {
|
if err := store.RecordLoginAttempt(ctx, "root", "toor", "10.0.0.1"); err != nil {
|
||||||
t.Fatalf("seeding attempt: %v", err)
|
t.Fatalf("seeding attempt: %v", err)
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ func newSeededTestServer(t *testing.T) *Server {
|
|||||||
func TestDashboardHandler(t *testing.T) {
|
func TestDashboardHandler(t *testing.T) {
|
||||||
t.Run("empty store", func(t *testing.T) {
|
t.Run("empty store", func(t *testing.T) {
|
||||||
srv := newTestServer(t)
|
srv := newTestServer(t)
|
||||||
req := httptest.NewRequest("GET", "/", nil)
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
srv.ServeHTTP(w, req)
|
srv.ServeHTTP(w, req)
|
||||||
@@ -73,7 +73,7 @@ func TestDashboardHandler(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("with data", func(t *testing.T) {
|
t.Run("with data", func(t *testing.T) {
|
||||||
srv := newSeededTestServer(t)
|
srv := newSeededTestServer(t)
|
||||||
req := httptest.NewRequest("GET", "/", nil)
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
srv.ServeHTTP(w, req)
|
srv.ServeHTTP(w, req)
|
||||||
@@ -93,7 +93,7 @@ func TestDashboardHandler(t *testing.T) {
|
|||||||
|
|
||||||
func TestFragmentStats(t *testing.T) {
|
func TestFragmentStats(t *testing.T) {
|
||||||
srv := newSeededTestServer(t)
|
srv := newSeededTestServer(t)
|
||||||
req := httptest.NewRequest("GET", "/fragments/stats", nil)
|
req := httptest.NewRequest(http.MethodGet, "/fragments/stats", nil)
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
srv.ServeHTTP(w, req)
|
srv.ServeHTTP(w, req)
|
||||||
@@ -113,7 +113,7 @@ func TestFragmentStats(t *testing.T) {
|
|||||||
|
|
||||||
func TestFragmentActiveSessions(t *testing.T) {
|
func TestFragmentActiveSessions(t *testing.T) {
|
||||||
srv := newSeededTestServer(t)
|
srv := newSeededTestServer(t)
|
||||||
req := httptest.NewRequest("GET", "/fragments/active-sessions", nil)
|
req := httptest.NewRequest(http.MethodGet, "/fragments/active-sessions", nil)
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
srv.ServeHTTP(w, req)
|
srv.ServeHTTP(w, req)
|
||||||
@@ -144,7 +144,7 @@ func TestStaticAssets(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.path, func(t *testing.T) {
|
t.Run(tt.path, func(t *testing.T) {
|
||||||
req := httptest.NewRequest("GET", tt.path, nil)
|
req := httptest.NewRequest(http.MethodGet, tt.path, nil)
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
srv.ServeHTTP(w, req)
|
srv.ServeHTTP(w, req)
|
||||||
|
|||||||
Reference in New Issue
Block a user