Skip to content

New spec suite for Active Job integrations #2933

Description

@solnic

Description

⚠️ This won't be done all-at-once - this issue is an anchor for all the work that must be done for the epic issue #2932. It will be growing and most likely being refined as we move forward. It will be done/closed once all the backends have been ported.


The existing activejob_spec.rb is a monolithic file that mixes lifecycle, context, tracing, retries and cron into one describe block and only runs against :test. It is not a suitable foundation for a rework that has to prove identical behavior across all AJ-based integrations.

The goal is to replace the original specs with a dedicated, shared-example-driven AJ spec suite under a new tree (e.g. sentry-rails/spec/active_job/). Each feature area lives in its own shared-example file. Each backend ships as a thin spec file whose entire body is roughly:

RSpec.describe "Sentry + ActiveJob on SolidQueue" do
  include_context "active_job backend harness", adapter: :solid_queue
  it_behaves_like "a Sentry-instrumented ActiveJob backend"
end

General principles and ideas:

  • One top-level shared example — "a Sentry-instrumented ActiveJob backend" — composes smaller per-area shared examples. Adding a new backend is a one-line include.
  • Shared examples never branch on adapter name. Per-backend quirks go through a harness helper (boot, enqueue, drain, reset) that each backend's spec file supplies via let or shared context.
  • Backend-specific behaviors (e.g. sidekiq retry middleware, solid_queue recurring jobs) live in the respective backend-specific dirs or dedicated gems (sentry-sidekiq).

Feature areas the shared suite must cover

  • Harness & scaffolding. New spec tree, shared context for adapter setup / teardown / draining, fixture jobs (normal, failing, retryable, discardable, scheduled, mailer, rescue-from, deserialization-error, nested-enqueue), transport/event helpers. No behavioral assertions yet — just the rails the rest of Phase 0 runs on.
  • Job lifecycle. perform_now, perform_later + drain, set(wait:) / set(wait_until:) scheduled jobs, retry_on, discard_on, rescue_from (both suppressing and re-raising), ActiveJob::DeserializationError root-cause unwrapping, ActionMailer::MailDeliveryJob path.
  • Error capture & context. Exception type/message/stacktrace round-trip, extra payload (active_job, arguments, scheduled_at, job_id, provider_job_id, locale, queue, executions), tags (job_id, provider_job_id, queue), scope cleared between jobs, rescue_from-suppressed errors produce no event, errors raised from rescue_from callbacks produce both events.
  • Consumer-side tracing. Transaction name = job class, source: :task, op + origin conventions, status: ok / internal_error, event linked to transaction via same trace_id, messaging span data (messaging.destination.name, messaging.message.id, retry count, latency, provider job id), gated correctly by traces_sample_rate.
  • Producer-side tracing. Enqueue within an active transaction produces a queue.publish child span; span carries the same messaging data; scheduled enqueue does the same. (Pending until Phase 1.)
  • Distributed tracing. Producer trace headers round-trip through the payload; consumer transaction's trace_id matches producer's; consumer's parent_span_id points at the producer's publish span; baggage survives. Verified per backend — this is the shared suite's most important test for adapter conformance.
  • Scope & context propagation. Hub cloned per job, user set on producer scope surfaces on consumer scope, tags propagate, no scope leak between consecutive jobs, no leak across threads/fibers.
  • Retry & discard semantics. active_job_report_on_retry_error true/false, final-attempt-only reporting, discarded jobs emit no event, retry count reflected in transaction span data and tags, exhausted-retry capture.
  • Argument serialization. Ports the existing GlobalID / nested / Range / TimeWithZone / raising-to_global_id coverage from the old file into a dedicated shared-examples file. Adds sensitive-value handling (send_default_pii) if applicable.
  • Concurrency. Parallel job execution keeps scopes isolated; thread-local hub is not shared; trace ids do not cross-contaminate.
  • Backend wiring. One thin spec file per adapter: :test, :inline, :async, solid_queue, good_job, sidekiq. Each just configures the adapter via the harness and includes the top-level shared example.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions