ratelimit_test.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. package discordgo
  2. import (
  3. "fmt"
  4. "net/http"
  5. "strconv"
  6. "testing"
  7. "time"
  8. )
  9. // This test takes ~2 seconds to run
  10. func TestRatelimitReset(t *testing.T) {
  11. rl := NewRatelimiter()
  12. sendReq := func(endpoint string) {
  13. bucket := rl.LockBucket(endpoint)
  14. headers := http.Header(make(map[string][]string))
  15. headers.Set("X-RateLimit-Remaining", "0")
  16. // Reset for approx 2 seconds from now
  17. headers.Set("X-RateLimit-Reset", fmt.Sprint(float64(time.Now().Add(time.Second*2).UnixNano())/1e9))
  18. headers.Set("Date", time.Now().Format(time.RFC850))
  19. err := bucket.Release(headers)
  20. if err != nil {
  21. t.Errorf("Release returned error: %v", err)
  22. }
  23. }
  24. sent := time.Now()
  25. sendReq("/guilds/99/channels")
  26. sendReq("/guilds/55/channels")
  27. sendReq("/guilds/66/channels")
  28. sendReq("/guilds/99/channels")
  29. sendReq("/guilds/55/channels")
  30. sendReq("/guilds/66/channels")
  31. // We hit the same endpoint 2 times, so we should only be ratelimited 2 second
  32. // And always less than 4 seconds (unless you're on a stoneage computer or using swap or something...)
  33. if time.Since(sent) >= time.Second && time.Since(sent) < time.Second*4 {
  34. t.Log("OK", time.Since(sent))
  35. } else {
  36. t.Error("Did not ratelimit correctly, got:", time.Since(sent))
  37. }
  38. }
  39. // This test takes ~1 seconds to run
  40. func TestRatelimitGlobal(t *testing.T) {
  41. rl := NewRatelimiter()
  42. sendReq := func(endpoint string) {
  43. bucket := rl.LockBucket(endpoint)
  44. headers := http.Header(make(map[string][]string))
  45. headers.Set("X-RateLimit-Global", "1")
  46. // Reset for approx 1 seconds from now
  47. headers.Set("X-RateLimit-Reset-After", "1")
  48. err := bucket.Release(headers)
  49. if err != nil {
  50. t.Errorf("Release returned error: %v", err)
  51. }
  52. }
  53. sent := time.Now()
  54. // This should trigger a global ratelimit
  55. sendReq("/guilds/99/channels")
  56. time.Sleep(time.Millisecond * 100)
  57. // This shouldn't go through in less than 1 second
  58. sendReq("/guilds/55/channels")
  59. if time.Since(sent) >= time.Second && time.Since(sent) < time.Second*2 {
  60. t.Log("OK", time.Since(sent))
  61. } else {
  62. t.Error("Did not ratelimit correctly, got:", time.Since(sent))
  63. }
  64. }
  65. func BenchmarkRatelimitSingleEndpoint(b *testing.B) {
  66. rl := NewRatelimiter()
  67. for i := 0; i < b.N; i++ {
  68. sendBenchReq("/guilds/99/channels", rl)
  69. }
  70. }
  71. func BenchmarkRatelimitParallelMultiEndpoints(b *testing.B) {
  72. rl := NewRatelimiter()
  73. b.RunParallel(func(pb *testing.PB) {
  74. i := 0
  75. for pb.Next() {
  76. sendBenchReq("/guilds/"+strconv.Itoa(i)+"/channels", rl)
  77. i++
  78. }
  79. })
  80. }
  81. // Does not actually send requests, but locks the bucket and releases it with made-up headers
  82. func sendBenchReq(endpoint string, rl *RateLimiter) {
  83. bucket := rl.LockBucket(endpoint)
  84. headers := http.Header(make(map[string][]string))
  85. headers.Set("X-RateLimit-Remaining", "10")
  86. headers.Set("X-RateLimit-Reset", fmt.Sprint(float64(time.Now().UnixNano())/1e9))
  87. headers.Set("Date", time.Now().Format(time.RFC850))
  88. bucket.Release(headers)
  89. }