fbe 0.6.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/Gemfile.lock +10 -8
- data/Rakefile +0 -2
- data/lib/fbe/conclude.rb +13 -14
- data/lib/fbe/iterate.rb +12 -8
- data/lib/fbe/octo.rb +9 -9
- data/lib/fbe.rb +1 -1
- data/test/fbe/test_conclude.rb +32 -0
- data/test/fbe/test_octo.rb +63 -28
- data/test/test__helper.rb +2 -0
- metadata +2 -4
- data/lib/fbe/middleware/quota.rb +0 -64
- data/test/fbe/middleware/test_quota.rb +0 -66
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e1ce252b4d0093ff1f94e319d2c65502e134dad520398ca018fad1820b9dac78
|
4
|
+
data.tar.gz: f0039a70dcd2533e2acd36f80b7ef3ac68441f1f68611b38ee07d9c67449ddf3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 25191aefeccb723456f4de449c6f042ce628c895fc7f2e941f5e63e0685bfb2bccf550e04c288ff28e288acf8a4c0b57f3402b2826cae5ccb5da549c977f1ffe
|
7
|
+
data.tar.gz: 6362777ada12c805d463db462afbfbf5bab5d20b897565b798c2ede567d097f3250622d1a67656731e94f0ac8fc37fb604ace94c150e51e47c2a83ec14f4be60
|
data/Gemfile
CHANGED
@@ -17,5 +17,6 @@ gem 'rubocop-performance', '>0', require: false
|
|
17
17
|
gem 'rubocop-rake', '>0', require: false
|
18
18
|
gem 'simplecov', '~>0.22', require: false
|
19
19
|
gem 'simplecov-cobertura', '~>2.1', require: false
|
20
|
+
gem 'veils', '>0', require: false
|
20
21
|
gem 'webmock', '~>3.25', require: false
|
21
22
|
gem 'yard', '~>0.9', require: false
|
data/Gemfile.lock
CHANGED
@@ -70,7 +70,7 @@ GEM
|
|
70
70
|
tago (> 0)
|
71
71
|
ethon (0.16.0)
|
72
72
|
ffi (>= 1.15.0)
|
73
|
-
factbase (0.
|
73
|
+
factbase (0.10.0)
|
74
74
|
backtrace (~> 0.4)
|
75
75
|
decoor (~> 0.0)
|
76
76
|
json (~> 2.7)
|
@@ -92,14 +92,14 @@ GEM
|
|
92
92
|
net-http (>= 0.5.0)
|
93
93
|
faraday-retry (2.3.1)
|
94
94
|
faraday (~> 2.0)
|
95
|
-
ffi (1.17.
|
96
|
-
ffi (1.17.
|
97
|
-
ffi (1.17.
|
98
|
-
ffi (1.17.
|
99
|
-
fiber-storage (1.0.
|
95
|
+
ffi (1.17.2-arm64-darwin)
|
96
|
+
ffi (1.17.2-x64-mingw-ucrt)
|
97
|
+
ffi (1.17.2-x86_64-darwin)
|
98
|
+
ffi (1.17.2-x86_64-linux-gnu)
|
99
|
+
fiber-storage (1.0.1)
|
100
100
|
gli (2.22.2)
|
101
101
|
ostruct
|
102
|
-
graphql (2.5.
|
102
|
+
graphql (2.5.3)
|
103
103
|
base64
|
104
104
|
fiber-storage
|
105
105
|
logger
|
@@ -158,7 +158,7 @@ GEM
|
|
158
158
|
os (1.1.4)
|
159
159
|
ostruct (0.6.1)
|
160
160
|
others (0.0.3)
|
161
|
-
parallel (1.
|
161
|
+
parallel (1.27.0)
|
162
162
|
parser (3.3.8.0)
|
163
163
|
ast (~> 2.4.1)
|
164
164
|
racc
|
@@ -225,6 +225,7 @@ GEM
|
|
225
225
|
unicode-emoji (~> 4.0, >= 4.0.4)
|
226
226
|
unicode-emoji (4.0.4)
|
227
227
|
uri (1.0.3)
|
228
|
+
veils (0.4.0)
|
228
229
|
verbose (0.0.2)
|
229
230
|
loog (~> 0.2)
|
230
231
|
tago (~> 0.0)
|
@@ -257,6 +258,7 @@ DEPENDENCIES
|
|
257
258
|
rubocop-rake (> 0)
|
258
259
|
simplecov (~> 0.22)
|
259
260
|
simplecov-cobertura (~> 2.1)
|
261
|
+
veils (> 0)
|
260
262
|
webmock (~> 3.25)
|
261
263
|
yard (~> 0.9)
|
262
264
|
|
data/Rakefile
CHANGED
data/lib/fbe/conclude.rb
CHANGED
@@ -160,21 +160,20 @@ class Fbe::Conclude
|
|
160
160
|
def roll(&)
|
161
161
|
passed = 0
|
162
162
|
start = Time.now
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
end
|
170
|
-
if Time.now > start + @timeout
|
171
|
-
@loog.debug("We've spent more than #{start.ago}, must stop here")
|
172
|
-
throw :stop
|
173
|
-
end
|
174
|
-
n = yield fbt, a
|
175
|
-
@loog.info("#{n.what}: #{n.details}") unless n.nil?
|
176
|
-
passed += 1
|
163
|
+
oct = Fbe.octo(loog: @loog, options: @options, global: @global)
|
164
|
+
@fb.txn do |fbt|
|
165
|
+
fbt.query(@query).each do |a|
|
166
|
+
if @quota_aware && oct.off_quota
|
167
|
+
@loog.debug('We ran out of GitHub quota, must stop here')
|
168
|
+
throw :commit
|
177
169
|
end
|
170
|
+
if Time.now > start + @timeout
|
171
|
+
@loog.debug("We've spent more than #{start.ago}, must stop here")
|
172
|
+
throw :commit
|
173
|
+
end
|
174
|
+
n = yield fbt, a
|
175
|
+
@loog.info("#{n.what}: #{n.details}") unless n.nil?
|
176
|
+
passed += 1
|
178
177
|
end
|
179
178
|
end
|
180
179
|
@loog.debug("Found and processed #{passed} facts by: #{@query}")
|
data/lib/fbe/iterate.rb
CHANGED
@@ -107,11 +107,23 @@ class Fbe::Iterate
|
|
107
107
|
raise 'Use "by" first' if @query.nil?
|
108
108
|
seen = {}
|
109
109
|
oct = Fbe.octo(loog: @loog, options: @options, global: @global)
|
110
|
+
if oct.off_quota
|
111
|
+
@loog.debug('We are off GitHub quota, cannot even start, sorry')
|
112
|
+
return
|
113
|
+
end
|
110
114
|
repos = Fbe.unmask_repos(loog: @loog, options: @options, global: @global)
|
111
115
|
restarted = []
|
112
116
|
start = Time.now
|
113
117
|
loop do
|
118
|
+
if oct.off_quota
|
119
|
+
@loog.info("We are off GitHub quota, time to stop after #{start.ago}")
|
120
|
+
break
|
121
|
+
end
|
114
122
|
repos.each do |repo|
|
123
|
+
if oct.off_quota
|
124
|
+
@loog.debug("We are off GitHub quota, we must skip #{repo}")
|
125
|
+
break
|
126
|
+
end
|
115
127
|
if Time.now - start > timeout
|
116
128
|
$loog.info("We are doing this for #{start.ago} already, won't check #{repo}")
|
117
129
|
next
|
@@ -152,14 +164,6 @@ class Fbe::Iterate
|
|
152
164
|
end
|
153
165
|
f.what = @label
|
154
166
|
seen[repo] += 1
|
155
|
-
if oct.off_quota
|
156
|
-
@loog.debug('We are off GitHub quota, time to stop')
|
157
|
-
break
|
158
|
-
end
|
159
|
-
end
|
160
|
-
if oct.off_quota
|
161
|
-
@loog.info("We are off GitHub quota, time to stop after #{start.ago}")
|
162
|
-
break
|
163
167
|
end
|
164
168
|
unless seen.any? { |r, v| v < @repeats && !restarted.include?(r) }
|
165
169
|
@loog.debug("No more repos to scan (out of #{repos.size}), quitting after #{start.ago}")
|
data/lib/fbe/octo.rb
CHANGED
@@ -13,7 +13,6 @@ require 'verbose'
|
|
13
13
|
require_relative '../fbe'
|
14
14
|
require_relative 'middleware'
|
15
15
|
require_relative 'middleware/formatter'
|
16
|
-
require_relative 'middleware/quota'
|
17
16
|
|
18
17
|
# Makes a call to the GitHub API.
|
19
18
|
#
|
@@ -71,7 +70,6 @@ def Fbe.octo(options: $options, global: $global, loog: $loog)
|
|
71
70
|
methods: [:get],
|
72
71
|
backoff_factor: 2
|
73
72
|
)
|
74
|
-
builder.use(Fbe::Middleware::Quota, loog:, pause: options.github_api_pause || 60)
|
75
73
|
builder.use(Faraday::HttpCache, serializer: Marshal, shared_cache: false, logger: Loog::NULL)
|
76
74
|
builder.use(Octokit::Response::RaiseError)
|
77
75
|
builder.use(Faraday::Response::Logger, loog, formatter: Fbe::Middleware::Formatter)
|
@@ -84,12 +82,13 @@ def Fbe.octo(options: $options, global: $global, loog: $loog)
|
|
84
82
|
o = Fbe::FakeOctokit.new
|
85
83
|
end
|
86
84
|
decoor(o, loog:) do
|
87
|
-
def off_quota
|
85
|
+
def off_quota(threshold: 50)
|
88
86
|
left = @origin.rate_limit.remaining
|
89
|
-
if left <
|
90
|
-
@loog.info("Too much GitHub API quota consumed already (
|
87
|
+
if left < threshold
|
88
|
+
@loog.info("Too much GitHub API quota consumed already (#{left} < #{threshold}), stopping")
|
91
89
|
true
|
92
90
|
else
|
91
|
+
@loog.debug("Still #{left} GitHub API quota left (>#{threshold})")
|
93
92
|
false
|
94
93
|
end
|
95
94
|
end
|
@@ -149,13 +148,14 @@ class Fbe::FakeOctokit
|
|
149
148
|
true
|
150
149
|
end
|
151
150
|
|
152
|
-
|
153
|
-
|
154
|
-
|
151
|
+
# Get details of the user.
|
152
|
+
# @param [String|Integer] uid The login of the user or its ID
|
153
|
+
def user(uid)
|
154
|
+
login = (uid == 526_301 ? 'yegor256' : 'torvalds') if uid.is_a?(Integer)
|
155
155
|
{
|
156
156
|
id: 444,
|
157
157
|
login:,
|
158
|
-
type:
|
158
|
+
type: uid == 29_139_614 ? 'Bot' : 'User'
|
159
159
|
}
|
160
160
|
end
|
161
161
|
|
data/lib/fbe.rb
CHANGED
data/test/fbe/test_conclude.rb
CHANGED
@@ -61,6 +61,38 @@ class TestConclude < Fbe::Test
|
|
61
61
|
assert_equal(42, f.bar)
|
62
62
|
end
|
63
63
|
|
64
|
+
def test_considers_until_quota
|
65
|
+
WebMock.disable_net_connect!
|
66
|
+
fb = Factbase.new
|
67
|
+
5.times do
|
68
|
+
fb.insert.foo = 1
|
69
|
+
end
|
70
|
+
options = Judges::Options.new
|
71
|
+
stub_request(:get, %r{https://api.github.com/users/.*}).to_return(
|
72
|
+
{
|
73
|
+
body: { id: rand(100) }.to_json,
|
74
|
+
headers: { 'Content-Type' => 'application/json', 'X-RateLimit-Remaining' => '999' }
|
75
|
+
},
|
76
|
+
{
|
77
|
+
body: { id: rand(100) }.to_json,
|
78
|
+
headers: { 'Content-Type' => 'application/json', 'X-RateLimit-Remaining' => '9' }
|
79
|
+
}
|
80
|
+
)
|
81
|
+
stub_request(:get, 'https://api.github.com/rate_limit').to_return(
|
82
|
+
body: 'hm...', headers: { 'X-RateLimit-Remaining' => '777' }
|
83
|
+
).times(1)
|
84
|
+
global = {}
|
85
|
+
o = Fbe.octo(loog: Loog::NULL, options:, global:)
|
86
|
+
Fbe.conclude(fb:, judge: 'boom', loog: Loog::NULL, options:, global:) do
|
87
|
+
quota_aware
|
88
|
+
on '(exists foo)'
|
89
|
+
consider do |f|
|
90
|
+
f.bar = o.user("user-#{rand(100)}")[:id]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
assert_equal(2, fb.query('(exists bar)').each.to_a.size)
|
94
|
+
end
|
95
|
+
|
64
96
|
def test_ignores_globals
|
65
97
|
$fb = nil
|
66
98
|
$loog = nil
|
data/test/fbe/test_octo.rb
CHANGED
@@ -56,7 +56,7 @@ class TestOcto < Fbe::Test
|
|
56
56
|
global = {}
|
57
57
|
o = Fbe.octo(loog: Loog::NULL, global:, options: Judges::Options.new)
|
58
58
|
stub_request(:get, 'https://api.github.com/users/yegor256')
|
59
|
-
.to_return(
|
59
|
+
.to_return(body: '{}', headers: { 'Cache-Control' => 'public, max-age=60', 'etag' => 'abc' })
|
60
60
|
.times(1)
|
61
61
|
.then
|
62
62
|
.to_raise('second request should be cached, not passed to GitHub API!')
|
@@ -64,12 +64,50 @@ class TestOcto < Fbe::Test
|
|
64
64
|
o.user('yegor256')
|
65
65
|
end
|
66
66
|
|
67
|
+
def test_rate_limit_remaining
|
68
|
+
WebMock.disable_net_connect!
|
69
|
+
stub_request(:get, 'https://api.github.com/rate_limit').to_return(
|
70
|
+
{ body: '{}', headers: { 'X-RateLimit-Remaining' => '222' } }
|
71
|
+
)
|
72
|
+
stub_request(:get, 'https://api.github.com/user/42').to_return(
|
73
|
+
body: '', headers: { 'X-RateLimit-Remaining' => '4' }
|
74
|
+
)
|
75
|
+
o = Octokit::Client.new
|
76
|
+
assert_equal(222, o.rate_limit.remaining)
|
77
|
+
o.user(42)
|
78
|
+
assert_equal(4, o.rate_limit.remaining)
|
79
|
+
assert_equal(4, o.rate_limit.remaining)
|
80
|
+
end
|
81
|
+
|
67
82
|
def test_off_quota
|
68
83
|
WebMock.disable_net_connect!
|
69
|
-
stub_request(:get, 'https://api.github.com/rate_limit')
|
70
|
-
|
84
|
+
stub_request(:get, 'https://api.github.com/rate_limit').to_return(
|
85
|
+
body: '{}', headers: { 'X-RateLimit-Remaining' => '333' }
|
86
|
+
)
|
87
|
+
stub_request(:get, 'https://api.github.com/user/42').to_return(
|
88
|
+
body: '', headers: { 'X-RateLimit-Remaining' => '3' }
|
89
|
+
)
|
71
90
|
o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new)
|
72
91
|
refute(o.off_quota)
|
92
|
+
o.user(42)
|
93
|
+
assert(o.off_quota)
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_off_quota_twice
|
97
|
+
WebMock.disable_net_connect!
|
98
|
+
stub_request(:get, 'https://api.github.com/rate_limit').to_return(
|
99
|
+
body: '{}', headers: { 'X-RateLimit-Remaining' => '333' }
|
100
|
+
)
|
101
|
+
stub_request(:get, 'https://api.github.com/user/42').to_return(
|
102
|
+
{ body: '', headers: { 'X-RateLimit-Remaining' => '5555' } },
|
103
|
+
{ body: '', headers: { 'X-RateLimit-Remaining' => '5' } }
|
104
|
+
)
|
105
|
+
o = Fbe.octo(loog: Loog::VERBOSE, global: {}, options: Judges::Options.new)
|
106
|
+
refute(o.off_quota)
|
107
|
+
o.user(42)
|
108
|
+
refute(o.off_quota)
|
109
|
+
o.user(42)
|
110
|
+
assert(o.off_quota)
|
73
111
|
end
|
74
112
|
|
75
113
|
def test_retrying
|
@@ -80,7 +118,7 @@ class TestOcto < Fbe::Test
|
|
80
118
|
.to_raise(Octokit::TooManyRequests.new)
|
81
119
|
.times(1)
|
82
120
|
.then
|
83
|
-
.to_return(
|
121
|
+
.to_return(body: '{}')
|
84
122
|
o.user('yegor256')
|
85
123
|
end
|
86
124
|
|
@@ -92,7 +130,7 @@ class TestOcto < Fbe::Test
|
|
92
130
|
.to_return(status: 503)
|
93
131
|
.times(1)
|
94
132
|
.then
|
95
|
-
.to_return(
|
133
|
+
.to_return(body: '{}')
|
96
134
|
o.user('yegor256')
|
97
135
|
end
|
98
136
|
|
@@ -133,29 +171,20 @@ class TestOcto < Fbe::Test
|
|
133
171
|
|
134
172
|
def test_pauses_when_quota_is_exceeded
|
135
173
|
WebMock.disable_net_connect!
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
'x-ratelimit-remaining' => limit.to_s
|
151
|
-
}
|
152
|
-
)
|
153
|
-
.times(1)
|
154
|
-
o.user(user)
|
155
|
-
refute(o.off_quota) if n > 100
|
156
|
-
limit -= 1
|
157
|
-
end
|
158
|
-
assert_in_delta(pause, Time.now - start_time, 5)
|
174
|
+
o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new({ 'github_api_pause' => 0.01 }))
|
175
|
+
stub_request(:get, 'https://api.github.com/users/foo')
|
176
|
+
.to_return(
|
177
|
+
body: '{}',
|
178
|
+
headers: { 'x-ratelimit-remaining' => '1' }
|
179
|
+
)
|
180
|
+
.to_return(
|
181
|
+
body: '{}',
|
182
|
+
headers: { 'x-ratelimit-remaining' => '10000' }
|
183
|
+
)
|
184
|
+
o.user('foo')
|
185
|
+
assert(o.off_quota)
|
186
|
+
o.user('foo')
|
187
|
+
refute(o.off_quota)
|
159
188
|
end
|
160
189
|
|
161
190
|
def test_fetches_fake_check_runs_for_ref
|
@@ -193,4 +222,10 @@ class TestOcto < Fbe::Test
|
|
193
222
|
result = o.workflow_run_job('zerocracy/baza', 0)
|
194
223
|
assert_equal(0, result[:id])
|
195
224
|
end
|
225
|
+
|
226
|
+
def test_reads_quota
|
227
|
+
WebMock.enable_net_connect!
|
228
|
+
o = Fbe.octo(loog: Loog::VERBOSE, global: {}, options: Judges::Options.new({ 'github_api_pause' => 0.01 }))
|
229
|
+
refute_nil(o.off_quota)
|
230
|
+
end
|
196
231
|
end
|
data/test/test__helper.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fbe
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yegor Bugayenko
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-04-
|
10
|
+
date: 2025-04-16 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: backtrace
|
@@ -305,7 +305,6 @@ files:
|
|
305
305
|
- lib/fbe/just_one.rb
|
306
306
|
- lib/fbe/middleware.rb
|
307
307
|
- lib/fbe/middleware/formatter.rb
|
308
|
-
- lib/fbe/middleware/quota.rb
|
309
308
|
- lib/fbe/octo.rb
|
310
309
|
- lib/fbe/overwrite.rb
|
311
310
|
- lib/fbe/pmp.rb
|
@@ -317,7 +316,6 @@ files:
|
|
317
316
|
- renovate.json
|
318
317
|
- rules/basic.fe
|
319
318
|
- test/fbe/middleware/test_formatter.rb
|
320
|
-
- test/fbe/middleware/test_quota.rb
|
321
319
|
- test/fbe/test_award.rb
|
322
320
|
- test/fbe/test_bylaws.rb
|
323
321
|
- test/fbe/test_conclude.rb
|
data/lib/fbe/middleware/quota.rb
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Zerocracy
|
4
|
-
# SPDX-License-Identifier: MIT
|
5
|
-
|
6
|
-
require 'faraday'
|
7
|
-
require_relative '../middleware'
|
8
|
-
|
9
|
-
# Faraday Middleware that monitors GitHub API rate limits.
|
10
|
-
#
|
11
|
-
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
12
|
-
# Copyright:: Copyright (c) 2024-2025 Zerocracy
|
13
|
-
# License:: MIT
|
14
|
-
class Fbe::Middleware::Quota < Faraday::Middleware
|
15
|
-
# Constructor.
|
16
|
-
#
|
17
|
-
# @param [Object] app The Faraday app
|
18
|
-
# @param [Loog] loog The logging facility
|
19
|
-
# @param [Integer] pause Seconds to pause when rate limit is reached
|
20
|
-
# @param [Integer] limit Maximum number of requests before checking rate limit
|
21
|
-
# @param [Integer] rate Minimum remaining requests threshold
|
22
|
-
def initialize(app, loog: Loog::NULL, pause: 60, limit: 100, rate: 5)
|
23
|
-
super(app)
|
24
|
-
@requests = 0
|
25
|
-
@app = app
|
26
|
-
raise 'The "loog" cannot be nil' if loog.nil?
|
27
|
-
@loog = loog
|
28
|
-
raise 'The "pause" cannot be nil' if pause.nil?
|
29
|
-
raise 'The "pause" must be a positive integer' unless pause.positive?
|
30
|
-
@pause = pause
|
31
|
-
raise 'The "limit" cannot be nil' if limit.nil?
|
32
|
-
raise 'The "limit" must be a positive integer' unless limit.positive?
|
33
|
-
@limit = limit
|
34
|
-
raise 'The "rate" cannot be nil' if rate.nil?
|
35
|
-
raise 'The "rate" must be a positive integer' unless rate.positive?
|
36
|
-
@rate = rate
|
37
|
-
end
|
38
|
-
|
39
|
-
# Process the request and handle rate limiting.
|
40
|
-
#
|
41
|
-
# @param [Faraday::Env] env The environment
|
42
|
-
# @return [Faraday::Response] The response
|
43
|
-
def call(env)
|
44
|
-
@requests += 1
|
45
|
-
response = @app.call(env)
|
46
|
-
if out_of_limit?(env)
|
47
|
-
@loog.info("Too much GitHub API quota consumed, pausing for #{@pause} seconds")
|
48
|
-
sleep(@pause)
|
49
|
-
@requests = 0
|
50
|
-
end
|
51
|
-
response
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
|
-
|
56
|
-
# Check if we're approaching the rate limit.
|
57
|
-
#
|
58
|
-
# @param [Faraday::Env] env The environment
|
59
|
-
# @return [Boolean] True if we should pause to avoid hitting rate limits
|
60
|
-
def out_of_limit?(env)
|
61
|
-
remaining = env.response_headers['x-ratelimit-remaining'].to_i
|
62
|
-
(@requests % @limit).zero? && remaining < @rate
|
63
|
-
end
|
64
|
-
end
|
@@ -1,66 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Zerocracy
|
4
|
-
# SPDX-License-Identifier: MIT
|
5
|
-
|
6
|
-
require 'faraday'
|
7
|
-
require 'logger'
|
8
|
-
require 'loog'
|
9
|
-
require 'judges'
|
10
|
-
require 'judges/options'
|
11
|
-
require_relative '../../../lib/fbe/middleware'
|
12
|
-
require_relative '../../../lib/fbe/middleware/quota'
|
13
|
-
require_relative '../../test__helper'
|
14
|
-
|
15
|
-
class QuotaTest < Fbe::Test
|
16
|
-
class FakeApp
|
17
|
-
def initialize
|
18
|
-
@calls = 0
|
19
|
-
end
|
20
|
-
|
21
|
-
def call(env)
|
22
|
-
@calls += 1
|
23
|
-
response_headers = {
|
24
|
-
'x-ratelimit-remaining' => (100 - @calls).to_s
|
25
|
-
}
|
26
|
-
env[:response_headers] = response_headers
|
27
|
-
env
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def test_quota_middleware_pauses_when_quota_low
|
32
|
-
loog = Loog::NULL
|
33
|
-
pause = 0.1
|
34
|
-
app = FakeApp.new
|
35
|
-
middleware = Fbe::Middleware::Quota.new(app, loog:, pause:)
|
36
|
-
start_time = Time.now
|
37
|
-
105.times do
|
38
|
-
env = Judges::Options.new(
|
39
|
-
'method' => :get,
|
40
|
-
'url' => 'http://example.com',
|
41
|
-
'request_headers' => {},
|
42
|
-
'response_headers' => {}
|
43
|
-
)
|
44
|
-
middleware.call(env)
|
45
|
-
end
|
46
|
-
assert_in_delta pause, Time.now - start_time, 0.4
|
47
|
-
end
|
48
|
-
|
49
|
-
def test_quota_middleware_logs_when_quota_low
|
50
|
-
pause = 0.1
|
51
|
-
log_output = StringIO.new
|
52
|
-
loog = Logger.new(log_output)
|
53
|
-
app = FakeApp.new
|
54
|
-
middleware = Fbe::Middleware::Quota.new(app, loog:, pause:)
|
55
|
-
105.times do
|
56
|
-
env = Judges::Options.new(
|
57
|
-
'method' => :get,
|
58
|
-
'url' => 'http://example.com',
|
59
|
-
'request_headers' => {},
|
60
|
-
'response_headers' => {}
|
61
|
-
)
|
62
|
-
middleware.call(env)
|
63
|
-
end
|
64
|
-
assert_match(/Too much GitHub API quota/, log_output.string)
|
65
|
-
end
|
66
|
-
end
|