|
3 | 3 | # Released under the MIT License. |
4 | 4 | # Copyright, 2025, by Samuel Williams. |
5 | 5 |
|
| 6 | +require "sus/fixtures/console" |
6 | 7 | require "protocol/rack/body" |
7 | 8 | require "protocol/http/body/readable" |
8 | 9 | require "console" |
9 | 10 |
|
10 | 11 | describe Protocol::Rack::Body do |
| 12 | + include Sus::Fixtures::Console::CapturedLogger |
11 | 13 | with "#no_content?" do |
12 | 14 | it "returns true for status codes that indicate no content" do |
13 | 15 | expect(subject.no_content?(204)).to be == true |
@@ -155,6 +157,97 @@ def body.each |
155 | 157 | expect(body).to be_nil |
156 | 158 | expect(called).to be == true |
157 | 159 | end |
| 160 | + |
| 161 | + it "invokes callbacks in reverse order of registration" do |
| 162 | + call_order = [] |
| 163 | + |
| 164 | + callback1 = proc do |env, status, headers, error| |
| 165 | + call_order << 1 |
| 166 | + end |
| 167 | + |
| 168 | + callback2 = proc do |env, status, headers, error| |
| 169 | + call_order << 2 |
| 170 | + end |
| 171 | + |
| 172 | + callback3 = proc do |env, status, headers, error| |
| 173 | + call_order << 3 |
| 174 | + end |
| 175 | + |
| 176 | + env[Protocol::Rack::RACK_RESPONSE_FINISHED] = [callback1, callback2, callback3] |
| 177 | + |
| 178 | + body = subject.wrap(env, 200, headers, ["body"]) |
| 179 | + body.close |
| 180 | + |
| 181 | + # Callbacks should be invoked in reverse order: 3, 2, 1 |
| 182 | + expect(call_order).to be == [3, 2, 1] |
| 183 | + end |
| 184 | + |
| 185 | + it "handles errors from callbacks gracefully and continues invoking other callbacks" do |
| 186 | + call_order = [] |
| 187 | + |
| 188 | + callback1 = proc do |env, status, headers, error| |
| 189 | + call_order << 1 |
| 190 | + end |
| 191 | + |
| 192 | + callback2 = proc do |env, status, headers, error| |
| 193 | + call_order << 2 |
| 194 | + raise StandardError.new("Callback error") |
| 195 | + end |
| 196 | + |
| 197 | + callback3 = proc do |env, status, headers, error| |
| 198 | + call_order << 3 |
| 199 | + end |
| 200 | + |
| 201 | + env[Protocol::Rack::RACK_RESPONSE_FINISHED] = [callback1, callback2, callback3] |
| 202 | + |
| 203 | + body = subject.wrap(env, 200, headers, ["body"]) |
| 204 | + body.close |
| 205 | + |
| 206 | + # All callbacks should be invoked despite callback2 raising an error |
| 207 | + # Callbacks should be invoked in reverse order: 3, 2, 1 |
| 208 | + expect(call_order).to be == [3, 2, 1] |
| 209 | + |
| 210 | + # Verify that the error was logged |
| 211 | + expect_console.to have_logged( |
| 212 | + severity: be == :error, |
| 213 | + subject: be == Protocol::Rack::Body, |
| 214 | + message: be =~ /Error occurred during response finished callback:/ |
| 215 | + ) |
| 216 | + end |
| 217 | + |
| 218 | + it "handles errors from callbacks when body is empty" do |
| 219 | + call_order = [] |
| 220 | + |
| 221 | + callback1 = proc do |env, status, headers, error| |
| 222 | + call_order << 1 |
| 223 | + end |
| 224 | + |
| 225 | + callback2 = proc do |env, status, headers, error| |
| 226 | + call_order << 2 |
| 227 | + raise StandardError.new("Callback error") |
| 228 | + end |
| 229 | + |
| 230 | + callback3 = proc do |env, status, headers, error| |
| 231 | + call_order << 3 |
| 232 | + end |
| 233 | + |
| 234 | + env[Protocol::Rack::RACK_RESPONSE_FINISHED] = [callback1, callback2, callback3] |
| 235 | + |
| 236 | + body = subject.wrap(env, 204, headers, []) |
| 237 | + |
| 238 | + expect(body).to be_nil |
| 239 | + |
| 240 | + # All callbacks should be invoked despite callback2 raising an error |
| 241 | + # Callbacks should be invoked in reverse order: 3, 2, 1 |
| 242 | + expect(call_order).to be == [3, 2, 1] |
| 243 | + |
| 244 | + # Verify that the error was logged: |
| 245 | + expect_console.to have_logged( |
| 246 | + severity: be == :error, |
| 247 | + subject: be == Protocol::Rack::Body, |
| 248 | + message: be =~ /Error occurred during response finished callback:/ |
| 249 | + ) |
| 250 | + end |
158 | 251 | end |
159 | 252 | end |
160 | 253 | end |
0 commit comments