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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +30 -8
  3. data/.rubocop.yml +68 -0
  4. data/Gemfile +1 -1
  5. data/README.md +1 -1
  6. data/Rakefile +0 -5
  7. data/lib/mailgun/client.rb +12 -8
  8. data/lib/mailgun/domains/domains.rb +4 -2
  9. data/lib/mailgun/lists/opt_in_handler.rb +2 -4
  10. data/lib/mailgun/messages/batch_message.rb +2 -1
  11. data/lib/mailgun/messages/message_builder.rb +4 -32
  12. data/lib/mailgun/metrics/metrics.rb +6 -2
  13. data/lib/mailgun/response.rb +2 -2
  14. data/lib/mailgun/tags/analytics_tags.rb +9 -5
  15. data/lib/mailgun/tags/tags.rb +4 -2
  16. data/lib/mailgun/version.rb +1 -1
  17. data/lib/railgun/attachment.rb +4 -6
  18. data/lib/railgun/mailer.rb +2 -2
  19. data/mailgun.gemspec +4 -1
  20. data/spec/integration/analytics_tags_spec.rb +1 -1
  21. data/spec/integration/domains_spec.rb +7 -13
  22. data/spec/integration/events_spec.rb +1 -3
  23. data/spec/integration/list_members_spec.rb +1 -1
  24. data/spec/integration/logs_spec.rb +1 -1
  25. data/spec/integration/mailgun_spec.rb +3 -2
  26. data/spec/integration/metrics_spec.rb +9 -3
  27. data/spec/integration/suppressions_spec.rb +203 -26
  28. data/spec/integration/webhook_spec.rb +7 -2
  29. data/spec/spec_helper.rb +7 -0
  30. data/spec/unit/client_spec.rb +424 -0
  31. data/spec/unit/connection/test_client.rb +60 -13
  32. data/spec/unit/events/events_spec.rb +25 -9
  33. data/spec/unit/helpers/api_version_checker_spec.rb +206 -0
  34. data/spec/unit/lists/opt_in_handler_spec.rb +4 -2
  35. data/spec/unit/mailgun_spec.rb +7 -5
  36. data/spec/unit/messages/batch_message_spec.rb +25 -24
  37. data/spec/unit/messages/message_builder_spec.rb +83 -86
  38. data/spec/unit/railgun/content_type_spec.rb +7 -7
  39. data/spec/unit/railgun/mailer_spec.rb +17 -14
  40. data/spec/unit/response_spec.rb +225 -0
  41. data/vcr_cassettes/For_the_suppressions_handling_class/creates_a_single_bounce.yml +55 -0
  42. data/vcr_cassettes/suppressions.yml +1053 -170
  43. 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