Fail rspec on unexpected DEPRECATION WARNING in CI#2692
Merged
Conversation
Adds an `around(:each)` hook in spec_helper.rb that, when `ENV["CI"]`
is truthy, captures stderr for the duration of the example. If the
captured stderr contains "DEPRECATION WARNING" — i.e. a deprecation
fired but no `expect { ... }.to output(/.../).to_stderr` matcher and
no `OracleEnhanced.deprecator.silence { ... }` block consumed it —
the example is failed with a message naming both supported
mechanisms. Outside CI the suite is unchanged.
The hook stacks correctly with both intentional-deprecation
mechanisms:
- `expect { ... }.to output(/.../).to_stderr` redirects `$stderr`
inside the matcher's block, so output written there doesn't reach
the outer capture.
- `deprecator.silence` short-circuits inside `Deprecation#warn`
before any behavior runs, so nothing is written at all.
Verified locally by temporarily removing the
`OracleEnhanced.deprecator.silence { ... }` wrapper that PR rsim#2689
added around the `should connect to database using service_name`
example: the example fails with the expected message naming the
leaked deprecation; restoring the silence makes it pass again.
Set `CI: true` explicitly in every workflow that runs rspec
(`test.yml`, `test_11g.yml`, `test_11g_ojdbc11.yml`,
`test_prepared_statements_false.yml`, `ruby_head.yml`,
`jruby_head.yml`). GitHub Actions already sets `CI=true` on every
runner; the explicit declaration documents intent in the workflow
file. Lint-only workflows (`rubocop.yml`, `linting.yml`,
`check_method_signatures.yml`, `check_method_visibility.yml`) and
non-rspec workflows (`devcontainer.yml`, `release.yml`) are left
alone — they don't load spec_helper.rb so the env var would have
no effect.
Test plan:
- `bundle exec rspec` — 748 examples, 0 failures
- `CI=true bundle exec rspec` — 748 examples, 0 failures
- With the silence wrapper temporarily removed and `CI=true`, the
targeted example fails with the expected RuntimeError naming
the leaked deprecation.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Today, an unexpected
ActiveSupport::Deprecationwarning fired during the rspec run shows up only as a stderr line in the CI log. It's easy to miss until much later. This PR turns that into a hard test failure whenCI=true(the default on every GitHub Actions runner).Mechanism
spec/spec_helper.rbgains anaround(:each)hook gated onENV["CI"]. For each example it:$stderrto an outerStringIO.$stderr."DEPRECATION WARNING", raises with a message naming both supported "I expected this" mechanisms.The hook stacks correctly with the two patterns specs already use to handle intentional deprecations:
expect { ... }.to output(/.../).to_stderr— the matcher itself redirects$stderrinside the block, so output it captures never reaches the outer hook.OracleEnhanced.deprecator.silence { ... }—Deprecation#warnshort-circuits before any behavior runs, so nothing is written at all.So no existing spec needs to change.
Negative-test verification
To confirm the hook actually catches a leaked deprecation, the
should connect to database using service_nameexample inconnection_spec.rb(whoseOracleEnhanced.deprecator.silencewrap was added by PR #2689) was temporarily un-silenced locally and run withCI=true:Restoring the silence wrapper makes the example pass again. The wrapper change is not part of the diff in this PR — it was a local validation only.
Workflow YAMLs
Every workflow that runs rspec gets
CI: trueset explicitly:test.ymltest_11g.ymltest_11g_ojdbc11.ymltest_prepared_statements_false.ymlruby_head.ymljruby_head.ymlGitHub Actions already sets
CI=trueon every runner, so this is technically redundant — but the explicit declaration documents intent in the workflow file and protects against an upstream policy change.Lint-only workflows (
rubocop.yml,linting.yml,check_method_signatures.yml,check_method_visibility.yml) and non-rspec workflows (devcontainer.yml,release.yml) are left alone — they don't loadspec_helper.rbso the env var would have no effect.Test plan
bundle exec rspec— 748 examples, 0 failures, 12 pending (no behavior change outside CI)CI=true bundle exec rspec— 748 examples, 0 failures, 12 pending (every existing intentional-deprecation site is correctly bracketed byto_stderrorsilence)should connect to database using service_name, ranCI=true bundle exec rspec, observed the expected RuntimeError. Reverted before commit.bundle exec rubocop— clean🤖 Generated with Claude Code