Ensure a network call is not made when it's known that API rate limit is still exceeded.
(t *testing.T)
| 2533 | |
| 2534 | // Ensure a network call is not made when it's known that API rate limit is still exceeded. |
| 2535 | func TestDo_rateLimit_noNetworkCall(t *testing.T) { |
| 2536 | t.Parallel() |
| 2537 | client, mux, _ := setup(t) |
| 2538 | |
| 2539 | reset := time.Now().UTC().Add(time.Minute).Round(time.Second) // Rate reset is a minute from now, with 1 second precision. |
| 2540 | |
| 2541 | mux.HandleFunc("/first", func(w http.ResponseWriter, _ *http.Request) { |
| 2542 | w.Header().Set(HeaderRateLimit, "60") |
| 2543 | w.Header().Set(HeaderRateRemaining, "0") |
| 2544 | w.Header().Set(HeaderRateUsed, "60") |
| 2545 | w.Header().Set(HeaderRateReset, fmt.Sprint(reset.Unix())) |
| 2546 | w.Header().Set(HeaderRateResource, "core") |
| 2547 | w.Header().Set("Content-Type", "application/json; charset=utf-8") |
| 2548 | w.WriteHeader(http.StatusForbidden) |
| 2549 | fmt.Fprintln(w, `{ |
| 2550 | "message": "API rate limit exceeded for xxx.xxx.xxx.xxx. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)", |
| 2551 | "documentation_url": "https://docs.github.com/en/rest/overview/resources-in-the-rest-api#abuse-rate-limits" |
| 2552 | }`) |
| 2553 | }) |
| 2554 | |
| 2555 | madeNetworkCall := false |
| 2556 | mux.HandleFunc("/second", func(http.ResponseWriter, *http.Request) { |
| 2557 | madeNetworkCall = true |
| 2558 | }) |
| 2559 | |
| 2560 | // First request is made, and it makes the client aware of rate reset time being in the future. |
| 2561 | req, _ := client.NewRequest(t.Context(), "GET", "first", nil) |
| 2562 | |
| 2563 | _, err := client.Do(req, nil) |
| 2564 | if err == nil { |
| 2565 | t.Error("Expected error to be returned.") |
| 2566 | } |
| 2567 | |
| 2568 | // Second request should not cause a network call to be made, since client can predict a rate limit error. |
| 2569 | req, _ = client.NewRequest(t.Context(), "GET", "second", nil) |
| 2570 | _, err = client.Do(req, nil) |
| 2571 | |
| 2572 | if madeNetworkCall { |
| 2573 | t.Fatal("Network call was made, even though rate limit is known to still be exceeded.") |
| 2574 | } |
| 2575 | |
| 2576 | if err == nil { |
| 2577 | t.Error("Expected error to be returned.") |
| 2578 | } |
| 2579 | var rateLimitErr *RateLimitError |
| 2580 | if !errors.As(err, &rateLimitErr) { |
| 2581 | t.Fatalf("Expected a *RateLimitError error; got %#v.", err) |
| 2582 | } |
| 2583 | if got, want := rateLimitErr.Rate.Limit, 60; got != want { |
| 2584 | t.Errorf("rateLimitErr rate limit = %v, want %v", got, want) |
| 2585 | } |
| 2586 | if got, want := rateLimitErr.Rate.Remaining, 0; got != want { |
| 2587 | t.Errorf("rateLimitErr rate remaining = %v, want %v", got, want) |
| 2588 | } |
| 2589 | if got, want := rateLimitErr.Rate.Used, 60; got != want { |
| 2590 | t.Errorf("rateLimitErr rate used = %v, want %v", got, want) |
| 2591 | } |
| 2592 | if !rateLimitErr.Rate.Reset.UTC().Equal(reset) { |