package surf import ( "net/http" "net/http/httptest" "net/netip" "os" "path/filepath" "testing" "time" "git.golem15.com/golem15/summercms/modules/compass" ) // Gap (b): MemoryStore's sweep goroutine (purge), not Attempt's lazy expiry. // Construct with a short sweep and assert the internal map drops // an expired entry without a later Attempt. func TestMemoryStoreSweepRemovesExpiredEntry(t *testing.T) { s := NewMemoryStore(15 * time.Millisecond) t.Cleanup(func() { close(s.stop) }) s.Attempt("k", 1, 25*time.Millisecond) s.mu.Lock() n := len(s.entries) s.mu.Unlock() if n != 1 { t.Fatalf("after Attempt, entries = %d", n) } deadline := time.Now().Add(200 * time.Millisecond) for time.Now().Before(deadline) { s.mu.Lock() n = len(s.entries) s.mu.Unlock() if n == 0 { return } time.Sleep(10 * time.Millisecond) } t.Fatalf("sweep did not drop expired entry, count=%d", n) } func TestMemoryStoreAttemptRetryAfterAndExpiry(t *testing.T) { s := NewMemoryStore(0) allowed, attempts, retryAfter := s.Attempt("k", 1, 20*time.Millisecond) if !allowed || attempts != 1 || retryAfter <= 0 { t.Fatalf("first attempt = allowed %v, attempts %d, retryAfter %s", allowed, attempts, retryAfter) } allowed, attempts, retryAfter = s.Attempt("k", 1, 20*time.Millisecond) if allowed || attempts != 1 || retryAfter <= 0 { t.Fatalf("denied attempt = allowed %v, attempts %d, retryAfter %s", allowed, attempts, retryAfter) } time.Sleep(30 * time.Millisecond) allowed, attempts, retryAfter = s.Attempt("k", 1, 20*time.Millisecond) if !allowed || attempts != 1 || retryAfter <= 0 { t.Fatalf("expired attempt = allowed %v, attempts %d, retryAfter %s", allowed, attempts, retryAfter) } } func TestTrustedProxiesParsesConfig(t *testing.T) { if TrustedProxies(nil) != nil { t.Fatal("nil cfg must return nil") } dir := t.TempDir() if err := os.WriteFile(filepath.Join(dir, "app.yaml"), []byte("name: t\n"), 0o644); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(dir, "http.yaml"), []byte("trusted_proxies:\n - 10.0.0.0/8\n - not-a-cidr\n - 192.168.0.0/16\n"), 0o644); err != nil { t.Fatal(err) } cfg, err := compass.Open(compass.Options{Dir: dir, Environ: []string{}}) if err != nil { t.Fatal(err) } got := TrustedProxies(cfg) if len(got) != 2 || got[0].String() != "10.0.0.0/8" || got[1].String() != "192.168.0.0/16" { t.Fatalf("TrustedProxies = %v", got) } } func TestClientIPNilRequestAndEmptyXFF(t *testing.T) { if got := ClientIP(nil, nil); got != "" { t.Fatalf("nil request = %q", got) } trusted := []netip.Prefix{mustPrefix("10.0.0.0/8")} req := httptest.NewRequest(http.MethodGet, "/", nil) req.RemoteAddr = "10.0.0.1:443" if got := ClientIP(req, trusted); got != "10.0.0.1" { t.Fatalf("trusted RemoteAddr, empty XFF = %q", got) } }