bug_bunny 4.18.0 → 5.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.
@@ -55,5 +55,181 @@ RSpec.describe BugBunny::Middleware::RaiseError do
55
55
  expect { middleware.on_complete(response) }.to raise_error(BugBunny::NotFound)
56
56
  end
57
57
  end
58
+
59
+ # Alcance issue #52: status + raw_response como materia prima en TODAS las
60
+ # clases de error, no solo en 422.
61
+ describe 'raw_response and status on every error class' do
62
+ def captured_error(response)
63
+ middleware.on_complete(response)
64
+ nil
65
+ rescue BugBunny::Error => e
66
+ e
67
+ end
68
+
69
+ {
70
+ 400 => BugBunny::BadRequest,
71
+ 409 => BugBunny::Conflict,
72
+ 500 => BugBunny::InternalServerError
73
+ }.each do |status, klass|
74
+ context "when status is #{status}" do
75
+ let(:body) { { 'error' => 'boom' } }
76
+ let(:error) { captured_error('status' => status, 'body' => body) }
77
+
78
+ it "raises #{klass} with status and raw_response populated" do
79
+ expect(error).to be_a(klass)
80
+ expect(error.status).to eq(status)
81
+ expect(error.raw_response).to eq(body)
82
+ end
83
+ end
84
+ end
85
+
86
+ context 'when status is 404 (NotFound)' do
87
+ let(:body) { { 'error' => 'missing' } }
88
+ let(:error) { captured_error('status' => 404, 'body' => body) }
89
+
90
+ it 'populates status and raw_response' do
91
+ expect(error.status).to eq(404)
92
+ expect(error.raw_response).to eq(body)
93
+ end
94
+ end
95
+
96
+ context 'when status is 422 (UnprocessableEntity)' do
97
+ let(:body) { { 'errors' => { 'name' => ['blank'] } } }
98
+ let(:error) { captured_error('status' => 422, 'body' => body) }
99
+
100
+ it 'keeps raw_response/error_messages and adds status from base' do
101
+ expect(error).to be_a(BugBunny::UnprocessableEntity)
102
+ expect(error.status).to eq(422)
103
+ expect(error.raw_response).to eq(body)
104
+ expect(error.error_messages).to eq('name' => ['blank'])
105
+ end
106
+ end
107
+
108
+ context 'when status is unmapped (418)' do
109
+ let(:body) { { 'error' => 'teapot' } }
110
+ let(:error) { captured_error('status' => 418, 'body' => body) }
111
+
112
+ it 'populates status and raw_response on the generic ClientError' do
113
+ expect(error).to be_a(BugBunny::ClientError)
114
+ expect(error.status).to eq(418)
115
+ expect(error.raw_response).to eq(body)
116
+ end
117
+ end
118
+ end
119
+
120
+ # Alcance issue #52: hardening de format_error_message contra el envelope
121
+ # anidado para no volcar un Hash#inspect en .message.
122
+ describe 'message hardening against the nested canonical envelope' do
123
+ def captured_error(response)
124
+ middleware.on_complete(response)
125
+ nil
126
+ rescue BugBunny::Error => e
127
+ e
128
+ end
129
+
130
+ context 'when error is the nested canonical envelope { error: { message } }' do
131
+ let(:body) { { 'error' => { 'code' => 'shortname_taken', 'message' => 'shortname already taken', 'details' => {} } } }
132
+ let(:error) { captured_error('status' => 409, 'body' => body) }
133
+
134
+ it 'extracts the human message and does not dump the Hash' do
135
+ expect(error.message).to eq('shortname already taken')
136
+ expect(error.message).not_to include('=>')
137
+ end
138
+ end
139
+
140
+ context 'when the nested envelope lacks a usable message' do
141
+ let(:body) { { 'error' => { 'code' => 'x', 'details' => {} } } }
142
+ let(:error) { captured_error('status' => 409, 'body' => body) }
143
+
144
+ it 'falls back to JSON instead of Hash#inspect' do
145
+ expect(error.message).to eq(body.to_json)
146
+ expect(error.message).not_to include('=>')
147
+ end
148
+ end
149
+
150
+ context 'when error is the flat historical shape { error: string, detail: string }' do
151
+ let(:body) { { 'error' => 'boom', 'detail' => 'because reasons' } }
152
+ let(:error) { captured_error('status' => 400, 'body' => body) }
153
+
154
+ it 'keeps concatenating error and detail (backward compatible)' do
155
+ expect(error.message).to eq('boom - because reasons')
156
+ end
157
+ end
158
+
159
+ context 'when error is an empty string (backward-compat edge case)' do
160
+ let(:body) { { 'error' => '', 'detail' => 'x' } }
161
+ let(:error) { captured_error('status' => 400, 'body' => body) }
162
+
163
+ it 'preserves the historical concatenation instead of dumping JSON' do
164
+ expect(error.message).to eq(' - x')
165
+ end
166
+ end
167
+ end
168
+
169
+ # Alcance issue #52: casos borde y paths no cubiertos arriba.
170
+ describe 'edge cases and uncovered paths' do
171
+ def captured_error(response)
172
+ middleware.on_complete(response)
173
+ nil
174
+ rescue BugBunny::Error => e
175
+ e
176
+ end
177
+
178
+ context 'when 5xx carries a serialized remote exception (bug_bunny_exception)' do
179
+ let(:body) do
180
+ { 'bug_bunny_exception' => { 'class' => 'ActiveRecord::RecordNotFound',
181
+ 'message' => 'User not found',
182
+ 'backtrace' => ['app.rb:1'] } }
183
+ end
184
+ let(:error) { captured_error('status' => 500, 'body' => body) }
185
+
186
+ it 'raises RemoteError with status and raw_response populated' do
187
+ expect(error).to be_a(BugBunny::RemoteError)
188
+ expect(error.status).to eq(500)
189
+ expect(error.raw_response).to eq(body)
190
+ expect(error.original_class).to eq('ActiveRecord::RecordNotFound')
191
+ end
192
+ end
193
+
194
+ context 'when 422 carries the nested envelope (consumer parses from raw_response)' do
195
+ let(:body) { { 'error' => { 'code' => 'taken', 'message' => 'shortname already taken', 'details' => {} } } }
196
+ let(:error) { captured_error('status' => 422, 'body' => body) }
197
+
198
+ it 'keeps raw_response intact for the service boundary' do
199
+ expect(error).to be_a(BugBunny::UnprocessableEntity)
200
+ expect(error.status).to eq(422)
201
+ expect(error.raw_response).to eq(body)
202
+ end
203
+ end
204
+
205
+ context 'when status is 406 (no-arg constructor)' do
206
+ let(:body) { { 'error' => 'nope' } }
207
+ let(:error) { captured_error('status' => 406, 'body' => body) }
208
+
209
+ it 'still populates status and raw_response' do
210
+ expect(error).to be_a(BugBunny::NotAcceptable)
211
+ expect(error.status).to eq(406)
212
+ expect(error.raw_response).to eq(body)
213
+ end
214
+ end
215
+
216
+ context 'when status is 408 (no-arg constructor)' do
217
+ let(:error) { captured_error('status' => 408, 'body' => nil) }
218
+
219
+ it 'still populates status' do
220
+ expect(error).to be_a(BugBunny::RequestTimeout)
221
+ expect(error.status).to eq(408)
222
+ end
223
+ end
224
+
225
+ context 'when body is not a Hash nor a String (e.g. Array)' do
226
+ let(:body) { [1, 2, 3] }
227
+ let(:error) { captured_error('status' => 400, 'body' => body) }
228
+
229
+ it 'falls back to JSON without raising in the formatter' do
230
+ expect(error.message).to eq(body.to_json)
231
+ end
232
+ end
233
+ end
58
234
  end
59
235
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bug_bunny
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.18.0
4
+ version: 5.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - gabix
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-05-26 00:00:00.000000000 Z
11
+ date: 2026-07-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bunny
@@ -229,12 +229,18 @@ executables: []
229
229
  extensions: []
230
230
  extra_rdoc_files: []
231
231
  files:
232
+ - AGENTS.md
232
233
  - CHANGELOG.md
233
234
  - CLAUDE.md
234
235
  - README.md
235
236
  - Rakefile
236
237
  - docs/behavior/behavior.md
238
+ - docs/config/configuracion.md
239
+ - docs/consumed/rabbitmq.md
240
+ - docs/errors/errors.md
237
241
  - docs/glossary/glossary.md
242
+ - docs/release/release.md
243
+ - docs/test/test.md
238
244
  - initializer_example.rb
239
245
  - lib/bug_bunny.rb
240
246
  - lib/bug_bunny/client.rb
@@ -307,7 +313,7 @@ metadata:
307
313
  homepage_uri: https://github.com/gedera/bug_bunny
308
314
  source_code_uri: https://github.com/gedera/bug_bunny
309
315
  changelog_uri: https://github.com/gedera/bug_bunny/blob/main/CHANGELOG.md
310
- documentation_uri: https://github.com/gedera/bug_bunny/blob/v4.18.0/skill
316
+ documentation_uri: https://github.com/gedera/bug_bunny/blob/v5.0.0/skill
311
317
  post_install_message:
312
318
  rdoc_options: []
313
319
  require_paths: