|
94 | 94 |
|
95 | 95 | describe "event leakage across clone_hub_to_current_thread (regression for #2951)" do |
96 | 96 | it "keeps sentry_events empty after setup_sentry_test even when an earlier request captured events through a cloned hub" do |
97 | | - # Cycle 1: a normal test that uses the test helper. |
98 | 97 | setup_sentry_test |
99 | 98 | Sentry.capture_message("event from a previous test") |
100 | 99 | teardown_sentry_test |
101 | 100 |
|
102 | | - # Simulate an unrelated request that runs *without* the test helper: |
103 | | - # Sentry::Rack::CaptureExceptions clones the main hub onto the request |
104 | | - # thread, then an event is captured through that cloned hub. |
105 | 101 | Sentry.clone_hub_to_current_thread |
106 | 102 | Sentry.capture_message("event from an unrelated request") |
107 | 103 |
|
108 | | - # Cycle 2: the next test sets the helper up again. |
109 | 104 | setup_sentry_test |
110 | 105 |
|
111 | 106 | expect(sentry_events).to be_empty |
112 | 107 |
|
113 | | - # The Rack middleware clones the hub again for *this* test's request. |
114 | 108 | Sentry.clone_hub_to_current_thread |
115 | 109 |
|
116 | 110 | expect(sentry_events).to be_empty |
|
125 | 119 | it "still exposes events captured through a hub the Rack middleware cloned after setup_sentry_test" do |
126 | 120 | setup_sentry_test |
127 | 121 |
|
128 | | - # Sentry::Rack::CaptureExceptions clones the main hub onto the request |
129 | | - # thread before the request body runs. |
130 | 122 | Sentry.clone_hub_to_current_thread |
131 | 123 | Sentry.capture_message("event from the request") |
132 | 124 |
|
133 | 125 | expect(sentry_events.map(&:message)).to include("event from the request") |
134 | 126 | end |
135 | 127 | end |
136 | 128 |
|
| 129 | + describe "Sentry::Rack::CaptureExceptions across consecutive requests (regression for #2951)", when: :rack_available? do |
| 130 | + # Drives a single request through the real Rack middleware. The middleware |
| 131 | + # calls Sentry.clone_hub_to_current_thread before handing off to the app, |
| 132 | + # exactly like a Rails request spec would. |
| 133 | + def perform_request(exception_message) |
| 134 | + exception = RuntimeError.new(exception_message) |
| 135 | + app = lambda do |env| |
| 136 | + env["rack.exception"] = exception |
| 137 | + [200, {}, ["ok"]] |
| 138 | + end |
| 139 | + stack = Sentry::Rack::CaptureExceptions.new(app) |
| 140 | + stack.call(Rack::MockRequest.env_for("/#{exception_message}")) |
| 141 | + end |
| 142 | + |
| 143 | + def captured_exception_messages |
| 144 | + sentry_events.map { |event| event.to_h.dig(:exception, :values, 0, :value) } |
| 145 | + end |
| 146 | + |
| 147 | + it "isolates each request's events and keeps them observable via sentry_events" do |
| 148 | + # First request, wrapped in the test helper just like a request spec. |
| 149 | + setup_sentry_test |
| 150 | + perform_request("first-request") |
| 151 | + messages = captured_exception_messages |
| 152 | + expect(messages.size).to eq(1) |
| 153 | + expect(messages.first).to include("first-request") |
| 154 | + teardown_sentry_test |
| 155 | + |
| 156 | + # Second request: a fresh setup must not see the first request's event, |
| 157 | + # even though the Rack middleware clones the main hub on every request. |
| 158 | + setup_sentry_test |
| 159 | + expect(sentry_events).to be_empty |
| 160 | + |
| 161 | + perform_request("second-request") |
| 162 | + messages = captured_exception_messages |
| 163 | + expect(messages.size).to eq(1) |
| 164 | + expect(messages.first).to include("second-request") |
| 165 | + expect(messages).not_to include(a_string_including("first-request")) |
| 166 | + |
| 167 | + teardown_sentry_test |
| 168 | + end |
| 169 | + end |
| 170 | + |
137 | 171 | describe "#teardown_sentry_test" do |
138 | 172 | before do |
139 | 173 | setup_sentry_test |
|
0 commit comments