pushover 3.0.2 → 4.0.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +28 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +11 -24
- data/.ruby-version +1 -0
- data/CONVENTIONS.md +19 -0
- data/Gemfile +1 -2
- data/Guardfile +3 -3
- data/README.md +236 -44
- data/Rakefile +9 -59
- data/exe/pushover +6 -5
- data/lib/pushover/client.rb +53 -0
- data/lib/pushover/glances.rb +86 -0
- data/lib/pushover/groups.rb +137 -0
- data/lib/pushover/licenses.rb +67 -0
- data/lib/pushover/limits.rb +18 -0
- data/lib/pushover/message.rb +29 -32
- data/lib/pushover/message_encryption.rb +48 -0
- data/lib/pushover/message_validator.rb +167 -0
- data/lib/pushover/messages.rb +20 -0
- data/lib/pushover/receipt.rb +23 -10
- data/lib/pushover/receipts.rb +60 -0
- data/lib/pushover/response.rb +8 -3
- data/lib/pushover/sounds.rb +18 -0
- data/lib/pushover/subscriptions.rb +48 -0
- data/lib/pushover/teams.rb +93 -0
- data/lib/pushover/users.rb +37 -0
- data/lib/pushover/version.rb +1 -1
- data/lib/pushover.rb +14 -1
- data/pushover.gemspec +9 -7
- data/spec/gem/specification_spec.rb +19 -0
- data/spec/pushover/client_spec.rb +149 -0
- data/spec/pushover/glances_spec.rb +206 -0
- data/spec/pushover/groups_spec.rb +486 -0
- data/spec/pushover/licenses_spec.rb +238 -0
- data/spec/pushover/limits_spec.rb +92 -0
- data/spec/pushover/message_spec.rb +146 -26
- data/spec/pushover/messages_spec.rb +199 -0
- data/spec/pushover/receipt_spec.rb +119 -11
- data/spec/pushover/receipts_spec.rb +232 -0
- data/spec/pushover/response_spec.rb +21 -16
- data/spec/pushover/sounds_spec.rb +92 -0
- data/spec/pushover/subscriptions_spec.rb +185 -0
- data/spec/pushover/teams_spec.rb +325 -0
- data/spec/pushover/users_spec.rb +115 -0
- data/spec/spec_helper.rb +2 -3
- metadata +54 -18
- data/.travis.yml +0 -8
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Pushover::Licenses do
|
|
4
|
+
subject(:licenses) { Pushover::Client.new(token: 'app-token').licenses }
|
|
5
|
+
|
|
6
|
+
let(:user) { 'u' * 30 }
|
|
7
|
+
|
|
8
|
+
after { Excon.stubs.clear }
|
|
9
|
+
|
|
10
|
+
describe '#get' do
|
|
11
|
+
context 'when Pushover returns available license credits' do
|
|
12
|
+
let(:request) { {} }
|
|
13
|
+
let(:response) { licenses.get }
|
|
14
|
+
|
|
15
|
+
before do
|
|
16
|
+
captured_request = request
|
|
17
|
+
Excon.stub(
|
|
18
|
+
method: :get,
|
|
19
|
+
path: '/1/licenses.json',
|
|
20
|
+
query: { token: 'app-token' },
|
|
21
|
+
body: nil
|
|
22
|
+
) do |params|
|
|
23
|
+
captured_request.replace(params)
|
|
24
|
+
{
|
|
25
|
+
body: Oj.dump('status' => 1, 'request' => 'request-id', 'credits' => 5),
|
|
26
|
+
headers: { 'X-Request-Id' => 'header-id' },
|
|
27
|
+
status: 200
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'gets credits with the application token in the query string' do
|
|
33
|
+
response
|
|
34
|
+
|
|
35
|
+
expect(request.slice(:method, :path, :query)).to eq(
|
|
36
|
+
method: :get, path: '/1/licenses.json', query: 'token=app-token'
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'does not send a request body' do
|
|
41
|
+
response
|
|
42
|
+
|
|
43
|
+
expect(request).not_to have_key(:body)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'returns response metadata and available credits' do
|
|
47
|
+
expect(response).to have_attributes(
|
|
48
|
+
status: 1, request: 'request-id', attributes: { 'credits' => 5 }
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'preserves response headers' do
|
|
53
|
+
expect(response.headers).to include('X-Request-Id' => 'header-id')
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
context 'when Pushover rejects the application token' do
|
|
58
|
+
before do
|
|
59
|
+
Excon.stub(
|
|
60
|
+
{ method: :get, path: '/1/licenses.json', query: { token: 'app-token' }, body: nil },
|
|
61
|
+
{
|
|
62
|
+
body: Oj.dump(
|
|
63
|
+
'status' => 0,
|
|
64
|
+
'request' => 'request-id',
|
|
65
|
+
'errors' => { 'token' => ['is invalid'] }
|
|
66
|
+
),
|
|
67
|
+
status: 400
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'preserves structured licensing errors' do
|
|
73
|
+
expect(licenses.get).to have_attributes(
|
|
74
|
+
status: 0, request: 'request-id', errors: { 'token' => ['is invalid'] }
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
describe '#assign' do
|
|
81
|
+
context 'with a user key and operating system' do
|
|
82
|
+
let(:request) { {} }
|
|
83
|
+
let(:response) { licenses.assign(user: user, os: 'Android') }
|
|
84
|
+
|
|
85
|
+
before do
|
|
86
|
+
captured_request = request
|
|
87
|
+
Excon.stub(method: :post, path: '/1/licenses/assign.json') do |params|
|
|
88
|
+
captured_request.replace(params)
|
|
89
|
+
{
|
|
90
|
+
body: Oj.dump('status' => 1, 'request' => 'request-id', 'credits' => 4),
|
|
91
|
+
headers: { 'X-Request-Id' => 'header-id' },
|
|
92
|
+
status: 200
|
|
93
|
+
}
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it 'posts one non-idempotent request to the assignment endpoint' do
|
|
98
|
+
response
|
|
99
|
+
|
|
100
|
+
expect(request.slice(:method, :path, :idempotent)).to eq(
|
|
101
|
+
method: :post, path: '/1/licenses/assign.json', idempotent: false
|
|
102
|
+
)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it 'serializes the application token and assignment fields with string keys' do
|
|
106
|
+
response
|
|
107
|
+
|
|
108
|
+
expect(Oj.strict_load(request[:body])).to eq(
|
|
109
|
+
'token' => 'app-token', 'user' => user, 'os' => 'Android'
|
|
110
|
+
)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it 'uses the JSON content type' do
|
|
114
|
+
response
|
|
115
|
+
|
|
116
|
+
expect(request[:headers]).to include('Content-Type' => 'application/json')
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it 'returns response metadata, remaining credits, and headers' do
|
|
120
|
+
expect(response).to have_attributes(
|
|
121
|
+
status: 1, request: 'request-id', attributes: { 'credits' => 4 }, headers: include('X-Request-Id' => 'header-id')
|
|
122
|
+
)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
context 'with an email address' do
|
|
127
|
+
let(:request) { {} }
|
|
128
|
+
|
|
129
|
+
before do
|
|
130
|
+
captured_request = request
|
|
131
|
+
Excon.stub(method: :post, path: '/1/licenses/assign.json') do |params|
|
|
132
|
+
captured_request.replace(params)
|
|
133
|
+
{ body: Oj.dump('status' => 1, 'request' => 'request-id', 'credits' => 4), status: 200 }
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
licenses.assign(email: 'person@example.test')
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it 'omits the user and operating system fields' do
|
|
140
|
+
expect(Oj.strict_load(request[:body])).to eq(
|
|
141
|
+
'token' => 'app-token', 'email' => 'person@example.test'
|
|
142
|
+
)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
context 'with both recipient identifiers' do
|
|
147
|
+
let(:request) { {} }
|
|
148
|
+
|
|
149
|
+
before do
|
|
150
|
+
captured_request = request
|
|
151
|
+
Excon.stub(method: :post, path: '/1/licenses/assign.json') do |params|
|
|
152
|
+
captured_request.replace(params)
|
|
153
|
+
{ body: Oj.dump('status' => 1, 'request' => 'request-id', 'credits' => 4), status: 200 }
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
licenses.assign(user: user, email: 'person@example.test')
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
it 'does not invent a mutual-exclusion rule' do
|
|
160
|
+
expect(Oj.strict_load(request[:body])).to include(
|
|
161
|
+
'user' => user, 'email' => 'person@example.test'
|
|
162
|
+
)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
context 'with a blank operating system' do
|
|
167
|
+
let(:request) { {} }
|
|
168
|
+
|
|
169
|
+
before do
|
|
170
|
+
captured_request = request
|
|
171
|
+
Excon.stub(method: :post, path: '/1/licenses/assign.json') do |params|
|
|
172
|
+
captured_request.replace(params)
|
|
173
|
+
{ body: Oj.dump('status' => 1, 'request' => 'request-id', 'credits' => 4), status: 200 }
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
licenses.assign(user: user, os: '')
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
it 'preserves the documented first-platform selection' do
|
|
180
|
+
expect(Oj.strict_load(request[:body])).to include('os' => '')
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
context 'when Pushover rejects the assignment' do
|
|
185
|
+
before do
|
|
186
|
+
Excon.stub(
|
|
187
|
+
{ method: :post, path: '/1/licenses/assign.json' },
|
|
188
|
+
{
|
|
189
|
+
body: Oj.dump(
|
|
190
|
+
'status' => 0,
|
|
191
|
+
'request' => 'request-id',
|
|
192
|
+
'errors' => { 'token' => ['is out of available license credits'] }
|
|
193
|
+
),
|
|
194
|
+
status: 400
|
|
195
|
+
}
|
|
196
|
+
)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
it 'returns structured errors without retrying or flattening them' do
|
|
200
|
+
expect(licenses.assign(user: user)).to have_attributes(
|
|
201
|
+
status: 0,
|
|
202
|
+
request: 'request-id',
|
|
203
|
+
errors: { 'token' => ['is out of available license credits'] }
|
|
204
|
+
)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
it 'requires a user key or email address' do
|
|
209
|
+
expect { licenses.assign }.to raise_error ArgumentError, /user or email must be supplied/
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
it 'validates a supplied blank user key' do
|
|
213
|
+
expect { licenses.assign(user: '', email: 'person@example.test') }
|
|
214
|
+
.to raise_error ArgumentError, /user must be supplied/
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
['u' * 29, 'u' * 31, "#{'u' * 29}-", 123].each do |invalid_user|
|
|
218
|
+
it 'requires a 30-character alphanumeric user key' do
|
|
219
|
+
expect { licenses.assign(user: invalid_user) }
|
|
220
|
+
.to raise_error ArgumentError, /user must be 30 alphanumeric characters/
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
['', ' ', 123].each do |invalid_email|
|
|
225
|
+
it 'requires a supplied email address to be a nonblank string' do
|
|
226
|
+
expect { licenses.assign(email: invalid_email) }
|
|
227
|
+
.to raise_error ArgumentError, /email must be a nonblank String/
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
['android', 'IOS', 'Windows', 123].each do |invalid_os|
|
|
232
|
+
it 'accepts only documented operating systems' do
|
|
233
|
+
expect { licenses.assign(user: user, os: invalid_os) }
|
|
234
|
+
.to raise_error ArgumentError, /os must be blank, Android, iOS, or Desktop/
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Pushover::Limits do
|
|
4
|
+
subject(:limits) { Pushover::Client.new(token: 'app-token').limits }
|
|
5
|
+
|
|
6
|
+
let(:usage_limits) do
|
|
7
|
+
{
|
|
8
|
+
'limit' => 10_000,
|
|
9
|
+
'remaining' => 7_496,
|
|
10
|
+
'reset' => 1_393_653_600
|
|
11
|
+
}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
after { Excon.stubs.clear }
|
|
15
|
+
|
|
16
|
+
describe '#get' do
|
|
17
|
+
context 'when Pushover returns account usage limits' do
|
|
18
|
+
let(:request) { {} }
|
|
19
|
+
let(:response) { limits.get }
|
|
20
|
+
|
|
21
|
+
before do
|
|
22
|
+
captured_request = request
|
|
23
|
+
Excon.stub(
|
|
24
|
+
method: :get,
|
|
25
|
+
path: '/1/apps/limits.json',
|
|
26
|
+
query: { token: 'app-token' },
|
|
27
|
+
body: nil
|
|
28
|
+
) do |params|
|
|
29
|
+
captured_request.replace(params)
|
|
30
|
+
{
|
|
31
|
+
body: Oj.dump({ 'status' => 1, 'request' => 'request-id' }.merge(usage_limits)),
|
|
32
|
+
headers: { 'X-Limit-App-Remaining' => '7496' },
|
|
33
|
+
status: 200
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'gets limits with the application token in the query string' do
|
|
39
|
+
response
|
|
40
|
+
|
|
41
|
+
expect(request.slice(:method, :path, :query)).to eq(
|
|
42
|
+
method: :get, path: '/1/apps/limits.json', query: 'token=app-token'
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'does not send a request body' do
|
|
47
|
+
response
|
|
48
|
+
|
|
49
|
+
expect(request).not_to have_key(:body)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'returns response metadata' do
|
|
53
|
+
expect(response).to have_attributes(status: 1, request: 'request-id')
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it 'returns the account usage limit, remaining messages, and reset time' do
|
|
57
|
+
expect(response.attributes).to eq(usage_limits)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'preserves response headers' do
|
|
61
|
+
expect(response.headers).to include('X-Limit-App-Remaining' => '7496')
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
context 'when Pushover rejects the application token' do
|
|
66
|
+
before do
|
|
67
|
+
Excon.stub(
|
|
68
|
+
{ method: :get, path: '/1/apps/limits.json', query: { token: 'app-token' }, body: nil },
|
|
69
|
+
{
|
|
70
|
+
body: Oj.dump(
|
|
71
|
+
'status' => 0,
|
|
72
|
+
'request' => 'request-id',
|
|
73
|
+
'errors' => ['application token is invalid'],
|
|
74
|
+
'token' => 'invalid'
|
|
75
|
+
),
|
|
76
|
+
status: 400
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'returns errors from a 4xx response' do
|
|
82
|
+
response = limits.get
|
|
83
|
+
|
|
84
|
+
expect(response).to have_attributes(status: 0, errors: ['application token is invalid'])
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it 'preserves endpoint-specific error details' do
|
|
88
|
+
expect(limits.get.attributes).to eq('token' => 'invalid')
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -1,35 +1,155 @@
|
|
|
1
|
+
require 'open3'
|
|
2
|
+
require 'rbconfig'
|
|
1
3
|
require 'spec_helper'
|
|
2
4
|
|
|
3
5
|
describe Pushover::Message do
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
it
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
6
|
+
subject(:legacy_message) { described_class.new(params) }
|
|
7
|
+
|
|
8
|
+
let(:params) { { token: 'app-token', user: 'user-key', message: 'Hello' } }
|
|
9
|
+
let(:standalone_script) do
|
|
10
|
+
<<~RUBY
|
|
11
|
+
require 'excon'
|
|
12
|
+
Excon.defaults[:mock] = true
|
|
13
|
+
require 'pushover/message'
|
|
14
|
+
Excon.stub(method: :post, path: '/1/messages.json') { { body: Oj.dump('status' => 1), status: 200 } }
|
|
15
|
+
abort unless Pushover::Message.new(token: 'app-token', user: 'user-key', message: 'Hello').push.status == 1
|
|
16
|
+
RUBY
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
after { Excon.stubs.clear }
|
|
20
|
+
|
|
21
|
+
it 'supports the standalone legacy require path' do
|
|
22
|
+
_stdout, stderr, status = Open3.capture3(RbConfig.ruby, '-Ilib', '-e', standalone_script)
|
|
23
|
+
|
|
24
|
+
expect(status).to be_success, stderr
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'uses an explicit compatibility class' do
|
|
28
|
+
expect(described_class.superclass).to eq(Object)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'accepts the legacy keyword form' do
|
|
32
|
+
message = described_class.new(token: 'app-token', user: 'user-key', message: 'Hello')
|
|
33
|
+
|
|
34
|
+
expect(message).to have_attributes(token: 'app-token', user: 'user-key', message: 'Hello')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'accepts a single Hash with string keys' do
|
|
38
|
+
message = described_class.new('token' => 'app-token', 'user' => 'user-key', 'message' => 'Hello')
|
|
39
|
+
|
|
40
|
+
expect(message).to have_attributes(token: 'app-token', user: 'user-key', message: 'Hello')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'keeps the legacy members writable' do
|
|
44
|
+
legacy_message.title = 'Updated'
|
|
45
|
+
|
|
46
|
+
expect(legacy_message.title).to eq('Updated')
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'rejects unknown members' do
|
|
50
|
+
expect { described_class.new(unknown: 'value') }.to raise_error ArgumentError
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'rejects non-Hash positional attributes' do
|
|
54
|
+
[nil, false, 1, 'invalid'].each do |attributes|
|
|
55
|
+
expect { described_class.new(attributes) }.to raise_error ArgumentError, /attributes must be supplied as a Hash/
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it { is_expected.to respond_to(:push).with(0).arguments }
|
|
60
|
+
|
|
61
|
+
describe '#push' do
|
|
62
|
+
before { allow(Warning).to receive(:warn) }
|
|
63
|
+
|
|
64
|
+
context 'with supported legacy fields' do
|
|
65
|
+
let(:request) { {} }
|
|
66
|
+
let(:response) { legacy_message.push }
|
|
67
|
+
let(:params) do
|
|
68
|
+
{
|
|
69
|
+
token: 'app-token',
|
|
70
|
+
user: 'user-key',
|
|
71
|
+
message: 'Hello',
|
|
72
|
+
device: 'phone-1',
|
|
73
|
+
title: 'Greeting',
|
|
74
|
+
url: 'https://example.test/details',
|
|
75
|
+
url_title: 'Details',
|
|
76
|
+
priority: 2,
|
|
77
|
+
sound: 'siren',
|
|
78
|
+
timestamp: 1_723_990_000,
|
|
79
|
+
expire: 60,
|
|
80
|
+
retry: 30,
|
|
81
|
+
callback: 'https://example.test/callback'
|
|
82
|
+
}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
before do
|
|
86
|
+
captured_request = request
|
|
87
|
+
Excon.stub(method: :post, path: '/1/messages.json') do |request_params|
|
|
88
|
+
captured_request.replace(request_params)
|
|
89
|
+
{
|
|
90
|
+
body: Oj.dump('status' => 1, 'request' => 'request-id'),
|
|
91
|
+
headers: { 'X-Limit-App-Remaining' => '9' },
|
|
92
|
+
status: 200
|
|
93
|
+
}
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it 'delegates to the shared message endpoint' do
|
|
98
|
+
response
|
|
99
|
+
|
|
100
|
+
expect(request.slice(:method, :path)).to eq(method: :post, path: '/1/messages.json')
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it 'serializes supported fields through the shared resource' do
|
|
104
|
+
response
|
|
105
|
+
expected = params.except(:token).transform_keys(&:to_s).merge('token' => 'app-token')
|
|
106
|
+
|
|
107
|
+
expect(Oj.strict_load(request[:body])).to eq(expected)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it 'uses the shared JSON content type' do
|
|
111
|
+
response
|
|
112
|
+
|
|
113
|
+
expect(request[:headers]).to include('Content-Type' => 'application/json')
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it 'returns the shared response' do
|
|
117
|
+
expect(response).to have_attributes(
|
|
118
|
+
status: 1, request: 'request-id', headers: include('X-Limit-App-Remaining' => '9')
|
|
119
|
+
)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'emits the exact migration warning once' do
|
|
123
|
+
response
|
|
124
|
+
|
|
125
|
+
expect(Warning).to have_received(:warn).with(
|
|
126
|
+
a_string_including('Pushover::Message#push is deprecated; use Pushover::Client.new(token: ...).messages.create(...)'),
|
|
127
|
+
category: nil
|
|
128
|
+
).once
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
it 'returns Pushover errors from the shared resource' do
|
|
133
|
+
Excon.stub(method: :post, path: '/1/messages.json') do |_request_params|
|
|
134
|
+
{ body: Oj.dump('status' => 0, 'errors' => ['user identifier is invalid']), status: 400 }
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
expect(legacy_message.push).to have_attributes(status: 0, errors: ['user identifier is invalid'])
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
%i[token user message].each do |parameter|
|
|
141
|
+
it "preserves the legacy missing-#{parameter} error" do
|
|
142
|
+
instance = described_class.new(params.except(parameter))
|
|
143
|
+
|
|
144
|
+
expect { instance.push }.to raise_error RuntimeError, "#{parameter} must be supplied"
|
|
145
|
+
end
|
|
26
146
|
end
|
|
27
147
|
|
|
28
|
-
['
|
|
29
|
-
|
|
30
|
-
|
|
148
|
+
['', 'raw attachment'].each do |attachment|
|
|
149
|
+
it 'rejects a non-nil raw attachment explicitly' do
|
|
150
|
+
instance = described_class.new(params.merge(attachment: attachment))
|
|
31
151
|
|
|
32
|
-
|
|
152
|
+
expect { instance.push }.to raise_error ArgumentError, /raw attachment is unsupported/
|
|
33
153
|
end
|
|
34
154
|
end
|
|
35
155
|
end
|