mock_gcm 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +56 -0
- data/Rakefile +1 -0
- data/lib/mock_gcm/server.rb +188 -0
- data/lib/mock_gcm/version.rb +3 -0
- data/lib/mock_gcm.rb +10 -0
- data/mock_gcm.gemspec +25 -0
- data/spec/mock_gcm_spec.rb +329 -0
- data/spec/spec_helper.rb +22 -0
- metadata +124 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Anders Carling
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# MockGcm
|
2
|
+
|
3
|
+
Fake GCM server for your integration testing needs.
|
4
|
+
|
5
|
+
|
6
|
+
Please be aware that this does not test everything as specific tests for errors like InvalidTtl, DataTooBig, InvalidRegistration are not made - but their results can be mocked.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
gem 'mock_gcm'
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install mock_gcm
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
require 'mock_gcm'
|
24
|
+
|
25
|
+
mock_gcm = MockGCM.new("my key", 8282)
|
26
|
+
mock_gcm.start
|
27
|
+
|
28
|
+
require 'gcm_client' # some gcm client library
|
29
|
+
client = GcmClient.new(:url => "http://localhost:8282/", :api_key => "my key")
|
30
|
+
client.send("registration_id1", {:some => :data})
|
31
|
+
client.send("registration_id2", {:some => :data})
|
32
|
+
|
33
|
+
mock_gcm.received_messages =>
|
34
|
+
# => [
|
35
|
+
# {
|
36
|
+
# "collapse_key" => nil,
|
37
|
+
# "time_to_live" => nil,
|
38
|
+
# "delay_while_idle" => nil,
|
39
|
+
# "data" => {"some" => "data"},
|
40
|
+
# "registration_id" => "registration_id1"
|
41
|
+
# }, {
|
42
|
+
# "collapse_key" => nil,
|
43
|
+
# "time_to_live" => nil,
|
44
|
+
# "delay_while_idle" => nil,
|
45
|
+
# "data" => {"some" => "data"},
|
46
|
+
# "registration_id" => "registration_id2"
|
47
|
+
# }
|
48
|
+
# ]
|
49
|
+
|
50
|
+
## Contributing
|
51
|
+
|
52
|
+
1. Fork it
|
53
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
54
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
55
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
56
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require 'xmlrpc/httpserver'
|
2
|
+
require 'json'
|
3
|
+
require 'thread'
|
4
|
+
require 'forwardable'
|
5
|
+
|
6
|
+
module MockGCM
|
7
|
+
class Server
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
DEFAULT_HOST = 'localhost'
|
11
|
+
|
12
|
+
def initialize(api_key, port)
|
13
|
+
@api_key = api_key
|
14
|
+
|
15
|
+
@received_messages = []
|
16
|
+
@mutex = Mutex.new
|
17
|
+
|
18
|
+
@server = HttpServer.new(self, port, DEFAULT_HOST, 1, File.open("/dev/null"), false, false)
|
19
|
+
|
20
|
+
# Configurable error behaviour
|
21
|
+
@next_request_errno = nil
|
22
|
+
@canonicals = {}
|
23
|
+
@errors = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
# Manage server state
|
27
|
+
|
28
|
+
def_delegators :@server, :start, :stop, :stopped?
|
29
|
+
|
30
|
+
def mock_next_request_failure(errno)
|
31
|
+
@mutex.synchronize { @next_request_errno = Integer(errno) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def mock_canonical_id(reg_id, canonical_reg_id)
|
35
|
+
@mutex.synchronize { @canonicals[reg_id] = canonical_reg_id }
|
36
|
+
end
|
37
|
+
|
38
|
+
def mock_error(reg_id, error, options = {})
|
39
|
+
@mutex.synchronize { @errors[reg_id] = { :error => error, :times => options[:times] || -1 } }
|
40
|
+
end
|
41
|
+
|
42
|
+
# Check server state from request thread
|
43
|
+
|
44
|
+
def error_for(reg_id)
|
45
|
+
@mutex.synchronize {
|
46
|
+
return unless hsh = @errors[reg_id]
|
47
|
+
return unless hsh[:times] != 0
|
48
|
+
|
49
|
+
hsh[:times] -= 1 if hsh[:times] >= 1
|
50
|
+
hsh[:error]
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def canonical_id_for(reg_id)
|
55
|
+
@mutex.synchronize { @canonicals[reg_id] }
|
56
|
+
end
|
57
|
+
|
58
|
+
# Message log
|
59
|
+
|
60
|
+
def received_messages
|
61
|
+
@mutex.synchronize { @received_messages.dup }
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_received(reg_id, collapse_key, time_to_live, delay_while_idle, data)
|
65
|
+
hsh = {
|
66
|
+
'registration_id' => reg_id.freeze,
|
67
|
+
'collapse_key' => collapse_key.freeze,
|
68
|
+
'time_to_live' => time_to_live.freeze,
|
69
|
+
'delay_while_idle' => delay_while_idle.freeze,
|
70
|
+
'data' => data.freeze,
|
71
|
+
}.freeze
|
72
|
+
@mutex.synchronize { @received_messages << hsh }
|
73
|
+
end
|
74
|
+
|
75
|
+
# Check stuff
|
76
|
+
|
77
|
+
def check_fail_next_request(request, response, req_data)
|
78
|
+
next_request_errno = @mutex.synchronize do
|
79
|
+
@next_request_errno.tap { @next_request_errno = nil }
|
80
|
+
end
|
81
|
+
|
82
|
+
if next_request_errno
|
83
|
+
response.status = next_request_errno
|
84
|
+
false
|
85
|
+
else
|
86
|
+
true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def check_authorization_header(request, response, req_data)
|
91
|
+
if request.header['Authorization'] == "key=#{@api_key}"
|
92
|
+
true
|
93
|
+
else
|
94
|
+
response.status = 401
|
95
|
+
false
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def check_content_type(request, response, req_data)
|
100
|
+
if request.header['Content-Type'] == "application/json"
|
101
|
+
true
|
102
|
+
else
|
103
|
+
response.status = 400
|
104
|
+
false
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def check_data_format(request, response, req_data)
|
109
|
+
fail = Proc.new do
|
110
|
+
response.status = 400
|
111
|
+
return false
|
112
|
+
end
|
113
|
+
json = JSON.parse(req_data) rescue fail.call
|
114
|
+
|
115
|
+
# Required
|
116
|
+
fail.call unless json["data"].is_a?(Hash)
|
117
|
+
fail.call unless json["registration_ids"].is_a?(Array) && json["registration_ids"].all? { |reg_id| reg_id.is_a?(String) } && json["registration_ids"].size <= 1000
|
118
|
+
# Optional
|
119
|
+
fail.call unless json.fetch("collapse_key", "").is_a?(String)
|
120
|
+
fail.call unless json.fetch("time_to_live", 1).is_a?(Integer)
|
121
|
+
fail.call unless [true,false].include?(json.fetch("delay_while_idle", false))
|
122
|
+
|
123
|
+
valid_fields = ["data", "registration_ids", "collapse_key", "time_to_live", "delay_while_idle"]
|
124
|
+
json.keys.each do |key|
|
125
|
+
fail.call unless valid_fields.include?(key)
|
126
|
+
end
|
127
|
+
|
128
|
+
true
|
129
|
+
end
|
130
|
+
|
131
|
+
def handle_req_data(req_data)
|
132
|
+
req_json = JSON.parse(req_data)
|
133
|
+
|
134
|
+
success, failure, canonical_ids, results = 0, 0, 0, []
|
135
|
+
|
136
|
+
reg_ids = req_json['registration_ids']
|
137
|
+
reg_ids.each do |reg_id|
|
138
|
+
results << {}.tap do |result|
|
139
|
+
if error = error_for(reg_id)
|
140
|
+
result['error'] = error
|
141
|
+
failure += 1
|
142
|
+
next
|
143
|
+
end
|
144
|
+
|
145
|
+
if canonical_id = canonical_id_for(reg_id)
|
146
|
+
result['registration_id'] = canonical_id
|
147
|
+
canonical_ids += 1
|
148
|
+
end
|
149
|
+
|
150
|
+
result['message_id'] = rand(100_000_000)
|
151
|
+
success += 1
|
152
|
+
|
153
|
+
add_received(reg_id, req_json['collapse_key'], req_json['time_to_live'],
|
154
|
+
req_json['delay_while_idle'], req_json.fetch('data'))
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
return success, failure, canonical_ids, results
|
159
|
+
end
|
160
|
+
|
161
|
+
# HttpServer handlers
|
162
|
+
|
163
|
+
def ip_auth_handler(io)
|
164
|
+
true
|
165
|
+
end
|
166
|
+
|
167
|
+
def request_handler(request, response)
|
168
|
+
req_data = request.data.read(request.content_length)
|
169
|
+
|
170
|
+
return unless check_fail_next_request(request, response, req_data)
|
171
|
+
return unless check_authorization_header(request, response, req_data)
|
172
|
+
return unless check_content_type(request, response, req_data)
|
173
|
+
return unless check_data_format(request, response, req_data)
|
174
|
+
|
175
|
+
success, failure, canonical_ids, results = handle_req_data(req_data)
|
176
|
+
|
177
|
+
response.header['Content-Type'] = 'application/json'
|
178
|
+
response.body = {
|
179
|
+
'multicast_id' => rand(100_000_000),
|
180
|
+
'success' => success,
|
181
|
+
'failure' => failure,
|
182
|
+
'canonical_ids' => canonical_ids,
|
183
|
+
'results' => results
|
184
|
+
}.to_json
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
end
|
data/lib/mock_gcm.rb
ADDED
data/mock_gcm.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'mock_gcm/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "mock_gcm"
|
8
|
+
spec.version = MockGCM::VERSION
|
9
|
+
spec.authors = ["Anders Carling"]
|
10
|
+
spec.email = ["anders.carling@d05.se"]
|
11
|
+
spec.description = %q{Fake GCM server for your integration testing needs}
|
12
|
+
spec.summary = %q{Fake GCM server for your integration testing needs}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
spec.add_development_dependency "httpclient"
|
25
|
+
end
|
@@ -0,0 +1,329 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'json'
|
3
|
+
require 'httpclient'
|
4
|
+
|
5
|
+
describe MockGCM do
|
6
|
+
let(:api_key) { "secrets" }
|
7
|
+
let(:mock_gcm_port) { 8282 }
|
8
|
+
let(:mock_gcm) { MockGCM.new(api_key, mock_gcm_port) }
|
9
|
+
let(:mock_gcm_url) { "http://localhost:#{mock_gcm_port}" }
|
10
|
+
let(:http_client) { HTTPClient.new }
|
11
|
+
let(:headers) {
|
12
|
+
{ "Content-Type" => "application/json",
|
13
|
+
"Authorization" => "key=#{api_key}" }
|
14
|
+
}
|
15
|
+
let(:valid_data) {
|
16
|
+
{
|
17
|
+
"collapse_key" => "score_update",
|
18
|
+
"time_to_live" => 108,
|
19
|
+
"delay_while_idle" => true,
|
20
|
+
"data" => {
|
21
|
+
"score" => "4x8",
|
22
|
+
"time" => "15:16.2342"
|
23
|
+
},
|
24
|
+
"registration_ids" => ["4", "8", "15", "16", "23", "42"]
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
before { mock_gcm.start }
|
29
|
+
after { mock_gcm.stop; sleep(0.01) until mock_gcm.stopped? }
|
30
|
+
|
31
|
+
context 'correct data' do
|
32
|
+
optional_keys = ["collapse_key", "time_to_live", "delay_while_idle"]
|
33
|
+
([:all, :no] + optional_keys).each do |included_key|
|
34
|
+
it "should accept and report messages including #{included_key} optional key(s)" do
|
35
|
+
unless included_key == :all
|
36
|
+
optional_keys.each { |key| valid_data.delete(key) unless key == included_key }
|
37
|
+
end
|
38
|
+
|
39
|
+
resp = http_client.post(mock_gcm_url, valid_data.to_json, headers)
|
40
|
+
resp.should be_ok
|
41
|
+
resp.headers.fetch('Content-type').should == 'application/json'
|
42
|
+
|
43
|
+
json = JSON.parse(resp.body)
|
44
|
+
|
45
|
+
json.should include('multicast_id')
|
46
|
+
json.fetch('success').should == 6
|
47
|
+
json.fetch('failure').should == 0
|
48
|
+
json.fetch('canonical_ids').should == 0
|
49
|
+
|
50
|
+
results = json.fetch('results')
|
51
|
+
results.size.should == 6
|
52
|
+
results.each do |res|
|
53
|
+
res.should include('message_id')
|
54
|
+
res.should_not include('registration_id')
|
55
|
+
res.should_not include('error')
|
56
|
+
end
|
57
|
+
|
58
|
+
expected_report = valid_data['registration_ids'].map do |registration_id|
|
59
|
+
{ "collapse_key" => valid_data["collapse_key"],
|
60
|
+
"time_to_live" => valid_data["time_to_live"],
|
61
|
+
"delay_while_idle" => valid_data['delay_while_idle'],
|
62
|
+
"data" => valid_data["data"],
|
63
|
+
"registration_id" => registration_id }
|
64
|
+
end
|
65
|
+
mock_gcm.received_messages.should == expected_report
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "#mock_error" do
|
70
|
+
it "should fail sends to specified registration_id in subsequent requests" do
|
71
|
+
cnt = 1+rand(100)
|
72
|
+
|
73
|
+
errors = %w{
|
74
|
+
MissingRegistration InvalidRegistration MismatchSenderId NotRegistered MessageTooBig
|
75
|
+
InvalidDataKey InvalidTtl Unavailable InternalServerError InvalidPackageName
|
76
|
+
}
|
77
|
+
fails = {
|
78
|
+
"42" => errors.sample,
|
79
|
+
"16" => errors.sample
|
80
|
+
}
|
81
|
+
fails.each_pair do |reg_id, error|
|
82
|
+
mock_gcm.mock_error(reg_id, error)
|
83
|
+
end
|
84
|
+
|
85
|
+
cnt.times do
|
86
|
+
resp = http_client.post(mock_gcm_url, valid_data.to_json, headers)
|
87
|
+
resp.should be_ok
|
88
|
+
|
89
|
+
json = JSON.parse(resp.body)
|
90
|
+
json.fetch('success').should == 4
|
91
|
+
json.fetch('failure').should == 2
|
92
|
+
json.fetch('canonical_ids').should == 0
|
93
|
+
|
94
|
+
result_for = lambda do |reg_id|
|
95
|
+
json.fetch('results').at(valid_data['registration_ids'].index(reg_id))
|
96
|
+
end
|
97
|
+
|
98
|
+
fails.each_pair do |reg_id, error|
|
99
|
+
result = result_for.(reg_id)
|
100
|
+
result.should_not include('message_id')
|
101
|
+
result.fetch('error').should == error
|
102
|
+
result.should_not include('registration_id')
|
103
|
+
end
|
104
|
+
|
105
|
+
(valid_data['registration_ids'] - fails.keys).each do |reg_id|
|
106
|
+
result = result_for.(reg_id)
|
107
|
+
result.should include('message_id')
|
108
|
+
result.should_not include('error')
|
109
|
+
result.should_not include('registration_id')
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
mock_gcm.received_messages.size.should == cnt * 4
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should limit error reporting to :times times if specified" do
|
117
|
+
cnt = 1 + rand(100)
|
118
|
+
|
119
|
+
errors = %w{
|
120
|
+
MissingRegistration InvalidRegistration MismatchSenderId NotRegistered MessageTooBig
|
121
|
+
InvalidDataKey InvalidTtl Unavailable InternalServerError InvalidPackageName
|
122
|
+
}
|
123
|
+
fails = {
|
124
|
+
"42" => errors.sample,
|
125
|
+
"16" => errors.sample
|
126
|
+
}
|
127
|
+
fails.each_pair do |reg_id, error|
|
128
|
+
mock_gcm.mock_error(reg_id, error, :times => cnt)
|
129
|
+
end
|
130
|
+
|
131
|
+
cnt.times do
|
132
|
+
resp = http_client.post(mock_gcm_url, valid_data.to_json, headers)
|
133
|
+
resp.should be_ok
|
134
|
+
|
135
|
+
json = JSON.parse(resp.body)
|
136
|
+
json.fetch('success').should == 4
|
137
|
+
json.fetch('failure').should == 2
|
138
|
+
json.fetch('canonical_ids').should == 0
|
139
|
+
|
140
|
+
result_for = lambda do |reg_id|
|
141
|
+
json.fetch('results').at(valid_data['registration_ids'].index(reg_id))
|
142
|
+
end
|
143
|
+
|
144
|
+
fails.each_pair do |reg_id, error|
|
145
|
+
result = result_for.(reg_id)
|
146
|
+
result.should_not include('message_id')
|
147
|
+
result.fetch('error').should == error
|
148
|
+
result.should_not include('registration_id')
|
149
|
+
end
|
150
|
+
|
151
|
+
(valid_data['registration_ids'] - fails.keys).each do |reg_id|
|
152
|
+
result = result_for.(reg_id)
|
153
|
+
result.should include('message_id')
|
154
|
+
result.should_not include('error')
|
155
|
+
result.should_not include('registration_id')
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
resp = http_client.post(mock_gcm_url, valid_data.to_json, headers)
|
160
|
+
resp.should be_ok
|
161
|
+
|
162
|
+
json = JSON.parse(resp.body)
|
163
|
+
json.fetch('success').should == 6
|
164
|
+
json.fetch('failure').should == 0
|
165
|
+
|
166
|
+
mock_gcm.received_messages.size.should == 6 + cnt * 4
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should not affect unrelated requests" do
|
170
|
+
mock_gcm.mock_error("not in valid data", "Unavailable")
|
171
|
+
|
172
|
+
resp = http_client.post(mock_gcm_url, valid_data.to_json, headers)
|
173
|
+
resp.should be_ok
|
174
|
+
|
175
|
+
json = JSON.parse(resp.body)
|
176
|
+
json.fetch('failure').should == 0
|
177
|
+
json.fetch('results').each { |hash| hash.should_not include('error') }
|
178
|
+
|
179
|
+
mock_gcm.received_messages.size.should == 6
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
describe "#mock_canonical_id" do
|
185
|
+
|
186
|
+
it "should return canonical registration_id for specified registration_ids in subsequent requests" do
|
187
|
+
cnt = 1 + rand(100)
|
188
|
+
|
189
|
+
canonicals = { "8" => "27", "42" => "19" }
|
190
|
+
canonicals.each_pair do |reg_id, can_id|
|
191
|
+
mock_gcm.mock_canonical_id(reg_id, can_id)
|
192
|
+
end
|
193
|
+
|
194
|
+
cnt.times do
|
195
|
+
resp = http_client.post(mock_gcm_url, valid_data.to_json, headers)
|
196
|
+
resp.should be_ok
|
197
|
+
|
198
|
+
json = JSON.parse(resp.body)
|
199
|
+
json.fetch('success').should == 6
|
200
|
+
json.fetch('failure').should == 0
|
201
|
+
json.fetch('canonical_ids').should == 2
|
202
|
+
|
203
|
+
result_for = lambda do |reg_id|
|
204
|
+
json.fetch('results').at(valid_data['registration_ids'].index(reg_id))
|
205
|
+
end
|
206
|
+
|
207
|
+
canonicals.each do |reg_id, can_id|
|
208
|
+
result = result_for.(reg_id)
|
209
|
+
result.should include('message_id')
|
210
|
+
result.should_not include('error')
|
211
|
+
result.fetch('registration_id').should == can_id
|
212
|
+
end
|
213
|
+
|
214
|
+
(valid_data['registration_ids'] - canonicals.keys).each do |reg_id|
|
215
|
+
result = result_for.(reg_id)
|
216
|
+
result.should include('message_id')
|
217
|
+
result.should_not include('error')
|
218
|
+
result.should_not include('registration_id')
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
mock_gcm.received_messages.size.should == cnt * 6
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should not affect unrelated requests" do
|
226
|
+
mock_gcm.mock_canonical_id("not in valid data", "1")
|
227
|
+
|
228
|
+
resp = http_client.post(mock_gcm_url, valid_data.to_json, headers)
|
229
|
+
resp.should be_ok
|
230
|
+
|
231
|
+
json = JSON.parse(resp.body)
|
232
|
+
json.fetch('canonical_ids').should == 0
|
233
|
+
json.fetch('results').each { |hash| hash.should_not include('registration_id') }
|
234
|
+
|
235
|
+
mock_gcm.received_messages.size.should == 6
|
236
|
+
end
|
237
|
+
|
238
|
+
end
|
239
|
+
|
240
|
+
describe "#mock_next_request_failure" do
|
241
|
+
|
242
|
+
5.times do
|
243
|
+
errno = 500 + rand(100)
|
244
|
+
it "should fail (#{errno}) if requested" do
|
245
|
+
mock_gcm.mock_next_request_failure(errno)
|
246
|
+
resp = http_client.post(mock_gcm_url, valid_data.to_json, headers)
|
247
|
+
resp.status.should == errno
|
248
|
+
mock_gcm.received_messages.should be_empty
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
it "should clear after one failure" do
|
253
|
+
mock_gcm.mock_next_request_failure(500)
|
254
|
+
resp = http_client.post(mock_gcm_url, valid_data.to_json, headers)
|
255
|
+
resp.status.should == 500
|
256
|
+
mock_gcm.received_messages.should be_empty
|
257
|
+
|
258
|
+
resp = http_client.post(mock_gcm_url, valid_data.to_json, headers)
|
259
|
+
resp.should be_ok
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
context 'missing api key' do
|
266
|
+
|
267
|
+
it "should fail (401)" do
|
268
|
+
resp = http_client.post(mock_gcm_url, valid_data.to_json, headers.reject { |k,v| k == 'Authorization' })
|
269
|
+
resp.status.should == 401
|
270
|
+
mock_gcm.received_messages.should be_empty
|
271
|
+
end
|
272
|
+
|
273
|
+
end
|
274
|
+
|
275
|
+
context "incorrect data" do
|
276
|
+
|
277
|
+
it "should fail (400) given more than 1000 registration_ids" do
|
278
|
+
resp = http_client.post(mock_gcm_url, valid_data.tap { |d| d['registration_ids'] = 1.upto(1001).map(&:to_s) }.to_json, headers)
|
279
|
+
resp.status.should == 400
|
280
|
+
mock_gcm.received_messages.should be_empty
|
281
|
+
end
|
282
|
+
|
283
|
+
['data', 'registration_ids'].each do |key|
|
284
|
+
it "should fail (400) when #{key} (required) is missing" do
|
285
|
+
resp = http_client.post(mock_gcm_url, valid_data.tap { |d| d.delete(key) }.to_json, headers)
|
286
|
+
resp.status.should == 400
|
287
|
+
mock_gcm.received_messages.should be_empty
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
[ ['data', 1],
|
292
|
+
['registration_ids', 1],
|
293
|
+
['registration_ids', [1]],
|
294
|
+
['time_to_live', "123"],
|
295
|
+
['collapse_key', 1],
|
296
|
+
['delay_while_idle', "1"]
|
297
|
+
].each do |key, value|
|
298
|
+
it "should fail (400) when #{key} = #{value.inspect} (incorrect type)" do
|
299
|
+
resp = http_client.post(mock_gcm_url, valid_data.tap { |d| d[key] = value }.to_json, headers)
|
300
|
+
resp.status.should == 400
|
301
|
+
mock_gcm.received_messages.should be_empty
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
it "should fail(400) when extra keys are present" do
|
306
|
+
resp = http_client.post(mock_gcm_url, valid_data.tap { |d| d['extra'] = 1 }.to_json, headers)
|
307
|
+
resp.status.should == 400
|
308
|
+
mock_gcm.received_messages.should be_empty
|
309
|
+
end
|
310
|
+
|
311
|
+
it "should fail (400) if non-valid-json data is sent" do
|
312
|
+
resp = http_client.post(mock_gcm_url, "garbage%s" % valid_data.to_json, headers)
|
313
|
+
resp.status.should == 400
|
314
|
+
mock_gcm.received_messages.should be_empty
|
315
|
+
end
|
316
|
+
|
317
|
+
end
|
318
|
+
|
319
|
+
context "incorrect content-type header" do
|
320
|
+
|
321
|
+
it "should fail (400)" do
|
322
|
+
resp = http_client.post(mock_gcm_url, valid_data.to_json, headers.tap { |h| h['Content-Type'] = 'text/plain' })
|
323
|
+
resp.status.should == 400
|
324
|
+
mock_gcm.received_messages.should be_empty
|
325
|
+
end
|
326
|
+
|
327
|
+
end
|
328
|
+
|
329
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'mock_gcm'
|
5
|
+
|
6
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
7
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
8
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
9
|
+
# loaded once.
|
10
|
+
#
|
11
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
14
|
+
config.run_all_when_everything_filtered = true
|
15
|
+
config.filter_run :focus
|
16
|
+
|
17
|
+
# Run specs in random order to surface order dependencies. If you find an
|
18
|
+
# order dependency and want to debug it, you can fix the order by providing
|
19
|
+
# the seed, which is printed after each run.
|
20
|
+
# --seed 1234
|
21
|
+
config.order = 'random'
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mock_gcm
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Anders Carling
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-10-18 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
version_requirements: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
none: false
|
21
|
+
name: bundler
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
requirement: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '1.3'
|
29
|
+
none: false
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
version_requirements: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ! '>='
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
none: false
|
37
|
+
name: rake
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
requirement: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
none: false
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
version_requirements: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
none: false
|
53
|
+
name: rspec
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
none: false
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ! '>='
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
none: false
|
69
|
+
name: httpclient
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
none: false
|
78
|
+
description: Fake GCM server for your integration testing needs
|
79
|
+
email:
|
80
|
+
- anders.carling@d05.se
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files: []
|
84
|
+
files:
|
85
|
+
- .gitignore
|
86
|
+
- Gemfile
|
87
|
+
- LICENSE.txt
|
88
|
+
- README.md
|
89
|
+
- Rakefile
|
90
|
+
- lib/mock_gcm.rb
|
91
|
+
- lib/mock_gcm/server.rb
|
92
|
+
- lib/mock_gcm/version.rb
|
93
|
+
- mock_gcm.gemspec
|
94
|
+
- spec/mock_gcm_spec.rb
|
95
|
+
- spec/spec_helper.rb
|
96
|
+
homepage: ''
|
97
|
+
licenses:
|
98
|
+
- MIT
|
99
|
+
post_install_message:
|
100
|
+
rdoc_options: []
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ! '>='
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
none: false
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ! '>='
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
none: false
|
115
|
+
requirements: []
|
116
|
+
rubyforge_project:
|
117
|
+
rubygems_version: 1.8.23
|
118
|
+
signing_key:
|
119
|
+
specification_version: 3
|
120
|
+
summary: Fake GCM server for your integration testing needs
|
121
|
+
test_files:
|
122
|
+
- spec/mock_gcm_spec.rb
|
123
|
+
- spec/spec_helper.rb
|
124
|
+
has_rdoc:
|