11# frozen_string_literal: true
22
3+ # Rails 5.2's TestAdapter stores a minimal hash per enqueued job (only job
4+ # class, args, queue) and its instantiate_job recreates jobs via `.new(*args)`
5+ # — never calling our `deserialize` override. That means the `_sentry`
6+ # payload injected by `serialize` is silently discarded before the consumer
7+ # ever sees it, breaking distributed-tracing propagation.
8+ #
9+ # This adapter subclass calls `job.serialize` a second time after `super` has
10+ # stored the minimal hash and saves the full output alongside it. The drain
11+ # then drives each job through `ActiveJob::Base.execute(full_payload)`, which
12+ # goes through the normal deserialize → perform_now path and picks up the
13+ # Sentry trace headers and user context that were captured at enqueue time.
14+ class Rails52FullPayloadTestAdapter < ::ActiveJob ::QueueAdapters ::TestAdapter
15+ def enqueue ( job )
16+ prev = enqueued_jobs . length
17+ super
18+ enqueued_jobs . last [ :_sentry_full_payload ] = job . serialize if enqueued_jobs . length > prev
19+ end
20+
21+ def enqueue_at ( job , timestamp )
22+ prev = enqueued_jobs . length
23+ super
24+ enqueued_jobs . last [ :_sentry_full_payload ] = job . serialize if enqueued_jobs . length > prev
25+ end
26+ end
27+
328RSpec . shared_context "active_job backend harness" do |adapter :|
429 let ( :adapter ) { adapter }
530 let ( :configure_sentry ) { proc { } }
833 make_basic_app ( &configure_sentry )
934 setup_sentry_test
1035
11- ::ActiveJob ::Base . queue_adapter = adapter
36+ # Rails 5.2's TestAdapter discards the full serialize output (including the
37+ # _sentry payload) when deferring jobs. Use our augmented subclass instead
38+ # so the drain can replay jobs through the proper deserialize path.
39+ #
40+ # NOTE: In Rails 5.2 test specs, ActiveJob::TestHelper installs a
41+ # _test_adapter on ActiveJob::Base via an outer around hook (before_setup).
42+ # The queue_adapter class method returns _test_adapter when present, so we
43+ # must use enable_test_adapter (not queue_adapter=) to override it.
44+ if RAILS_VERSION < 6.0 && adapter == :test
45+ @_original_test_adapter = ::ActiveJob ::Base . _test_adapter
46+ ::ActiveJob ::Base . enable_test_adapter ( Rails52FullPayloadTestAdapter . new )
47+ else
48+ ::ActiveJob ::Base . queue_adapter = adapter
49+ end
1250
1351 boot_adapter ( adapter )
1452
1553 example . run
1654 ensure
55+ if RAILS_VERSION < 6.0 && adapter == :test
56+ if @_original_test_adapter
57+ ::ActiveJob ::Base . enable_test_adapter ( @_original_test_adapter )
58+ else
59+ ::ActiveJob ::Base . disable_test_adapter
60+ end
61+ end
62+
1763 reset_adapter ( adapter )
1864
1965 teardown_sentry_test
@@ -35,9 +81,19 @@ def drain(at: nil)
3581 if RAILS_VERSION < 6.0
3682 # Rails 5.2: perform_enqueued_jobs always requires a block and only runs
3783 # jobs enqueued *inside* the block. Manually flush already-enqueued jobs.
84+ # When using Rails52FullPayloadTestAdapter, each payload also carries a
85+ # :_sentry_full_payload key with the complete serialize output. Drive
86+ # those jobs through Base.execute so our deserialize override runs and
87+ # populates @_sentry_trace_headers / @_sentry_user before perform_now.
3888 jobs = queue_adapter . enqueued_jobs . dup
3989 queue_adapter . enqueued_jobs . clear
40- jobs . each { |payload | send ( :instantiate_job , payload ) . perform_now }
90+ jobs . each do |payload |
91+ if ( full = payload [ :_sentry_full_payload ] )
92+ ::ActiveJob ::Base . execute ( full )
93+ else
94+ send ( :instantiate_job , payload ) . perform_now
95+ end
96+ end
4197 else
4298 kwargs = at ? { at : at } : { }
4399 perform_enqueued_jobs ( **kwargs )
0 commit comments