mailgun-ruby 1.4.2 → 1.4.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +30 -8
- data/.rubocop.yml +68 -0
- data/Gemfile +1 -1
- data/README.md +1 -1
- data/Rakefile +0 -5
- data/lib/mailgun/client.rb +12 -8
- data/lib/mailgun/domains/domains.rb +4 -2
- data/lib/mailgun/lists/opt_in_handler.rb +2 -4
- data/lib/mailgun/messages/batch_message.rb +2 -1
- data/lib/mailgun/messages/message_builder.rb +4 -32
- data/lib/mailgun/metrics/metrics.rb +6 -2
- data/lib/mailgun/response.rb +2 -2
- data/lib/mailgun/tags/analytics_tags.rb +9 -5
- data/lib/mailgun/tags/tags.rb +4 -2
- data/lib/mailgun/version.rb +1 -1
- data/lib/railgun/attachment.rb +4 -6
- data/lib/railgun/mailer.rb +2 -2
- data/mailgun.gemspec +4 -1
- data/spec/integration/analytics_tags_spec.rb +1 -1
- data/spec/integration/domains_spec.rb +7 -13
- data/spec/integration/events_spec.rb +1 -3
- data/spec/integration/list_members_spec.rb +1 -1
- data/spec/integration/logs_spec.rb +1 -1
- data/spec/integration/mailgun_spec.rb +3 -2
- data/spec/integration/metrics_spec.rb +9 -3
- data/spec/integration/suppressions_spec.rb +203 -26
- data/spec/integration/webhook_spec.rb +7 -2
- data/spec/spec_helper.rb +7 -0
- data/spec/unit/client_spec.rb +424 -0
- data/spec/unit/connection/test_client.rb +60 -13
- data/spec/unit/events/events_spec.rb +25 -9
- data/spec/unit/helpers/api_version_checker_spec.rb +206 -0
- data/spec/unit/lists/opt_in_handler_spec.rb +4 -2
- data/spec/unit/mailgun_spec.rb +7 -5
- data/spec/unit/messages/batch_message_spec.rb +25 -24
- data/spec/unit/messages/message_builder_spec.rb +83 -86
- data/spec/unit/railgun/content_type_spec.rb +7 -7
- data/spec/unit/railgun/mailer_spec.rb +17 -14
- data/spec/unit/response_spec.rb +225 -0
- data/vcr_cassettes/For_the_suppressions_handling_class/creates_a_single_bounce.yml +55 -0
- data/vcr_cassettes/suppressions.yml +1053 -170
- metadata +55 -5
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'yaml'
|
|
6
|
+
require 'mailgun/response'
|
|
7
|
+
require 'mailgun/exceptions/exceptions'
|
|
8
|
+
|
|
9
|
+
describe Mailgun::Response do
|
|
10
|
+
subject(:response) { described_class.new(http_response) }
|
|
11
|
+
|
|
12
|
+
let(:json_body) { '{"id":"<message-id@example.com>","message":"Queued. Thank you."}' }
|
|
13
|
+
let(:status200) { 200 }
|
|
14
|
+
|
|
15
|
+
# Minimal double that mimics a RestClient::Response
|
|
16
|
+
let(:http_response) do
|
|
17
|
+
double('http_response', body: json_body, status: status200)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
# .new
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
describe '#initialize' do
|
|
24
|
+
it 'sets body from the underlying response' do
|
|
25
|
+
expect(response.body).to eq(json_body)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'sets status from the underlying response' do
|
|
29
|
+
expect(response.status).to eq(status200)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'aliases code to status' do
|
|
33
|
+
expect(response.code).to eq(status200)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
# .from_hash
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
describe '.from_hash' do
|
|
41
|
+
subject(:from_hash_response) do
|
|
42
|
+
described_class.from_hash(body: json_body, status: status200)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'returns a Response instance' do
|
|
46
|
+
expect(from_hash_response).to be_a(described_class)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'sets body from the hash' do
|
|
50
|
+
expect(from_hash_response.body).to eq(json_body)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'sets status from the hash' do
|
|
54
|
+
expect(from_hash_response.status).to eq(status200)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'aliases code to status' do
|
|
58
|
+
expect(from_hash_response.code).to eq(status200)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# ---------------------------------------------------------------------------
|
|
63
|
+
# #to_h
|
|
64
|
+
# ---------------------------------------------------------------------------
|
|
65
|
+
describe '#to_h' do
|
|
66
|
+
it 'returns a Hash' do
|
|
67
|
+
expect(response.to_h).to be_a(Hash)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it 'correctly parses the JSON body' do
|
|
71
|
+
result = response.to_h
|
|
72
|
+
expect(result['id']).to eq('<message-id@example.com>')
|
|
73
|
+
expect(result['message']).to eq('Queued. Thank you.')
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'does not mutate the body attribute' do
|
|
77
|
+
expect { response.to_h }.not_to(change(response, :body))
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
context 'when the body is invalid JSON' do
|
|
81
|
+
let(:http_response) { double('http_response', body: 'not-json', status: 200) }
|
|
82
|
+
|
|
83
|
+
it 'raises a Mailgun::ParseError' do
|
|
84
|
+
expect { response.to_h }.to raise_error(Mailgun::ParseError)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# ---------------------------------------------------------------------------
|
|
90
|
+
# #to_h!
|
|
91
|
+
# ---------------------------------------------------------------------------
|
|
92
|
+
describe '#to_h!' do
|
|
93
|
+
it 'returns a Hash' do
|
|
94
|
+
expect(response.to_h!).to be_a(Hash)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it 'correctly parses the JSON body' do
|
|
98
|
+
result = response.to_h!
|
|
99
|
+
expect(result['message']).to eq('Queued. Thank you.')
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it 'replaces body with the parsed Hash' do
|
|
103
|
+
response.to_h!
|
|
104
|
+
expect(response.body).to be_a(Hash)
|
|
105
|
+
expect(response.body['id']).to eq('<message-id@example.com>')
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
context 'when the body is invalid JSON' do
|
|
109
|
+
let(:http_response) { double('http_response', body: 'not-json', status: 200) }
|
|
110
|
+
|
|
111
|
+
it 'raises a Mailgun::ParseError' do
|
|
112
|
+
expect { response.to_h! }.to raise_error(Mailgun::ParseError)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# ---------------------------------------------------------------------------
|
|
118
|
+
# #to_yaml
|
|
119
|
+
# ---------------------------------------------------------------------------
|
|
120
|
+
describe '#to_yaml' do
|
|
121
|
+
it 'returns a String' do
|
|
122
|
+
expect(response.to_yaml).to be_a(String)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it 'returns valid YAML that round-trips back to the expected hash' do
|
|
126
|
+
parsed = YAML.safe_load(response.to_yaml)
|
|
127
|
+
expect(parsed['id']).to eq('<message-id@example.com>')
|
|
128
|
+
expect(parsed['message']).to eq('Queued. Thank you.')
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it 'does not mutate the body attribute' do
|
|
132
|
+
expect { response.to_yaml }.not_to(change(response, :body))
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
context 'when the body is invalid JSON' do
|
|
136
|
+
let(:http_response) { double('http_response', body: 'not-json', status: 200) }
|
|
137
|
+
|
|
138
|
+
it 'raises a Mailgun::ParseError' do
|
|
139
|
+
expect { response.to_yaml }.to raise_error(Mailgun::ParseError)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# ---------------------------------------------------------------------------
|
|
145
|
+
# #to_yaml!
|
|
146
|
+
# ---------------------------------------------------------------------------
|
|
147
|
+
describe '#to_yaml!' do
|
|
148
|
+
it 'returns a String' do
|
|
149
|
+
expect(response.to_yaml!).to be_a(String)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
it 'returns valid YAML that round-trips back to the expected hash' do
|
|
153
|
+
yaml_str = response.to_yaml!
|
|
154
|
+
parsed = YAML.safe_load(yaml_str)
|
|
155
|
+
expect(parsed['message']).to eq('Queued. Thank you.')
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
it 'replaces body with the YAML string' do
|
|
159
|
+
response.to_yaml!
|
|
160
|
+
expect(response.body).to be_a(String)
|
|
161
|
+
expect(response.body).to include('Queued. Thank you.')
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
context 'when the body is invalid JSON' do
|
|
165
|
+
let(:http_response) { double('http_response', body: 'not-json', status: 200) }
|
|
166
|
+
|
|
167
|
+
it 'raises a Mailgun::ParseError' do
|
|
168
|
+
expect { response.to_yaml! }.to raise_error(Mailgun::ParseError)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# ---------------------------------------------------------------------------
|
|
174
|
+
# #success?
|
|
175
|
+
# ---------------------------------------------------------------------------
|
|
176
|
+
describe '#success?' do
|
|
177
|
+
context 'with a 2xx status code' do
|
|
178
|
+
[200, 201, 204, 299].each do |code|
|
|
179
|
+
it "returns true for status #{code}" do
|
|
180
|
+
allow(http_response).to receive(:status).and_return(code)
|
|
181
|
+
expect(described_class.new(http_response).success?).to be(true)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
context 'with a non-2xx status code' do
|
|
187
|
+
[400, 401, 403, 404, 422, 500, 503].each do |code|
|
|
188
|
+
it "returns false for status #{code}" do
|
|
189
|
+
allow(http_response).to receive(:status).and_return(code)
|
|
190
|
+
expect(described_class.new(http_response).success?).to be(false)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
it 'returns false for status 199 (just below 2xx range)' do
|
|
196
|
+
allow(http_response).to receive(:status).and_return(199)
|
|
197
|
+
expect(described_class.new(http_response).success?).to be(false)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
it 'returns false for status 300 (just above 2xx range)' do
|
|
201
|
+
allow(http_response).to receive(:status).and_return(300)
|
|
202
|
+
expect(described_class.new(http_response).success?).to be(false)
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# ---------------------------------------------------------------------------
|
|
207
|
+
# attr_accessor sanity checks
|
|
208
|
+
# ---------------------------------------------------------------------------
|
|
209
|
+
describe 'attribute accessors' do
|
|
210
|
+
it 'allows body to be overwritten' do
|
|
211
|
+
response.body = 'new body'
|
|
212
|
+
expect(response.body).to eq('new body')
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
it 'allows status to be overwritten' do
|
|
216
|
+
response.status = 404
|
|
217
|
+
expect(response.status).to eq(404)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
it 'allows code to be overwritten' do
|
|
221
|
+
response.code = 500
|
|
222
|
+
expect(response.code).to eq(500)
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
http_interactions:
|
|
3
|
+
- request:
|
|
4
|
+
method: post
|
|
5
|
+
uri: https://api.mailgun.net/bounces
|
|
6
|
+
body:
|
|
7
|
+
encoding: UTF-8
|
|
8
|
+
string: address=test5%40example.info&code=500&error=integration+testing
|
|
9
|
+
headers:
|
|
10
|
+
User-Agent:
|
|
11
|
+
- mailgun-sdk-ruby/1.4.2
|
|
12
|
+
Accept:
|
|
13
|
+
- "*/*"
|
|
14
|
+
Authorization:
|
|
15
|
+
- Basic XXX
|
|
16
|
+
Content-Type:
|
|
17
|
+
- application/x-www-form-urlencoded
|
|
18
|
+
Accept-Encoding:
|
|
19
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
|
20
|
+
response:
|
|
21
|
+
status:
|
|
22
|
+
code: 200
|
|
23
|
+
message: OK
|
|
24
|
+
headers:
|
|
25
|
+
Access-Control-Allow-Credentials:
|
|
26
|
+
- 'true'
|
|
27
|
+
Access-Control-Allow-Origin:
|
|
28
|
+
- "*"
|
|
29
|
+
Cache-Control:
|
|
30
|
+
- no-store
|
|
31
|
+
Content-Length:
|
|
32
|
+
- '89'
|
|
33
|
+
Content-Type:
|
|
34
|
+
- application/json; charset=utf-8
|
|
35
|
+
Date:
|
|
36
|
+
- Wed, 25 Mar 2026 18:09:17 GMT
|
|
37
|
+
Strict-Transport-Security:
|
|
38
|
+
- max-age=63072000; includeSubDomains
|
|
39
|
+
X-Mailgun-Key-Id:
|
|
40
|
+
- XXX
|
|
41
|
+
X-Request-Limit:
|
|
42
|
+
- '2500'
|
|
43
|
+
X-Request-Remaining:
|
|
44
|
+
- '2498'
|
|
45
|
+
X-Request-Reset:
|
|
46
|
+
- '1774462171770'
|
|
47
|
+
X-Xss-Protection:
|
|
48
|
+
- 1; mode=block
|
|
49
|
+
body:
|
|
50
|
+
encoding: UTF-8
|
|
51
|
+
string: '{"message":"Address has been added to the bounces table","address":"test5@example.info"}
|
|
52
|
+
|
|
53
|
+
'
|
|
54
|
+
recorded_at: Wed, 25 Mar 2026 18:09:17 GMT
|
|
55
|
+
recorded_with: VCR 6.4.0
|