fcm 2.0.1 → 2.0.3

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/spec/fcm_spec.rb CHANGED
@@ -1,57 +1,138 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "spec_helper"
4
+ require "tempfile"
2
5
 
3
6
  describe FCM do
4
- let(:project_name) { 'test-project' }
5
- let(:json_key_path) { 'path/to/json/key.json' }
6
- let(:client) { FCM.new(json_key_path) }
7
+ let(:project_name) { "test-project" }
8
+ let(:json_key_path) { "path/to/json/key.json" }
9
+ let(:client) { described_class.new(json_key_path) }
7
10
 
8
11
  let(:mock_token) { "access_token" }
9
12
  let(:mock_headers) do
10
13
  {
11
14
  "Content-Type" => "application/json",
12
- "Authorization" => "Bearer #{mock_token}",
15
+ "Authorization" => "Bearer #{mock_token}"
13
16
  }
14
17
  end
15
18
 
19
+ let(:client_email) do
20
+ "83315528762cf7e0-7bbcc3aad87e0083391bc7f234d487" \
21
+ "c8@developer.gserviceaccount.com"
22
+ end
23
+
24
+ let(:client_x509_cert_url) do
25
+ "https://www.googleapis.com/robot/v1/metadata/x509/" \
26
+ 'fd6b61037dd2bb8585527679" + "-7bbcc3aad87e0083391b' \
27
+ "c7f234d487c8%40developer.gserviceaccount.com"
28
+ end
29
+
30
+ let(:large_file_name) do
31
+ "#{Array.new(1021) { "a" }.join}.txt"
32
+ end
33
+
34
+ let(:creds_error) do
35
+ FCM::InvalidCredentialError
36
+ end
37
+
38
+ let(:json_credentials) do
39
+ {
40
+ type: "service_account",
41
+ project_id: "example",
42
+ private_key_id: "c09c4593eee53707ca9f4208fbd6fe72b29fc7ab",
43
+ private_key: OpenSSL::PKey::RSA.new(2048).to_s,
44
+ client_email: client_email,
45
+ client_id: "acedc3c0a63b3562376386f0.apps.googleusercontent.com",
46
+ auth_uri: "https://accounts.google.com/o/oauth2/auth",
47
+ token_uri: "https://oauth2.googleapis.com/token",
48
+ auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs",
49
+ client_x509_cert_url: client_x509_cert_url,
50
+ universe_domain: "googleapis.com"
51
+ }.to_json
52
+ end
53
+
16
54
  before do
17
55
  allow(client).to receive(:json_key)
18
56
 
19
57
  # Mock the Google::Auth::ServiceAccountCredentials
20
- allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds).
21
- and_return(double(fetch_access_token!: { 'access_token' => mock_token }))
58
+ allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds)
59
+ .and_return(double(fetch_access_token!: { "access_token" => mock_token }))
22
60
  end
23
61
 
24
- it "should initialize" do
62
+ it "initializes" do
25
63
  expect { client }.not_to raise_error
26
64
  end
27
65
 
28
66
  describe "credentials path" do
29
67
  it "can be a path to a file" do
30
- fcm = FCM.new("README.md")
68
+ fcm = described_class.new("README.md")
31
69
  expect(fcm.__send__(:json_key).class).to eq(File)
32
70
  end
33
71
 
72
+ it "raises an error when passed a large path" do
73
+ expect do
74
+ described_class.new(large_file_name).__send__(:json_key)
75
+ end.to raise_error(creds_error)
76
+ end
77
+
34
78
  it "can be an IO object" do
35
- fcm = FCM.new(StringIO.new("hey"))
79
+ fcm = described_class.new(StringIO.new("hey"))
36
80
  expect(fcm.__send__(:json_key).class).to eq(StringIO)
81
+
82
+ temp_file = Tempfile.new("hello_world.json")
83
+ temp_file.write(json_credentials)
84
+ fcm_with_temp_file = described_class.new(temp_file)
85
+
86
+ expect do
87
+ fcm_with_temp_file
88
+ end.not_to raise_error
89
+ temp_file.close
90
+ temp_file.unlink
91
+ end
92
+
93
+ it "raises an error when passed a non IO-like object" do
94
+ expect do
95
+ described_class.new(nil, "", {}).__send__(:json_key)
96
+ end.to raise_error(creds_error, "credentials must be " \
97
+ "an IO-like object or path. You passed nil.")
98
+
99
+ expect do
100
+ described_class.new(json_credentials, "", {}).__send__(:json_key)
101
+ end.to raise_error(creds_error, "credentials must be " \
102
+ "an IO-like object or path. You passed a String.")
103
+
104
+ expect do
105
+ described_class.new({}, "", {}).__send__(:json_key)
106
+ end.to raise_error(creds_error, "credentials must be " \
107
+ "an IO-like object or path. You passed a Hash.")
108
+ end
109
+
110
+ it "raises an error when passed a non-existent credentials file path" do
111
+ fcm = described_class.new("spec/fake_credentials.json", "", {})
112
+ expect { fcm.__send__(:json_key) }.to raise_error(creds_error)
113
+ end
114
+
115
+ it "raises an error when passed a string of a file that does not exist" do
116
+ fcm = described_class.new("example.txt", "", {})
117
+ expect { fcm.__send__(:json_key) }.to raise_error(creds_error)
37
118
  end
38
119
  end
39
120
 
40
121
  describe "#send_v1 or #send_notification_v1" do
41
- let(:client) { FCM.new(json_key_path, project_name) }
122
+ let(:client) { described_class.new(json_key_path, project_name) }
42
123
 
43
124
  let(:uri) { "#{FCM::BASE_URI_V1}#{project_name}/messages:send" }
44
125
  let(:status_code) { 200 }
45
126
 
46
127
  let(:stub_fcm_send_v1_request) do
47
128
  stub_request(:post, uri).with(
48
- body: { 'message' => send_v1_params }.to_json,
129
+ body: { "message" => send_v1_params }.to_json,
49
130
  headers: mock_headers
50
131
  ).to_return(
51
132
  # ref: https://firebase.google.com/docs/cloud-messaging/http-server-ref#interpret-downstream
52
133
  body: "{}",
53
134
  headers: {},
54
- status: status_code,
135
+ status: status_code
55
136
  )
56
137
  end
57
138
 
@@ -59,222 +140,222 @@ describe FCM do
59
140
  stub_fcm_send_v1_request
60
141
  end
61
142
 
62
- shared_examples 'succesfuly send notification' do
63
- it 'should send notification of HTTP V1 using POST to FCM server' do
143
+ shared_examples "succesfuly send notification" do
144
+ it "sends notification of HTTP V1 using POST to FCM server" do
64
145
  client.send_v1(send_v1_params).should eq(
65
- response: 'success', body: '{}', headers: {}, status_code: 200
146
+ response: "success", body: "{}", headers: {}, status_code: 200
66
147
  )
67
148
  stub_fcm_send_v1_request.should have_been_made.times(1)
68
149
  end
69
150
  end
70
151
 
71
- describe 'send to token' do
72
- let(:token) { '4sdsx' }
152
+ describe "send to token" do
153
+ let(:token) { "4sdsx" }
73
154
  let(:send_v1_params) do
74
155
  {
75
- 'token' => token,
76
- 'notification' => {
77
- 'title' => 'Breaking News',
78
- 'body' => 'New news story available.'
156
+ "token" => token,
157
+ "notification" => {
158
+ "title" => "Breaking News",
159
+ "body" => "New news story available."
79
160
  },
80
- 'data' => {
81
- 'story_id' => 'story_12345'
161
+ "data" => {
162
+ "story_id" => "story_12345"
82
163
  },
83
- 'android' => {
84
- 'notification' => {
85
- 'click_action' => 'TOP_STORY_ACTIVITY',
86
- 'body' => 'Check out the Top Story'
164
+ "android" => {
165
+ "notification" => {
166
+ "click_action" => "TOP_STORY_ACTIVITY",
167
+ "body" => "Check out the Top Story"
87
168
  }
88
169
  },
89
- 'apns' => {
90
- 'payload' => {
91
- 'aps' => {
92
- 'category' => 'NEW_MESSAGE_CATEGORY'
170
+ "apns" => {
171
+ "payload" => {
172
+ "aps" => {
173
+ "category" => "NEW_MESSAGE_CATEGORY"
93
174
  }
94
175
  }
95
176
  }
96
177
  }
97
178
  end
98
179
 
99
- include_examples 'succesfuly send notification'
180
+ it_behaves_like "succesfuly send notification"
100
181
 
101
- it 'includes all the response' do
182
+ it "includes all the response" do
102
183
  response = client.send_v1(send_v1_params)
103
184
  expect(response[:status_code]).to eq(status_code)
104
- expect(response[:response]).to eq('success')
105
- expect(response[:body]).to eq('{}')
185
+ expect(response[:response]).to eq("success")
186
+ expect(response[:body]).to eq("{}")
106
187
  expect(response[:headers]).to eq({})
107
188
  expect(response[:canonical_ids]).to be_nil
108
189
  expect(response[:not_registered_ids]).to be_nil
109
190
  end
110
191
  end
111
192
 
112
- describe 'send to multiple tokens' do
113
- let(:tokens) { ['4sdsx', '4sdsy'] }
193
+ describe "send to multiple tokens" do
194
+ let(:tokens) { %w[4sdsx 4sdsy] }
114
195
  let(:send_v1_params) do
115
196
  {
116
- 'token' => tokens,
117
- 'notification' => {
118
- 'title' => 'Breaking News',
119
- 'body' => 'New news story available.'
197
+ "token" => tokens,
198
+ "notification" => {
199
+ "title" => "Breaking News",
200
+ "body" => "New news story available."
120
201
  }
121
202
  }
122
203
  end
123
204
 
124
- include_examples 'succesfuly send notification'
205
+ it_behaves_like "succesfuly send notification"
125
206
  end
126
207
 
127
- describe 'send to topic' do
128
- let(:topic) { 'news' }
208
+ describe "send to topic" do
209
+ let(:topic) { "news" }
129
210
  let(:send_v1_params) do
130
211
  {
131
- 'topic' => topic,
132
- 'notification' => {
133
- 'title' => 'Breaking News',
134
- 'body' => 'New news story available.'
212
+ "topic" => topic,
213
+ "notification" => {
214
+ "title" => "Breaking News",
215
+ "body" => "New news story available."
135
216
  }
136
217
  }
137
218
  end
138
219
 
139
- include_examples 'succesfuly send notification'
220
+ it_behaves_like "succesfuly send notification"
140
221
 
141
- context 'when topic is invalid' do
142
- let(:topic) { '/topics/news$' }
222
+ context "when topic is invalid" do
223
+ let(:topic) { "/topics/news$" }
143
224
 
144
- it 'should raise error' do
225
+ it "raises error" do
145
226
  stub_fcm_send_v1_request.should_not have_been_requested
146
227
  end
147
228
  end
148
229
  end
149
230
 
150
- describe 'send to condition' do
231
+ describe "send to condition" do
151
232
  let(:condition) { "'foo' in topics" }
152
233
  let(:send_v1_params) do
153
234
  {
154
- 'condition' => condition,
155
- 'notification' => {
156
- 'title' => 'Breaking News',
157
- 'body' => 'New news story available.'
158
- },
235
+ "condition" => condition,
236
+ "notification" => {
237
+ "title" => "Breaking News",
238
+ "body" => "New news story available."
239
+ }
159
240
  }
160
241
  end
161
242
 
162
- include_examples 'succesfuly send notification'
243
+ it_behaves_like "succesfuly send notification"
163
244
  end
164
245
 
165
- describe 'send to notification_key' do
166
- let(:notification_key) { 'notification_key' }
246
+ describe "send to notification_key" do
247
+ let(:notification_key) { "notification_key" }
167
248
  let(:send_v1_params) do
168
249
  {
169
- 'notification_key' => notification_key,
170
- 'notification' => {
171
- 'title' => 'Breaking News',
172
- 'body' => 'New news story available.'
250
+ "notification_key" => notification_key,
251
+ "notification" => {
252
+ "title" => "Breaking News",
253
+ "body" => "New news story available."
173
254
  }
174
255
  }
175
256
  end
176
257
 
177
- include_examples 'succesfuly send notification'
258
+ it_behaves_like "succesfuly send notification"
178
259
  end
179
260
 
180
- context 'when project_name is empty' do
181
- let(:project_name) { '' }
261
+ context "when project_name is empty" do
262
+ let(:project_name) { "" }
182
263
  let(:send_v1_params) do
183
264
  {
184
- 'token' => '4sdsx',
185
- 'notification' => {
186
- 'title' => 'Breaking News',
187
- 'body' => 'New news story available.'
265
+ "token" => "4sdsx",
266
+ "notification" => {
267
+ "title" => "Breaking News",
268
+ "body" => "New news story available."
188
269
  }
189
270
  }
190
271
  end
191
272
 
192
- it 'should not send notification' do
273
+ it "does not send notification" do
193
274
  client.send_v1(send_v1_params)
194
275
  stub_fcm_send_v1_request.should_not have_been_requested
195
276
  end
196
277
  end
197
278
 
198
- describe 'error handling' do
279
+ describe "error handling" do
199
280
  let(:send_v1_params) do
200
281
  {
201
- 'token' => '4sdsx',
202
- 'notification' => {
203
- 'title' => 'Breaking News',
204
- 'body' => 'New news story available.'
282
+ "token" => "4sdsx",
283
+ "notification" => {
284
+ "title" => "Breaking News",
285
+ "body" => "New news story available."
205
286
  }
206
287
  }
207
288
  end
208
289
 
209
- context 'when status_code is 400' do
290
+ context "when status_code is 400" do
210
291
  let(:status_code) { 400 }
211
292
 
212
- it 'should raise error' do
293
+ it "raises error" do
213
294
  response = client.send_v1(send_v1_params)
214
295
  expect(response[:status_code]).to eq(status_code)
215
- expect(response[:response]).to include('Only applies for JSON requests')
296
+ expect(response[:response]).to include("Only applies for JSON requests")
216
297
  end
217
298
  end
218
299
 
219
- context 'when status_code is 401' do
300
+ context "when status_code is 401" do
220
301
  let(:status_code) { 401 }
221
302
 
222
- it 'should raise error' do
303
+ it "raises error" do
223
304
  response = client.send_v1(send_v1_params)
224
305
  expect(response[:status_code]).to eq(status_code)
225
- expect(response[:response]).to include('There was an error authenticating')
306
+ expect(response[:response]).to include("There was an error authenticating")
226
307
  end
227
308
  end
228
309
 
229
- context 'when status_code is 500' do
310
+ context "when status_code is 500" do
230
311
  let(:status_code) { 500 }
231
312
 
232
- it 'should raise error' do
313
+ it "raises error" do
233
314
  response = client.send_v1(send_v1_params)
234
315
  expect(response[:status_code]).to eq(status_code)
235
- expect(response[:response]).to include('There was an internal error')
316
+ expect(response[:response]).to include("There was an internal error")
236
317
  end
237
318
  end
238
319
 
239
- context 'when status_code is 503' do
320
+ context "when status_code is 503" do
240
321
  let(:status_code) { 503 }
241
322
 
242
- it 'should raise error' do
323
+ it "raises error" do
243
324
  response = client.send_v1(send_v1_params)
244
325
  expect(response[:status_code]).to eq(status_code)
245
- expect(response[:response]).to include('Server is temporarily unavailable')
326
+ expect(response[:response]).to include("Server is temporarily unavailable")
246
327
  end
247
328
  end
248
329
  end
249
330
  end
250
331
 
251
- describe '#send_to_topic' do
252
- let(:client) { FCM.new(json_key_path, project_name) }
332
+ describe "#send_to_topic" do
333
+ let(:client) { described_class.new(json_key_path, project_name) }
253
334
 
254
335
  let(:uri) { "#{FCM::BASE_URI_V1}#{project_name}/messages:send" }
255
336
 
256
- let(:topic) { 'news' }
337
+ let(:topic) { "news" }
257
338
  let(:params) do
258
339
  {
259
- 'topic' => topic
340
+ "topic" => topic
260
341
  }.merge(options)
261
342
  end
262
343
  let(:options) do
263
344
  {
264
- 'data' => {
265
- 'story_id' => 'story_12345'
345
+ "data" => {
346
+ "story_id" => "story_12345"
266
347
  }
267
348
  }
268
349
  end
269
350
 
270
351
  let(:stub_fcm_send_to_topic_request) do
271
352
  stub_request(:post, uri).with(
272
- body: { 'message' => params }.to_json,
353
+ body: { "message" => params }.to_json,
273
354
  headers: mock_headers
274
355
  ).to_return(
275
356
  body: "{}",
276
357
  headers: {},
277
- status: 200,
358
+ status: 200
278
359
  )
279
360
  end
280
361
 
@@ -282,17 +363,17 @@ describe FCM do
282
363
  stub_fcm_send_to_topic_request
283
364
  end
284
365
 
285
- it 'should send notification to topic using POST to FCM server' do
366
+ it "sends notification to topic using POST to FCM server" do
286
367
  client.send_to_topic(topic, options).should eq(
287
- response: 'success', body: '{}', headers: {}, status_code: 200
368
+ response: "success", body: "{}", headers: {}, status_code: 200
288
369
  )
289
370
  stub_fcm_send_to_topic_request.should have_been_made.times(1)
290
371
  end
291
372
 
292
- context 'when topic is invalid' do
293
- let(:topic) { '/topics/news$' }
373
+ context "when topic is invalid" do
374
+ let(:topic) { "/topics/news$" }
294
375
 
295
- it 'should raise error' do
376
+ it "raises error" do
296
377
  client.send_to_topic(topic, options)
297
378
  stub_fcm_send_to_topic_request.should_not have_been_requested
298
379
  end
@@ -300,32 +381,32 @@ describe FCM do
300
381
  end
301
382
 
302
383
  describe "#send_to_topic_condition" do
303
- let(:client) { FCM.new(json_key_path, project_name) }
384
+ let(:client) { described_class.new(json_key_path, project_name) }
304
385
 
305
386
  let(:uri) { "#{FCM::BASE_URI_V1}#{project_name}/messages:send" }
306
387
 
307
388
  let(:topic_condition) { "'foo' in topics" }
308
389
  let(:params) do
309
390
  {
310
- 'condition' => topic_condition
391
+ "condition" => topic_condition
311
392
  }.merge(options)
312
393
  end
313
394
  let(:options) do
314
395
  {
315
- 'data' => {
316
- 'story_id' => 'story_12345'
396
+ "data" => {
397
+ "story_id" => "story_12345"
317
398
  }
318
399
  }
319
400
  end
320
401
 
321
402
  let(:stub_fcm_send_to_topic_condition_request) do
322
403
  stub_request(:post, uri).with(
323
- body: { 'message' => params }.to_json,
404
+ body: { "message" => params }.to_json,
324
405
  headers: mock_headers
325
406
  ).to_return(
326
407
  body: "{}",
327
408
  headers: {},
328
- status: 200,
409
+ status: 200
329
410
  )
330
411
  end
331
412
 
@@ -333,17 +414,17 @@ describe FCM do
333
414
  stub_fcm_send_to_topic_condition_request
334
415
  end
335
416
 
336
- it 'should send notification to topic_condition using POST to FCM server' do
417
+ it "sends notification to topic_condition using POST to FCM server" do
337
418
  client.send_to_topic_condition(topic_condition, options).should eq(
338
- response: 'success', body: '{}', headers: {}, status_code: 200
419
+ response: "success", body: "{}", headers: {}, status_code: 200
339
420
  )
340
421
  stub_fcm_send_to_topic_condition_request.should have_been_made.times(1)
341
422
  end
342
423
 
343
- context 'when topic_condition is invalid' do
424
+ context "when topic_condition is invalid" do
344
425
  let(:topic_condition) { "'foo' in topics$" }
345
426
 
346
- it 'should raise error' do
427
+ it "raises error" do
347
428
  client.send_to_topic_condition(topic_condition, options)
348
429
  stub_fcm_send_to_topic_condition_request.should_not have_been_requested
349
430
  end
@@ -358,19 +439,19 @@ describe FCM do
358
439
  let(:uri) { "#{base_uri}/#{registration_token}" }
359
440
  let(:registration_token) { "42" }
360
441
 
361
- context 'without options' do
362
- it 'calls info endpoint' do
442
+ context "without options" do
443
+ it "calls info endpoint" do
363
444
  endpoint = stub_request(:get, uri).with(headers: mock_headers)
364
445
  get_info
365
446
  expect(endpoint).to have_been_requested
366
447
  end
367
448
  end
368
449
 
369
- context 'with detail option' do
450
+ context "with detail option" do
370
451
  let(:uri) { "#{base_uri}/#{registration_token}?details=true" }
371
452
  let(:options) { { details: true } }
372
453
 
373
- it 'calls info endpoint' do
454
+ it "calls info endpoint" do
374
455
  endpoint = stub_request(:get, uri).with(headers: mock_headers)
375
456
  get_info
376
457
  expect(endpoint).to have_been_requested
@@ -379,17 +460,17 @@ describe FCM do
379
460
  end
380
461
 
381
462
  describe "topic subscriptions" do
382
- let(:topic) { 'news' }
463
+ let(:topic) { "news" }
383
464
  let(:registration_token) { "42" }
384
- let(:registration_token_2) { "43" }
385
- let(:registration_tokens) { [registration_token, registration_token_2] }
465
+ let(:registration_token2) { "43" }
466
+ let(:registration_tokens) { [registration_token, registration_token2] }
386
467
 
387
468
  describe "#topic_subscription" do
388
469
  subject(:subscribe) { client.topic_subscription(topic, registration_token) }
389
470
 
390
471
  let(:uri) { "#{FCM::INSTANCE_ID_API}/iid/v1/#{registration_token}/rel/topics/#{topic}" }
391
472
 
392
- it 'subscribes to a topic' do
473
+ it "subscribes to a topic" do
393
474
  endpoint = stub_request(:post, uri).with(headers: mock_headers)
394
475
  subscribe
395
476
  expect(endpoint).to have_been_requested
@@ -402,7 +483,7 @@ describe FCM do
402
483
  let(:uri) { "#{FCM::INSTANCE_ID_API}/iid/v1:batchRemove" }
403
484
  let(:params) { { to: "/topics/#{topic}", registration_tokens: [registration_token] } }
404
485
 
405
- it 'unsubscribes from a topic' do
486
+ it "unsubscribes from a topic" do
406
487
  endpoint = stub_request(:post, uri).with(body: params.to_json, headers: mock_headers)
407
488
  unsubscribe
408
489
  expect(endpoint).to have_been_requested
@@ -415,7 +496,7 @@ describe FCM do
415
496
  let(:uri) { "#{FCM::INSTANCE_ID_API}/iid/v1:batchAdd" }
416
497
  let(:params) { { to: "/topics/#{topic}", registration_tokens: registration_tokens } }
417
498
 
418
- it 'subscribes to a topic' do
499
+ it "subscribes to a topic" do
419
500
  endpoint = stub_request(:post, uri).with(body: params.to_json, headers: mock_headers)
420
501
  batch_subscribe
421
502
  expect(endpoint).to have_been_requested
@@ -428,11 +509,95 @@ describe FCM do
428
509
  let(:uri) { "#{FCM::INSTANCE_ID_API}/iid/v1:batchRemove" }
429
510
  let(:params) { { to: "/topics/#{topic}", registration_tokens: registration_tokens } }
430
511
 
431
- it 'unsubscribes from a topic' do
512
+ it "unsubscribes from a topic" do
432
513
  endpoint = stub_request(:post, uri).with(body: params.to_json, headers: mock_headers)
433
514
  batch_unsubscribe
434
515
  expect(endpoint).to have_been_requested
435
516
  end
436
517
  end
437
518
  end
519
+
520
+ describe "keep_alive_connections" do
521
+ let(:client) { described_class.new(json_key_path, project_name, keep_alive_connections: true) }
522
+ let(:uri) { "#{FCM::BASE_URI_V1}#{project_name}/messages:send" }
523
+ let(:send_v1_params) { { "token" => "token", "notification" => { "title" => "hi" } } }
524
+
525
+ before do
526
+ stub_request(:post, uri).to_return(body: "{}", headers: {}, status: 200)
527
+ end
528
+
529
+ it "caches a Faraday connection per (thread, uri) and reuses it across calls" do
530
+ client.send_v1(send_v1_params)
531
+ first = client.__send__(:thread_connections)[FCM::BASE_URI_V1]
532
+
533
+ client.send_v1(send_v1_params)
534
+ second = client.__send__(:thread_connections)[FCM::BASE_URI_V1]
535
+
536
+ expect(first).to be_a(Faraday::Connection)
537
+ expect(second).to equal(first)
538
+ end
539
+
540
+ it "discards the cached connection when a request raises" do
541
+ client.send_v1(send_v1_params)
542
+ expect(client.__send__(:thread_connections)[FCM::BASE_URI_V1]).to be_a(Faraday::Connection)
543
+
544
+ stub_request(:post, uri).to_raise(Faraday::ConnectionFailed.new("boom"))
545
+
546
+ expect { client.send_v1(send_v1_params) }.to raise_error(Faraday::ConnectionFailed)
547
+ expect(client.__send__(:thread_connections)).not_to have_key(FCM::BASE_URI_V1)
548
+ end
549
+
550
+ it "does not share connections across FCM instances" do
551
+ other_client = described_class.new(json_key_path, project_name, keep_alive_connections: true)
552
+ allow(other_client).to receive(:json_key)
553
+
554
+ client.send_v1(send_v1_params)
555
+ other_client.send_v1(send_v1_params)
556
+
557
+ expect(client.__send__(:thread_connections)[FCM::BASE_URI_V1])
558
+ .not_to equal(other_client.__send__(:thread_connections)[FCM::BASE_URI_V1])
559
+ end
560
+
561
+ it "falls back to one-shot connections when disabled" do
562
+ one_shot_client = described_class.new(json_key_path, project_name)
563
+ allow(one_shot_client).to receive(:json_key)
564
+ one_shot_client.send_v1(send_v1_params)
565
+
566
+ expect(one_shot_client.__send__(:thread_connections)).to be_empty
567
+ end
568
+ end
569
+
570
+ describe "request timeouts" do
571
+ it "defaults timeout and open_timeout to DEFAULT_TIMEOUT" do
572
+ fcm = described_class.new(json_key_path, project_name)
573
+ allow(fcm).to receive(:json_key)
574
+
575
+ fcm.__send__(:for_uri, FCM::BASE_URI_V1) do |conn|
576
+ expect(conn.options.timeout).to eq(FCM::DEFAULT_TIMEOUT)
577
+ expect(conn.options.open_timeout).to eq(FCM::DEFAULT_TIMEOUT)
578
+ end
579
+ end
580
+
581
+ it "honours :timeout and :open_timeout from http_options" do
582
+ fcm = described_class.new(json_key_path, project_name, timeout: 7, open_timeout: 3)
583
+ allow(fcm).to receive(:json_key)
584
+
585
+ fcm.__send__(:for_uri, FCM::BASE_URI_V1) do |conn|
586
+ expect(conn.options.timeout).to eq(7)
587
+ expect(conn.options.open_timeout).to eq(3)
588
+ end
589
+ end
590
+
591
+ it "honours :timeout and :open_timeout on keep-alive connections" do
592
+ fcm = described_class.new(
593
+ json_key_path, project_name, keep_alive_connections: true, timeout: 7, open_timeout: 3
594
+ )
595
+ allow(fcm).to receive(:json_key)
596
+
597
+ fcm.__send__(:for_uri, FCM::BASE_URI_V1) do |conn|
598
+ expect(conn.options.timeout).to eq(7)
599
+ expect(conn.options.open_timeout).to eq(3)
600
+ end
601
+ end
602
+ end
438
603
  end