fbe 0.48.5 → 0.48.6

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