Skip to content

Commit a303979

Browse files
add tests for ratelimit stuff
1 parent cceca89 commit a303979

File tree

1 file changed

+191
-0
lines changed

1 file changed

+191
-0
lines changed

exporter/prometheus_test.go

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
package exporter
2+
3+
import (
4+
"crypto/rand"
5+
"crypto/rsa"
6+
"crypto/x509"
7+
"encoding/pem"
8+
"os"
9+
"testing"
10+
11+
"github.com/githubexporter/github-exporter/config"
12+
"github.com/google/go-github/v76/github"
13+
"github.com/stretchr/testify/assert"
14+
"github.com/stretchr/testify/require"
15+
)
16+
17+
// writeTempRSAKey generates a 2048-bit RSA private key and writes it as a
18+
// PKCS#1 PEM file to a temp path. The file is removed automatically when the
19+
// test finishes.
20+
func writeTempRSAKey(t *testing.T) string {
21+
t.Helper()
22+
key, err := rsa.GenerateKey(rand.Reader, 2048)
23+
require.NoError(t, err)
24+
25+
f, err := os.CreateTemp("", "test-github-app-key-*.pem")
26+
require.NoError(t, err)
27+
defer f.Close()
28+
29+
err = pem.Encode(f, &pem.Block{
30+
Type: "RSA PRIVATE KEY",
31+
Bytes: x509.MarshalPKCS1PrivateKey(key),
32+
})
33+
require.NoError(t, err)
34+
t.Cleanup(func() { os.Remove(f.Name()) })
35+
return f.Name()
36+
}
37+
38+
func newTestExporter(cfg config.Config) *Exporter {
39+
return &Exporter{
40+
Client: github.NewClient(nil),
41+
Config: cfg,
42+
}
43+
}
44+
45+
func TestRateLimitRefresh(t *testing.T) {
46+
// Shared rate-limit slices used across sub-tests.
47+
aboveThreshold := []RateLimit{{Resource: "core", Remaining: 200, Limit: 5000}}
48+
atThreshold := []RateLimit{{Resource: "core", Remaining: 100, Limit: 5000}}
49+
belowThreshold := []RateLimit{{Resource: "core", Remaining: 50, Limit: 5000}}
50+
noCoreEntry := []RateLimit{{Resource: "search", Remaining: 5, Limit: 30}}
51+
52+
baseCfg := config.Config{
53+
GitHubResultsPerPage: 100,
54+
FetchReposConcurrency: 1,
55+
FetchOrgsConcurrency: 1,
56+
FetchOrgReposConcurrency: 1,
57+
FetchUsersConcurrency: 1,
58+
}
59+
60+
t.Run("no-op when GitHubRateLimit is zero", func(t *testing.T) {
61+
cfg := baseCfg
62+
cfg.GitHubRateLimit = 0
63+
e := newTestExporter(cfg)
64+
original := e.Client
65+
66+
e.rateLimitRefresh(&atThreshold)
67+
68+
assert.Same(t, original, e.Client)
69+
})
70+
71+
t.Run("no-op when GitHubRateLimit is negative", func(t *testing.T) {
72+
cfg := baseCfg
73+
cfg.GitHubRateLimit = -1
74+
e := newTestExporter(cfg)
75+
original := e.Client
76+
77+
e.rateLimitRefresh(&atThreshold)
78+
79+
assert.Same(t, original, e.Client)
80+
})
81+
82+
t.Run("no-op when rates is nil", func(t *testing.T) {
83+
cfg := baseCfg
84+
cfg.GitHubRateLimit = 100
85+
e := newTestExporter(cfg)
86+
original := e.Client
87+
88+
e.rateLimitRefresh(nil)
89+
90+
assert.Same(t, original, e.Client)
91+
})
92+
93+
t.Run("no-op when core remaining is above threshold", func(t *testing.T) {
94+
cfg := baseCfg
95+
cfg.GitHubRateLimit = 100 // remaining=200 > threshold=100
96+
e := newTestExporter(cfg)
97+
original := e.Client
98+
99+
e.rateLimitRefresh(&aboveThreshold)
100+
101+
assert.Same(t, original, e.Client)
102+
})
103+
104+
t.Run("no-op when rates has no core entry", func(t *testing.T) {
105+
cfg := baseCfg
106+
cfg.GitHubRateLimit = 100
107+
e := newTestExporter(cfg)
108+
original := e.Client
109+
110+
e.rateLimitRefresh(&noCoreEntry)
111+
112+
assert.Same(t, original, e.Client)
113+
})
114+
115+
t.Run("no-op when GitHubApp is false and core is at threshold", func(t *testing.T) {
116+
cfg := baseCfg
117+
cfg.GitHubRateLimit = 100 // remaining=100 <= threshold=100
118+
cfg.GitHubApp = false
119+
e := newTestExporter(cfg)
120+
original := e.Client
121+
122+
e.rateLimitRefresh(&atThreshold)
123+
124+
assert.Same(t, original, e.Client)
125+
})
126+
127+
t.Run("no-op when GitHubApp is false and core is below threshold", func(t *testing.T) {
128+
cfg := baseCfg
129+
cfg.GitHubRateLimit = 100 // remaining=50 < threshold=100
130+
cfg.GitHubApp = false
131+
e := newTestExporter(cfg)
132+
original := e.Client
133+
134+
e.rateLimitRefresh(&belowThreshold)
135+
136+
assert.Same(t, original, e.Client)
137+
})
138+
139+
t.Run("no-op when GitHubApp is true but GetClient fails", func(t *testing.T) {
140+
cfg := baseCfg
141+
cfg.GitHubRateLimit = 100
142+
cfg.GitHubApp = true
143+
cfg.GitHubAppConfig = &config.GitHubAppConfig{
144+
GitHubAppKeyPath: "/nonexistent/key.pem",
145+
GitHubAppId: 1,
146+
GitHubAppInstallationId: 1,
147+
}
148+
e := newTestExporter(cfg)
149+
original := e.Client
150+
151+
e.rateLimitRefresh(&atThreshold)
152+
153+
assert.Same(t, original, e.Client)
154+
})
155+
156+
t.Run("replaces client when GitHubApp is true and core is at threshold", func(t *testing.T) {
157+
keyPath := writeTempRSAKey(t)
158+
cfg := baseCfg
159+
cfg.GitHubRateLimit = 100 // remaining=100 <= threshold=100
160+
cfg.GitHubApp = true
161+
cfg.GitHubAppConfig = &config.GitHubAppConfig{
162+
GitHubAppKeyPath: keyPath,
163+
GitHubAppId: 1,
164+
GitHubAppInstallationId: 1,
165+
}
166+
e := newTestExporter(cfg)
167+
original := e.Client
168+
169+
e.rateLimitRefresh(&atThreshold)
170+
171+
assert.NotSame(t, original, e.Client, "expected client to be replaced after rate limit refresh")
172+
})
173+
174+
t.Run("replaces client when GitHubApp is true and core is below threshold", func(t *testing.T) {
175+
keyPath := writeTempRSAKey(t)
176+
cfg := baseCfg
177+
cfg.GitHubRateLimit = 100 // remaining=50 < threshold=100
178+
cfg.GitHubApp = true
179+
cfg.GitHubAppConfig = &config.GitHubAppConfig{
180+
GitHubAppKeyPath: keyPath,
181+
GitHubAppId: 1,
182+
GitHubAppInstallationId: 1,
183+
}
184+
e := newTestExporter(cfg)
185+
original := e.Client
186+
187+
e.rateLimitRefresh(&belowThreshold)
188+
189+
assert.NotSame(t, original, e.Client, "expected client to be replaced after rate limit refresh")
190+
})
191+
}

0 commit comments

Comments
 (0)