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