forked from bwmarrin/discordgo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathratelimit_test.go
129 lines (102 loc) · 3.19 KB
/
ratelimit_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package discordgo
import (
"net/http"
"strconv"
"testing"
"time"
)
func TestGlobalRateLimit(t *testing.T) {
StartGlobalLimit()
b1, _ := New("")
b2, _ := New("")
_, _ = b1.User("633751371404804107")
_, _ = b2.User("485853402560200704")
if GlobalRateLimit == 48 {
t.Fatal("Global Rate Limit did not decrease!")
}
time.Sleep(time.Second)
if GlobalRateLimit != 48 {
t.Fatal("Global Rate Limit did not refresh!")
}
}
// This test takes ~2 seconds to run
func TestRatelimitReset(t *testing.T) {
rl := NewRatelimiter()
sendReq := func(endpoint string) {
bucket := rl.LockBucket(endpoint)
headers := http.Header(make(map[string][]string))
headers.Set("X-RateLimit-Remaining", "0")
// Reset for approx 2 seconds from now
headers.Set("X-RateLimit-Reset", strconv.FormatInt(time.Now().Add(time.Second*2).Unix(), 10))
headers.Set("Date", time.Now().Format(time.RFC850))
err := bucket.Release(headers)
if err != nil {
t.Errorf("Release returned error: %v", err)
}
}
sent := time.Now()
sendReq("/guilds/99/channels")
sendReq("/guilds/55/channels")
sendReq("/guilds/66/channels")
sendReq("/guilds/99/channels")
sendReq("/guilds/55/channels")
sendReq("/guilds/66/channels")
// We hit the same endpoint 2 times, so we should only be ratelimited 2 second
// And always less than 4 seconds (unless you're on a stoneage computer or using swap or something...)
if time.Since(sent) >= time.Second && time.Since(sent) < time.Second*4 {
t.Log("OK", time.Since(sent))
} else {
t.Error("Did not ratelimit correctly, got:", time.Since(sent))
}
}
// This test takes ~1 seconds to run
func TestRatelimitGlobal(t *testing.T) {
rl := NewRatelimiter()
sendReq := func(endpoint string) {
bucket := rl.LockBucket(endpoint)
headers := http.Header(make(map[string][]string))
headers.Set("X-RateLimit-Global", "1")
// Reset for approx 1 seconds from now
headers.Set("Retry-After", "1000")
err := bucket.Release(headers)
if err != nil {
t.Errorf("Release returned error: %v", err)
}
}
sent := time.Now()
// This should trigger a global ratelimit
sendReq("/guilds/99/channels")
time.Sleep(time.Millisecond * 100)
// This shouldn't go through in less than 1 second
sendReq("/guilds/55/channels")
if time.Since(sent) >= time.Second && time.Since(sent) < time.Second*2 {
t.Log("OK", time.Since(sent))
} else {
t.Error("Did not ratelimit correctly, got:", time.Since(sent))
}
}
func BenchmarkRatelimitSingleEndpoint(b *testing.B) {
rl := NewRatelimiter()
for i := 0; i < b.N; i++ {
sendBenchReq("/guilds/99/channels", rl)
}
}
func BenchmarkRatelimitParallelMultiEndpoints(b *testing.B) {
rl := NewRatelimiter()
b.RunParallel(func(pb *testing.PB) {
i := 0
for pb.Next() {
sendBenchReq("/guilds/"+strconv.Itoa(i)+"/channels", rl)
i++
}
})
}
// Does not actually send requests, but locks the bucket and releases it with made-up headers
func sendBenchReq(endpoint string, rl *RateLimiter) {
bucket := rl.LockBucket(endpoint)
headers := http.Header(make(map[string][]string))
headers.Set("X-RateLimit-Remaining", "10")
headers.Set("X-RateLimit-Reset", strconv.FormatInt(time.Now().Unix(), 10))
headers.Set("Date", time.Now().Format(time.RFC850))
bucket.Release(headers)
}