| 30 | } |
| 31 | |
| 32 | func TestPollRateLimited(t *testing.T) { |
| 33 | srv := httptest.NewServer(New(Deps{ |
| 34 | Authenticator: func(r *http.Request) (*identity.User, error) { |
| 35 | if r.Header.Get("Authorization") == "Bearer good" { |
| 36 | return &identity.User{ID: "u_1"}, nil |
| 37 | } |
| 38 | return nil, errors.New("no") |
| 39 | }, |
| 40 | ListWebhooks: func(ctx context.Context, userID string) ([]identity.Webhook, error) { |
| 41 | t.Error("ListWebhooks must NOT be reached when poll-limited") |
| 42 | return nil, nil |
| 43 | }, |
| 44 | // blocked: 3s retry-after, quota 60, 0 remaining, resets in 12s. |
| 45 | PollLimit: func(key string) (bool, time.Duration, int, int, int) { |
| 46 | if key != "u_1" { |
| 47 | t.Errorf("poll key = %q, want u_1", key) |
| 48 | } |
| 49 | return false, 3 * time.Second, 60, 0, 12 |
| 50 | }, |
| 51 | })) |
| 52 | t.Cleanup(srv.Close) |
| 53 | |
| 54 | resp := getRaw(t, srv.URL+"/v1/webhooks", "good") |
| 55 | defer resp.Body.Close() |
| 56 | if resp.StatusCode != 429 { |
| 57 | t.Fatalf("want 429, got %d", resp.StatusCode) |
| 58 | } |
| 59 | for h, want := range map[string]string{ |
| 60 | "Retry-After": "3", "RateLimit-Limit": "60", |
| 61 | "RateLimit-Remaining": "0", "RateLimit-Reset": "12", |
| 62 | } { |
| 63 | if got := resp.Header.Get(h); got != want { |
| 64 | t.Errorf("header %s = %q, want %q", h, got, want) |
| 65 | } |
| 66 | } |
| 67 | var body map[string]any |
| 68 | _ = json.NewDecoder(resp.Body).Decode(&body) |
| 69 | if errCode(body) != "rate_limited" { |
| 70 | t.Fatalf("want rate_limited, got %v", body) |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | func TestPollRateHeadersOnAllowed(t *testing.T) { |
| 75 | reached := false |