package auth import ( "context" "path/filepath" "testing" "time" "github.com/prism/proxy/internal/store" ) func TestLoginLimiterLockAndClear(t *testing.T) { st, err := store.Open(filepath.Join(t.TempDir(), "data")) if err != nil { t.Fatal(err) } defer st.Close() ctx := context.Background() limiter := NewLoginLimiter(st) cfg := LoginConfig{MaxAttempts: 3, Window: time.Minute, LockDuration: 2 * time.Minute} ip := "203.0.113.1" for i := 0; i < 2; i++ { res, err := limiter.RecordFailure(ctx, ip, cfg) if err != nil { t.Fatal(err) } if res.Locked { t.Fatalf("unexpected lock at attempt %d", i+1) } if res.Remaining != cfg.MaxAttempts-i-1 { t.Fatalf("remaining=%d want %d", res.Remaining, cfg.MaxAttempts-i-1) } } res, err := limiter.RecordFailure(ctx, ip, cfg) if err != nil { t.Fatal(err) } if !res.Locked || res.RetryAfter <= 0 { t.Fatalf("expected lock, got %+v", res) } check, err := limiter.Check(ctx, ip, cfg) if err != nil { t.Fatal(err) } if check.Allowed { t.Fatal("expected blocked while locked") } if err := limiter.RecordSuccess(ctx, ip); err != nil { t.Fatal(err) } check, err = limiter.Check(ctx, ip, cfg) if err != nil { t.Fatal(err) } if !check.Allowed || check.Remaining != cfg.MaxAttempts { t.Fatalf("expected reset, got %+v", check) } }