jira-auto-tool 1.1.5 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -83,15 +83,17 @@ module Jira
83
83
  end
84
84
 
85
85
  def jira_client
86
- RateLimitedJiraClient.new(jira_client_options,
87
- rate_interval:
88
- jat_rate_interval_in_seconds_when_defined_else(
89
- RateLimitedJiraClient::NO_RATE_INTERVAL_IN_SECONDS
90
- ).to_i,
91
- rate_limit:
92
- jat_rate_limit_in_seconds_when_defined_else(
93
- RateLimitedJiraClient::NO_RATE_LIMIT_IN_SECONDS
94
- ).to_i)
86
+ RateLimitedJiraClient
87
+ .implementation_class_for(self)
88
+ .new(jira_client_options,
89
+ rate_interval_in_seconds:
90
+ jat_rate_interval_in_seconds_when_defined_else(
91
+ RateLimitedJiraClient::RedisBased::NO_RATE_INTERVAL_IN_SECONDS
92
+ ).to_i,
93
+ rate_limit_per_interval:
94
+ jat_rate_limit_per_interval_when_defined_else(
95
+ RateLimitedJiraClient::RedisBased::NO_RATE_LIMIT_PER_INTERVAL
96
+ ).to_i)
95
97
  end
96
98
 
97
99
  def jira_client_options
@@ -105,6 +107,7 @@ module Jira
105
107
  }
106
108
  end
107
109
 
110
+ # TODO: fix this overly complex logic
108
111
  def jira_http_debug?
109
112
  value = if config.key?(:jira_http_debug)
110
113
  config[:jira_http_debug]
@@ -136,24 +139,29 @@ module Jira
136
139
  jira_base_url + url
137
140
  end
138
141
 
139
- %i[
140
- art_sprint_regex
141
- expected_start_date_field_name
142
- implementation_team_field_name
143
- jat_rate_limit_in_seconds
144
- jat_rate_interval_in_seconds
145
- jat_tickets_for_team_sprint_ticket_dispatcher_jql
146
- jira_api_token
147
- jira_board_name
148
- jira_board_name_regex
149
- jira_context_path
150
- jira_http_debug
151
- jira_project_key
152
- jira_site_url
153
- jira_username
154
- jira_sprint_field_name
155
- ].each do |method_name|
156
- define_overridable_environment_based_value(method_name)
142
+ HOLDS_A_SECRET = true
143
+ ENVIRONMENT_BASED_VALUE_SYMBOLS =
144
+ ([[:jira_api_token, HOLDS_A_SECRET]] + %i[
145
+ art_sprint_regex
146
+ expected_start_date_field_name
147
+ implementation_team_field_name
148
+ jat_rate_interval_in_seconds
149
+ jat_rate_limit_implementation
150
+ jat_rate_limit_per_interval
151
+ jat_tickets_for_team_sprint_ticket_dispatcher_jql
152
+ jira_board_name
153
+ jira_board_name_regex
154
+ jira_context_path
155
+ jira_http_debug
156
+ jira_project_key
157
+ jira_site_url
158
+ jira_sprint_field_name
159
+ jira_username
160
+ ].collect { |value_name| [value_name, !HOLDS_A_SECRET] }).freeze
161
+
162
+ ENVIRONMENT_BASED_VALUE_SYMBOLS.each do |method_name, holds_a_secret|
163
+ holds_a_secret ||= false
164
+ define_overridable_environment_based_value(method_name, holds_a_secret)
157
165
  end
158
166
 
159
167
  def board_controller
@@ -29,7 +29,7 @@ end
29
29
  def amend_commit_to_include_gemfile_lock_changes
30
30
  puts "Amending commit to include Gemfile.lock update..."
31
31
  system("git add .")
32
- system(%(git commit --amend --no-edit))
32
+ system("git commit --amend --no-edit")
33
33
  end
34
34
 
35
35
  namespace :version do
@@ -5,6 +5,7 @@ require "rspec"
5
5
  module Jira
6
6
  module Auto
7
7
  class Tool
8
+ # rubocop:disable Metrics/ClassLength
8
9
  class EnvironmentLoader
9
10
  RSpec.describe EnvironmentLoader do
10
11
  let(:environment_loader) { described_class.new(tool, auto_setup: auto_setup) }
@@ -56,7 +57,7 @@ module Jira
56
57
  end
57
58
 
58
59
  describe "#tool_environment" do
59
- let(:environment_keys) { %i[JIRA_HOST JIRA_USER JIRA_PASSWORD] }
60
+ let(:environment_keys) { %i[JIRA_SITE_URL JIRA_USERNAME JIRA_API_TOKEN] }
60
61
 
61
62
  before do
62
63
  allow(Environment).to receive(:constants).and_return(environment_keys)
@@ -64,14 +65,29 @@ module Jira
64
65
  environment_keys.each do |environment_key|
65
66
  allow(ENV).to receive(:fetch).with(environment_key.to_s, nil).and_return("#{environment_key} value")
66
67
  end
68
+
69
+ allow(tool).to receive_messages(
70
+ jira_api_token_holds_a_secret?: true,
71
+ jira_site_url_holds_a_secret?: false,
72
+ jira_username_holds_a_secret?: false
73
+ )
67
74
  end
68
75
 
69
76
  it do
70
- expect(environment_loader.tool_environment).to eq(
71
- "JIRA_HOST" => "JIRA_HOST value",
72
- "JIRA_USER" => "JIRA_USER value",
73
- "JIRA_PASSWORD" => "JIRA_PASSWORD value"
74
- )
77
+ expect(environment_loader.tool_environment)
78
+ .to eq(
79
+ "JIRA_API_TOKEN" => "****",
80
+ "JIRA_SITE_URL" => "JIRA_SITE_URL value",
81
+ "JIRA_USERNAME" => "JIRA_USERNAME value"
82
+ )
83
+ end
84
+ end
85
+
86
+ describe "#environment_variable_holds_a_secret?" do
87
+ it "allows checking that the corresponding constant is a secret or not" do
88
+ allow(tool).to receive(:jira_api_token_holds_a_secret?).and_return(true)
89
+
90
+ expect(environment_loader).to be_environment_variable_holds_a_secret("JIRA_API_TOKEN")
75
91
  end
76
92
  end
77
93
 
@@ -132,10 +148,12 @@ module Jira
132
148
  end
133
149
 
134
150
  describe "#setup" do
135
- it "sets up the value according to the configuration file content" do
136
- allow(environment_loader).to receive_messages(file_path: "file_path")
151
+ before do
152
+ allow(environment_loader).to receive_messages(file_path: "path/to/config/file.yaml")
137
153
  allow(environment_loader).to receive_messages(config_file_content: "file_content")
154
+ end
138
155
 
156
+ it "sets up the value according to the configuration file content" do
139
157
  allow(YAML)
140
158
  .to receive(:safe_load)
141
159
  .with("file_content")
@@ -147,10 +165,25 @@ module Jira
147
165
 
148
166
  environment_loader.send(:setup)
149
167
  end
168
+
169
+ context "when the YAML parsing fails" do
170
+ it "generates an error message including the file name" do
171
+ allow(YAML).to receive(:safe_load)
172
+ .with("file_content")
173
+ .and_raise(RuntimeError, "could not find expected ':' at line 3 column 6)")
174
+
175
+ expect { environment_loader.send(:setup) }
176
+ .to raise_error(RuntimeError, <<~EOEMSG
177
+ path/to/config/file.yaml:188: failed to load with the following error:
178
+ could not find expected ':' at line 3 column 6)
179
+ EOEMSG
180
+ )
181
+ end
182
+ end
150
183
  end
151
184
 
152
185
  describe "#config_file_content" do
153
- let(:file_path) { "file_path" }
186
+ let(:file_path) { "path/to/config/file" }
154
187
  let(:file_content) do
155
188
  <<-YAML_ERB
156
189
  ---
@@ -174,9 +207,31 @@ module Jira
174
207
  end
175
208
 
176
209
  it { expect(environment_loader.send(:config_file_content)).to eq(erb_result) }
210
+
211
+ context "when the ERB evaluation fails" do
212
+ let(:file_content) do
213
+ <<-YAML_ERB
214
+ ---
215
+ a_key: <%= 4*4 %>
216
+ another_key: <%= 4*4 %>
217
+ <%
218
+ raise "An error that should be caught and reported!"#{" "}
219
+ %>
220
+ YAML_ERB
221
+ end
222
+
223
+ it "generates an error message including the file name" do
224
+ expect { environment_loader.send(:config_file_content) }
225
+ .to raise_error(RuntimeError, <<~EOEMSG)
226
+ path/to/config/file:5: failed to load with the following error:
227
+ An error that should be caught and reported!
228
+ EOEMSG
229
+ end
230
+ end
177
231
  end
178
232
  end
179
233
  end
234
+ # rubocop:enable Metrics/ClassLength
180
235
  end
181
236
  end
182
237
  end
@@ -33,27 +33,90 @@ module Jira
33
33
  end
34
34
  end
35
35
 
36
+ # rubocop:disable Naming/VariableNumber, RSpec/IndexedLet
36
37
  describe "#act_on_sprints_for_sprint_prefix" do
37
- def get_sprint(name, attributes)
38
- instance_double(Sprint, name: name, to_s: name, **attributes)
38
+ def get_sprint(name, attributes = {})
39
+ instance_double(Sprint, name: name, to_s: name, **attributes,
40
+ parsed_name: Sprint::Name.parse(name))
39
41
  end
40
42
 
41
- let(:last_sprint) { get_sprint "Food_Delivery_25.2.1", end_date: "2025-02-16 12:45", length_in_days: 10 }
43
+ let(:sprint_prefix) do
44
+ instance_double(Sprint::Prefix, name: "Food_Delivery", last_sprint: last_sprint)
45
+ end
46
+
47
+ context "when the sprints to create are all posterior to the last sprint from a naming perspective" do
48
+ let(:last_sprint) do
49
+ get_sprint "Food_Delivery_25.2.1", end_date: "2025-02-16 12:45", length_in_days: 10
50
+ end
51
+
52
+ let(:sprint_25_3_1) { get_sprint("Food_Delivery_25.3.1") }
53
+ let(:sprint_25_3_2) { get_sprint("Food_Delivery_25.3.2") }
54
+ let(:sprint_25_3_3) { get_sprint("Food_Delivery_25.3.3") }
55
+ let(:sprint_25_3_4) { get_sprint("Food_Delivery_25.3.4") }
56
+
57
+ before do
58
+ allow(updater).to receive(:create_sprint_for).with(last_sprint,
59
+ "Food_Delivery_25.3.1")
60
+ .and_return(sprint_25_3_1)
61
+
62
+ allow(updater).to receive(:create_sprint_for).with(sprint_25_3_1, "Food_Delivery_25.3.2")
63
+ .and_return(sprint_25_3_2)
64
+
65
+ allow(updater).to receive(:create_sprint_for).with(sprint_25_3_2, "Food_Delivery_25.3.3")
66
+ .and_return(sprint_25_3_3)
67
+
68
+ allow(updater).to receive(:create_sprint_for).with(sprint_25_3_3, "Food_Delivery_25.3.4")
69
+ .and_return(sprint_25_3_4)
70
+ end
71
+
72
+ it "creates the expected number of sprints with the expected names" do
73
+ expect(sprint_prefix).to receive(:<<).with(sprint_25_3_1)
74
+ expect(sprint_prefix).to receive(:<<).with(sprint_25_3_2)
75
+ expect(sprint_prefix).to receive(:<<).with(sprint_25_3_3)
76
+ expect(sprint_prefix).to receive(:<<).with(sprint_25_3_4)
77
+
78
+ updater.act_on_sprints_for_sprint_prefix(sprint_prefix)
79
+ end
80
+ end
81
+
82
+ context "when some sprints to create are anterior to the last sprint from a naming perspective" do
83
+ let(:last_sprint) do
84
+ get_sprint "Food_Delivery_25.3.2", end_date: "2025-02-16 12:45", length_in_days: 10
85
+ end
86
+
87
+ let(:sprint_25_3_3) { get_sprint("Food_Delivery_25.3.3") }
88
+ let(:sprint_25_3_4) { get_sprint("Food_Delivery_25.3.4") }
89
+
90
+ before do
91
+ allow(updater).to receive(:create_sprint_for).with(last_sprint, "Food_Delivery_25.3.3")
92
+ .and_return(sprint_25_3_3)
93
+
94
+ allow(updater).to receive(:create_sprint_for).with(sprint_25_3_3, "Food_Delivery_25.3.4")
95
+ .and_return(sprint_25_3_4)
96
+ end
97
+
98
+ it "only creates the ones posterior to the last sprint" do
99
+ expect(sprint_prefix).to receive(:<<).with(sprint_25_3_3)
100
+ expect(sprint_prefix).to receive(:<<).with(sprint_25_3_4)
101
+
102
+ updater.act_on_sprints_for_sprint_prefix(sprint_prefix)
103
+ end
104
+ end
42
105
 
43
- let(:sprint_prefix) { instance_double(Sprint::Prefix, name: "Food_Delivery", last_sprint: last_sprint) }
106
+ context "when requested sprints to create are anterior to the last sprint from a naming perspective" do
107
+ let(:last_sprint) do
108
+ get_sprint "Food_Delivery_25.4.1", end_date: "2025-02-16 12:45", length_in_days: 10
109
+ end
44
110
 
45
- # rubocop:disable RSpec/MultipleExpectations
46
- it "creates the expected number of sprints with the expected names" do
47
- expect(updater).to receive(:create_sprint_for).ordered.with(last_sprint, "Food_Delivery_25.3.1")
48
- expect(updater).to receive(:create_sprint_for).ordered.with(nil, "Food_Delivery_25.3.2")
49
- expect(updater).to receive(:create_sprint_for).ordered.with(nil, "Food_Delivery_25.3.3")
50
- expect(updater).to receive(:create_sprint_for).ordered.with(nil, "Food_Delivery_25.3.4")
51
- expect(sprint_prefix).to receive(:<<).exactly(4).times
111
+ it "does not create any since they would be anterior to the last sprint" do
112
+ expect(updater).not_to receive(:create_sprint_for)
113
+ expect(sprint_prefix).not_to receive(:<<)
52
114
 
53
- updater.act_on_sprints_for_sprint_prefix(sprint_prefix)
115
+ updater.act_on_sprints_for_sprint_prefix(sprint_prefix)
116
+ end
54
117
  end
55
- # rubocop:enable RSpec/MultipleExpectations
56
118
  end
119
+ # rubocop:enable Naming/VariableNumber, RSpec/IndexedLet
57
120
  end
58
121
  end
59
122
  end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jira/auto/tool/rate_limited_jira_client/in_process_based"
4
+
5
+ module Jira
6
+ module Auto
7
+ class Tool
8
+ class RateLimitedJiraClient
9
+ class InProcessBased
10
+ RSpec.describe InProcessBased do
11
+ def build_client
12
+ described_class.new({}, rate_interval_in_seconds:, rate_limit_per_interval:)
13
+ end
14
+
15
+ let(:client) { build_client }
16
+
17
+ let(:rate_interval_in_seconds) { 2 }
18
+ let(:rate_limit_per_interval) { 1 }
19
+
20
+ describe "#rate_limit" do
21
+ let(:rate_queue) { instance_double(Limiter::RateQueue) }
22
+
23
+ it "properly initializes the rate queue" do
24
+ allow(Limiter::RateQueue)
25
+ .to receive(:new).with(rate_limit_per_interval, interval: rate_interval_in_seconds)
26
+ .and_return(rate_queue)
27
+
28
+ allow(rate_queue).to receive(:shift)
29
+
30
+ expect(client.rate_limit { :do_nothing }).to eq(:do_nothing)
31
+ end
32
+
33
+ context "when rate limiting multiple requests" do
34
+ let(:rate_limit_4_calls_to_original_request_code) do
35
+ 4.times { client.rate_limit { client.original_request(:get, "/path/to/resource") } }
36
+ end
37
+
38
+ before do
39
+ allow(client).to receive(:original_request).with(:get, "/path/to/resource")
40
+ allow(client).to receive_messages(rate_queue: rate_queue)
41
+ allow(rate_queue).to receive(:shift)
42
+ end
43
+
44
+ it "shifts the queue and performs the request call" do
45
+ rate_limit_4_calls_to_original_request_code
46
+
47
+ expect(rate_queue).to have_received(:shift).exactly(4).times
48
+ expect(client).to have_received(:original_request).exactly(4).times
49
+ end
50
+ end
51
+
52
+ describe "#rate_queue" do
53
+ let(:another_client) { build_client }
54
+
55
+ it "creating a second client will return another queue" do
56
+ expect(another_client.rate_queue).not_to equal(client.rate_queue)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jira/auto/tool/rate_limited_jira_client/redis_based"
4
+
5
+ module Jira
6
+ module Auto
7
+ class Tool
8
+ class RateLimitedJiraClient
9
+ class RedisBased
10
+ RSpec.describe RedisBased do
11
+ describe "#rate_limit" do
12
+ let(:client) { described_class.new({}, rate_interval_in_seconds:, rate_limit_per_interval:) }
13
+
14
+ let(:rate_interval_in_seconds) { 2 }
15
+ let(:rate_limit_per_interval) { 1 }
16
+ let(:rate_limiter) { instance_double(Ratelimit) }
17
+
18
+ let(:rate_limit_4_calls_to_original_request_code) do
19
+ 4.times { client.rate_limit { client.original_request(:get, "/path/to/resource") } }
20
+ end
21
+
22
+ before do
23
+ allow(described_class).to receive_messages(rate_limiter: rate_limiter)
24
+ allow(client).to receive(:original_request)
25
+ end
26
+
27
+ it "uses :exec_within_threshold to control rate limiting" do
28
+ allow(rate_limiter).to receive(:exec_within_threshold)
29
+
30
+ rate_limit_4_calls_to_original_request_code
31
+
32
+ expect(rate_limiter)
33
+ .to have_received(:exec_within_threshold)
34
+ .with("jira_auto_tool_api_requests", { interval: rate_interval_in_seconds,
35
+ threshold: rate_limit_per_interval })
36
+ .exactly(4).times
37
+ end
38
+
39
+ it "adds keeps track of the rate limiter key calls" do
40
+ allow(rate_limiter).to receive(:exec_within_threshold).and_yield
41
+ allow(rate_limiter).to receive(:add)
42
+
43
+ rate_limit_4_calls_to_original_request_code
44
+
45
+ expect(rate_limiter)
46
+ .to have_received(:add)
47
+ .with("jira_auto_tool_api_requests")
48
+ .exactly(4).times
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,78 +1,99 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "jira/auto/tool/rate_limited_jira_client"
3
+ require "rspec"
4
4
 
5
5
  module Jira
6
6
  module Auto
7
7
  class Tool
8
- class RateLimitedJiraClient
9
- RSpec.describe RateLimitedJiraClient do
10
- describe "#request" do
11
- let(:client) { described_class.new({}, rate_interval:, rate_limit:) }
12
- let(:rate_interval) { 2 }
13
- let(:rate_limit) { 1 }
14
- let(:oauth_client) { instance_double(JIRA::OauthClient, request: nil, consumer: nil) }
15
- let(:rate_limiter) { instance_double(Ratelimit) }
8
+ RSpec.describe RateLimitedJiraClient do
9
+ describe ".implementation_class_for" do
10
+ let(:result) { described_class.implementation_class_for(tool) }
11
+ let(:tool) { instance_double(Tool) }
16
12
 
13
+ context "when the rate limiting implementation is unspecified" do
17
14
  before do
18
- allow(described_class).to receive_messages(rate_limiter: rate_limiter)
19
-
20
- allow(JIRA::OauthClient).to receive_messages(new: oauth_client)
15
+ allow(tool).to receive_messages(jat_rate_limit_implementation_when_defined_else: nil)
16
+ end
21
17
 
22
- allow(client).to receive_messages(original_request: :response)
18
+ it { expect(result).to eq(RateLimitedJiraClient::InProcessBased) }
19
+ end
23
20
 
24
- allow(rate_limiter).to receive_messages(add: nil)
25
- allow(rate_limiter).to receive(:exec_within_threshold).and_yield
21
+ context "when using in process based rate limiting" do
22
+ before do
23
+ allow(tool).to receive_messages(jat_rate_limit_implementation_when_defined_else: "in_process")
26
24
  end
27
25
 
28
- it "returns the response" do
29
- expect(client.request(:get, "/path/to/resource")).to eq(:response)
26
+ it { expect(result).to eq(RateLimitedJiraClient::InProcessBased) }
27
+ end
28
+
29
+ context "when using in Redis based rate limiting" do
30
+ before do
31
+ allow(tool).to receive_messages(jat_rate_limit_implementation_when_defined_else: "redis")
30
32
  end
31
33
 
32
- it "calls the original request method" do
33
- client.request(:get, "/path/to/resource")
34
+ it { expect(result).to eq(RateLimitedJiraClient::RedisBased) }
35
+ end
36
+
37
+ context "when the request implementation is unexpected" do
38
+ before do
39
+ allow(tool)
40
+ .to receive_messages(jat_rate_limit_implementation_when_defined_else: "unexpected_implementation")
41
+ end
34
42
 
35
- expect(client).to have_received(:original_request).with(:get, "/path/to/resource")
43
+ it do
44
+ expect { result }
45
+ .to raise_error(RuntimeError,
46
+ %("unexpected_implementation": unexpected rate limiting implementation specified!"))
36
47
  end
48
+ end
49
+ end
37
50
 
38
- context "when it leverages the rate limiter" do
39
- it "uses :exec_within_threshold to control rate limiting" do
40
- allow(rate_limiter).to receive_messages(exec_within_threshold: nil)
51
+ RSpec.shared_examples "a rate limited client" do
52
+ before do
53
+ allow(client).to receive_messages(original_request: :response)
54
+ end
41
55
 
42
- 4.times { client.request(:get, "/path/to/resource") }
56
+ it "returns the response" do
57
+ expect(client.request(:get, "/path/to/resource")).to eq(:response)
58
+ end
43
59
 
44
- expect(rate_limiter)
45
- .to have_received(:exec_within_threshold)
46
- .with("jira_auto_tool_api_requests", { interval: rate_interval, threshold: rate_limit })
47
- .exactly(4).times
48
- end
60
+ it "calls the original request method" do
61
+ client.request(:get, "/path/to/resource")
49
62
 
50
- it "adds keeps track of the rate limiter key calls" do
51
- allow(rate_limiter).to receive_messages(add: nil)
63
+ expect(client).to have_received(:original_request).with(:get, "/path/to/resource")
64
+ end
65
+ end
52
66
 
53
- 4.times { client.request(:get, "/path/to/resource") }
67
+ describe "#request" do
68
+ let(:client) { described_class.new({}, rate_interval_in_seconds:, rate_limit_per_interval:) }
54
69
 
55
- expect(rate_limiter)
56
- .to have_received(:add)
57
- .with("jira_auto_tool_api_requests")
58
- .exactly(4).times
59
- end
60
- end
70
+ context "when the rate limiter is not needed" do
71
+ let(:rate_interval_in_seconds) { 0 }
72
+ let(:rate_limit_per_interval) { 0 }
61
73
 
62
- context "when it does not leverage the rate limiter" do
63
- let(:rate_limit) { 0 }
74
+ it_behaves_like "a rate limited client"
64
75
 
65
- it "does not use :exec_within_threshold to control rate limiting" do
66
- client.request(:get, "/path/to/resource")
76
+ it "does not use the rate limiter" do
77
+ allow(client).to receive(:original_request).with(:get, "/path/to/resource")
78
+ expect(client).not_to receive(:rate_limit)
67
79
 
68
- expect(rate_limiter).not_to have_received(:exec_within_threshold)
69
- end
80
+ client.request(:get, "/path/to/resource")
81
+ end
82
+ end
83
+
84
+ context "when the rate limiter is needed" do
85
+ let(:rate_interval_in_seconds) { 2 }
86
+ let(:rate_limit_per_interval) { 1 }
70
87
 
71
- it "calls the original request method" do
72
- client.request(:get, "/path/to/resource")
88
+ it_behaves_like "a rate limited client" do
89
+ before { allow(client).to receive(:rate_limit).and_yield }
90
+ end
91
+
92
+ it "uses the rate limiter" do
93
+ allow(client).to receive(:original_request).with(:get, "/path/to/resource")
94
+ expect(client).to receive(:rate_limit).and_yield
73
95
 
74
- expect(client).to have_received(:original_request).with(:get, "/path/to/resource")
75
- end
96
+ client.request(:get, "/path/to/resource")
76
97
  end
77
98
  end
78
99
  end