35 lines
1.4 KiB
Go
35 lines
1.4 KiB
Go
package cache
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestConsumeFixedWindowFailsClosedWithoutRedis(t *testing.T) {
|
|
_, err := (*Redis)(nil).ConsumeFixedWindow(context.Background(), time.Minute, []FixedWindowRule{{Key: "rate:{external-login}:system", Limit: 300}})
|
|
if !errors.Is(err, ErrRedisUnavailable) {
|
|
t.Fatalf("error = %v, want ErrRedisUnavailable", err)
|
|
}
|
|
}
|
|
|
|
func TestFixedWindowLuaConsumesAllKeysWithoutRenewingExistingTTL(t *testing.T) {
|
|
// These source-level invariants protect the security semantics even when unit
|
|
// tests run without a Redis daemon: all keys increment before the one final
|
|
// return, and PEXPIRE remains inside count==1 so rejected requests never slide TTL.
|
|
if strings.Count(fixedWindowScript, `redis.call("INCR"`) != 1 {
|
|
t.Fatalf("fixed-window script must increment through one loop: %s", fixedWindowScript)
|
|
}
|
|
if !strings.Contains(fixedWindowScript, `if count == 1 then`) || !strings.Contains(fixedWindowScript, `redis.call("PEXPIRE"`) {
|
|
t.Fatal("fixed-window script must assign expiry only on first increment")
|
|
}
|
|
if strings.Count(fixedWindowScript, "return {") != 1 {
|
|
t.Fatal("fixed-window script must not return early before consuming all keys")
|
|
}
|
|
if strings.Index(fixedWindowScript, `redis.call("INCR"`) > strings.Index(fixedWindowScript, `if count > limit then`) {
|
|
t.Fatal("fixed-window script must increment before deciding")
|
|
}
|
|
}
|