fcm 1.0.8 → 2.0.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.
data/spec/fcm_spec.rb CHANGED
@@ -1,547 +1,438 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe FCM do
4
- let(:send_url) { "#{FCM::BASE_URI}/fcm/send" }
5
- let(:group_notification_base_uri) { "#{FCM::GROUP_NOTIFICATION_BASE_URI}/gcm/notification" }
6
- let(:api_key) { "AIzaSyB-1uEai2WiUapxCs2Q0GZYzPu7Udno5aA" }
7
- let(:registration_id) { "42" }
8
- let(:registration_ids) { ["42"] }
9
- let(:key_name) { "appUser-Chris" }
10
- let(:project_id) { "123456789" } # https://developers.google.com/cloud-messaging/gcm#senderid
11
- let(:notification_key) { "APA91bGHXQBB...9QgnYOEURwm0I3lmyqzk2TXQ" }
12
- let(:valid_topic) { "TopicA" }
13
- let(:invalid_topic) { "TopicA$" }
14
- let(:valid_condition) { "'TopicA' in topics && ('TopicB' in topics || 'TopicC' in topics)" }
15
- let(:invalid_condition) { "'TopicA' in topics and some other text ('TopicB' in topics || 'TopicC' in topics)" }
16
- let(:invalid_condition_topic) { "'TopicA$' in topics" }
17
-
18
- it "should raise an error if the api key is not provided" do
19
- expect { FCM.new }.to raise_error(ArgumentError)
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
+
8
+ let(:mock_token) { "access_token" }
9
+ let(:mock_headers) do
10
+ {
11
+ "Content-Type" => "application/json",
12
+ "Authorization" => "Bearer #{mock_token}",
13
+ }
20
14
  end
21
15
 
22
- it "should raise error if time_to_live is given" do
23
- # ref: https://firebase.google.com/docs/cloud-messaging/http-server-ref#ttl
16
+ before do
17
+ allow(client).to receive(:json_key)
18
+
19
+ # 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 }))
24
22
  end
25
23
 
26
- describe "#send_v1" do
27
- pending "should send message"
24
+ it "should initialize" do
25
+ expect { client }.not_to raise_error
28
26
  end
29
27
 
30
- describe "sending notification" do
31
- let(:valid_request_body) do
32
- { registration_ids: registration_ids }
33
- end
34
- let(:valid_request_body_with_string) do
35
- { registration_ids: registration_id }
36
- end
37
- let(:valid_request_headers) do
38
- {
39
- "Content-Type" => "application/json",
40
- "Authorization" => "key=#{api_key}",
41
- }
28
+ describe "credentials path" do
29
+ it "can be a path to a file" do
30
+ fcm = FCM.new("README.md")
31
+ expect(fcm.__send__(:json_key).class).to eq(File)
42
32
  end
43
33
 
44
- let(:stub_fcm_send_request) do
45
- stub_request(:post, send_url).with(
46
- body: valid_request_body.to_json,
47
- headers: valid_request_headers,
48
- ).to_return(
49
- # ref: https://firebase.google.com/docs/cloud-messaging/http-server-ref#interpret-downstream
50
- body: "{}",
51
- headers: {},
52
- status: 200,
53
- )
34
+ it "can be an IO object" do
35
+ fcm = FCM.new(StringIO.new("hey"))
36
+ expect(fcm.__send__(:json_key).class).to eq(StringIO)
54
37
  end
38
+ end
39
+
40
+ describe "#send_v1 or #send_notification_v1" do
41
+ let(:client) { FCM.new(json_key_path, project_name) }
42
+
43
+ let(:uri) { "#{FCM::BASE_URI_V1}#{project_name}/messages:send" }
44
+ let(:status_code) { 200 }
55
45
 
56
- let(:stub_fcm_send_request_with_string) do
57
- stub_request(:post, send_url).with(
58
- body: valid_request_body_with_string.to_json,
59
- headers: valid_request_headers,
46
+ let(:stub_fcm_send_v1_request) do
47
+ stub_request(:post, uri).with(
48
+ body: { 'message' => send_v1_params }.to_json,
49
+ headers: mock_headers
60
50
  ).to_return(
51
+ # ref: https://firebase.google.com/docs/cloud-messaging/http-server-ref#interpret-downstream
61
52
  body: "{}",
62
53
  headers: {},
63
- status: 200,
54
+ status: status_code,
64
55
  )
65
56
  end
66
57
 
67
- let(:stub_fcm_send_request_with_basic_auth) do
68
- uri = URI.parse(send_url)
69
- uri.user = "a"
70
- uri.password = "b"
71
- stub_request(:post, uri.to_s).to_return(body: "{}", headers: {}, status: 200)
58
+ before do
59
+ stub_fcm_send_v1_request
72
60
  end
73
61
 
74
- before(:each) do
75
- stub_fcm_send_request
76
- stub_fcm_send_request_with_string
77
- stub_fcm_send_request_with_basic_auth
78
- end
79
-
80
- it "should send notification using POST to FCM server" do
81
- fcm = FCM.new(api_key)
82
- fcm.send(registration_ids).should eq(response: "success", body: "{}", headers: {}, status_code: 200, canonical_ids: [], not_registered_ids: [])
83
- stub_fcm_send_request.should have_been_made.times(1)
84
- end
85
-
86
- it "should send notification using POST to FCM if id provided as string" do
87
- fcm = FCM.new(api_key)
88
- fcm.send(registration_id).should eq(response: "success", body: "{}", headers: {}, status_code: 200, canonical_ids: [], not_registered_ids: [])
89
- stub_fcm_send_request.should have_been_made.times(1)
90
- end
91
-
92
- context "send notification with data" do
93
- let!(:stub_with_data) do
94
- stub_request(:post, send_url)
95
- .with(body: '{"registration_ids":["42"],"data":{"score":"5x1","time":"15:10"}}',
96
- headers: valid_request_headers)
97
- .to_return(status: 200, body: "", headers: {})
98
- end
99
- before do
100
- end
101
- it "should send the data in a post request to fcm" do
102
- fcm = FCM.new(api_key)
103
- fcm.send(registration_ids, data: { score: "5x1", time: "15:10" })
104
- stub_with_data.should have_been_requested
62
+ shared_examples 'succesfuly send notification' do
63
+ it 'should send notification of HTTP V1 using POST to FCM server' do
64
+ client.send_v1(send_v1_params).should eq(
65
+ response: 'success', body: '{}', headers: {}, status_code: 200
66
+ )
67
+ stub_fcm_send_v1_request.should have_been_made.times(1)
105
68
  end
106
69
  end
107
70
 
108
- context "sending notification to a topic" do
109
- let!(:stub_with_valid_topic) do
110
- stub_request(:post, send_url)
111
- .with(body: '{"to":"/topics/TopicA","data":{"score":"5x1","time":"15:10"}}',
112
- headers: valid_request_headers)
113
- .to_return(status: 200, body: "", headers: {})
114
- end
115
- let!(:stub_with_invalid_topic) do
116
- stub_request(:post, send_url)
117
- .with(body: '{"condition":"/topics/TopicA$","data":{"score":"5x1","time":"15:10"}}',
118
- headers: valid_request_headers)
119
- .to_return(status: 200, body: "", headers: {})
71
+ describe 'send to token' do
72
+ let(:token) { '4sdsx' }
73
+ let(:send_v1_params) do
74
+ {
75
+ 'token' => token,
76
+ 'notification' => {
77
+ 'title' => 'Breaking News',
78
+ 'body' => 'New news story available.'
79
+ },
80
+ 'data' => {
81
+ 'story_id' => 'story_12345'
82
+ },
83
+ 'android' => {
84
+ 'notification' => {
85
+ 'click_action' => 'TOP_STORY_ACTIVITY',
86
+ 'body' => 'Check out the Top Story'
87
+ }
88
+ },
89
+ 'apns' => {
90
+ 'payload' => {
91
+ 'aps' => {
92
+ 'category' => 'NEW_MESSAGE_CATEGORY'
93
+ }
94
+ }
95
+ }
96
+ }
120
97
  end
121
98
 
122
- describe "#send_to_topic" do
123
- it "should send the data in a post request to fcm" do
124
- fcm = FCM.new(api_key)
125
- fcm.send_to_topic(valid_topic, data: { score: "5x1", time: "15:10" })
126
- stub_with_valid_topic.should have_been_requested
127
- end
99
+ include_examples 'succesfuly send notification'
128
100
 
129
- it "should not send to invalid topics" do
130
- fcm = FCM.new(api_key)
131
- fcm.send_to_topic(invalid_topic, data: { score: "5x1", time: "15:10" })
132
- stub_with_invalid_topic.should_not have_been_requested
133
- end
101
+ it 'includes all the response' do
102
+ response = client.send_v1(send_v1_params)
103
+ expect(response[:status_code]).to eq(status_code)
104
+ expect(response[:response]).to eq('success')
105
+ expect(response[:body]).to eq('{}')
106
+ expect(response[:headers]).to eq({})
107
+ expect(response[:canonical_ids]).to be_nil
108
+ expect(response[:not_registered_ids]).to be_nil
134
109
  end
135
110
  end
136
111
 
137
- context "sending notification to a topic condition" do
138
- let!(:stub_with_valid_condition) do
139
- stub_request(:post, send_url)
140
- .with(body: '{"condition":"\'TopicA\' in topics && (\'TopicB\' in topics || \'TopicC\' in topics)","data":{"score":"5x1","time":"15:10"}}',
141
- headers: valid_request_headers)
142
- .to_return(status: 200, body: "", headers: {})
143
- end
144
- let!(:stub_with_invalid_condition) do
145
- stub_request(:post, send_url)
146
- .with(body: '{"condition":"\'TopicA\' in topics and some other text (\'TopicB\' in topics || \'TopicC\' in topics)","data":{"score":"5x1","time":"15:10"}}',
147
- headers: valid_request_headers)
148
- .to_return(status: 200, body: "", headers: {})
149
- end
150
- let!(:stub_with_invalid_condition_topic) do
151
- stub_request(:post, send_url)
152
- .with(body: '{"condition":"\'TopicA$\' in topics","data":{"score":"5x1","time":"15:10"}}',
153
- headers: valid_request_headers)
154
- .to_return(status: 200, body: "", headers: {})
112
+ describe 'send to multiple tokens' do
113
+ let(:tokens) { ['4sdsx', '4sdsy'] }
114
+ let(:send_v1_params) do
115
+ {
116
+ 'token' => tokens,
117
+ 'notification' => {
118
+ 'title' => 'Breaking News',
119
+ 'body' => 'New news story available.'
120
+ }
121
+ }
155
122
  end
156
123
 
157
- describe "#send_to_topic_condition" do
158
- it "should send the data in a post request to fcm" do
159
- fcm = FCM.new(api_key)
160
- fcm.send_to_topic_condition(valid_condition, data: { score: "5x1", time: "15:10" })
161
- stub_with_valid_condition.should have_been_requested
162
- end
163
-
164
- it "should not send to invalid conditions" do
165
- fcm = FCM.new(api_key)
166
- fcm.send_to_topic_condition(invalid_condition, data: { score: "5x1", time: "15:10" })
167
- stub_with_invalid_condition.should_not have_been_requested
168
- end
169
-
170
- it "should not send to invalid topics in a condition" do
171
- fcm = FCM.new(api_key)
172
- fcm.send_to_topic_condition(invalid_condition_topic, data: { score: "5x1", time: "15:10" })
173
- stub_with_invalid_condition_topic.should_not have_been_requested
174
- end
175
- end
124
+ include_examples 'succesfuly send notification'
176
125
  end
177
126
 
178
- context "when send_notification responds with failure" do
179
- let(:mock_request_attributes) do
127
+ describe 'send to topic' do
128
+ let(:topic) { 'news' }
129
+ let(:send_v1_params) do
180
130
  {
181
- body: valid_request_body.to_json,
182
- headers: valid_request_headers,
131
+ 'topic' => topic,
132
+ 'notification' => {
133
+ 'title' => 'Breaking News',
134
+ 'body' => 'New news story available.'
135
+ }
183
136
  }
184
137
  end
185
138
 
186
- subject { FCM.new(api_key) }
187
-
188
- context "on failure code 400" do
189
- before do
190
- stub_request(:post, send_url).with(
191
- mock_request_attributes
192
- ).to_return(
193
- # ref: https://firebase.google.com/docs/cloud-messaging/http-server-ref#interpret-downstream
194
- body: "{}",
195
- headers: {},
196
- status: 400,
197
- )
198
- end
199
- it "should not send notification due to 400" do
200
- subject.send(registration_ids).should eq(body: "{}",
201
- headers: {},
202
- response: "Only applies for JSON requests. Indicates that the request could not be parsed as JSON, or it contained invalid fields.",
203
- status_code: 400)
204
- end
205
- end
139
+ include_examples 'succesfuly send notification'
206
140
 
207
- context "on failure code 401" do
208
- before do
209
- stub_request(:post, send_url).with(
210
- mock_request_attributes
211
- ).to_return(
212
- # ref: https://firebase.google.com/docs/cloud-messaging/http-server-ref#interpret-downstream
213
- body: "{}",
214
- headers: {},
215
- status: 401,
216
- )
217
- end
141
+ context 'when topic is invalid' do
142
+ let(:topic) { '/topics/news$' }
218
143
 
219
- it "should not send notification due to 401" do
220
- subject.send(registration_ids).should eq(body: "{}",
221
- headers: {},
222
- response: "There was an error authenticating the sender account.",
223
- status_code: 401)
144
+ it 'should raise error' do
145
+ stub_fcm_send_v1_request.should_not have_been_requested
224
146
  end
225
147
  end
148
+ end
226
149
 
227
- context "on failure code 503" do
228
- before do
229
- stub_request(:post, send_url).with(
230
- mock_request_attributes
231
- ).to_return(
232
- # ref: https://firebase.google.com/docs/cloud-messaging/http-server-ref#interpret-downstream
233
- body: "{}",
234
- headers: {},
235
- status: 503,
236
- )
237
- end
238
-
239
- it "should not send notification due to 503" do
240
- subject.send(registration_ids).should eq(body: "{}",
241
- headers: {},
242
- response: "Server is temporarily unavailable.",
243
- status_code: 503)
244
- end
150
+ describe 'send to condition' do
151
+ let(:condition) { "'foo' in topics" }
152
+ let(:send_v1_params) do
153
+ {
154
+ 'condition' => condition,
155
+ 'notification' => {
156
+ 'title' => 'Breaking News',
157
+ 'body' => 'New news story available.'
158
+ },
159
+ }
245
160
  end
246
161
 
247
- context "on failure code 5xx" do
248
- before do
249
- stub_request(:post, send_url).with(
250
- mock_request_attributes
251
- ).to_return(
252
- # ref: https://firebase.google.com/docs/cloud-messaging/http-server-ref#interpret-downstream
253
- body: '{"body-key" => "Body value"}',
254
- headers: { "header-key" => "Header value" },
255
- status: 599,
256
- )
257
- end
258
-
259
- it "should not send notification due to 599" do
260
- subject.send(registration_ids).should eq(body: '{"body-key" => "Body value"}',
261
- headers: { "header-key" => "Header value" },
262
- response: "There was an internal error in the FCM server while trying to process the request.",
263
- status_code: 599)
264
- end
265
- end
162
+ include_examples 'succesfuly send notification'
266
163
  end
267
164
 
268
- context "when send_notification responds canonical_ids" do
269
- let(:mock_request_attributes) do
165
+ describe 'send to notification_key' do
166
+ let(:notification_key) { 'notification_key' }
167
+ let(:send_v1_params) do
270
168
  {
271
- body: valid_request_body.to_json,
272
- headers: valid_request_headers,
169
+ 'notification_key' => notification_key,
170
+ 'notification' => {
171
+ 'title' => 'Breaking News',
172
+ 'body' => 'New news story available.'
173
+ }
273
174
  }
274
175
  end
275
176
 
276
- let(:valid_response_body_with_canonical_ids) do
177
+ include_examples 'succesfuly send notification'
178
+ end
179
+
180
+ context 'when project_name is empty' do
181
+ let(:project_name) { '' }
182
+ let(:send_v1_params) do
277
183
  {
278
- failure: 0, canonical_ids: 1, results: [{ registration_id: "43", message_id: "0:1385025861956342%572c22801bb3" }],
184
+ 'token' => '4sdsx',
185
+ 'notification' => {
186
+ 'title' => 'Breaking News',
187
+ 'body' => 'New news story available.'
188
+ }
279
189
  }
280
190
  end
281
191
 
282
- subject { FCM.new(api_key) }
192
+ it 'should not send notification' do
193
+ client.send_v1(send_v1_params)
194
+ stub_fcm_send_v1_request.should_not have_been_requested
195
+ end
196
+ end
283
197
 
284
- before do
285
- stub_request(:post, send_url).with(
286
- mock_request_attributes
287
- ).to_return(
288
- # ref: https://firebase.google.com/docs/cloud-messaging/http-server-ref#interpret-downstream
289
- body: valid_response_body_with_canonical_ids.to_json,
290
- headers: {},
291
- status: 200,
292
- )
198
+ describe 'error handling' do
199
+ let(:send_v1_params) do
200
+ {
201
+ 'token' => '4sdsx',
202
+ 'notification' => {
203
+ 'title' => 'Breaking News',
204
+ 'body' => 'New news story available.'
205
+ }
206
+ }
293
207
  end
294
208
 
295
- it "should contain canonical_ids" do
296
- response = subject.send(registration_ids)
209
+ context 'when status_code is 400' do
210
+ let(:status_code) { 400 }
297
211
 
298
- response.should eq(headers: {},
299
- canonical_ids: [{ old: "42", new: "43" }],
300
- not_registered_ids: [],
301
- status_code: 200,
302
- response: "success",
303
- body: '{"failure":0,"canonical_ids":1,"results":[{"registration_id":"43","message_id":"0:1385025861956342%572c22801bb3"}]}')
212
+ it 'should raise error' do
213
+ response = client.send_v1(send_v1_params)
214
+ expect(response[:status_code]).to eq(status_code)
215
+ expect(response[:response]).to include('Only applies for JSON requests')
216
+ end
304
217
  end
305
- end
306
218
 
307
- context "when send_notification responds with NotRegistered" do
308
- subject { FCM.new(api_key) }
219
+ context 'when status_code is 401' do
220
+ let(:status_code) { 401 }
309
221
 
310
- let(:mock_request_attributes) do
311
- {
312
- body: valid_request_body.to_json,
313
- headers: valid_request_headers,
314
- }
222
+ it 'should raise error' do
223
+ response = client.send_v1(send_v1_params)
224
+ expect(response[:status_code]).to eq(status_code)
225
+ expect(response[:response]).to include('There was an error authenticating')
226
+ end
315
227
  end
316
228
 
317
- let(:valid_response_body_with_not_registered_ids) do
318
- {
319
- canonical_ids: 0, failure: 1, results: [{ error: "NotRegistered" }],
320
- }
321
- end
229
+ context 'when status_code is 500' do
230
+ let(:status_code) { 500 }
322
231
 
323
- before do
324
- stub_request(:post, send_url).with(
325
- mock_request_attributes
326
- ).to_return(
327
- body: valid_response_body_with_not_registered_ids.to_json,
328
- headers: {},
329
- status: 200,
330
- )
232
+ it 'should raise error' do
233
+ response = client.send_v1(send_v1_params)
234
+ expect(response[:status_code]).to eq(status_code)
235
+ expect(response[:response]).to include('There was an internal error')
236
+ end
331
237
  end
332
238
 
333
- it "should contain not_registered_ids" do
334
- response = subject.send(registration_ids)
335
- response.should eq(
336
- headers: {},
337
- canonical_ids: [],
338
- not_registered_ids: registration_ids,
339
- status_code: 200,
340
- response: "success",
341
- body: '{"canonical_ids":0,"failure":1,"results":[{"error":"NotRegistered"}]}',
342
- )
239
+ context 'when status_code is 503' do
240
+ let(:status_code) { 503 }
241
+
242
+ it 'should raise error' do
243
+ response = client.send_v1(send_v1_params)
244
+ expect(response[:status_code]).to eq(status_code)
245
+ expect(response[:response]).to include('Server is temporarily unavailable')
246
+ end
343
247
  end
344
248
  end
345
249
  end
346
250
 
347
- describe "sending group notifications" do
348
- # TODO: refactor to should_behave_like
349
- let(:valid_request_headers) do
251
+ describe '#send_to_topic' do
252
+ let(:client) { FCM.new(json_key_path, project_name) }
253
+
254
+ let(:uri) { "#{FCM::BASE_URI_V1}#{project_name}/messages:send" }
255
+
256
+ let(:topic) { 'news' }
257
+ let(:params) do
350
258
  {
351
- "Authorization" => "key=#{api_key}",
352
- "Content-Type" => "application/json",
353
- "Project-Id" => project_id,
354
- }
259
+ 'topic' => topic
260
+ }.merge(options)
355
261
  end
356
- let(:valid_response_body) do
357
- { notification_key: "APA91bGHXQBB...9QgnYOEURwm0I3lmyqzk2TXQ" }
358
- end
359
-
360
- let(:default_valid_request_body) do
262
+ let(:options) do
361
263
  {
362
- registration_ids: registration_ids,
363
- operation: "create",
364
- notification_key_name: key_name,
264
+ 'data' => {
265
+ 'story_id' => 'story_12345'
266
+ }
365
267
  }
366
268
  end
367
269
 
368
- subject { FCM.new(api_key) }
270
+ let(:stub_fcm_send_to_topic_request) do
271
+ stub_request(:post, uri).with(
272
+ body: { 'message' => params }.to_json,
273
+ headers: mock_headers
274
+ ).to_return(
275
+ body: "{}",
276
+ headers: {},
277
+ status: 200,
278
+ )
279
+ end
369
280
 
370
- # ref: https://firebase.google.com/docs/cloud-messaging/notifications#managing-device-groups-on-the-app-server
371
- context "create" do
372
- let(:valid_request_body) do
373
- default_valid_request_body.merge({
374
- operation: "create",
375
- })
376
- end
281
+ before do
282
+ stub_fcm_send_to_topic_request
283
+ end
377
284
 
378
- let(:mock_request_attributes) do
379
- {
380
- body: valid_request_body.to_json,
381
- headers: valid_request_headers,
382
- }
383
- end
285
+ it 'should send notification to topic using POST to FCM server' do
286
+ client.send_to_topic(topic, options).should eq(
287
+ response: 'success', body: '{}', headers: {}, status_code: 200
288
+ )
289
+ stub_fcm_send_to_topic_request.should have_been_made.times(1)
290
+ end
384
291
 
385
- before do
386
- stub_request(:post, group_notification_base_uri).with(
387
- mock_request_attributes
388
- ).to_return(
389
- body: valid_response_body.to_json,
390
- headers: {},
391
- status: 200,
392
- )
393
- end
292
+ context 'when topic is invalid' do
293
+ let(:topic) { '/topics/news$' }
394
294
 
395
- it "should send a post request" do
396
- response = subject.create(key_name, project_id, registration_ids)
397
- response.should eq(
398
- headers: {},
399
- status_code: 200,
400
- response: "success",
401
- body: valid_response_body.to_json,
402
- )
403
- end
404
- end # create context
405
-
406
- context "add" do
407
- let(:valid_request_body) do
408
- default_valid_request_body.merge({
409
- operation: "add",
410
- notification_key: notification_key,
411
- })
412
- end
413
-
414
- let(:mock_request_attributes) do
415
- {
416
- body: valid_request_body.to_json,
417
- headers: valid_request_headers,
418
- }
295
+ it 'should raise error' do
296
+ client.send_to_topic(topic, options)
297
+ stub_fcm_send_to_topic_request.should_not have_been_requested
419
298
  end
299
+ end
300
+ end
420
301
 
421
- before do
422
- stub_request(:post, group_notification_base_uri).with(
423
- mock_request_attributes
424
- ).to_return(
425
- body: valid_response_body.to_json,
426
- headers: {},
427
- status: 200,
428
- )
429
- end
302
+ describe "#send_to_topic_condition" do
303
+ let(:client) { FCM.new(json_key_path, project_name) }
430
304
 
431
- it "should send a post request" do
432
- response = subject.add(key_name, project_id, notification_key, registration_ids)
433
- response.should eq(
434
- headers: {},
435
- status_code: 200,
436
- response: "success",
437
- body: valid_response_body.to_json,
438
- )
439
- end
440
- end # add context
441
-
442
- context "remove" do
443
- let(:valid_request_body) do
444
- default_valid_request_body.merge({
445
- operation: "remove",
446
- notification_key: notification_key,
447
- })
448
- end
305
+ let(:uri) { "#{FCM::BASE_URI_V1}#{project_name}/messages:send" }
449
306
 
450
- let(:mock_request_attributes) do
451
- {
452
- body: valid_request_body.to_json,
453
- headers: valid_request_headers,
307
+ let(:topic_condition) { "'foo' in topics" }
308
+ let(:params) do
309
+ {
310
+ 'condition' => topic_condition
311
+ }.merge(options)
312
+ end
313
+ let(:options) do
314
+ {
315
+ 'data' => {
316
+ 'story_id' => 'story_12345'
454
317
  }
455
- end
318
+ }
319
+ end
456
320
 
457
- before do
458
- stub_request(:post, group_notification_base_uri).with(
459
- mock_request_attributes
460
- ).to_return(
461
- body: valid_response_body.to_json,
462
- headers: {},
463
- status: 200,
464
- )
465
- end
321
+ let(:stub_fcm_send_to_topic_condition_request) do
322
+ stub_request(:post, uri).with(
323
+ body: { 'message' => params }.to_json,
324
+ headers: mock_headers
325
+ ).to_return(
326
+ body: "{}",
327
+ headers: {},
328
+ status: 200,
329
+ )
330
+ end
466
331
 
467
- it "should send a post request" do
468
- response = subject.remove(key_name, project_id, notification_key, registration_ids)
469
- response.should eq(
470
- headers: {},
471
- status_code: 200,
472
- response: "success",
473
- body: valid_response_body.to_json,
474
- )
475
- end
476
- end # remove context
477
- end
332
+ before do
333
+ stub_fcm_send_to_topic_condition_request
334
+ end
478
335
 
479
- describe "#recover_notification_key" do
480
- it "sends a 'retrieve notification key' request" do
481
- uri = "#{FCM::GROUP_NOTIFICATION_BASE_URI}/gcm/notification"
482
- endpoint = stub_request(:get, uri).with(
483
- headers: {
484
- "Content-Type" => "application/json",
485
- "Authorization" => "key=TEST_SERVER_KEY",
486
- "project_id" => "TEST_PROJECT_ID",
487
- },
488
- query: { notification_key_name: "TEST_KEY_NAME" },
336
+ it 'should send notification to topic_condition using POST to FCM server' do
337
+ client.send_to_topic_condition(topic_condition, options).should eq(
338
+ response: 'success', body: '{}', headers: {}, status_code: 200
489
339
  )
490
- client = FCM.new("TEST_SERVER_KEY")
340
+ stub_fcm_send_to_topic_condition_request.should have_been_made.times(1)
341
+ end
491
342
 
492
- client.recover_notification_key("TEST_KEY_NAME", "TEST_PROJECT_ID")
343
+ context 'when topic_condition is invalid' do
344
+ let(:topic_condition) { "'foo' in topics$" }
493
345
 
494
- expect(endpoint).to have_been_requested
346
+ it 'should raise error' do
347
+ client.send_to_topic_condition(topic_condition, options)
348
+ stub_fcm_send_to_topic_condition_request.should_not have_been_requested
349
+ end
495
350
  end
496
351
  end
497
352
 
498
- describe "subscribing to a topic" do
499
- # TODO
500
- end
501
-
502
- describe 'getting instance info' do
503
- subject(:get_info) { client.get_instance_id_info(registration_id, options) }
353
+ describe "#get_instance_id_info" do
354
+ subject(:get_info) { client.get_instance_id_info(registration_token, options) }
504
355
 
505
356
  let(:options) { nil }
506
- let(:client) { FCM.new('TEST_SERVER_KEY') }
507
357
  let(:base_uri) { "#{FCM::INSTANCE_ID_API}/iid/info" }
508
- let(:uri) { "#{base_uri}/#{registration_id}" }
509
- let(:mock_request_attributes) do
510
- { headers: {
511
- 'Authorization' => 'key=TEST_SERVER_KEY',
512
- 'Content-Type' => 'application/json'
513
- } }
514
- end
358
+ let(:uri) { "#{base_uri}/#{registration_token}" }
359
+ let(:registration_token) { "42" }
515
360
 
516
361
  context 'without options' do
517
362
  it 'calls info endpoint' do
518
- endpoint = stub_request(:get, uri).with(mock_request_attributes)
363
+ endpoint = stub_request(:get, uri).with(headers: mock_headers)
519
364
  get_info
520
365
  expect(endpoint).to have_been_requested
521
366
  end
522
367
  end
523
368
 
524
369
  context 'with detail option' do
525
- let(:uri) { "#{base_uri}/#{registration_id}?details=true" }
370
+ let(:uri) { "#{base_uri}/#{registration_token}?details=true" }
526
371
  let(:options) { { details: true } }
527
372
 
528
373
  it 'calls info endpoint' do
529
- endpoint = stub_request(:get, uri).with(mock_request_attributes)
374
+ endpoint = stub_request(:get, uri).with(headers: mock_headers)
530
375
  get_info
531
376
  expect(endpoint).to have_been_requested
532
377
  end
533
378
  end
534
379
  end
535
380
 
536
- describe "credentials path" do
537
- it "can be a path to a file" do
538
- fcm = FCM.new("test", "README.md")
539
- expect(fcm.__send__(:json_key).class).to eq(File)
381
+ describe "topic subscriptions" do
382
+ let(:topic) { 'news' }
383
+ let(:registration_token) { "42" }
384
+ let(:registration_token_2) { "43" }
385
+ let(:registration_tokens) { [registration_token, registration_token_2] }
386
+
387
+ describe "#topic_subscription" do
388
+ subject(:subscribe) { client.topic_subscription(topic, registration_token) }
389
+
390
+ let(:uri) { "#{FCM::INSTANCE_ID_API}/iid/v1/#{registration_token}/rel/topics/#{topic}" }
391
+
392
+ it 'subscribes to a topic' do
393
+ endpoint = stub_request(:post, uri).with(headers: mock_headers)
394
+ subscribe
395
+ expect(endpoint).to have_been_requested
396
+ end
540
397
  end
541
398
 
542
- it "can be an IO object" do
543
- fcm = FCM.new("test", StringIO.new("hey"))
544
- expect(fcm.__send__(:json_key).class).to eq(StringIO)
399
+ describe "#topic_unsubscription" do
400
+ subject(:unsubscribe) { client.topic_unsubscription(topic, registration_token) }
401
+
402
+ let(:uri) { "#{FCM::INSTANCE_ID_API}/iid/v1:batchRemove" }
403
+ let(:params) { { to: "/topics/#{topic}", registration_tokens: [registration_token] } }
404
+
405
+ it 'unsubscribes from a topic' do
406
+ endpoint = stub_request(:post, uri).with(body: params.to_json, headers: mock_headers)
407
+ unsubscribe
408
+ expect(endpoint).to have_been_requested
409
+ end
410
+ end
411
+
412
+ describe "#batch_topic_subscription" do
413
+ subject(:batch_subscribe) { client.batch_topic_subscription(topic, registration_tokens) }
414
+
415
+ let(:uri) { "#{FCM::INSTANCE_ID_API}/iid/v1:batchAdd" }
416
+ let(:params) { { to: "/topics/#{topic}", registration_tokens: registration_tokens } }
417
+
418
+ it 'subscribes to a topic' do
419
+ endpoint = stub_request(:post, uri).with(body: params.to_json, headers: mock_headers)
420
+ batch_subscribe
421
+ expect(endpoint).to have_been_requested
422
+ end
423
+ end
424
+
425
+ describe "#batch_topic_unsubscription" do
426
+ subject(:batch_unsubscribe) { client.batch_topic_unsubscription(topic, registration_tokens) }
427
+
428
+ let(:uri) { "#{FCM::INSTANCE_ID_API}/iid/v1:batchRemove" }
429
+ let(:params) { { to: "/topics/#{topic}", registration_tokens: registration_tokens } }
430
+
431
+ it 'unsubscribes from a topic' do
432
+ endpoint = stub_request(:post, uri).with(body: params.to_json, headers: mock_headers)
433
+ batch_unsubscribe
434
+ expect(endpoint).to have_been_requested
435
+ end
545
436
  end
546
437
  end
547
438
  end