ratelimit_test.go 2.8 KB

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