fbe 0.48.5 → 0.48.7

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.
@@ -0,0 +1,1797 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2026 Zerocracy
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require 'time'
7
+
8
+ # Fake GitHub client for testing purposes.
9
+ #
10
+ # This class provides mock implementations of Octokit methods for testing.
11
+ # It returns predictable, deterministic data structures that mimic GitHub API
12
+ # responses without making actual API calls. The mock data uses consistent
13
+ # patterns:
14
+ # - IDs are generated from string names using character code sums
15
+ # - Timestamps are random but within recent past
16
+ # - Repository and user data follows GitHub's JSON structure
17
+ #
18
+ # @example Using FakeOctokit in tests
19
+ # client = Fbe::FakeOctokit.new
20
+ # repo = client.repository('octocat/hello-world')
21
+ # puts repo[:full_name] # => "octocat/hello-world"
22
+ # puts repo[:id] # => 1224 (deterministic from name)
23
+ #
24
+ # @note All methods return static or pseudo-random data
25
+ # @note No actual API calls are made
26
+ class Fbe::FakeOctokit # rubocop:disable Metrics/ClassLength
27
+ # Generates a random time in the past.
28
+ #
29
+ # @return [Time] A random time within the last 10,000 seconds
30
+ # @example
31
+ # fake_client = Fbe::FakeOctokit.new
32
+ # time = fake_client.random_time #=> 2024-09-04 12:34:56 -0700
33
+ def random_time
34
+ Time.now - rand(10_000)
35
+ end
36
+
37
+ # Converts a string name to a deterministic integer.
38
+ #
39
+ # @param [String, Integer] name The name to convert or pass through
40
+ # @return [Integer, String] The sum of character codes if input is a string, otherwise the original input
41
+ # @example
42
+ # fake_client = Fbe::FakeOctokit.new
43
+ # fake_client.name_to_number("octocat") #=> 728
44
+ # fake_client.name_to_number(42) #=> 42
45
+ def name_to_number(name)
46
+ return name unless name.is_a?(String)
47
+ name.chars.sum(&:ord)
48
+ end
49
+
50
+ def auto_paginate=(_); end
51
+
52
+ def auto_paginate; end
53
+
54
+ # Returns a mock rate limit object.
55
+ #
56
+ # @return [Object] An object with a remaining method that returns 100
57
+ # @example
58
+ # fake_client = Fbe::FakeOctokit.new
59
+ # fake_client.rate_limit.remaining #=> 100
60
+ def rate_limit
61
+ Veil.new(nil, remaining: 100)
62
+ end
63
+
64
+ alias rate_limit! rate_limit
65
+
66
+ # Lists repositories for a user or organization.
67
+ #
68
+ # @param [String] _user The user/org name (ignored in mock)
69
+ # @return [Array<Hash>] Array of repository hashes
70
+ # @example
71
+ # client.repositories('octocat')
72
+ # # => [{:id=>123, :full_name=>"yegor256/judges", ...}, ...]
73
+ def repositories(_user = nil)
74
+ [
75
+ repository('yegor256/judges'),
76
+ repository('yegor256/factbase')
77
+ ]
78
+ end
79
+
80
+ # Lists organization repositories, including private ones when the token has access.
81
+ #
82
+ # @param [String] _org The organization name (ignored in mock)
83
+ # @param [Hash] _options Filters such as +type:+ (e.g. 'all', 'private', 'public')
84
+ # @return [Array<Hash>] Array of repository hashes
85
+ # @example
86
+ # client.organization_repositories('zerocracy', type: 'all')
87
+ # # => [{:id=>123, :full_name=>"yegor256/judges", ...}, ...]
88
+ def organization_repositories(_org, _options = {})
89
+ [
90
+ repository('yegor256/judges'),
91
+ repository('yegor256/factbase')
92
+ ]
93
+ end
94
+
95
+ # Gets repository invitations for the authenticated user.
96
+ #
97
+ # @param [Hash] _options Additional options (not used in mock)
98
+ # @return [Array<Hash>] Array of invitation objects with repository and inviter information
99
+ # @example
100
+ # fake_client = Fbe::FakeOctokit.new
101
+ # fake_client.user_repository_invitations #=> [{:id=>1, :node_id=>"INV_", ...}]
102
+ def user_repository_invitations(_options = {})
103
+ [
104
+ {
105
+ id: 1,
106
+ node_id: 'INV_kwDOJRF-Hq4B_yXr',
107
+ repository: repository('zerocracy/fbe'),
108
+ invitee: user(526_301),
109
+ inviter: user(888),
110
+ permissions: 'write',
111
+ created_at: random_time,
112
+ url: 'https://api.github.com/user/repository_invitations/1',
113
+ html_url: 'https://github.com/zerocracy/fbe/invitations',
114
+ expired: false
115
+ },
116
+ {
117
+ id: 2,
118
+ node_id: 'INV_kwDOJRF-Hq4B_yXs',
119
+ repository: repository('yegor256/takes'),
120
+ invitee: user(526_301),
121
+ inviter: user(888),
122
+ permissions: 'admin',
123
+ created_at: random_time,
124
+ url: 'https://api.github.com/user/repository_invitations/2',
125
+ html_url: 'https://github.com/yegor256/takes/invitations',
126
+ expired: false
127
+ }
128
+ ]
129
+ end
130
+
131
+ # Gets organization memberships for the authenticated user.
132
+ #
133
+ # @param [Hash] _options Additional options (not used in mock)
134
+ # @return [Array<Hash>] Array of organization membership objects
135
+ # @example
136
+ # fake_client = Fbe::FakeOctokit.new
137
+ # fake_client.organization_memberships #=> [{:url=>"https://api.github.com/orgs/...", ...}]
138
+ def organization_memberships(_options = {})
139
+ [
140
+ {
141
+ url: 'https://api.github.com/orgs/zerocracy/memberships/yegor256',
142
+ state: 'active',
143
+ role: 'admin',
144
+ organization_url: 'https://api.github.com/orgs/zerocracy',
145
+ organization: {
146
+ login: 'zerocracy',
147
+ id: 24_234_201,
148
+ node_id: 'MDEyOk9yZ2FuaXphdGlvbjI0MjM0MjAx',
149
+ url: 'https://api.github.com/orgs/zerocracy',
150
+ avatar_url: 'https://avatars.githubusercontent.com/u/24234201?v=4',
151
+ description: 'AI-managed software development',
152
+ name: 'Zerocracy',
153
+ company: nil,
154
+ blog: 'https://www.zerocracy.com',
155
+ location: nil,
156
+ email: 'team@zerocracy.com',
157
+ twitter_username: nil,
158
+ is_verified: false,
159
+ has_organization_projects: true,
160
+ has_repository_projects: true,
161
+ public_repos: 30,
162
+ public_gists: 0,
163
+ followers: 0,
164
+ following: 0,
165
+ html_url: 'https://github.com/zerocracy',
166
+ created_at: random_time,
167
+ updated_at: random_time,
168
+ type: 'Organization'
169
+ },
170
+ user: user(526_301)
171
+ },
172
+ {
173
+ url: 'https://api.github.com/orgs/objectionary/memberships/yegor256',
174
+ state: 'active',
175
+ role: 'member',
176
+ organization_url: 'https://api.github.com/orgs/objectionary',
177
+ organization: {
178
+ login: 'objectionary',
179
+ id: 80_033_603,
180
+ node_id: 'MDEyOk9yZ2FuaXphdGlvbjgwMDMzNjAz',
181
+ url: 'https://api.github.com/orgs/objectionary',
182
+ avatar_url: 'https://avatars.githubusercontent.com/u/80033603?v=4',
183
+ description: 'EO/EOLANG, an object-oriented language',
184
+ name: 'Objectionary',
185
+ company: nil,
186
+ blog: 'https://www.eolang.org',
187
+ location: nil,
188
+ email: nil,
189
+ twitter_username: nil,
190
+ is_verified: false,
191
+ has_organization_projects: true,
192
+ has_repository_projects: true,
193
+ public_repos: 15,
194
+ public_gists: 0,
195
+ followers: 0,
196
+ following: 0,
197
+ html_url: 'https://github.com/objectionary',
198
+ created_at: random_time,
199
+ updated_at: random_time,
200
+ type: 'Organization'
201
+ },
202
+ user: user(526_301)
203
+ }
204
+ ]
205
+ end
206
+
207
+ # Updates the authenticated user's organization membership.
208
+ #
209
+ # @param [String] org The organization name (e.g., 'zerocracy')
210
+ # @param [Hash] _options Additional options (typically includes :state to update membership state)
211
+ # @return [Hash] Updated membership information
212
+ # @example
213
+ # fake_client = Fbe::FakeOctokit.new
214
+ # fake_client.update_organization_membership('zerocracy', state: 'active')
215
+ def update_organization_membership(org, _options = {})
216
+ {
217
+ url: "https://api.github.com/orgs/#{org}/memberships/yegor256",
218
+ state: 'active',
219
+ role: 'member',
220
+ organization_url: "https://api.github.com/orgs/#{org}",
221
+ organization: {
222
+ login: org,
223
+ id: 24_234_201,
224
+ node_id: 'MDEyOk9yZ2FuaXphdGlvbjI0MjM0MjAx',
225
+ url: "https://api.github.com/orgs/#{org}",
226
+ avatar_url: 'https://avatars.githubusercontent.com/u/24234201?v=4',
227
+ description: 'Organization description',
228
+ name: org.capitalize,
229
+ company: nil,
230
+ blog: "https://www.#{org}.com",
231
+ location: nil,
232
+ email: "team@#{org}.com",
233
+ twitter_username: nil,
234
+ is_verified: false,
235
+ has_organization_projects: true,
236
+ has_repository_projects: true,
237
+ public_repos: 30,
238
+ public_gists: 0,
239
+ followers: 0,
240
+ following: 0,
241
+ html_url: "https://github.com/#{org}",
242
+ created_at: random_time,
243
+ updated_at: random_time,
244
+ type: 'Organization'
245
+ },
246
+ user: user(526_301)
247
+ }
248
+ end
249
+
250
+ # Removes a user from an organization.
251
+ #
252
+ # @param [String] _org The organization name (e.g., 'zerocracy')
253
+ # @param [String] _user The user login (not used in this mock implementation)
254
+ # @return [Boolean] Returns true when successful (204 No Content in actual API)
255
+ # @example
256
+ # fake_client = Fbe::FakeOctokit.new
257
+ # fake_client.remove_organization_membership('zerocracy') #=> true
258
+ # rubocop:disable Naming/PredicateMethod
259
+ def remove_organization_membership(_org, _user = nil)
260
+ true
261
+ end
262
+ # rubocop:enable Naming/PredicateMethod
263
+
264
+ # Accepts a repository invitation.
265
+ #
266
+ # @param [Integer] id The invitation ID
267
+ # @return [Boolean] Returns true when successful (204 No Content in actual API)
268
+ # @example
269
+ # fake_client = Fbe::FakeOctokit.new
270
+ # fake_client.accept_repository_invitation(1) #=> true
271
+ # rubocop:disable Naming/PredicateMethod
272
+ def accept_repository_invitation(id)
273
+ raise(Octokit::NotFound) if id == 404_000
274
+ true
275
+ end
276
+ # rubocop:enable Naming/PredicateMethod
277
+
278
+ # Gives a star to a repository.
279
+ #
280
+ # @param [String] _repo The repository name (e.g., 'user/repo')
281
+ # @return [Boolean] Always returns true
282
+ # @example
283
+ # fake_client = Fbe::FakeOctokit.new
284
+ # fake_client.star('octocat/Hello-World') #=> true
285
+ # rubocop:disable Naming/PredicateMethod
286
+ def star(_repo)
287
+ true
288
+ end
289
+ # rubocop:enable Naming/PredicateMethod
290
+
291
+ # Gets details of a GitHub user.
292
+ #
293
+ # @param [String, Integer] uid The login of the user or its numeric ID
294
+ # @return [Hash] User information including id, login, and type
295
+ # @example
296
+ # fake_client = Fbe::FakeOctokit.new
297
+ # fake_client.user(526_301) #=> {:id=>444, :login=>"yegor256", :type=>"User"}
298
+ # fake_client.user('octocat') #=> {:id=>444, :login=>nil, :type=>"User"}
299
+ def user(uid)
300
+ raise(Octokit::NotFound) if [404_001, 404_002].include?(uid)
301
+ login = (uid == 526_301 ? 'yegor256' : 'torvalds') if uid.is_a?(Integer)
302
+ {
303
+ id: 444,
304
+ login:,
305
+ type: uid == 29_139_614 ? 'Bot' : 'User'
306
+ }
307
+ end
308
+
309
+ # Gets workflow runs for a repository.
310
+ #
311
+ # @param [String] repo The repository name
312
+ # @param [Hash] _opts Additional options (not used in mock)
313
+ # @return [Hash] Information about workflow runs including counts and details
314
+ # @example
315
+ # fake_client = Fbe::FakeOctokit.new
316
+ # result = fake_client.repository_workflow_runs('octocat/Hello-World')
317
+ # result[:total_count] #=> 2
318
+ def repository_workflow_runs(repo, _opts = {})
319
+ {
320
+ total_count: 2,
321
+ workflow_runs: [
322
+ workflow_run(repo, 42),
323
+ workflow_run(repo, 7)
324
+ ]
325
+ }
326
+ end
327
+
328
+ # Gets usage information for a specific workflow run.
329
+ #
330
+ # @param [String] _repo The repository name
331
+ # @param [Integer] _id The workflow run ID
332
+ # @return [Hash] Billing and usage information for the workflow run
333
+ # @example
334
+ # fake_client = Fbe::FakeOctokit.new
335
+ # usage = fake_client.workflow_run_usage('octocat/Hello-World', 42)
336
+ # usage[:run_duration_ms] #=> 53000
337
+ def workflow_run_usage(_repo, _id)
338
+ {
339
+ billable: {
340
+ UBUNTU: {
341
+ total_ms: 0,
342
+ jobs: 1,
343
+ job_runs: [
344
+ {
345
+ job_id: 1,
346
+ duration_ms: 0
347
+ }
348
+ ]
349
+ }
350
+ },
351
+ run_duration_ms: 53_000
352
+ }
353
+ end
354
+
355
+ # Lists releases for a repository.
356
+ #
357
+ # @param [String] _repo Repository name (ignored in mock)
358
+ # @param [Hash] _opts Options hash (ignored in mock)
359
+ # @return [Array<Hash>] Array of release hashes
360
+ # @example
361
+ # client.releases('octocat/Hello-World')
362
+ # # => [{:tag_name=>"0.19.0", :name=>"just a fake name", ...}, ...]
363
+ def releases(_repo, _opts = {})
364
+ [
365
+ release('https://github...'),
366
+ release('https://gith')
367
+ ]
368
+ end
369
+
370
+ # Gets a single release.
371
+ #
372
+ # @param [String] _url Release URL (ignored in mock)
373
+ # @return [Hash] Release information
374
+ # @example
375
+ # client.release('https://api.github.com/repos/octocat/Hello-World/releases/1')
376
+ # # => {:tag_name=>"0.19.0", :name=>"just a fake name", ...}
377
+ def release(_url)
378
+ {
379
+ node_id: 'RE_kwDOL6GCO84J7Cen',
380
+ tag_name: '0.19.0',
381
+ target_commitish: 'master',
382
+ name: 'just a fake name',
383
+ draft: false,
384
+ prerelease: false,
385
+ created_at: random_time,
386
+ published_at: random_time,
387
+ assets: []
388
+ }
389
+ end
390
+
391
+ # Gets repository information.
392
+ #
393
+ # @param [String, Integer] name Repository name ('owner/repo') or ID
394
+ # @return [Hash] Repository information
395
+ # @raise [Octokit::NotFound] If name is 404123 or 404124 (for testing)
396
+ # @example
397
+ # client.repository('octocat/Hello-World')
398
+ # # => {:id=>1296269, :full_name=>"octocat/Hello-World", ...}
399
+ def repository(name)
400
+ raise(Octokit::NotFound) if [404_123, 404_124].include?(name)
401
+ repo = name.is_a?(Integer) ? 'yegor256/test' : name
402
+ repo = 'zerocracy/baza' if name == 1439
403
+ repo = 'foo/bazz' if name == 810
404
+ {
405
+ id: name_to_number(name),
406
+ full_name: repo,
407
+ default_branch: 'master',
408
+ private: false,
409
+ owner: { login: name.to_s.split('/')[0], id: 526_301, site_admin: false },
410
+ html_url: "https://github.com/#{name}",
411
+ description: 'something',
412
+ fork: false,
413
+ url: "https://github.com/#{name}",
414
+ created_at: random_time,
415
+ updated_at: random_time,
416
+ pushed_at: random_time,
417
+ size: name == 'yegor256/empty-repo' ? 0 : 470,
418
+ stargazers_count: 1,
419
+ watchers_count: 1,
420
+ language: 'Ruby',
421
+ has_issues: true,
422
+ has_projects: true,
423
+ has_downloads: true,
424
+ has_wiki: true,
425
+ has_pages: false,
426
+ has_discussions: false,
427
+ forks_count: 0,
428
+ archived: name == 'zerocracy/datum',
429
+ disabled: false,
430
+ open_issues_count: 6,
431
+ license: { key: 'mit', name: 'MIT License' },
432
+ allow_forking: true,
433
+ is_template: false,
434
+ visibility: 'public',
435
+ forks: 0,
436
+ open_issues: 6,
437
+ watchers: 1
438
+ }
439
+ end
440
+
441
+ # Lists pull requests associated with a commit.
442
+ #
443
+ # @param [String] repo Repository name ('owner/repo')
444
+ # @param [String] _sha Commit SHA (ignored in mock)
445
+ # @return [Array<Hash>] Array of pull request hashes
446
+ # @example
447
+ # client.commit_pulls('octocat/Hello-World', 'abc123')
448
+ # # => [{:number=>42, :state=>"open", ...}]
449
+ def commit_pulls(repo, _sha)
450
+ [
451
+ pull_request(repo, 42)
452
+ ]
453
+ end
454
+
455
+ # Lists issues for a repository.
456
+ #
457
+ # @param [String] repo Repository name ('owner/repo')
458
+ # @param [Hash] _options Query options (ignored in mock)
459
+ # @return [Array<Hash>] Array of issue hashes
460
+ # @example
461
+ # client.list_issues('octocat/Hello-World', state: 'open')
462
+ # # => [{:number=>42, :title=>"Found a bug", ...}, ...]
463
+ def list_issues(repo, _options = {})
464
+ [
465
+ issue(repo, 42),
466
+ issue(repo, 43)
467
+ ].tap do |list|
468
+ list.prepend(issue(repo, 144)) if repo == 'foo/bazz'
469
+ end
470
+ end
471
+
472
+ # Gets a single issue.
473
+ #
474
+ # @param [String] repo Repository name ('owner/repo')
475
+ # @param [Integer] number Issue number
476
+ # @return [Hash] Issue information
477
+ # @example
478
+ # client.issue('octocat/Hello-World', 42)
479
+ # # => {:id=>42, :number=>42, :created_at=>...}
480
+ def issue(repo, number)
481
+ case number
482
+ when 94
483
+ {
484
+ id: 42,
485
+ number:,
486
+ repo: {
487
+ full_name: repo
488
+ },
489
+ pull_request: {
490
+ merged_at: nil
491
+ },
492
+ created_at: Time.parse('2024-09-20 19:00:00 UTC')
493
+ }
494
+ when 142
495
+ {
496
+ id: 655,
497
+ number:,
498
+ repo: { full_name: repo },
499
+ user: { login: 'yegor256', id: 526_301, type: 'User' },
500
+ state: 'closed',
501
+ created_at: Time.parse('2025-06-01 12:00:55 UTC'),
502
+ updated_at: Time.parse('2025-06-01 15:47:18 UTC'),
503
+ closed_at: Time.parse('2025-06-02 15:00:00 UTC'),
504
+ closed_by: { id: 526_301, login: 'yegor256' }
505
+ }
506
+ when 143
507
+ {
508
+ id: 656,
509
+ number:,
510
+ repo: { full_name: repo },
511
+ user: { login: 'yegor256', id: 526_301, type: 'User' },
512
+ pull_request: { merged_at: nil },
513
+ state: 'closed',
514
+ created_at: Time.parse('2025-05-29 17:00:55 UTC'),
515
+ updated_at: Time.parse('2025-05-29 19:00:00 UTC'),
516
+ closed_at: Time.parse('2025-06-01 18:20:00 UTC'),
517
+ closed_by: { id: 526_301, login: 'yegor256' }
518
+ }
519
+ when 144
520
+ {
521
+ id: 657,
522
+ number:,
523
+ repo: { full_name: repo },
524
+ user: { login: 'yegor256', id: 526_301, type: 'User' },
525
+ pull_request: { merged_at: nil },
526
+ created_at: Time.parse('2025-05-29 17:00:55 UTC')
527
+ }
528
+ else
529
+ {
530
+ id: 42,
531
+ number:,
532
+ repo: {
533
+ full_name: repo
534
+ },
535
+ user: { login: 'yegor256', id: 526_301, type: 'User' },
536
+ pull_request: {
537
+ merged_at: nil
538
+ },
539
+ created_at: Time.parse('2024-09-20 19:00:00 UTC')
540
+ }
541
+ end
542
+ end
543
+
544
+ # Gets a single pull request.
545
+ #
546
+ # @param [String] repo Repository name ('owner/repo')
547
+ # @param [Integer] number Pull request number
548
+ # @return [Hash] Pull request information
549
+ # @example
550
+ # client.pull_request('octocat/Hello-World', 1)
551
+ # # => {:id=>42, :number=>1, :additions=>12, ...}
552
+ def pull_request(repo, number)
553
+ if number == 29
554
+ {
555
+ id: 42,
556
+ number:,
557
+ user: { id: 421, login: 'user' },
558
+ created_at: Time.parse('2024-08-20 15:35:30 UTC'),
559
+ additions: 12,
560
+ deletions: 5
561
+ }
562
+ elsif number == 172
563
+ {
564
+ id: 1_990_323_142,
565
+ number: 172,
566
+ url: 'https://api.github.com/repos/yegor256/judges/pulls/93',
567
+ node_id: 'PR_kwDOL6GCO852oevG',
568
+ state: 'closed',
569
+ locked: false,
570
+ title: '#999 new feature',
571
+ user: {
572
+ login: 'test',
573
+ id: 88_084_038,
574
+ node_id: 'MDQ6VXNlcjE2NDYwMjA=',
575
+ type: 'User',
576
+ site_admin: false
577
+ },
578
+ base: {
579
+ ref: 'master',
580
+ sha: '125f234967de0f690805c6943e78db42a294c1a',
581
+ repo: { id: repo, name: 'judges' }
582
+ },
583
+ head: {
584
+ ref: 'zerocracy/judges',
585
+ sha: '74d0c234967de0f690805c6943e78db42a294c1a'
586
+ },
587
+ merged_at: Time.now,
588
+ comments: 2,
589
+ review_comments: 2,
590
+ commits: 1,
591
+ additions: 3,
592
+ deletions: 3,
593
+ changed_files: 2
594
+ }
595
+ else
596
+ {
597
+ id: 42,
598
+ number:,
599
+ repo: {
600
+ full_name: repo
601
+ },
602
+ base: {
603
+ repo: {
604
+ full_name: repo
605
+ }
606
+ },
607
+ state: 'closed',
608
+ user: { login: 'yegor256', id: 526_301, type: 'User' },
609
+ head: { ref: 'master', sha: '6dcb09b5b57875f334f61aebed695e2e4193db5e' },
610
+ additions: 12,
611
+ deletions: 5,
612
+ changed_files: 3,
613
+ comments: 2,
614
+ review_comments: 2,
615
+ closed_at: Time.parse('2024-12-20'),
616
+ merged_at: Time.parse('2024-12-20'),
617
+ created_at: Time.parse('2024-09-20')
618
+ }
619
+ end
620
+ end
621
+
622
+ # Lists pull requests for a repository.
623
+ #
624
+ # @param [String] _repo Repository name (ignored in mock)
625
+ # @param [Hash] _options Query options (ignored in mock)
626
+ # @return [Array<Hash>] Array of pull request hashes
627
+ # @example
628
+ # client.pull_requests('octocat/Hello-World', state: 'open')
629
+ # # => [{:number=>100, :state=>"closed", :title=>"#90: some title", ...}]
630
+ def pull_requests(_repo, _options = {})
631
+ [
632
+ {
633
+ id: 2_072_543_250,
634
+ number: 100,
635
+ state: 'closed',
636
+ locked: false,
637
+ title: '#90: some title',
638
+ user: { login: 'yegor256', id: 526_301, type: 'User' },
639
+ body: 'Closes #90',
640
+ created_at: Time.parse('2024-09-15 09:32:49 UTC'),
641
+ updated_at: Time.parse('2024-09-15 10:06:23 UTC'),
642
+ closed_at: Time.parse('2024-09-15 10:05:34 UTC'),
643
+ merged_at: Time.parse('2024-09-15 10:05:34 UTC'),
644
+ merge_commit_sha: '0527cc188b0495e',
645
+ draft: false,
646
+ head: {
647
+ label: 'yegor256:90',
648
+ ref: '90',
649
+ sha: '0527cc188b049',
650
+ user: { login: 'yegor256', id: 526_301, type: 'User' },
651
+ repo: repository('yegor256/repo')
652
+ },
653
+ base: {
654
+ label: 'zerocracy:master',
655
+ ref: 'master',
656
+ sha: '4643eb3c7a0ccb3c',
657
+ user: { login: 'zerocracy', id: 24_234_201, type: 'Organization' },
658
+ repo: repository('zerocracy/repo')
659
+ }
660
+ },
661
+ {
662
+ id: 2_072_543_240,
663
+ number: 95,
664
+ state: 'open',
665
+ locked: false,
666
+ title: '#80: some title',
667
+ user: { login: 'yegor256', id: 526_301, type: 'User' },
668
+ body: 'Closes #80',
669
+ created_at: Time.parse('2024-09-14 09:32:49 UTC'),
670
+ updated_at: Time.parse('2024-09-14 10:06:23 UTC'),
671
+ closed_at: nil,
672
+ merged_at: nil,
673
+ merge_commit_sha: '0627cc188b0497e',
674
+ draft: false,
675
+ head: {
676
+ label: 'yegor256:80',
677
+ ref: '80',
678
+ sha: '1527cc188b040',
679
+ user: { login: 'yegor256', id: 526_301, type: 'User' },
680
+ repo: repository('yegor256/repo')
681
+ },
682
+ base: {
683
+ label: 'zerocracy:master',
684
+ ref: 'master',
685
+ sha: '5643eb3c7a0ccb3b',
686
+ user: { login: 'zerocracy', id: 24_234_201, type: 'Organization' },
687
+ repo: repository('zerocracy/repo')
688
+ }
689
+ }
690
+ ]
691
+ end
692
+
693
+ def pull_request_reviews(_repo, _number)
694
+ [
695
+ {
696
+ id: 22_449_327,
697
+ user: { login: 'yegor256', id: 526_301, type: 'User' },
698
+ body: 'Some text 2',
699
+ state: 'CHANGES_REQUESTED',
700
+ author_association: 'CONTRIBUTOR',
701
+ submitted_at: Time.parse('2024-08-22 10:00:00 UTC'),
702
+ commit_id: 'b15c2893f1b5453'
703
+ },
704
+ {
705
+ id: 22_449_326,
706
+ user: { login: 'yegor256', id: 526_301, type: 'User' },
707
+ body: 'Some text 1',
708
+ state: 'CHANGES_REQUESTED',
709
+ author_association: 'CONTRIBUTOR',
710
+ submitted_at: Time.parse('2024-08-21 22:00:00 UTC'),
711
+ commit_id: 'a15c2893f1b5453'
712
+ }
713
+ ]
714
+ end
715
+
716
+ def pull_request_review_comments(_repo, _number, _review, _options = {})
717
+ [
718
+ { id: 22_447_120, user: { login: 'yegor256', id: 526_301, type: 'User' } },
719
+ { id: 22_447_121, user: { login: 'yegor256', id: 526_301, type: 'User' } }
720
+ ]
721
+ end
722
+
723
+ def review_comments(_repo, _number)
724
+ [
725
+ {
726
+ pull_request_review_id: 22_687_249,
727
+ id: 17_361_949,
728
+ body: 'Some comment 1',
729
+ user: { login: 'yegor256', id: 526_301, type: 'User' },
730
+ created_at: Time.parse('2024-09-05 15:31:06 UTC'),
731
+ updated_at: Time.parse('2024-09-05 15:33:04 UTC')
732
+ },
733
+ {
734
+ pull_request_review_id: 22_687_503,
735
+ id: 17_361_950,
736
+ body: 'Some comment 2',
737
+ user: { login: 'yegor256', id: 526_301, type: 'User' },
738
+ created_at: Time.parse('2024-09-06 14:20:00 UTC'),
739
+ updated_at: Time.parse('2024-09-06 14:20:50 UTC')
740
+ },
741
+ {
742
+ pull_request_review_id: 22_687_255,
743
+ id: 17_361_970,
744
+ body: 'Some comment 3',
745
+ user: { login: 'yegor256', id: 526_301, type: 'User' },
746
+ created_at: Time.parse('2024-09-06 20:45:30 UTC'),
747
+ updated_at: Time.parse('2024-09-06 20:45:30 UTC')
748
+ }
749
+ ]
750
+ end
751
+
752
+ def add_comment(_repo, _issue, _text)
753
+ {
754
+ id: 42
755
+ }
756
+ end
757
+
758
+ def create_commit_comment(_repo, sha, text)
759
+ {
760
+ commit_id: sha,
761
+ id: 42,
762
+ body: text,
763
+ path: 'something.txt',
764
+ line: 1,
765
+ position: 1
766
+ }
767
+ end
768
+
769
+ def search_issues(query, _options = {})
770
+ if query.include?('type:pr') && query.include?('is:unmerged')
771
+ {
772
+ total_count: 1,
773
+ incomplete_results: false,
774
+ items: [
775
+ {
776
+ id: 42,
777
+ number: 10,
778
+ title: 'Awesome PR 10'
779
+ }
780
+ ]
781
+ }
782
+ elsif query.include?('type:pr') && query.include?('is:merged')
783
+ {
784
+ total_count: 1,
785
+ incomplete_results: false,
786
+ items: [
787
+ {
788
+ id: 42,
789
+ number: 10,
790
+ title: 'Awesome PR 10',
791
+ created_at: Time.parse('2024-08-21 19:00:00 UTC'),
792
+ pull_request: { merged_at: Time.parse('2024-08-23 19:00:00 UTC') }
793
+ }
794
+ ]
795
+ }
796
+ elsif query.include?('type:pr')
797
+ {
798
+ total_count: 2,
799
+ incomplete_results: false,
800
+ items: [
801
+ {
802
+ id: 42,
803
+ number: 10,
804
+ title: 'Awesome PR 10',
805
+ created_at: Time.parse('2024-08-21 19:00:00 UTC')
806
+ },
807
+ {
808
+ id: 43,
809
+ number: 11,
810
+ title: 'Awesome PR 11',
811
+ created_at: Time.parse('2024-08-21 20:00:00 UTC')
812
+ }
813
+ ]
814
+ }
815
+ else
816
+ {
817
+ total_count: 1,
818
+ incomplete_results: false,
819
+ items: [
820
+ {
821
+ number: 42,
822
+ labels: [
823
+ {
824
+ name: 'bug'
825
+ }
826
+ ],
827
+ user: { login: 'yegor256', id: 526_301, type: 'User' },
828
+ created_at: Time.parse('2024-08-20 19:00:00 UTC')
829
+ }
830
+ ]
831
+ }
832
+ end
833
+ end
834
+
835
+ def commits_since(repo, _since)
836
+ [
837
+ commit(repo, 'a1b2c3d4e5f6a1b2c3d4e5f6'),
838
+ commit(repo, 'a1b2c3d4e5fff1b2c3d4e5f6')
839
+ ]
840
+ end
841
+
842
+ def commit(_repo, sha)
843
+ {
844
+ sha:,
845
+ stats: {
846
+ total: 123
847
+ }
848
+ }
849
+ end
850
+
851
+ def search_commits(_query, _options = {})
852
+ {
853
+ total_count: 3,
854
+ incomplete_results: false,
855
+ items: [
856
+ {
857
+ commit: {
858
+ author: { name: 'Yegor', email: 'yegor@gmail.com', date: Time.parse('2024-09-15 12:23:25 UTC') },
859
+ committer: { name: 'Yegor', email: 'yegor@gmail.com', date: Time.parse('2024-09-15 12:23:25 UTC') },
860
+ message: 'Some text',
861
+ tree: { sha: '6e04579960bf67610d' },
862
+ comment_count: 0
863
+ },
864
+ author: { login: 'yegor256', id: 526_301, type: 'User', site_admin: false },
865
+ committer: { login: 'yegor256', id: 526_301, type: 'User', site_admin: false },
866
+ parents: [{ sha: '60cff20bdb66' }],
867
+ repository: {
868
+ id: 799_177_290, name: 'judges-action', full_name: 'zerocracy/judges-action',
869
+ owner: { login: 'zerocracy', id: 24_234_201, type: 'Organization', site_admin: false }
870
+ }
871
+ },
872
+ {
873
+ commit: {
874
+ author: { name: 'Yegor', email: 'yegor2@gmail.com', date: Time.parse('2024-09-14 12:23:25 UTC') },
875
+ committer: { name: 'Yegor', email: 'yegor2@gmail.com', date: Time.parse('2024-09-14 12:23:25 UTC') },
876
+ message: 'Some text 2',
877
+ tree: { sha: 'defa18e4e2250987' },
878
+ comment_count: 0
879
+ },
880
+ author: { login: 'yegor257', id: 526_302, type: 'User', site_admin: false },
881
+ committer: { login: 'yegor257', id: 526_302, type: 'User', site_admin: false },
882
+ parents: [{ sha: 'a04c15bb34fddbba' }],
883
+ repository: {
884
+ id: 799_177_290, name: 'judges-action', full_name: 'zerocracy/judges-action',
885
+ owner: { login: 'zerocracy', id: 24_234_201, type: 'Organization', site_admin: false }
886
+ }
887
+ },
888
+ {
889
+ commit: {
890
+ author: { name: 'Yegor', email: 'yegor3@gmail.com', date: Time.parse('2024-09-13 12:23:25 UTC') },
891
+ committer: { name: 'Yegor', email: 'yegor3@gmail.com', date: Time.parse('2024-09-13 12:23:25 UTC') },
892
+ message: 'Some text 3',
893
+ tree: { sha: 'bb7277441139739b902a' },
894
+ comment_count: 0
895
+ },
896
+ author: { login: 'yegor258', id: 526_303, type: 'User', site_admin: false },
897
+ committer: { login: 'yegor258', id: 526_303, type: 'User', site_admin: false },
898
+ parents: [{ sha: '18db84d469bb727' }],
899
+ repository: {
900
+ id: 799_177_290, name: 'judges-action', full_name: 'zerocracy/judges-action',
901
+ owner: { login: 'zerocracy', id: 24_234_201, type: 'Organization', site_admin: false }
902
+ }
903
+ }
904
+ ]
905
+ }
906
+ end
907
+
908
+ def issue_timeline(_repo, _issue, _options = {})
909
+ [
910
+ {
911
+ event: 'renamed',
912
+ actor: {
913
+ id: 888,
914
+ login: 'torvalds'
915
+ },
916
+ repository: {
917
+ id: name_to_number('yegor256/judges'),
918
+ full_name: 'yegor256/judges'
919
+ },
920
+ rename: {
921
+ from: 'before',
922
+ to: 'after'
923
+ },
924
+ created_at: random_time
925
+ },
926
+ {
927
+ event: 'labeled',
928
+ actor: {
929
+ id: 888,
930
+ login: 'torvalds'
931
+ },
932
+ repository: {
933
+ id: name_to_number('yegor256/judges'),
934
+ full_name: 'yegor256/judges'
935
+ },
936
+ label: {
937
+ name: 'bug'
938
+ },
939
+ created_at: random_time
940
+ },
941
+ {
942
+ node_id: 'ITAE_examplevq862Ga8lzwAAAAQZanzv',
943
+ event: 'issue_type_added',
944
+ actor: {
945
+ id: 526_301,
946
+ login: 'yegor256'
947
+ },
948
+ repository: {
949
+ id: name_to_number('yegor256/judges'),
950
+ full_name: 'yegor256/judges'
951
+ },
952
+ created_at: random_time
953
+ },
954
+ {
955
+ node_id: 'ITCE_examplevq862Ga8lzwAAAAQZbq9S',
956
+ event: 'issue_type_changed',
957
+ actor: {
958
+ id: 526_301,
959
+ login: 'yegor256'
960
+ },
961
+ repository: {
962
+ id: name_to_number('yegor256/judges'),
963
+ full_name: 'yegor256/judges'
964
+ },
965
+ created_at: random_time
966
+ }
967
+ ]
968
+ end
969
+
970
+ def repository_events(repo, _options = {}) # rubocop:disable Metrics/MethodLength
971
+ [
972
+ {
973
+ id: '123',
974
+ type: 'PushEvent',
975
+ repo: {
976
+ id: name_to_number(repo),
977
+ name: repo,
978
+ url: "https://api.github.com/repos/#{repo}"
979
+ },
980
+ payload: {
981
+ push_id: 42,
982
+ ref: 'refs/heads/master',
983
+ head: 'b7089c51cc2526a0d2619d35379f921d53c72731',
984
+ before: '12d3bff1a55bad50ee2e8f29ade7f1c1e07bb025'
985
+ },
986
+ actor: {
987
+ id: 888,
988
+ login: 'torvalds',
989
+ display_login: 'torvalds'
990
+ },
991
+ created_at: random_time,
992
+ public: true
993
+ },
994
+ {
995
+ id: '124',
996
+ type: 'IssuesEvent',
997
+ repo: {
998
+ id: name_to_number(repo),
999
+ name: repo,
1000
+ url: "https://api.github.com/repos/#{repo}"
1001
+ },
1002
+ payload: {
1003
+ action: 'closed',
1004
+ issue: {
1005
+ number: 42
1006
+ }
1007
+ },
1008
+ actor: {
1009
+ id: 888,
1010
+ login: 'torvalds',
1011
+ display_login: 'torvalds'
1012
+ },
1013
+ created_at: random_time,
1014
+ public: true
1015
+ },
1016
+ {
1017
+ id: '125',
1018
+ type: 'IssuesEvent',
1019
+ repo: {
1020
+ id: name_to_number(repo),
1021
+ name: repo,
1022
+ url: "https://api.github.com/repos/#{repo}"
1023
+ },
1024
+ payload: {
1025
+ action: 'opened',
1026
+ issue: {
1027
+ number: 42
1028
+ }
1029
+ },
1030
+ actor: {
1031
+ id: 888,
1032
+ login: 'torvalds',
1033
+ display_login: 'torvalds'
1034
+ },
1035
+ created_at: random_time,
1036
+ public: true
1037
+ },
1038
+ {
1039
+ id: 42,
1040
+ created_at: Time.now,
1041
+ actor: { id: 42 },
1042
+ type: 'PullRequestEvent',
1043
+ repo: { id: repo },
1044
+ payload: {
1045
+ action: 'closed',
1046
+ number: 172,
1047
+ pull_request: {
1048
+ url: 'https://api.github.com/repos/yegor256/judges/pulls/93',
1049
+ id: 1_990_323_142,
1050
+ number: 172,
1051
+ base: {
1052
+ ref: 'master',
1053
+ sha: '93fe488b9967de0f690805c6943e78db42a294c1a',
1054
+ repo: {
1055
+ id: repo,
1056
+ name: 'baza'
1057
+ }
1058
+ },
1059
+ head: {
1060
+ ref: 'zerocracy/baza',
1061
+ sha: '74d0c234967de0f690805c6943e78db42a294c1a'
1062
+ }
1063
+ }
1064
+ }
1065
+ },
1066
+ {
1067
+ id: 43,
1068
+ created_at: Time.now,
1069
+ actor: { id: 42 },
1070
+ type: 'PullRequestEvent',
1071
+ repo: { id: repo },
1072
+ payload: {
1073
+ action: 'closed',
1074
+ number: 172,
1075
+ pull_request: {
1076
+ url: 'https://api.github.com/repos/yegor256/judges/pulls/93',
1077
+ id: 1_990_323_142,
1078
+ number: 172,
1079
+ base: {
1080
+ ref: 'master',
1081
+ sha: '125f234967de0f690805c6943e78db42a294c1a',
1082
+ repo: {
1083
+ id: repo,
1084
+ name: 'judges-action'
1085
+ }
1086
+ },
1087
+ head: {
1088
+ ref: 'zerocracy/judges-action',
1089
+ sha: '74d0c234967de0f690805c6943e78db42a294c1a'
1090
+ }
1091
+ }
1092
+ }
1093
+ }
1094
+ ]
1095
+ end
1096
+
1097
+ def issue_events(_repo, number)
1098
+ if number == 120
1099
+ [
1100
+ {
1101
+ id: 1010, actor: { login: 'user2', id: 422, type: 'User' },
1102
+ event: 'assigned', created_at: Time.parse('2025-10-27 14:00:00 UTC'),
1103
+ assignee: { login: 'user2', id: 422, type: 'User' },
1104
+ assigner: { login: 'user', id: 411, type: 'User' }
1105
+ },
1106
+ {
1107
+ id: 1011, actor: { login: 'user2', id: 422, type: 'User' },
1108
+ event: 'unassigned', created_at: Time.parse('2025-10-27 15:00:00 UTC'),
1109
+ assignee: { login: 'user2', id: 422, type: 'User' },
1110
+ assigner: { login: 'user', id: 411, type: 'User' }
1111
+ }
1112
+ ]
1113
+ else
1114
+ [
1115
+ {
1116
+ id: 126, actor: { login: 'user', id: 411, type: 'User' },
1117
+ event: 'labeled', created_at: Time.parse('2025-05-30 14:41:00 UTC'),
1118
+ label: { name: 'bug', color: 'd73a4a' }
1119
+ },
1120
+ {
1121
+ id: 206, actor: { login: 'user', id: 411, type: 'User' },
1122
+ event: 'mentioned', created_at: Time.parse('2025-05-30 14:41:10 UTC')
1123
+ },
1124
+ {
1125
+ id: 339, actor: { login: 'user2', id: 422, type: 'User' },
1126
+ event: 'subscribed', created_at: Time.parse('2025-05-30 14:41:10 UTC')
1127
+ },
1128
+ {
1129
+ id: 490, actor: { login: 'github-actions[bot]', id: 41_898_282, type: 'Bot' },
1130
+ event: 'renamed', created_at: Time.parse('2025-05-30 14:41:30 UTC'),
1131
+ rename: { from: 'some title', to: 'some title 2' }
1132
+ },
1133
+ {
1134
+ id: 505, actor: { login: 'user', id: 411, type: 'User' },
1135
+ event: 'subscribed', created_at: Time.parse('2025-05-30 16:18:24 UTC')
1136
+ },
1137
+ {
1138
+ id: 608, actor: { login: 'user2', id: 422, type: 'User', test: 123 },
1139
+ event: 'assigned', created_at: Time.parse('2025-05-30 17:59:08 UTC'),
1140
+ assignee: { login: 'user2', id: 422, type: 'User' },
1141
+ assigner: { login: 'user', id: 411, type: 'User' }
1142
+ },
1143
+ {
1144
+ id: 776, actor: { login: 'user2', id: 422, type: 'User' },
1145
+ event: 'referenced', commit_id: '4621af032170f43d',
1146
+ commit_url: 'https://api.github.com/repos/foo/foo/commits/4621af032170f43d',
1147
+ created_at: Time.parse('2025-05-30 19:57:50 UTC')
1148
+ }
1149
+ ]
1150
+ end
1151
+ end
1152
+
1153
+ def pull_request_comments(_name, _number)
1154
+ [
1155
+ {
1156
+ pull_request_review_id: 2_227_372_510,
1157
+ id: 1_709_082_318,
1158
+ path: 'test/baza/test_locks.rb',
1159
+ commit_id: 'a9f5f94cf28f29a64d5dd96d0ee23b4174572847',
1160
+ original_commit_id: 'e8c6f94274d14ed3cb26fe71467a9c3f229df59c',
1161
+ user: {
1162
+ login: 'Reviewer',
1163
+ id: 2_566_462
1164
+ },
1165
+ body: 'Most likely, parentheses were missed here.',
1166
+ created_at: '2024-08-08T09:41:46Z',
1167
+ updated_at: '2024-08-08T09:42:46Z',
1168
+ reactions: {
1169
+ url: 'https://api.github.com/repos/zerocracy/baza/pulls/comments/1709082318/reactions',
1170
+ total_count: 0
1171
+ },
1172
+ start_line: 'null',
1173
+ original_start_line: 'null',
1174
+ start_side: 'null',
1175
+ line: 'null',
1176
+ original_line: 62,
1177
+ side: 'RIGHT',
1178
+ original_position: 25,
1179
+ position: 'null',
1180
+ subject_type: 'line'
1181
+ },
1182
+ {
1183
+ pull_request_review_id: 2_227_372_510,
1184
+ id: 1_709_082_319,
1185
+ path: 'test/baza/test_locks.rb',
1186
+ commit_id: 'a9f5f94cf28f29a64d5dd96d0ee23b4174572847',
1187
+ original_commit_id: 'e8c6f94274d14ed3cb26fe71467a9c3f229df59c',
1188
+ user: {
1189
+ login: 'test',
1190
+ id: 88_084_038
1191
+ },
1192
+ body: 'definitely a typo',
1193
+ created_at: '2024-08-08T09:42:46Z',
1194
+ updated_at: '2024-08-08T09:42:46Z',
1195
+ reactions: {
1196
+ url: 'https://api.github.com/repos/zerocracy/baza/pulls/comments/1709082319/reactions',
1197
+ total_count: 0
1198
+ },
1199
+ start_line: 'null',
1200
+ original_start_line: 'null',
1201
+ start_side: 'null',
1202
+ line: 'null',
1203
+ original_line: 62,
1204
+ side: 'RIGHT',
1205
+ original_position: 25,
1206
+ in_reply_to_id: 1_709_082_318,
1207
+ position: 'null',
1208
+ subject_type: 'line'
1209
+ }
1210
+ ]
1211
+ end
1212
+
1213
+ def issue_comments(_name, _number)
1214
+ [
1215
+ {
1216
+ pull_request_review_id: 2_227_372_510,
1217
+ id: 1_709_082_320,
1218
+ path: 'test/baza/test_locks.rb',
1219
+ commit_id: 'a9f5f94cf28f29a64d5dd96d0ee23b4174572847',
1220
+ original_commit_id: 'e8c6f94274d14ed3cb26fe71467a9c3f229df59c',
1221
+ user: {
1222
+ login: 'Reviewer',
1223
+ id: 2_566_462
1224
+ },
1225
+ body: 'reviewer comment',
1226
+ created_at: '2024-08-08T09:41:46Z',
1227
+ updated_at: '2024-08-08T09:42:46Z',
1228
+ reactions: {
1229
+ url: 'https://api.github.com/repos/zerocracy/baza/pulls/comments/1709082320/reactions',
1230
+ total_count: 1
1231
+ },
1232
+ start_line: 'null',
1233
+ original_start_line: 'null',
1234
+ start_side: 'null',
1235
+ line: 'null',
1236
+ original_line: 62,
1237
+ side: 'RIGHT',
1238
+ original_position: 25,
1239
+ position: 'null',
1240
+ subject_type: 'line'
1241
+ },
1242
+ {
1243
+ pull_request_review_id: 2_227_372_510,
1244
+ id: 1_709_082_321,
1245
+ path: 'test/baza/test_locks.rb',
1246
+ commit_id: 'a9f5f94cf28f29a64d5dd96d0ee23b4174572847',
1247
+ original_commit_id: 'e8c6f94274d14ed3cb26fe71467a9c3f229df59c',
1248
+ user: {
1249
+ login: 'test',
1250
+ id: 88_084_038
1251
+ },
1252
+ body: 'author comment',
1253
+ created_at: '2024-08-08T09:42:46Z',
1254
+ updated_at: '2024-08-08T09:42:46Z',
1255
+ reactions: {
1256
+ url: 'https://api.github.com/repos/zerocracy/baza/pulls/comments/1709082321/reactions',
1257
+ total_count: 1
1258
+ },
1259
+ start_line: 'null',
1260
+ original_start_line: 'null',
1261
+ start_side: 'null',
1262
+ line: 'null',
1263
+ original_line: 62,
1264
+ side: 'RIGHT',
1265
+ original_position: 25,
1266
+ in_reply_to_id: 1_709_082_318,
1267
+ position: 'null',
1268
+ subject_type: 'line'
1269
+ }
1270
+ ]
1271
+ end
1272
+
1273
+ def issue_comment_reactions(_name, _comment)
1274
+ [
1275
+ {
1276
+ id: 248_923_574,
1277
+ user: {
1278
+ login: 'user',
1279
+ id: 8_086_956
1280
+ },
1281
+ content: 'heart'
1282
+ }
1283
+ ]
1284
+ end
1285
+
1286
+ def pull_request_review_comment_reactions(_name, _comment)
1287
+ [
1288
+ {
1289
+ id: 248_923_574,
1290
+ user: {
1291
+ login: 'user',
1292
+ id: 8_086_956
1293
+ },
1294
+ content: 'heart'
1295
+ }
1296
+ ]
1297
+ end
1298
+
1299
+ def check_runs_for_ref(repo, sha) # rubocop:disable Metrics/MethodLength
1300
+ data = {
1301
+ 'zerocracy/baza' => {
1302
+ total_count: 7,
1303
+ check_runs: [
1304
+ {
1305
+ id: 28_907_016_501,
1306
+ name: 'make',
1307
+ head_sha: sha,
1308
+ started_at: '2024-08-18T08:04:44Z',
1309
+ completed_at: '2024-08-18T08:20:17Z',
1310
+ app: {
1311
+ slug: 'github-actions'
1312
+ }
1313
+ },
1314
+ {
1315
+ id: 28_906_596_603,
1316
+ name: 'copyrights',
1317
+ head_sha: sha,
1318
+ started_at: '2024-08-18T08:04:44Z',
1319
+ completed_at: '2024-08-18T08:20:17Z',
1320
+ app: {
1321
+ slug: 'github-actions'
1322
+ }
1323
+ },
1324
+ {
1325
+ id: 28_906_596_550,
1326
+ name: 'markdown-lint',
1327
+ head_sha: sha,
1328
+ started_at: '2024-08-18T08:04:44Z',
1329
+ completed_at: '2024-08-18T08:20:17Z',
1330
+ app: {
1331
+ slug: 'github-actions'
1332
+ }
1333
+ },
1334
+ {
1335
+ id: 28_906_596_483,
1336
+ name: 'pdd',
1337
+ head_sha: sha,
1338
+ started_at: '2024-08-18T08:04:44Z',
1339
+ completed_at: '2024-08-18T08:20:17Z',
1340
+ app: {
1341
+ slug: 'github-actions'
1342
+ }
1343
+ },
1344
+ {
1345
+ id: 28_906_596_433,
1346
+ name: 'rake',
1347
+ head_sha: sha,
1348
+ started_at: '2024-08-18T08:04:44Z',
1349
+ completed_at: '2024-08-18T08:20:17Z',
1350
+ app: {
1351
+ slug: 'github-actions'
1352
+ }
1353
+ },
1354
+ {
1355
+ id: 28_906_596_405,
1356
+ name: 'shellcheck',
1357
+ head_sha: sha,
1358
+ started_at: '2024-08-18T08:04:44Z',
1359
+ completed_at: '2024-08-18T08:20:17Z',
1360
+ app: {
1361
+ slug: 'github-actions'
1362
+ }
1363
+ },
1364
+ {
1365
+ id: 28_906_596_379,
1366
+ name: 'yamllint',
1367
+ head_sha: sha,
1368
+ started_at: '2024-08-18T08:04:44Z',
1369
+ completed_at: '2024-08-18T08:20:17Z',
1370
+ app: {
1371
+ slug: 'github-actions'
1372
+ }
1373
+ }
1374
+ ]
1375
+ },
1376
+ 'zerocracy/judges-action' => {
1377
+ total_count: 7,
1378
+ check_runs: [
1379
+ {
1380
+ id: 28_907_016_501,
1381
+ name: 'Codacy Static Code Analysis',
1382
+ head_sha: sha,
1383
+ started_at: '2024-08-18T08:04:44Z',
1384
+ completed_at: '2024-08-18T08:20:17Z',
1385
+ app: {
1386
+ slug: 'codacy-production'
1387
+ }
1388
+ },
1389
+ {
1390
+ id: 28_906_596_603,
1391
+ name: 'copyrights',
1392
+ head_sha: sha,
1393
+ started_at: '2024-08-18T08:04:44Z',
1394
+ completed_at: '2024-08-18T08:20:17Z',
1395
+ app: {
1396
+ slug: 'github-actions'
1397
+ }
1398
+ },
1399
+ {
1400
+ id: 28_906_596_550,
1401
+ name: 'markdown-lint',
1402
+ head_sha: sha,
1403
+ started_at: '2024-08-18T08:04:44Z',
1404
+ completed_at: '2024-08-18T08:20:17Z',
1405
+ app: {
1406
+ slug: 'github-actions'
1407
+ }
1408
+ },
1409
+ {
1410
+ id: 28_906_596_483,
1411
+ name: 'pdd',
1412
+ head_sha: sha,
1413
+ started_at: '2024-08-18T08:04:44Z',
1414
+ completed_at: '2024-08-18T08:20:17Z',
1415
+ app: {
1416
+ slug: 'github-actions'
1417
+ }
1418
+ },
1419
+ {
1420
+ id: 28_906_596_433,
1421
+ name: 'rake',
1422
+ head_sha: sha,
1423
+ started_at: '2024-08-18T08:04:44Z',
1424
+ completed_at: '2024-08-18T08:20:17Z',
1425
+ app: {
1426
+ slug: 'github-actions'
1427
+ }
1428
+ },
1429
+ {
1430
+ id: 28_906_596_405,
1431
+ name: 'shellcheck',
1432
+ head_sha: sha,
1433
+ started_at: '2024-08-18T08:04:44Z',
1434
+ completed_at: '2024-08-18T08:20:17Z',
1435
+ app: {
1436
+ slug: 'github-actions'
1437
+ }
1438
+ },
1439
+ {
1440
+ id: 28_906_596_379,
1441
+ name: 'yamllint',
1442
+ head_sha: sha,
1443
+ started_at: '2024-08-18T08:04:44Z',
1444
+ completed_at: '2024-08-18T08:20:17Z',
1445
+ app: {
1446
+ slug: 'github-actions'
1447
+ }
1448
+ }
1449
+ ]
1450
+ }
1451
+ }
1452
+ data.fetch(repo) do
1453
+ { total_count: 0, check_runs: [] }
1454
+ end
1455
+ end
1456
+
1457
+ def workflow_run_job(_repo, job)
1458
+ [
1459
+ {
1460
+ id: 28_907_016_501,
1461
+ run_id: 10_438_531_072,
1462
+ name: 'make',
1463
+ started_at: '2024-08-18T08:04:44Z',
1464
+ completed_at: '2024-08-18T08:20:17Z'
1465
+ },
1466
+ {
1467
+ id: 28_906_596_603,
1468
+ run_id: 10_438_531_073,
1469
+ name: 'copyrights',
1470
+ started_at: '2024-08-18T08:04:44Z',
1471
+ completed_at: '2024-08-18T08:20:17Z'
1472
+ },
1473
+ {
1474
+ id: 28_906_596_550,
1475
+ run_id: 10_438_531_074,
1476
+ name: 'markdown-lint',
1477
+ started_at: '2024-08-18T08:04:44Z',
1478
+ completed_at: '2024-08-18T08:20:17Z'
1479
+ },
1480
+ {
1481
+ id: 28_906_596_483,
1482
+ run_id: 10_438_531_075,
1483
+ name: 'pdd',
1484
+ started_at: '2024-08-18T08:04:44Z',
1485
+ completed_at: '2024-08-18T08:20:17Z'
1486
+ },
1487
+ {
1488
+ id: 28_906_596_433,
1489
+ run_id: 10_438_531_076,
1490
+ name: 'rake',
1491
+ started_at: '2024-08-18T08:04:44Z',
1492
+ completed_at: '2024-08-18T08:20:17Z'
1493
+ },
1494
+ {
1495
+ id: 28_906_596_405,
1496
+ run_id: 10_438_531_077,
1497
+ name: 'shellcheck',
1498
+ started_at: '2024-08-18T08:04:44Z',
1499
+ completed_at: '2024-08-18T08:20:17Z'
1500
+ },
1501
+ {
1502
+ id: 28_906_596_379,
1503
+ run_id: 10_438_531_078,
1504
+ name: 'yamllint',
1505
+ started_at: '2024-08-18T08:04:44Z',
1506
+ completed_at: '2024-08-18T08:20:17Z'
1507
+ }
1508
+ ].find { |json| json[:id] == job } || {
1509
+ id: job,
1510
+ run_id: 1234,
1511
+ name: 'run job',
1512
+ started_at: '2024-08-18T08:04:44Z',
1513
+ completed_at: '2024-08-18T08:20:17Z'
1514
+ }
1515
+ end
1516
+
1517
+ def workflow_run(repo, id)
1518
+ [
1519
+ {
1520
+ id: 10_438_531_072,
1521
+ event: 'pull_request',
1522
+ conclusion: 'success',
1523
+ name: 'make',
1524
+ started_at: '2024-08-18T08:04:44Z',
1525
+ completed_at: '2024-08-18T08:20:17Z'
1526
+ },
1527
+ {
1528
+ id: 10_438_531_073,
1529
+ event: 'pull_request',
1530
+ conclusion: 'success',
1531
+ name: 'copyrights',
1532
+ started_at: '2024-08-18T08:04:44Z',
1533
+ run_started_at: '2024-08-18T08:04:44Z',
1534
+ completed_at: '2024-08-18T08:20:17Z'
1535
+ },
1536
+ {
1537
+ id: 10_438_531_074,
1538
+ event: 'pull_request',
1539
+ conclusion: 'success',
1540
+ name: 'markdown-lint',
1541
+ started_at: '2024-08-18T08:04:44Z',
1542
+ run_started_at: '2024-08-18T08:04:44Z',
1543
+ completed_at: '2024-08-18T08:20:17Z'
1544
+ },
1545
+ {
1546
+ id: 10_438_531_075,
1547
+ event: 'pull_request',
1548
+ conclusion: 'failure',
1549
+ name: 'pdd',
1550
+ started_at: '2024-08-18T08:04:44Z',
1551
+ run_started_at: '2024-08-18T08:04:44Z',
1552
+ completed_at: '2024-08-18T08:20:17Z'
1553
+ },
1554
+ {
1555
+ id: 10_438_531_076,
1556
+ event: 'pull_request',
1557
+ conclusion: 'success',
1558
+ name: 'rake',
1559
+ started_at: '2024-08-18T08:04:44Z',
1560
+ run_started_at: '2024-08-18T08:04:44Z',
1561
+ completed_at: '2024-08-18T08:20:17Z'
1562
+ },
1563
+ {
1564
+ id: 10_438_531_077,
1565
+ event: 'commit',
1566
+ conclusion: 'success',
1567
+ name: 'shellcheck',
1568
+ started_at: '2024-08-18T08:04:44Z',
1569
+ run_started_at: '2024-08-18T08:04:44Z',
1570
+ completed_at: '2024-08-18T08:20:17Z'
1571
+ },
1572
+ {
1573
+ id: 10_438_531_078,
1574
+ event: 'pull_request',
1575
+ conclusion: 'failure',
1576
+ name: 'yamllint',
1577
+ started_at: '2024-08-18T08:04:44Z',
1578
+ run_started_at: '2024-08-18T08:04:44Z',
1579
+ completed_at: '2024-08-18T08:20:17Z'
1580
+ }
1581
+ ].find { |json| json[:id] == id } || {
1582
+ id:,
1583
+ name: 'copyrights',
1584
+ head_branch: 'master',
1585
+ head_sha: '7d34c53e6743944dbf6fc729b1066bcbb3b18443',
1586
+ event: 'push',
1587
+ status: 'completed',
1588
+ conclusion: 'success',
1589
+ workflow_id: id,
1590
+ created_at: random_time,
1591
+ run_started_at: random_time,
1592
+ repository: repository(repo)
1593
+ }
1594
+ end
1595
+
1596
+ def compare(_repo, _start, _end)
1597
+ {
1598
+ base_commit: {
1599
+ sha: '498464613c0b9',
1600
+ commit: {
1601
+ author: {
1602
+ name: 'Yegor Bugayenko', email: 'yegor256@gmail.com', date: Time.parse('2024-09-04 15:23:25 UTC')
1603
+ },
1604
+ committer: {
1605
+ name: 'Yegor Bugayenko', email: 'yegor256@gmail.com', date: Time.parse('2024-09-04 15:23:25 UTC')
1606
+ },
1607
+ message: 'Some text',
1608
+ tree: { sha: '51aee236ba884' },
1609
+ comment_count: 0,
1610
+ verification: { verified: false, reason: 'unsigned', signature: nil, payload: nil }
1611
+ },
1612
+ author: { login: 'yegor256', id: 526_301, type: 'User', site_admin: false },
1613
+ committer: { login: 'yegor256', id: 526_301, type: 'User', site_admin: false },
1614
+ parents: [{ sha: '9763dab47b50a12f59c3630690ec2c0f6bdda0b3' }]
1615
+ },
1616
+ merge_base_commit: {
1617
+ sha: '8e4348746638595a7e',
1618
+ commit: {
1619
+ author: {
1620
+ name: 'Yegor Bugayenko', email: 'yegor256@gmail.com', date: Time.parse('2024-08-25 15:57:35 UTC')
1621
+ },
1622
+ committer: {
1623
+ name: 'Yegor Bugayenko', email: 'yegor256@gmail.com', date: Time.parse('2024-08-25 15:57:35 UTC')
1624
+ },
1625
+ message: 'Some text',
1626
+ tree: { sha: '7145fc122e70bf51e1d' },
1627
+ comment_count: 0,
1628
+ verification: { verified: true, reason: 'valid', signature: '', payload: '' }
1629
+ },
1630
+ author: { login: 'yegor256', id: 526_301, type: 'User', site_admin: false },
1631
+ committer: { login: 'yegor256', id: 526_301, type: 'User', site_admin: false },
1632
+ parents: [
1633
+ { sha: '8c8278efedbd795e70' },
1634
+ { sha: '7dfd2e0186113f66f' }
1635
+ ]
1636
+ },
1637
+ status: 'diverged',
1638
+ ahead_by: 1,
1639
+ behind_by: 30,
1640
+ total_commits: 1,
1641
+ commits: [
1642
+ {
1643
+ sha: 'ee04386901692abb',
1644
+ commit: {
1645
+ author: {
1646
+ name: 'Yegor Bugayenko', email: 'yegor256@gmail.com', date: Time.parse('2024-08-25 15:57:35 UTC')
1647
+ },
1648
+ committer: {
1649
+ name: 'Yegor Bugayenko', email: 'yegor256@gmail.com', date: Time.parse('2024-08-25 15:57:35 UTC')
1650
+ },
1651
+ message: 'Some text',
1652
+ tree: { sha: '7a6124a500aed8c92' },
1653
+ comment_count: 0,
1654
+ verification: { verified: false, reason: 'unsigned', signature: nil, payload: nil }
1655
+ },
1656
+ author: { login: 'yegor256', id: 526_301, type: 'User', site_admin: false },
1657
+ committer: { login: 'yegor256', id: 526_301, type: 'User', site_admin: false },
1658
+ parents: [{ sha: '8e4348746638595a7e' }]
1659
+ }
1660
+ ],
1661
+ files: [
1662
+ {
1663
+ sha: '9e100c7246c0cc9', filename: 'file.txt', status: 'modified',
1664
+ additions: 1, deletions: 1, changes: 2,
1665
+ patch: '@@ -24,7 +24,7 @@ text ...'
1666
+ },
1667
+ {
1668
+ sha: 'f97818271059e5455', filename: 'file2.txt', status: 'modified',
1669
+ additions: 1, deletions: 1, changes: 2,
1670
+ patch: '@@ -25,7 +25,7 @@ text ...'
1671
+ },
1672
+ {
1673
+ sha: '5a957c57d090bfeccb', filename: 'file3.txt', status: 'modified',
1674
+ additions: 1, deletions: 1, changes: 2,
1675
+ patch: '@@ -27,7 +27,7 @@ text ...'
1676
+ }
1677
+ ]
1678
+ }
1679
+ end
1680
+
1681
+ def tree(_repo, _tree_sha, _options = {})
1682
+ {
1683
+ sha: '492072971ad3c8644a191f62426bd3',
1684
+ tree: [
1685
+ {
1686
+ path: '.github',
1687
+ mode: '040000',
1688
+ type: 'tree',
1689
+ sha: '438682e07e45ccbf9ca58f294a'
1690
+ },
1691
+ {
1692
+ path: '.github/workflows',
1693
+ mode: '040000',
1694
+ type: 'tree',
1695
+ sha: 'dea8a01c236530cc92a63c5774'
1696
+ },
1697
+ {
1698
+ path: '.github/workflows/actionlint.yml',
1699
+ mode: '100644',
1700
+ type: 'blob',
1701
+ sha: 'ffed2deef2383d6f685489b289',
1702
+ size: 1671
1703
+ },
1704
+ {
1705
+ path: '.github/workflows/copyrights.yml',
1706
+ mode: '100644',
1707
+ type: 'blob',
1708
+ sha: 'ab8357cfd94e0628676aff34cd',
1709
+ size: 1293
1710
+ },
1711
+ {
1712
+ path: '.github/workflows/zerocracy.yml',
1713
+ mode: '100644',
1714
+ type: 'blob',
1715
+ sha: '5c224c7742e5ebeeb176b90605',
1716
+ size: 2005
1717
+ },
1718
+ {
1719
+ path: '.gitignore',
1720
+ mode: '100644',
1721
+ type: 'blob',
1722
+ sha: '9383e7111a173b44baa0692775',
1723
+ size: 27
1724
+ },
1725
+ {
1726
+ path: '.rubocop.yml',
1727
+ mode: '100644',
1728
+ type: 'blob',
1729
+ sha: 'cb9b62eb1979589daa18142008',
1730
+ size: 1963
1731
+ },
1732
+ {
1733
+ path: 'README.md',
1734
+ mode: '100644',
1735
+ type: 'blob',
1736
+ sha: '8011ad43c37edbaf1969417b94',
1737
+ size: 4877
1738
+ },
1739
+ {
1740
+ path: 'Rakefile',
1741
+ mode: '100644',
1742
+ type: 'blob',
1743
+ sha: 'a0ac9bf2643d9f5392e1119301',
1744
+ size: 1805
1745
+ }
1746
+ ],
1747
+ truncated: false
1748
+ }
1749
+ end
1750
+
1751
+ def contributors(_repo, _anon = nil, _options = {})
1752
+ [
1753
+ {
1754
+ login: 'yegor256',
1755
+ id: 526_301,
1756
+ type: 'User',
1757
+ contributions: 500
1758
+ },
1759
+ {
1760
+ login: 'renovate[bot]',
1761
+ id: 29_139_614,
1762
+ type: 'Bot',
1763
+ contributions: 320
1764
+ },
1765
+ {
1766
+ login: 'user1',
1767
+ id: 2_476_362,
1768
+ type: 'User',
1769
+ contributions: 120
1770
+ },
1771
+ {
1772
+ login: 'rultor',
1773
+ id: 8_086_956,
1774
+ type: 'Bot',
1775
+ contributions: 87
1776
+ },
1777
+ {
1778
+ login: 'user2',
1779
+ id: 5_427_638,
1780
+ type: 'User',
1781
+ contributions: 49
1782
+ },
1783
+ {
1784
+ login: 'user3',
1785
+ id: 2_648_875,
1786
+ type: 'User',
1787
+ contributions: 10
1788
+ },
1789
+ {
1790
+ login: 'user4',
1791
+ id: 7_125_293,
1792
+ type: 'User',
1793
+ contributions: 1
1794
+ }
1795
+ ]
1796
+ end
1797
+ end