diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-02-16 20:05:39 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-02-16 20:13:47 +0900 |
| commit | 071e7cc78d3f13fa782dbc6ca5fcec3a37263a4d (patch) | |
| tree | d0174928c0d320bb76e0cdb899beee0476643d55 /backend/ratelimit/ratelimit_test.go | |
| parent | 5ed369a6c70707543fd5ec9a13c79851fdfc5d6c (diff) | |
| download | phperkaigi-2026-albatross-071e7cc78d3f13fa782dbc6ca5fcec3a37263a4d.tar.gz phperkaigi-2026-albatross-071e7cc78d3f13fa782dbc6ca5fcec3a37263a4d.tar.zst phperkaigi-2026-albatross-071e7cc78d3f13fa782dbc6ca5fcec3a37263a4d.zip | |
test(backend): add unit tests for auth, config, ratelimit, game, and api
Cover previously untested logic: session ID generation/hashing,
password authentication, IP rate limiting, game state helpers,
handler endpoints, task enqueue/result processing, and config loading.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'backend/ratelimit/ratelimit_test.go')
| -rw-r--r-- | backend/ratelimit/ratelimit_test.go | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/backend/ratelimit/ratelimit_test.go b/backend/ratelimit/ratelimit_test.go new file mode 100644 index 0000000..65878c4 --- /dev/null +++ b/backend/ratelimit/ratelimit_test.go @@ -0,0 +1,123 @@ +package ratelimit + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/labstack/echo/v4" + "golang.org/x/time/rate" +) + +func TestGetLimiter_SameIP(t *testing.T) { + rl := &IPRateLimiter{ + rate: rate.Limit(10), + burst: 1, + } + + l1 := rl.getLimiter("192.168.1.1") + l2 := rl.getLimiter("192.168.1.1") + if l1 != l2 { + t.Error("expected same limiter for same IP") + } +} + +func TestGetLimiter_DifferentIP(t *testing.T) { + rl := &IPRateLimiter{ + rate: rate.Limit(10), + burst: 1, + } + + l1 := rl.getLimiter("192.168.1.1") + l2 := rl.getLimiter("192.168.1.2") + if l1 == l2 { + t.Error("expected different limiters for different IPs") + } +} + +func TestLoginRateLimitMiddleware_AllowsNonLogin(t *testing.T) { + rl := &IPRateLimiter{ + rate: rate.Limit(0), // zero rate = deny all + burst: 0, + } + + e := echo.New() + req := httptest.NewRequest(http.MethodGet, "/api/games", nil) + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) + c.SetPath("/api/games") + + handler := LoginRateLimitMiddleware(rl)(func(c echo.Context) error { + return c.NoContent(http.StatusOK) + }) + + if err := handler(c); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if rec.Code != http.StatusOK { + t.Errorf("expected 200, got %d", rec.Code) + } +} + +func TestLoginRateLimitMiddleware_BlocksExcessiveLogin(t *testing.T) { + rl := &IPRateLimiter{ + rate: rate.Limit(0.001), // very low rate + burst: 1, + } + + e := echo.New() + + // First request should succeed (burst = 1) + req1 := httptest.NewRequest(http.MethodPost, "/api/login", nil) + rec1 := httptest.NewRecorder() + c1 := e.NewContext(req1, rec1) + c1.SetPath("/api/login") + + handler := LoginRateLimitMiddleware(rl)(func(c echo.Context) error { + return c.NoContent(http.StatusOK) + }) + + if err := handler(c1); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if rec1.Code != http.StatusOK { + t.Errorf("first request: expected 200, got %d", rec1.Code) + } + + // Second request should be rate limited + req2 := httptest.NewRequest(http.MethodPost, "/api/login", nil) + rec2 := httptest.NewRecorder() + c2 := e.NewContext(req2, rec2) + c2.SetPath("/api/login") + + if err := handler(c2); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if rec2.Code != http.StatusTooManyRequests { + t.Errorf("second request: expected 429, got %d", rec2.Code) + } +} + +func TestLoginRateLimitMiddleware_AllowsNonPostLogin(t *testing.T) { + rl := &IPRateLimiter{ + rate: rate.Limit(0), + burst: 0, + } + + e := echo.New() + req := httptest.NewRequest(http.MethodGet, "/api/login", nil) + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) + c.SetPath("/api/login") + + handler := LoginRateLimitMiddleware(rl)(func(c echo.Context) error { + return c.NoContent(http.StatusOK) + }) + + if err := handler(c); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if rec.Code != http.StatusOK { + t.Errorf("expected 200 for GET /login, got %d", rec.Code) + } +} |
