fbe 0.30.0 → 0.31.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.
@@ -1,791 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Zerocracy
4
- # SPDX-License-Identifier: MIT
5
-
6
- require 'judges/options'
7
- require 'loog'
8
- require 'webmock/minitest'
9
- require_relative '../../lib/fbe/octo'
10
- require_relative '../test__helper'
11
-
12
- # Test.
13
- # Author:: Yegor Bugayenko (yegor256@gmail.com)
14
- # Copyright:: Copyright (c) 2024-2025 Zerocracy
15
- # License:: MIT
16
- class TestOcto < Fbe::Test
17
- def test_simple_use
18
- global = {}
19
- options = Judges::Options.new({ 'testing' => true })
20
- o = Fbe.octo(loog: Loog::NULL, global:, options:)
21
- refute_predicate(o, :off_quota?)
22
- refute_nil(o.pull_request('foo/foo', 42))
23
- refute_nil(o.commit_pulls('foo/foo', 'sha'))
24
- end
25
-
26
- def test_post_comment
27
- global = {}
28
- options = Judges::Options.new({ 'testing' => true })
29
- o = Fbe.octo(loog: Loog::NULL, global:, options:)
30
- assert_equal(42, o.add_comment('foo/foo', 4, 'hello!')[:id])
31
- end
32
-
33
- def test_give_repo_a_star
34
- global = {}
35
- options = Judges::Options.new({ 'testing' => true })
36
- o = Fbe.octo(loog: Loog::NULL, global:, options:)
37
- assert(o.star('foo/foo'))
38
- end
39
-
40
- def test_detect_bot
41
- global = {}
42
- options = Judges::Options.new({ 'testing' => true })
43
- o = Fbe.octo(loog: Loog::NULL, global:, options:)
44
- assert_equal('Bot', o.user(29_139_614)[:type])
45
- assert_equal('User', o.user('yegor256')[:type])
46
- assert_equal('User', o.user(42)[:type])
47
- end
48
-
49
- def test_rate_limit
50
- o = Fbe::FakeOctokit.new
51
- assert_equal(100, o.rate_limit.remaining)
52
- end
53
-
54
- def test_reads_nickname_by_id
55
- WebMock.disable_net_connect!
56
- stub_request(:get, 'https://api.github.com/rate_limit').to_return(
57
- { body: '{}', headers: { 'X-RateLimit-Remaining' => '222' } }
58
- )
59
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new)
60
- stub_request(:get, 'https://api.github.com/user/42').to_return(
61
- body: { login: 'Dude56' }.to_json, headers: { 'Content-Type': 'application/json' }
62
- )
63
- nick = o.user_name_by_id(42)
64
- assert_equal('dude56', nick)
65
- end
66
-
67
- def test_reads_repo_id_by_name
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
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new)
73
- stub_request(:get, 'https://api.github.com/repos/foo/bar').to_return(
74
- body: { id: 42 }.to_json, headers: { 'Content-Type': 'application/json' }
75
- )
76
- id = o.repo_id_by_name('foo/bar')
77
- assert_equal(42, id)
78
- end
79
-
80
- def test_reads_lost_repo_id_by_name
81
- WebMock.disable_net_connect!
82
- stub_request(:get, 'https://api.github.com/rate_limit').to_return(
83
- { body: '{}', headers: { 'X-RateLimit-Remaining' => '222' } }
84
- )
85
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new)
86
- stub_request(:get, 'https://api.github.com/repos/foo/bar').to_return(status: 404)
87
- assert_raises(StandardError) { o.repo_id_by_name('foo/bar') }
88
- end
89
-
90
- def test_fails_user_request_when_off_quota
91
- WebMock.disable_net_connect!
92
- stub_request(:get, 'https://api.github.com/rate_limit').to_return(
93
- { body: '{}', headers: { 'X-RateLimit-Remaining' => '3' } }
94
- )
95
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new)
96
- assert_raises(StandardError) { o.user(42) }
97
- end
98
-
99
- def test_no_failure_on_printing_when_off_quota
100
- WebMock.disable_net_connect!
101
- stub_request(:get, 'https://api.github.com/rate_limit').to_return(
102
- { body: '{}', headers: { 'X-RateLimit-Remaining' => '3' } }
103
- )
104
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new)
105
- o.print_trace!
106
- end
107
-
108
- def test_reads_repo_name_by_id
109
- WebMock.disable_net_connect!
110
- stub_request(:get, 'https://api.github.com/rate_limit').to_return(
111
- { body: '{}', headers: { 'X-RateLimit-Remaining' => '222' } }
112
- )
113
- global = {}
114
- o = Fbe.octo(loog: Loog::NULL, global:, options: Judges::Options.new)
115
- stub_request(:get, 'https://api.github.com/repositories/42').to_return(
116
- body: { full_name: 'Foo/bar56-Ff' }.to_json, headers: { 'Content-Type': 'application/json' }
117
- )
118
- nick = o.repo_name_by_id(42)
119
- assert_equal('foo/bar56-ff', nick)
120
- end
121
-
122
- def test_caching
123
- WebMock.disable_net_connect!
124
- stub_request(:get, 'https://api.github.com/rate_limit').to_return(
125
- { body: '{"rate":{"remaining":222}}', headers: { 'X-RateLimit-Remaining' => '222' } }
126
- )
127
- global = {}
128
- o = Fbe.octo(loog: Loog::NULL, global:, options: Judges::Options.new)
129
- stub_request(:get, 'https://api.github.com/users/yegor256')
130
- .to_return(
131
- body: '{}',
132
- headers: { 'Cache-Control' => 'public, max-age=60', 'etag' => 'abc', 'x-ratelimit-remaining' => '10000' }
133
- )
134
- .times(1)
135
- .then
136
- .to_raise('second request should be cached, not passed to GitHub API!')
137
- o.user('yegor256')
138
- o.user('yegor256')
139
- end
140
-
141
- def test_rate_limit_remaining
142
- WebMock.disable_net_connect!
143
- stub_request(:get, 'https://api.github.com/rate_limit').to_return(
144
- { body: '{}', headers: { 'X-RateLimit-Remaining' => '222' } }
145
- )
146
- stub_request(:get, 'https://api.github.com/user/42').to_return(
147
- body: '', headers: { 'X-RateLimit-Remaining' => '4' }
148
- )
149
- o = Octokit::Client.new
150
- assert_equal(222, o.rate_limit.remaining)
151
- o.user(42)
152
- assert_equal(4, o.rate_limit.remaining)
153
- assert_equal(4, o.rate_limit.remaining)
154
- end
155
-
156
- def test_off_quota?
157
- WebMock.disable_net_connect!
158
- stub_request(:get, 'https://api.github.com/rate_limit').to_return(
159
- { body: '{"rate":{"remaining":50}}', headers: { 'X-RateLimit-Remaining' => '50' } }
160
- )
161
- stub_request(:get, 'https://api.github.com/user/42').to_return(
162
- body: '', headers: { 'X-RateLimit-Remaining' => '49' }
163
- )
164
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new)
165
- refute_predicate(o, :off_quota?)
166
- o.user(42)
167
- assert_predicate(o, :off_quota?)
168
- end
169
-
170
- def test_off_quota_twice
171
- WebMock.disable_net_connect!
172
- stub_request(:get, 'https://api.github.com/rate_limit').to_return(
173
- { body: '{"rate":{"remaining":51}}', headers: { 'X-RateLimit-Remaining' => '51' } }
174
- )
175
- stub_request(:get, 'https://api.github.com/user/42').to_return(
176
- { body: '', headers: { 'X-RateLimit-Remaining' => '5555' } },
177
- { body: '', headers: { 'X-RateLimit-Remaining' => '5' } }
178
- )
179
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new)
180
- refute_predicate(o, :off_quota?)
181
- o.user(42)
182
- refute_predicate(o, :off_quota?)
183
- o.user(42)
184
- assert_predicate(o, :off_quota?)
185
- end
186
-
187
- def test_print_quota_left_while_initialize
188
- WebMock.disable_net_connect!
189
- stub_request(:get, 'https://api.github.com/rate_limit').to_return(
190
- body: '{}', headers: { 'X-RateLimit-Remaining' => '1234' }
191
- )
192
- buf = Loog::Buffer.new
193
- Fbe.octo(loog: buf, global: {}, options: Judges::Options.new({ 'github_token' => 'secret_github_token' }))
194
- assert_match(/Accessing GitHub API with a token \(19 chars, ending by "oken", 1234 quota remaining\)/, buf.to_s)
195
- end
196
-
197
- def test_retrying
198
- WebMock.disable_net_connect!
199
- stub_request(:get, 'https://api.github.com/rate_limit').to_return(
200
- { body: '{}', headers: { 'X-RateLimit-Remaining' => '222' } }
201
- )
202
- global = {}
203
- o = Fbe.octo(loog: Loog::NULL, global:, options: Judges::Options.new)
204
- stub_request(:get, 'https://api.github.com/users/yegor256')
205
- .to_raise(Octokit::TooManyRequests.new)
206
- .times(1)
207
- .then
208
- .to_return(body: '{}')
209
- o.user('yegor256')
210
- end
211
-
212
- def test_retrying_on_error_response
213
- WebMock.disable_net_connect!
214
- stub_request(:get, 'https://api.github.com/rate_limit').to_return(
215
- { body: '{}', headers: { 'X-RateLimit-Remaining' => '222' } }
216
- )
217
- global = {}
218
- o = Fbe.octo(loog: Loog::NULL, global:, options: Judges::Options.new)
219
- stub_request(:get, 'https://api.github.com/users/yegor256')
220
- .to_return(status: 503)
221
- .times(1)
222
- .then
223
- .to_return(body: '{}')
224
- o.user('yegor256')
225
- end
226
-
227
- def test_with_broken_token
228
- skip("it's a live test, run it manually if you need it")
229
- WebMock.enable_net_connect!
230
- global = {}
231
- options = Judges::Options.new({ 'github_token' => 'incorrect-value' })
232
- o = Fbe.octo(loog: Loog::NULL, global:, options:)
233
- assert_raises(StandardError) { o.repository('zerocracy/fbe') }
234
- end
235
-
236
- def test_workflow_run_usage
237
- WebMock.disable_net_connect!
238
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new({ 'testing' => true }))
239
- assert_equal(53_000, o.workflow_run_usage('zerocracy/fbe', 1)[:run_duration_ms])
240
- end
241
-
242
- def test_commit_pulls
243
- skip("it's a live test, run it manually if you need it")
244
- WebMock.enable_net_connect!
245
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new)
246
- assert_equal(1, o.commit_pulls('zerocracy/fbe', '0b7d0699bd744b62c0731064c2adaad0c58e1416').size)
247
- assert_equal(0, o.commit_pulls('zerocracy/fbe', '16b3ea6b71c6e932ba7666c40ca846ecaa6d6f0d').size)
248
- end
249
-
250
- def test_search_issues
251
- WebMock.disable_net_connect!
252
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new({ 'testing' => true }))
253
- assert_equal(42, o.search_issues('repo:zerocracy/fbe type:issue').dig(:items, 0, :number))
254
- total_pr_count = 2
255
- assert_equal(total_pr_count, o.search_issues('repo:zerocracy/fbe type:pr')[:total_count])
256
- assert_equal(total_pr_count, o.search_issues('repo:zerocracy/fbe type:pr')[:items].count)
257
- unmereged_pr_count = 1
258
- assert_equal(unmereged_pr_count, o.search_issues('repo:zerocracy/fbe type:pr is:unmerged')[:total_count])
259
- assert_equal(unmereged_pr_count, o.search_issues('repo:zerocracy/fbe type:pr is:unmerged')[:items].count)
260
- end
261
-
262
- def test_pauses_when_quota_is_exceeded
263
- WebMock.disable_net_connect!
264
- stub_request(:get, 'https://api.github.com/rate_limit').to_return(
265
- { body: '{}', headers: { 'X-RateLimit-Remaining' => '222' } },
266
- { body: '{}', headers: { 'X-RateLimit-Remaining' => '1' } },
267
- { body: '{}', headers: { 'X-RateLimit-Remaining' => '10000' } }
268
- )
269
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new({ 'github_api_pause' => 0.01 }))
270
- stub_request(:get, 'https://api.github.com/users/foo')
271
- .to_return(
272
- body: '{}',
273
- headers: { 'x-ratelimit-remaining' => '1' }
274
- )
275
- .to_return(
276
- body: '{}',
277
- headers: { 'x-ratelimit-remaining' => '10000' }
278
- )
279
- o.user('foo')
280
- assert_predicate(o, :off_quota?)
281
- end
282
-
283
- def test_fetches_fake_check_runs_for_ref
284
- WebMock.disable_net_connect!
285
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new({ 'testing' => true }))
286
- sha = 'f2ca1bb6c7e907d06'
287
- result = o.check_runs_for_ref('zerocracy/baza', sha)
288
- assert_equal(7, result[:total_count])
289
- assert_equal(7, result[:check_runs].count)
290
- result = o.check_runs_for_ref('zerocracy/judges-action', sha)
291
- assert_equal(7, result[:total_count])
292
- assert_equal(7, result[:check_runs].count)
293
- result = o.check_runs_for_ref('zerocracy/something', sha)
294
- assert_equal(0, result[:total_count])
295
- assert_instance_of(Array, result[:check_runs])
296
- assert_equal(0, result[:check_runs].count)
297
- end
298
-
299
- def test_fetches_fake_workflow_run
300
- WebMock.disable_net_connect!
301
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new({ 'testing' => true }))
302
- id = 10_438_531_072
303
- result = o.workflow_run('zerocracy/baza', id)
304
- assert_equal(id, result[:id])
305
- result = o.workflow_run('zerocracy/baza', 0)
306
- assert_equal(0, result[:id])
307
- end
308
-
309
- def test_fetches_fake_workflow_run_job
310
- WebMock.disable_net_connect!
311
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new({ 'testing' => true }))
312
- id = 28_906_596_433
313
- result = o.workflow_run_job('zerocracy/baza', id)
314
- assert_equal(id, result[:id])
315
- result = o.workflow_run_job('zerocracy/baza', 0)
316
- assert_equal(0, result[:id])
317
- end
318
-
319
- def test_reads_quota
320
- WebMock.disable_net_connect!
321
- stub_request(:get, 'https://api.github.com/rate_limit').to_return(
322
- { body: '{}', headers: { 'X-RateLimit-Remaining' => '222' } }
323
- )
324
- o = Fbe.octo(loog: Loog::VERBOSE, global: {}, options: Judges::Options.new({ 'github_api_pause' => 0.01 }))
325
- refute_nil(o.off_quota?)
326
- end
327
-
328
- def test_fetches_fake_not_found_users
329
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new({ 'testing' => true }))
330
- assert_raises(Octokit::NotFound) { o.user(404_001) }
331
- assert_raises(Octokit::NotFound) { o.user(404_002) }
332
- end
333
-
334
- def test_fetches_fake_not_found_repos
335
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new({ 'testing' => true }))
336
- assert_raises(Octokit::NotFound) { o.repository(404_123) }
337
- assert_raises(Octokit::NotFound) { o.repository(404_124) }
338
- end
339
-
340
- def test_fetches_fake_zerocracy_baza_repo
341
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new({ 'testing' => true }))
342
- assert_equal('zerocracy/baza', o.repository(1439)[:full_name])
343
- end
344
-
345
- def test_fetches_fake_issue_events_has_assigned_event
346
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new({ 'testing' => true }))
347
- result = o.issue_events('foo/foo', 123)
348
- assert_instance_of(Array, result)
349
- assert_equal(7, result.size)
350
- event = result.find { _1[:event] == 'assigned' }
351
- assert_equal(608, event[:id])
352
- assert_pattern do
353
- event => {
354
- id: Integer,
355
- actor: { login: 'user2', id: 422, type: 'User' },
356
- event: 'assigned',
357
- created_at: Time,
358
- assignee: { login: 'user2', id: 422, type: 'User' },
359
- assigner: { login: 'user', id: 411, type: 'User' }
360
- }
361
- end
362
- end
363
-
364
- def test_fetch_fake_issue_and_pr
365
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new({ 'testing' => true }))
366
- result = o.issue('yegor256/test', 142)
367
- assert_equal(Time.parse('2025-06-02 15:00:00 UTC'), result[:closed_at])
368
- assert_pattern do
369
- result => {
370
- id: 655,
371
- number: 142,
372
- user: { login: 'yegor256', id: 526_301, type: 'User' },
373
- state: 'closed',
374
- created_at: Time,
375
- updated_at: Time,
376
- closed_at: Time
377
- }
378
- end
379
- result = o.issue('yegor256/test', 143)
380
- assert_equal(Time.parse('2025-06-01 18:20:00 UTC'), result[:closed_at])
381
- assert_pattern do
382
- result => {
383
- id: 656,
384
- number: 143,
385
- user: { login: 'yegor256', id: 526_301, type: 'User' },
386
- state: 'closed',
387
- pull_request: { merged_at: nil },
388
- created_at: Time,
389
- updated_at: Time,
390
- closed_at: Time
391
- }
392
- end
393
- end
394
-
395
- def test_print_trace
396
- loog = Loog::Buffer.new
397
- WebMock.disable_net_connect!
398
- stub_request(:get, 'https://api.github.com/rate_limit').to_return(
399
- { body: '{"rate":{"remaining":222}}', headers: { 'X-RateLimit-Remaining' => '222' } },
400
- { body: '{"rate":{"remaining":222}}', headers: { 'X-RateLimit-Remaining' => '222' } },
401
- { body: '{"rate":{"remaining":222}}', headers: { 'X-RateLimit-Remaining' => '222' } }
402
- )
403
- stub_request(:get, 'https://api.github.com/user/123').to_return do
404
- {
405
- status: 200,
406
- body: '{"id":123,"login":"test"}',
407
- headers: { 'X-RateLimit-Remaining' => '222' }
408
- }
409
- end
410
- stub_request(:get, 'https://api.github.com/repos/foo/bar').to_return do
411
- {
412
- status: 200,
413
- body: '{"id":456,"full_name":"foo/bar"}',
414
- headers: { 'X-RateLimit-Remaining' => '222' }
415
- }
416
- end
417
- octo = Fbe.octo(loog:, global: {}, options: Judges::Options.new)
418
- octo.user(123)
419
- octo.repository('foo/bar')
420
- octo.repository('foo/bar')
421
- octo.print_trace!(all: true, max: 9_999)
422
- output = loog.to_s
423
- assert_includes output, '3 URLs vs 4 requests'
424
- assert_includes output, '219 quota left'
425
- assert_includes output, '/rate_limit: 1'
426
- assert_includes output, '/user/123: 1'
427
- assert_includes output, '/repos/foo/bar: 2'
428
- repo_index = output.index('/repos/foo/bar: 2')
429
- user_index = output.index('/user/123: 1')
430
- assert_operator repo_index, :<, user_index, 'URLs should be sorted by request count (highest first)'
431
- end
432
-
433
- def test_prints_only_real_requests
434
- WebMock.disable_net_connect!
435
- stub_request(:get, 'https://api.github.com/rate_limit').to_return(
436
- { body: '{}', headers: { 'X-RateLimit-Remaining' => '222' } }
437
- )
438
- stub = stub_request(:get, 'https://api.github.com/user/123').to_return(
439
- status: 200,
440
- body: '{"id":123,"login":"test"}',
441
- headers: {
442
- 'X-RateLimit-Remaining' => '222',
443
- 'Content-Type' => 'application/json',
444
- 'Cache-Control' => 'public, max-age=60, s-maxage=60',
445
- 'Etag' => 'W/"2ff9dd4c3153f006830b2b8b721f6a4bb400a1eb81a2e1fa0a3b846ad349b9ec"',
446
- 'Last-Modified' => 'Wed, 01 May 2025 20:00:00 GMT'
447
- }
448
- )
449
- Dir.mktmpdir do |dir|
450
- fcache = File.expand_path('test.db', dir)
451
- octo = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new({ 'sqlite_cache' => fcache }))
452
- octo.user(123)
453
- loog = Loog::Buffer.new
454
- octo = Fbe.octo(loog:, global: {}, options: Judges::Options.new({ 'sqlite_cache' => fcache }))
455
- WebMock.remove_request_stub(stub)
456
- octo.user(123)
457
- octo.print_trace!(all: true)
458
- refute_match('/user/123: 1', loog.to_s)
459
- end
460
- end
461
-
462
- def test_octo_not_trace_cached_requests
463
- WebMock.disable_net_connect!
464
- now = Time.now
465
- stub_request(:get, 'https://api.github.com/rate_limit')
466
- .to_return(
467
- status: 200, headers: { 'Content-Type' => 'application/json', 'X-RateLimit-Remaining' => '5000' },
468
- body: { 'rate' => { 'limit' => 5000, 'remaining' => 5000, 'reset' => 1_672_531_200 } }.to_json
469
- )
470
- stub_request(:get, 'https://api.github.com/repos/zerocracy/baza.rb')
471
- .to_return(
472
- status: 200,
473
- headers: {
474
- 'date' => now.httpdate,
475
- 'cache-control' => 'public, max-age=60, s-maxage=60',
476
- 'last-modified' => (now - (6 * 60 * 60)).httpdate,
477
- 'content-type' => 'application/json; charset=utf-8'
478
- },
479
- body: { id: 840_215_648, name: 'baza.rb' }.to_json
480
- )
481
- .times(1)
482
- .then
483
- .to_return(
484
- status: 200,
485
- headers: {
486
- 'date' => (now + 70).httpdate,
487
- 'cache-control' => 'public, max-age=60, s-maxage=60',
488
- 'last-modified' => (now - (6 * 60 * 60)).httpdate,
489
- 'content-type' => 'application/json; charset=utf-8'
490
- },
491
- body: { id: 840_215_648, name: 'baza.rb' }.to_json
492
- )
493
- .times(1)
494
- .then.to_raise('no more request to /repos/zerocracy/baza.rb')
495
- loog = Loog::Buffer.new
496
- o = Fbe.octo(loog:, global: {}, options: Judges::Options.new({}))
497
- o.print_trace!(all: true)
498
- Time.stub(:now, now) do
499
- 5.times do
500
- o.repo('zerocracy/baza.rb')
501
- end
502
- end
503
- o.print_trace!(all: true)
504
- Time.stub(:now, now + 70) do
505
- 25.times do
506
- o.repo('zerocracy/baza.rb')
507
- end
508
- end
509
- o.print_trace!(all: true)
510
- assert_requested :get, 'https://api.github.com/repos/zerocracy/baza.rb', times: 2
511
- output = loog.to_s
512
- assert_match('/repos/zerocracy/baza.rb: 1', output)
513
- refute_match('/repos/zerocracy/baza.rb: 5', output)
514
- refute_match('/repos/zerocracy/baza.rb: 25', output)
515
- end
516
-
517
- def test_trace_gets_cleared_after_print
518
- WebMock.disable_net_connect!
519
- stub_request(:get, 'https://api.github.com/rate_limit').to_return(
520
- { body: '{}', headers: { 'X-RateLimit-Remaining' => '222' } }
521
- )
522
- stub_request(:get, 'https://api.github.com/user/456').to_return(
523
- status: 200,
524
- body: '{"id":456,"login":"testuser"}',
525
- headers: { 'X-RateLimit-Remaining' => '222' }
526
- )
527
- first_loog = Loog::Buffer.new
528
- octo = Fbe.octo(loog: first_loog, global: {}, options: Judges::Options.new)
529
- octo.user(456)
530
- octo.print_trace!
531
- first_output = first_loog.to_s
532
- assert_includes first_output, 'GitHub API trace'
533
- end
534
-
535
- def test_works_via_sqlite_store
536
- WebMock.disable_net_connect!
537
- stub_request(:get, 'https://api.github.com/rate_limit').to_return(
538
- { body: '{}', headers: { 'X-RateLimit-Remaining' => '222' } }
539
- )
540
- Dir.mktmpdir do |dir|
541
- sqlite_cache = File.expand_path('test.db', dir)
542
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new({ 'sqlite_cache' => sqlite_cache }))
543
- stub = stub_request(:get, 'https://api.github.com/user/42').to_return(
544
- status: 200,
545
- body: { login: 'user1' }.to_json,
546
- headers: {
547
- 'Content-Type' => 'application/json',
548
- 'Cache-Control' => 'public, max-age=60, s-maxage=60',
549
- 'Etag' => 'W/"2ff9dd4c3153f006830b2b8b721f6a4bb400a1eb81a2e1fa0a3b846ad349b9ec"',
550
- 'Last-Modified' => 'Wed, 01 May 2025 20:00:00 GMT'
551
- }
552
- )
553
- assert_equal('user1', o.user_name_by_id(42))
554
- WebMock.remove_request_stub(stub)
555
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new({ 'sqlite_cache' => sqlite_cache }))
556
- assert_equal('user1', o.user_name_by_id(42))
557
- end
558
- end
559
-
560
- def test_through_sqlite_store_when_broken_token
561
- WebMock.disable_net_connect!
562
- stub_request(:get, 'https://api.github.com/rate_limit').to_return(
563
- { body: '{}', headers: { 'X-RateLimit-Remaining' => '222' } }
564
- )
565
- Dir.mktmpdir do |dir|
566
- file = File.expand_path('test.db', dir)
567
- stub_request(:get, 'https://api.github.com/user/4242').to_return(status: 401)
568
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new({ 'sqlite_cache' => file }))
569
- assert_raises(StandardError) do
570
- assert_equal('user1', o.user_name_by_id(4242))
571
- end
572
- assert_path_exists(file)
573
- end
574
- end
575
-
576
- def test_sqlite_store_for_use_in_different_versions
577
- WebMock.disable_net_connect!
578
- stub_request(:get, 'https://api.github.com/rate_limit').to_return(
579
- { body: '{}', headers: { 'X-RateLimit-Remaining' => '222' } }
580
- )
581
- Dir.mktmpdir do |dir|
582
- stub =
583
- stub_request(:get, 'https://api.github.com/user/42')
584
- .to_return(
585
- status: 200,
586
- body: { login: 'user1' }.to_json,
587
- headers: {
588
- 'Content-Type' => 'application/json',
589
- 'Cache-Control' => 'public, max-age=60, s-maxage=60',
590
- 'Etag' => 'W/"2ff9dd4c3153f006830b2b8b721f6a4bb400a1eb81a2e1fa0a3b846ad349b9ec"',
591
- 'Last-Modified' => 'Wed, 01 May 2025 20:00:00 GMT'
592
- }
593
- )
594
- sqlite_cache = File.expand_path('test.db', dir)
595
- Fbe.stub_const(:VERSION, '0.0.1') do
596
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new({ 'sqlite_cache' => sqlite_cache }))
597
- assert_equal('user1', o.user_name_by_id(42))
598
- end
599
- WebMock.remove_request_stub(stub)
600
- stub_request(:get, 'https://api.github.com/user/42')
601
- .to_return(
602
- status: 200,
603
- body: { login: 'user2' }.to_json,
604
- headers: {
605
- 'Content-Type' => 'application/json',
606
- 'Cache-Control' => 'public, max-age=60, s-maxage=60',
607
- 'Etag' => 'W/"2ff9dd4c3153f006830b2b8b721f6a4bb400a1eb81a2e1fa0a3b846ad349b9ec"',
608
- 'Last-Modified' => 'Wed, 01 May 2025 20:00:00 GMT'
609
- }
610
- )
611
- Fbe.stub_const(:VERSION, '0.0.2') do
612
- o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new({ 'sqlite_cache' => sqlite_cache }))
613
- assert_equal('user2', o.user_name_by_id(42))
614
- end
615
- end
616
- end
617
-
618
- def test_fetch_rate_limit_by_making_new_request
619
- WebMock.disable_net_connect!
620
- stub_request(:get, 'https://api.github.com/rate_limit').to_return(
621
- { body: '{"rate":{"remaining":321}}', headers: { 'X-RateLimit-Remaining' => '321' } }
622
- )
623
- loog = Loog::Buffer.new
624
- o = Fbe.octo(loog:, global: {}, options: Judges::Options.new)
625
- refute_predicate(o, :off_quota?)
626
- assert_match(/321 GitHub API quota left/, loog.to_s)
627
- o.print_trace!(all: true)
628
- assert_match(/321 quota left/, loog.to_s)
629
- end
630
-
631
- def test_throttling_request_to_rate_limit
632
- WebMock.disable_net_connect!
633
- stub_request(:get, 'https://api.github.com/rate_limit')
634
- .to_return(
635
- status: 200, headers: { 'Content-Type' => 'application/json', 'X-RateLimit-Remaining' => '5000' },
636
- body: { 'rate' => { 'limit' => 5000, 'remaining' => 5000, 'reset' => 1_672_531_200 } }.to_json
637
- )
638
- .then.to_return(
639
- status: 200, headers: { 'Content-Type' => 'application/json', 'X-RateLimit-Remaining' => '4900' },
640
- body: { 'rate' => { 'limit' => 5000, 'remaining' => 4900, 'reset' => 1_672_531_200 } }.to_json
641
- )
642
- .then.to_return(
643
- status: 200, headers: { 'Content-Type' => 'application/json', 'X-RateLimit-Remaining' => '4800' },
644
- body: { 'rate' => { 'limit' => 5000, 'remaining' => 4800, 'reset' => 1_672_531_200 } }.to_json
645
- )
646
- .then.to_raise('no more request to /rate_limit')
647
- stub_request(:get, 'https://api.github.com/user/1')
648
- .to_return(
649
- status: 200, headers: { 'Content-Type' => 'application/json' },
650
- body: { 'id' => 1, 'login' => 'user1' }.to_json
651
- ).times(1)
652
- stub_request(:get, 'https://api.github.com/user/111')
653
- .to_return(
654
- status: 200, headers: { 'Content-Type' => 'application/json' },
655
- body: { 'id' => 111, 'login' => 'user111' }.to_json
656
- )
657
- .times(201)
658
- .then.to_raise('no more request to /user/111')
659
- loog = Loog::Buffer.new
660
- o = Fbe.octo(loog:, global: {}, options: Judges::Options.new({}))
661
- o.user(1)
662
- o.print_trace!(all: true)
663
- 201.times do
664
- o.user(111)
665
- o.rate_limit!.remaining
666
- end
667
- o.print_trace!(all: true)
668
- output = loog.to_s
669
- assert_requested :get, 'https://api.github.com/user/1', times: 1
670
- assert_requested :get, 'https://api.github.com/user/111', times: 201
671
- assert_requested :get, 'https://api.github.com/rate_limit', times: 3
672
- assert_match('2 URLs vs 2 requests', output)
673
- assert_match('/user/1: 1', output)
674
- assert_match('/rate_limit: 1', output)
675
- assert_match('2 URLs vs 203 requests', output)
676
- assert_match('/user/111: 201', output)
677
- assert_match('/rate_limit: 2', output)
678
- end
679
-
680
- def test_octo_http_cache_middleware_located_in_end_of_chain
681
- WebMock.disable_net_connect!
682
- stub_request(:get, 'https://api.github.com/rate_limit')
683
- .to_return(
684
- status: 200, headers: { 'Content-Type' => 'application/json', 'X-RateLimit-Remaining' => '5000' },
685
- body: { 'rate' => { 'limit' => 5000, 'remaining' => 5000, 'reset' => 1_672_531_200 } }.to_json
686
- )
687
- o = Fbe.octo(loog: fake_loog, global: {}, options: Judges::Options.new({}))
688
- assert_equal('Faraday::HttpCache', o.middleware.handlers.last.name, <<~MSG.strip.gsub!(/\s+/, ' '))
689
- Faraday::HttpCache middleware must be located in the end of chain middlewares,
690
- because the Oktokit client change Faraday::HttpCache position to the last,
691
- for more info, see: https://github.com/zerocracy/fbe/issues/230#issuecomment-3020551743 and
692
- https://github.com/octokit/octokit.rb/blob/ea3413c3174571e87c83d358fc893cc7613091fa/lib/octokit/connection.rb#L109-L119
693
- MSG
694
- end
695
-
696
- def test_octo_cache_still_available_on_duration_of_age
697
- WebMock.disable_net_connect!
698
- now = Time.now
699
- age = 60
700
- stub_request(:get, 'https://api.github.com/rate_limit')
701
- .to_return(
702
- status: 200, headers: { 'Content-Type' => 'application/json', 'X-RateLimit-Remaining' => '5000' },
703
- body: { 'rate' => { 'limit' => 5000, 'remaining' => 5000, 'reset' => 1_672_531_200 } }.to_json
704
- )
705
- Dir.mktmpdir do |dir|
706
- sqlite_cache = File.expand_path('t.db', dir)
707
- o = Fbe.octo(loog: fake_loog, global: {}, options: Judges::Options.new({ 'sqlite_cache' => sqlite_cache }))
708
- stub_request(:get, 'https://api.github.com/repositories/798641472').to_return(
709
- status: 200,
710
- body: { id: 798_641_472, name: 'factbase' }.to_json,
711
- headers: {
712
- 'Date' => now.httpdate,
713
- 'Content-Type' => 'application/json; charset=utf-8',
714
- 'Cache-Control' => "public, max-age=#{age}, s-maxage=#{age}",
715
- 'Etag' => 'W/"f5f1ea995fd7266816f681aca5a81f539420c469070a47568bebdaa3055487bc"',
716
- 'Last-Modified' => 'Fri, 04 Jul 2025 13:39:42 GMT'
717
- }
718
- ).times(1).then.to_raise('no more request to /repositories/798641472')
719
- assert_equal('factbase', o.repo(798_641_472)['name'])
720
- Time.stub(:now, now + age - 1) do
721
- assert_equal('factbase', o.repo(798_641_472)['name'])
722
- end
723
- stub_request(:get, 'https://api.github.com/repositories/798641472').to_return(
724
- status: 200,
725
- body: { id: 798_641_472, name: 'factbase_changed' }.to_json,
726
- headers: {
727
- 'Date' => (now + age).httpdate,
728
- 'Content-Type' => 'application/json; charset=utf-8',
729
- 'Cache-Control' => "public, max-age=#{age}, s-maxage=#{age}",
730
- 'Etag' => 'W/"f5f1ea995fd7266816f681aca5a81f539420c469070a47568bebdaa3055487be"',
731
- 'Last-Modified' => 'Fri, 04 Jul 2025 13:39:42 GMT'
732
- }
733
- ).times(1).then.to_raise('no more request to /repositories/798641472')
734
- Time.stub(:now, now + age) do
735
- assert_equal('factbase_changed', o.repo(798_641_472)['name'])
736
- end
737
- Time.stub(:now, now + (2 * age) - 1) do
738
- assert_equal('factbase_changed', o.repo(798_641_472)['name'])
739
- end
740
- end
741
- end
742
-
743
- def test_octo_with_set_sqlite_cache_min_age
744
- WebMock.disable_net_connect!
745
- now = Time.now
746
- stub_request(:get, 'https://api.github.com/rate_limit')
747
- .to_return(
748
- status: 200, headers: { 'Content-Type' => 'application/json', 'X-RateLimit-Remaining' => '5000' },
749
- body: { 'rate' => { 'limit' => 5000, 'remaining' => 5000, 'reset' => 1_672_531_200 } }.to_json
750
- )
751
- Dir.mktmpdir do |dir|
752
- sqlite_cache = File.expand_path('t.db', dir)
753
- options = Judges::Options.new({ 'sqlite_cache' => sqlite_cache, 'sqlite_cache_min_age' => 120 })
754
- o = Fbe.octo(loog: fake_loog, global: {}, options:)
755
- stub_request(:get, 'https://api.github.com/repositories/798641472').to_return(
756
- status: 200,
757
- body: { id: 798_641_472, name: 'factbase' }.to_json,
758
- headers: {
759
- 'Date' => now.httpdate,
760
- 'Content-Type' => 'application/json; charset=utf-8',
761
- 'Cache-Control' => 'public, max-age=60, s-maxage=60',
762
- 'Etag' => 'W/"f5f1ea995fd7266816f681aca5a81f539420c469070a47568bebdaa3055487bc"',
763
- 'Last-Modified' => 'Fri, 04 Jul 2025 13:39:42 GMT'
764
- }
765
- ).times(1).then.to_raise('no more request to /repositories/798641472')
766
- Time.stub(:now, now) do
767
- assert_equal('factbase', o.repo(798_641_472)['name'])
768
- end
769
- Time.stub(:now, now + 50) do
770
- assert_equal('factbase', o.repo(798_641_472)['name'])
771
- end
772
- Time.stub(:now, now + 100) do
773
- assert_equal('factbase', o.repo(798_641_472)['name'])
774
- end
775
- stub_request(:get, 'https://api.github.com/repositories/798641472').to_return(
776
- status: 200,
777
- body: { id: 798_641_472, name: 'factbase_changed' }.to_json,
778
- headers: {
779
- 'Date' => (now + 120).httpdate,
780
- 'Content-Type' => 'application/json; charset=utf-8',
781
- 'Cache-Control' => 'public, max-age=60, s-maxage=60',
782
- 'Etag' => 'W/"f5f1ea995fd7266816f681aca5a81f539420c469070a47568bebdaa3055487bc"',
783
- 'Last-Modified' => 'Fri, 04 Jul 2025 13:39:42 GMT'
784
- }
785
- )
786
- Time.stub(:now, now + 120) do
787
- assert_equal('factbase_changed', o.repo(798_641_472)['name'])
788
- end
789
- end
790
- end
791
- end