Skip to content

[bot] Fix RSpec/SubjectStub#893

Open
6[bot] wants to merge 2 commits intomainfrom
fix/rspec-subject_stub-23729875530
Open

[bot] Fix RSpec/SubjectStub#893
6[bot] wants to merge 2 commits intomainfrom
fix/rspec-subject_stub-23729875530

Conversation

@6
Copy link
Copy Markdown
Contributor

@6 6 bot commented Mar 30, 2026

Automated fix fix for RSpec/SubjectStub corpus conformance.

Refs #588

Details

Cop RSpec/SubjectStub
Backend codex / hard
Model gpt-5.4 (xhigh)
Mode fix
Turns 1
Run #448

Result

Updated [subject_stub.rs](/home/runner/work/nitrocop/nitrocop/src/cop/rspec/subject_stub.rs#L66) to fix two behaviors. First, `shared_examples`/`shared_context` no longer contribute named subjects like `adapter`, which matches RuboCop for the FP pattern in [no_offense.rb](/home/runner/work/nitrocop/nitrocop/tests/fixtures/cops/rspec/subject_stub/no_offense.rb#L23). Second, the cop now keeps recursing into a block after flagging an outer subject stub, so nested stubs inside `expect(subject).to receive(:fork) do ... end` are caught, covered by [offense.rb](/home/runner/work/nitrocop/nitrocop/tests/fixtures/cops/rspec/subject_stub/offense.rb#L90).

I verified with vendored RuboCop behavior first: the `spec/support/shared_examples` `allow(adapter)` case is accepted, while the normal `describe` subject-stub pattern is still an offense. Commit: `d0be7284`.

Validation:
- `cargo test --lib -- cop::rspec::subject_stub`
- `python3 scripts/check_cop.py RSpec/SubjectStub --rerun --clone --sample 15`

Corpus check passed with no new regressions vs baseline, and the sample resolved 11 FP and all 5 FN oracle examples.

Changes

  3 files changed
Agent conversation excerpt

Reading the CI instructions and the cop’s current implementation first, then I’ll verify RuboCop on the representative FP pattern before deciding whether this is a detection bug or a config/context issue.

Reading the CI instructions and the cop’s current implementation first, then I’ll verify RuboCop on the representative FP pattern before deciding whether this is a detection bug or a config/context issue.

exec_command: sed -n '1,220p' docs/agent-ci.md

exec_command: sed -n '1,260p' src/cop/rspec/subject_stub.rs

exec_command: sed -n '1,240p' vendor/rubocop-rspec/lib/rubocop/cop/rspec/subject_stub.rb

exec_command: sed -n '1,260p' vendor/rubocop-rspec/spec/rubocop/cop/rspec/subject_stub_spec.rb

exec_command: sed -n '1,260p' tests/fixtures/cops/rspec/subject_stub/offense.rb

exec_command: sed -n '1,260p' tests/fixtures/cops/rspec/subject_stub/no_offense.rb

The current fixture already has the five FN snippets appended at top level, which is suspicious. I’m checking the rest of the cop implementation and then I’ll run RuboCop on the representative FP case plus the broader pattern the prompt called out.

The current fixture already has the five FN snippets appended at top level, which is suspicious. I’m checking the rest of the cop implementation and then I’ll run RuboCop on the representative FP case plus the broader pattern the prompt called out.

exec_command: sed -n '261,520p' src/cop/rspec/subject_stub.rs

exec_command: `tmp=$(mktemp); cat > "$tmp" <<'RUBY'
RSpec.shared_examples "a web server adapter" do
subject(:adapter) { described_class.new(upgrader) }

let(:upgrader) { instance_double(DockerManager::Upgrader, `

exec_command: `tmp=$(mktemp); cat > "$tmp" <<'RUBY'
describe Foo do
subject(:foo) { described_class.new }

before do
allow(foo).to receive(:bar).and_return(baz)
end
end
RUBY
rubocop --only RSpec/SubjectStu`

write_stdin: ``

write_stdin: ``

The system rubocop doesn’t have rubocop-rspec loaded, so the direct command from the prompt isn’t enough in this environment. I’m checking how this repo expects RuboCop to be invoked so I can verify against the actual RSpec/SubjectStub cop instead of a missing plugin.

The system rubocop doesn’t have rubocop-rspec loaded, so the direct command from the prompt isn’t enough in this environment. I’m checking how this repo expects RuboCop to be invoked so I can verify against the actual RSpec/SubjectStub cop instead of a missing plugin.

exec_command: rg -n "rubocop-rspec|bundle exec rubocop|RSpec/SubjectStub" -S Gemfile* .rubocop* vendor scripts docs -g '!vendor/ruby'

exec_command: ls -1

exec_command: git status --short

tests/fixtures/cops/rspec/subject_stub/offense.rb is already dirty before I’ve edited anything. I’m checking that diff now so I can avoid clobbering unrelated work and decide whether those appended FN snippets are part of the task setup or an incomplete prior attempt.

tests/fixtures/cops/rspec/subject_stub/offense.rb is already dirty before I’ve edited anything. I’m checking that diff now so I can avoid clobbering unrelated work and decide whether those appended FN snippets are part of the task setup or an incomplete prior attempt.

exec_command: git diff -- tests/fixtures/cops/rspec/subject_stub/offense.rb

exec_command: find vendor -maxdepth 3 \( -name Gemfile -o -name '*.gemspec' \) | sort | sed -n '1,120p'

exec_command: sed -n '1,220p' Cargo.toml

exec_command: sed -n '1,220p' vendor/rubocop-rspec/Gemfile

exec_command: sed -n '1,220p' vendor/rubocop-rspec/spec/spec_helper.rb

exec_command: bundle -v

I’ve got the local RuboCop extension checkout. Next I’m using the vendored rubocop-rspec bundle so the RSpec/SubjectStub cop is actually loaded, then I’ll inspect the current fixture failure state before touching code.

I’ve got the local RuboCop extension checkout. Next I’m using the vendored rubocop-rspec bundle so the RSpec/SubjectStub cop is actually loaded, then I’ll inspect the current fixture failure state before touching code.

exec_command: ls vendor/rubocop-rspec/Gemfile.lock

exec_command: BUNDLE_GEMFILE=vendor/rubocop-rspec/Gemfile bundle exec rubocop -V

exec_command: cargo test --lib -- cop::rspec::subject_stub

write_stdin: ``

write_stdin: ``

The local problem is clearer now: all 17 FP examples in the prompt are either config-dependent or oracle artifacts, while the only concrete failing signal in this checkout is the malformed FN fixture. I’m verifying whether I can load the vendored rubocop-rspec directly without a full bundle install, and then I’ll replace the broken top-level snippets with a real nested example that reproduces the miss.

The local problem is clearer now: all 17 FP examples in the prompt are either config-dependent or oracle artifacts, while the only concrete failing signal in this checkout is the malformed FN fixture. I’m verifying whether I can load the vendored rubocop-rspec directly without a full bundle install, and then I’ll replace the broken top-level snippets with a real nested example that reproduces the miss.

exec_command: sed -n '1,200p' vendor/rubocop-rspec/lib/rubocop-rspec.rb

exec_command: `tmp=$(mktemp --suffix=.rb); cat > "$tmp" <<'RUBY'
RSpec.shared_examples "a web server adapter" do
subject(:adapter) { described_class.new(upgrader) }

let(:upgrader) { instance_double(DockerManage`

exec_command: `tmp=$(mktemp --suffix=.rb); cat > "$tmp" <<'RUBY'
describe Foo do
subject(:foo) { described_class.new }

before do
allow(foo).to receive(:bar).and_return(baz)
end
end
RUBY
rubocop --require `

write_stdin: ``

exec_command: `tmp_rb=$(mktemp --suffix=.rb)
tmp_yml=$(mktemp --suffix=.yml)
cat > "$tmp_rb" <<'RUBY'
RSpec.shared_examples "a web server adapter" do
subject(:adapter) { described_class.new(upgrader) }

let(:upg`

exec_command: `tmp_rb=$(mktemp --suffix=.rb)
tmp_yml=$(mktemp --suffix=.yml)
cat > "$tmp_rb" <<'RUBY'
describe Foo do
subject(:foo) { described_class.new }

before do
allow(foo).to receive(:bar).and_return(b`

write_stdin: ``

write_stdin: ``

I found why the quick RuboCop run was crashing: loading the local extension without its default config leaves config['RSpec']['Language'] nil. I’m rerunning with the vendored rubocop-rspec default config inherited so the comparison is meaningful.

I found why the quick RuboCop run was crashing: loading the local extension without its default config leaves config['RSpec']['Language'] nil. I’m rerunning with the vendored rubocop-rspec default config inherited so the comparison is meaningful.

exec_command: `tmp_rb=$(mktemp --suffix=.rb)
tmp_yml=$(mktemp --suffix=.yml)
cat > "$tmp_rb" <<'RUBY'
RSpec.shared_examples "a web server adapter" do
subject(:adapter) { described_class.new(upgrader) }

let(:upg`

exec_command: `tmp_rb=$(mktemp --suffix=.rb)
tmp_yml=$(mktemp --suffix=.yml)
cat > "$tmp_rb" <<'RUBY'
describe Foo do
subject(:foo) { described_class.new }

before do
allow(foo).to receive(:bar).and_return(b`

write_stdin: ``

write_stdin: ``

The previous RuboCop run looked clean for both cases because I used /tmp/*.rb, so the cop never actually applied under RSpec’s Include globs. I’m rerunning both snippets under spec-like paths now, plus I’m checking the vendored default config for this cop’s file globs.

The previous RuboCop run looked clean for both cases because I used /tmp/*.rb, so the cop never actually applied under RSpec’s Include globs. I’m rerunning both snippets under spec-like paths now, plus I’m checking the vendored default config for this cop’s file globs.

exec_command: rg -n "SubjectStub:|Include:" vendor/rubocop-rspec/config/default.yml vendor/rubocop/config/default.yml

exec_command: tmpdir=$(mktemp -d) mkdir -p "$tmpdir/spec/support/shared_examples" cat > "$tmpdir/spec/support/shared_examples/web_server_adapter.rb" <<'RUBY' RSpec.shared_examples "a web server adapter" do subjec

exec_command: `tmpdir=$(mktemp -d)
mkdir -p "$tmpdir/spec/models"
cat > "$tmpdir/spec/models/foo_spec.rb" <<'RUBY'
describe Foo do
subject(:foo) { described_class.new }

before do
allow(foo).to receive(:bar)`

write_stdin: ``

write_stdin: ``

I’ve isolated the detection bug. The cop stops after reporting the outer expect(subject).to receive(:fork) do ... end, so it never descends into that block and misses the inner server_running?/open! stubs. I’m verifying the exact RuboCop behavior on a proper nested spec, then I’ll rewrite the malformed fixture into that real reproduction before patching the traversal.

I’ve isolated the detection bug. The cop stops after reporting the outer expect(subject).to receive(:fork) do ... end, so it never descends into that block and misses the inner server_running?/open! stubs. I’m verifying the exact RuboCop behavior on a proper nested spec, then I’ll rewrite the malformed fixture into that real reproduction before patching the traversal.

Task prompt (11326 tokens)

Fix RSpec/SubjectStub — 17 FP, 5 FN

Instructions

You are fixing ONE cop in nitrocop, a Rust Ruby linter that uses Prism for parsing.

Current state: 7,401 matches, 17 false positives, 5 false negatives.
Focus on: FP (nitrocop flags code RuboCop does not).

⚠ 7,401 existing matches must not regress. Validate with check_cop.py before committing.

Workflow

  1. Read the Pre-diagnostic Results and Corpus FP/FN Examples sections below first
  2. Verify with RuboCop first (for FP fixes): before writing any code, confirm RuboCop's
    behavior on BOTH the specific FP case AND the general pattern:
    echo '<specific FP case>' > /tmp/test.rb && rubocop --only RSpec/SubjectStub /tmp/test.rb
    echo '<general pattern>' > /tmp/test.rb && rubocop --only RSpec/SubjectStub /tmp/test.rb
    If RuboCop flags the general pattern, your fix must be narrow enough to not suppress it.
  3. Add a test case FIRST:
    • FN fix: add the missed pattern to tests/fixtures/cops/rspec/subject_stub/offense.rb with ^ annotation
    • FP fix: add the false-positive pattern to tests/fixtures/cops/rspec/subject_stub/no_offense.rb
  4. Verify test fails: cargo test --lib -- cop::rspec::subject_stub
  5. Fix src/cop/rspec/subject_stub.rs
  6. Verify test passes: cargo test --lib -- cop::rspec::subject_stub
  7. Validate against corpus (REQUIRED before committing):
    python3 scripts/check_cop.py RSpec/SubjectStub --rerun --clone --sample 15
    If this reports FP or FN regression, your fix is too broad — narrow it down.
  8. Add a /// doc comment on the cop struct documenting what you found and fixed
  9. Commit only your cop's files

Fixture Format

Mark offenses with ^ markers on the line AFTER the offending source line.
The ^ characters must align with the offending columns. The message format is RSpec/SubjectStub: <message text>.
See the Current Fixture sections below for real examples from this cop.

Mixed issues: some code bugs, some config issues

Pre-diagnostic shows SOME patterns are correctly detected in isolation (config issues)
and SOME are genuinely missed (code bugs). See the per-example diagnosis below.

  • For examples marked CODE BUG: follow the standard TDD workflow
  • For examples marked CONFIG/CONTEXT: investigate config resolution, not detection logic

If your test passes immediately

If you add a test case and it passes without code changes, the corpus mismatch is
caused by config/context differences, not a detection bug.
Do NOT loop trying to make the test fail. Instead:

  1. Investigate config resolution (Include/Exclude, cop enablement, disable comments)
  2. The fix is likely in src/config/ or the cop's config handling, not detection logic
  3. If you cannot determine the root cause within 5 minutes, document your findings as
    a /// comment on the cop struct and commit

CRITICAL: Avoid regressions in the opposite direction

When fixing FPs, your change MUST NOT suppress legitimate detections. When fixing FNs,
your change MUST NOT flag code that RuboCop accepts. A fix that eliminates a few issues
in one direction but introduces hundreds in the other is a catastrophic regression.

Before exempting a category of patterns, verify with RuboCop that the general case
is still an offense:

rubocop --only RSpec/SubjectStub /tmp/test.rb

If RuboCop flags the general pattern but not your specific case, the difference is in
a narrow context (e.g., enclosing structure, receiver type, argument count) — your fix
must target that specific context, not the broad category.

Rule of thumb: if your fix adds an early return or continue that skips a whole
node type, operator class, or naming pattern, it's probably too broad. Prefer adding a
condition that matches the SPECIFIC differentiating context.

Rules

  • Only modify src/cop/rspec/subject_stub.rs and tests/fixtures/cops/rspec/subject_stub/
  • Run cargo test --lib -- cop::rspec::subject_stub to verify your fix (do NOT run the full test suite)
  • Run python3 scripts/check_cop.py RSpec/SubjectStub --rerun --clone --sample 15 before committing to catch regressions
  • Do NOT touch unrelated files
  • Do NOT use git stash
  • Do NOT push — you do not have push permission; the workflow handles pushing after you exit

Prism Notes

  • const splits into ConstantReadNode (simple Foo) and ConstantPathNode (qualified Foo::Bar). If you handle one, check if you need the other.
  • begin is overloaded: explicit begin..end is BeginNode, implicit method body is StatementsNode. Check which one(s) your cop needs.

Current Fixture: offense.rb

tests/fixtures/cops/rspec/subject_stub/offense.rb

describe Foo do
  subject(:foo) { described_class.new }

  before do
    allow(foo).to receive(:bar).and_return(baz)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RSpec/SubjectStub: Do not stub methods of the object under test.
  end

  it 'uses expect twice' do
    expect(foo.bar).to eq(baz)
  end
end

describe Bar do
  subject(:bar) { described_class.new }

  before do
    expect(bar).to receive(:baz)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RSpec/SubjectStub: Do not stub methods of the object under test.
  end

  it 'tests bar' do
    expect(bar.baz).to eq(true)
  end
end

describe Baz do
  subject { described_class.new }

  it 'stubs subject' do
    expect(subject).to receive(:qux)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RSpec/SubjectStub: Do not stub methods of the object under test.
  end
end

# do...end block on receive chain followed by chain method
describe Processor do
  subject { described_class.new }

  it 'detects do...end with chain' do
    expect(subject).to receive(:process).and_wrap_original do |original, item|
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RSpec/SubjectStub: Do not stub methods of the object under test.
      original.call(item)
    end.at_least(:once)
    subject.call
  end
end

# Explicit parens on .to(receive(...)).and_return(...)
describe Handler do
  subject { described_class.new }

  it 'detects explicit parens with chain' do
    allow(subject).to(receive(:load_resource)).and_return(resource)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RSpec/SubjectStub: Do not stub methods of the object under test.
    subject.run
  end
end

# do...end block on receive chain followed by .and_return
describe Forwarder do
  subject(:forwarder) { described_class.new }

  it 'detects do...end with and_return' do
    expect(forwarder).to receive(:spawn) do |*args|
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RSpec/SubjectStub: Do not stub methods of the object under test.
      expect(args).to start_with('ssh')
    end.and_return(9999)
    forwarder.forward
  end
end

# Subject stub inside a nested block on an unrelated call chain
# (e.g., expect(Thread).to receive(:new) do |&block| expect(subject_name).to receive(:method); block.call end)
describe Worker do
  subject(:worker) { described_class.new }

  let(:fake_thread) { double }

  it 'detects subject stub inside nested block' do
    expect(Thread).to receive(:new) do |&block|
      expect(worker).to receive(:shutdown!)
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RSpec/SubjectStub: Do not stub methods of the object under test.
      block.call
    end.and_return(fake_thread)
    worker.run
  end
end

Current Fixture: no_offense.rb

tests/fixtures/cops/rspec/subject_stub/no_offense.rb

require "spec_helper"

describe Foo do
  subject(:foo) { described_class.new }

  before do
    allow(other_obj).to receive(:bar).and_return(baz)
  end

  it 'does something' do
    expect(foo.bar).to eq(baz)
  end
end

describe Bar do
  let(:bar) { double }

  before do
    allow(bar).to receive(:baz)
  end
end

# When require is at top level alongside a module wrapper, RuboCop's TopLevelGroup
# does not recurse into the module (begin returns children directly, module is
# not a spec group so it is skipped).
module SomeModule
  describe Builder do
    subject { described_class.new }

    before do
      allow(subject).to receive(:windows?)
    end
  end
end

# Local variable named subject is not the RSpec subject method
describe Agent do
  it 'returns false when failed?' do
    subject = Agent.new(0)
    allow(subject).to receive(:failed?).and_return(true)
    expect(subject.send { nil }).to be false
  end
end

# Subject name redefined by let in same or child scope
RSpec.describe Foo do
  subject(:foo) { described_class.new }

  context 'when foo is redefined by let' do
    let(:foo) { described_class.new }

    before do
      allow(foo).to receive(:active?).and_return(true)
    end
  end
end

# Subject name redefined by let in same scope
RSpec.describe Widget do
  subject(:widget) { described_class.new }
  let(:widget) { described_class.new }

  before do
    allow(widget).to receive(:enabled?).and_return(false)
  end
end

# Stubs inside class methods (def self.) are not flagged — RuboCop's
# find_subject_expectations recurses into :def but not :defs nodes.
describe Runner do
  subject(:runner) { described_class.new(stdout, stderr) }

  let(:stdout) { StringIO.new }
  let(:stderr) { StringIO.new }

  def self.cmds(cmds)
    before { cmds.each { |cmd, str| allow(runner).to receive(:`).with(cmd.to_s).and_return(str) } }
  end
end

# Subject from parent redefined with let in nested context (vendor spec case)
RSpec.describe Service do
  subject(:service) { described_class.new }

  context 'nested context' do
    subject(:record) { service.record }

    let(:service) { described_class.new }

    before do
      allow(service).to receive(:active?).and_return(true)
    end
  end
end

Key Source Files

  • Rust implementation: src/cop/rspec/subject_stub.rs
  • RuboCop Ruby source (ground truth): vendor/rubocop-rspec/lib/rubocop/cop/rspec/subject_stub.rb
  • RuboCop test excerpts: vendor/rubocop-rspec/spec/rubocop/cop/rspec/subject_stub_spec.rb

Read these files before making changes.

Start Here

Use the existing corpus data to focus on the most concentrated regressions first.

Helpful local commands:

  • python3 scripts/investigate_cop.py RSpec/SubjectStub --repos-only
  • python3 scripts/investigate_cop.py RSpec/SubjectStub --context
  • python3 scripts/verify_cop_locations.py RSpec/SubjectStub

Top FP repos:

  • discourse__docker_manager__370ca86 (11 FP) — example spec/support/shared_examples/web_server_adapter.rb:10
  • mysociety__alaveteli__d5b834f (4 FP) — example spec/models/project_spec.rb:342
  • ubicloud__ubicloud__b181cb6 (2 FP) — example spec/prog/vm/host_nexus_spec.rb:173

Top FN repos:

  • DannyBen__madness__5e61446 (5 FN) — example spec/madness/browser_spec.rb:47

Representative FP examples:

  • discourse__docker_manager__370ca86: spec/support/shared_examples/web_server_adapter.rb:10 — Do not stub methods of the object under test.
  • discourse__docker_manager__370ca86: spec/support/shared_examples/web_server_adapter.rb:47 — Do not stub methods of the object under test.
  • discourse__docker_manager__370ca86: spec/support/shared_examples/web_server_adapter.rb:61 — Do not stub methods of the object under test.

Representative FN examples:

  • DannyBen__madness__5e61446: spec/madness/browser_spec.rb:47 — Do not stub methods of the object under test.
  • DannyBen__madness__5e61446: spec/madness/browser_spec.rb:60 — Do not stub methods of the object under test.
  • DannyBen__madness__5e61446: spec/madness/browser_spec.rb:61 — Do not stub methods of the object under test.

Pre-diagnostic Results

Diagnosis Summary

Each example was tested by running nitrocop on the extracted source in isolation
with --force-default-config to determine if the issue is a code bug or config issue.
Note: source context is truncated and may not parse perfectly. If a diagnosis
seems wrong (e.g., your test passes immediately for a 'CODE BUG'), treat it as
a config/context issue instead.

  • FN: 5 code bug(s)
  • FP: 15 context-dependent

FN #1: DannyBen__madness__5e61446: spec/madness/browser_spec.rb:47

NOT DETECTED — CODE BUG
The cop fails to detect this pattern. Fix the detection logic.

Full-file enclosing chain: block (do..end) (line 46: expect(subject).to receive(:fork) do |&block|) > block (do..end) (line 45: it 'yields an error message' do) > block (do..end) (line 44: context 'when the server is not running' do) > block (do..end) (line 43: describe '#open' do) > block (do..end) (line 1: describe Browser do)

Prism AST at offense line: StatementsNode > CallNode > CallNode > ArgumentsNode > CallNode > ArgumentsNode > CallNode > CallNode > ArgumentsNode > SymbolNode > ArgumentsNode > FalseNode

Message: Do not stub methods of the object under test.

Ready-made test snippet (add to offense.rb, adjust ^ count):

          expect(subject).to receive(:server_running?).and_return false
^ RSpec/SubjectStub: Do not stub methods of the object under test.

Full file context (30 lines before offense):

       17:     context 'when host is not a localhost address' do
       18:       let(:host) { '1.2.3.4' }
       19: 
       20:       it 'returns it as is' do
       21:         expect(subject.server_url).to eq 'http://1.2.3.4:3456'
       22:       end
       23:     end
       24:   end
       25: 
       26:   describe '#server_running?' do
       27:     let(:mock_socket) { Class.new { def close; end }.new }
       28: 
       29:     it 'attempts to connect several times and returns true on success' do
       30:       expect(Socket).to receive(:tcp).with(subject.host, subject.port).and_raise 'cannot connect'
       31:       expect(Socket).to receive(:tcp).with(subject.host, subject.port).and_return mock_socket
       32:       expect(subject.server_running?).to be true
       33:     end
       34: 
       35:     context 'when it fails to connect' do
       36:       it 'returns false' do
       37:         expect(Socket).to receive(:tcp).with(subject.host, subject.port).and_return false
       38:         expect(subject.server_running?).to be false
       39:       end
       40:     end
       41:   end
       42: 
       43:   describe '#open' do
       44:     context 'when the server is not running' do
       45:       it 'yields an error message' do
       46:         expect(subject).to receive(:fork) do |&block|
>>>    47:           expect(subject).to receive(:server_running?).and_return false
       48:           block.call
       49:         end
       50: 
       51:         expect do |b|
       52:           subject.open(&b)
       53:         end.to yield_with_args('Failed connecting to http://localhost:3456. Is the server running?')
       54:       end

FN #2: DannyBen__madness__5e61446: spec/madness/browser_spec.rb:60

NOT DETECTED — CODE BUG
The cop fails to detect this pattern. Fix the detection logic.

Full-file enclosing chain: block (do..end) (line 59: expect(subject).to receive(:fork) do |&block|) > block (do..end) (line 58: it 'yields an error message' do) > block (do..end) (line 57: context 'when the server is running but the launch command fails' do) > block (do..end) (line 43: describe '#open' do) > block (do..end) (line 1: describe Browser do)

Prism AST at offense line: StatementsNode > CallNode > CallNode > ArgumentsNode > CallNode > ArgumentsNode > CallNode > CallNode > ArgumentsNode > SymbolNode > ArgumentsNode > TrueNode

Message: Do not stub methods of the object under test.

Ready-made test snippet (add to offense.rb, adjust ^ count):

          expect(subject).to receive(:server_running?).and_return true
^ RSpec/SubjectStub: Do not stub methods of the object under test.

Full file context (30 lines before offense):

       30:       expect(Socket).to receive(:tcp).with(subject.host, subject.port).and_raise 'cannot connect'
       31:       expect(Socket).to receive(:tcp).with(subject.host, subject.port).and_return mock_socket
       32:       expect(subject.server_running?).to be true
       33:     end
       34: 
       35:     context 'when it fails to connect' do
       36:       it 'returns false' do
       37:         expect(Socket).to receive(:tcp).with(subject.host, subject.port).and_return false
       38:         expect(subject.server_running?).to be false
       39:       end
       40:     end
       41:   end
       42: 
       43:   describe '#open' do
       44:     context 'when the server is not running' do
       45:       it 'yields an error message' do
       46:         expect(subject).to receive(:fork) do |&block|
       47:           expect(subject).to receive(:server_running?).and_return false
       48:           block.call
       49:         end
       50: 
       51:         expect do |b|
       52:           subject.open(&b)
       53:         end.to yield_with_args('Failed connecting to http://localhost:3456. Is the server running?')
       54:       end
       55:     end
       56: 
       57:     context 'when the server is running but the launch command fails' do
       58:       it 'yields an error message' do
       59:         expect(subject).to receive(:fork) do |&block|
>>>    60:           expect(subject).to receive(:server_running?).and_return true
       61:           expect(subject).to receive(:open!).and_return false
       62:           block.call
       63:         end
       64: 
       65:         regexp = Regexp.new 'Failed opening browser (.*open http://localhost:3456)'
       66:         expect { |b| subject.open(&b) }.to yield_with_args(regexp)
       67:       end

FN #3: DannyBen__madness__5e61446: spec/madness/browser_spec.rb:61

NOT DETECTED — CODE BUG
The cop fails to detect this pattern. Fix the detection logic.

Full-file enclosing chain: block (do..end) (line 59: expect(subject).to receive(:fork) do |&block|) > block (do..end) (line 58: it 'yields an error message' do) > block (do..end) (line 57: context 'when the server is running but the launch command fails' do) > block (do..end) (line 43: describe '#open' do) > block (do..end) (line 1: describe Browser do)

Prism AST at offense line: CallNode > CallNode > ArgumentsNode > CallNode > ArgumentsNode > CallNode > CallNode > ArgumentsNode > SymbolNode > ArgumentsNode > FalseNode

Message: Do not stub methods of the object under test.

Ready-made test snippet (add to offense.rb, adjust ^ count):

          expect(subject).to receive(:open!).and_return false
^ RSpec/SubjectStub: Do not stub methods of the object under test.

Full file context (30 lines before offense):

       31:       expect(Socket).to receive(:tcp).with(subject.host, subject.port).and_return mock_socket
       32:       expect(subject.server_running?).to be true
       33:     end
       34: 
       35:     context 'when it fails to connect' do
       36:       it 'returns false' do
       37:         expect(Socket).to receive(:tcp).with(subject.host, subject.port).and_return false
       38:         expect(subject.server_running?).to be false
       39:       end
       40:     end
       41:   end
       42: 
       43:   describe '#open' do
       44:     context 'when the server is not running' do
       45:       it 'yields an error message' do
       46:         expect(subject).to receive(:fork) do |&block|
       47:           expect(subject).to receive(:server_running?).and_return false
       48:           block.call
       49:         end
       50: 
       51:         expect do |b|
       52:           subject.open(&b)
       53:         end.to yield_with_args('Failed connecting to http://localhost:3456. Is the server running?')
       54:       end
       55:     end
       56: 
       57:     context 'when the server is running but the launch command fails' do
       58:       it 'yields an error message' do
       59:         expect(subject).to receive(:fork) do |&block|
       60:           expect(subject).to receive(:server_running?).and_return true
>>>    61:           expect(subject).to receive(:open!).and_return false
       62:           block.call
       63:         end
       64: 
       65:         regexp = Regexp.new 'Failed opening browser (.*open http://localhost:3456)'
       66:         expect { |b| subject.open(&b) }.to yield_with_args(regexp)
       67:       end
       68:     end

FN #4: DannyBen__madness__5e61446: spec/madness/browser_spec.rb:73

NOT DETECTED — CODE BUG
The cop fails to detect this pattern. Fix the detection logic.

Full-file enclosing chain: block (do..end) (line 72: expect(subject).to receive(:fork) do |&block|) > block (do..end) (line 71: it 'yields nil' do) > block (do..end) (line 70: context 'when the server is running and the launch command succeeds' do) > block (do..end) (line 43: describe '#open' do) > block (do..end) (line 1: describe Browser do)

Prism AST at offense line: StatementsNode > CallNode > CallNode > ArgumentsNode > CallNode > ArgumentsNode > CallNode > CallNode > ArgumentsNode > SymbolNode > ArgumentsNode > TrueNode

Message: Do not stub methods of the object under test.

Ready-made test snippet (add to offense.rb, adjust ^ count):

          expect(subject).to receive(:server_running?).and_return true
^ RSpec/SubjectStub: Do not stub methods of the object under test.

Full file context (30 lines before offense):

       43:   describe '#open' do
       44:     context 'when the server is not running' do
       45:       it 'yields an error message' do
       46:         expect(subject).to receive(:fork) do |&block|
       47:           expect(subject).to receive(:server_running?).and_return false
       48:           block.call
       49:         end
       50: 
       51:         expect do |b|
       52:           subject.open(&b)
       53:         end.to yield_with_args('Failed connecting to http://localhost:3456. Is the server running?')
       54:       end
       55:     end
       56: 
       57:     context 'when the server is running but the launch command fails' do
       58:       it 'yields an error message' do
       59:         expect(subject).to receive(:fork) do |&block|
       60:           expect(subject).to receive(:server_running?).and_return true
       61:           expect(subject).to receive(:open!).and_return false
       62:           block.call
       63:         end
       64: 
       65:         regexp = Regexp.new 'Failed opening browser (.*open http://localhost:3456)'
       66:         expect { |b| subject.open(&b) }.to yield_with_args(regexp)
       67:       end
       68:     end
       69: 
       70:     context 'when the server is running and the launch command succeeds' do
       71:       it 'yields nil' do
       72:         expect(subject).to receive(:fork) do |&block|
>>>    73:           expect(subject).to receive(:server_running?).and_return true
       74:           expect(subject).to receive(:open!).and_return true
       75:           block.call
       76:         end
       77: 
       78:         expect { |b| subject.open(&b) }.to yield_with_args(nil)
       79:       end
       80:     end

FN #5: DannyBen__madness__5e61446: spec/madness/browser_spec.rb:74

NOT DETECTED — CODE BUG
The cop fails to detect this pattern. Fix the detection logic.

Full-file enclosing chain: block (do..end) (line 72: expect(subject).to receive(:fork) do |&block|) > block (do..end) (line 71: it 'yields nil' do) > block (do..end) (line 70: context 'when the server is running and the launch command succeeds' do) > block (do..end) (line 43: describe '#open' do) > block (do..end) (line 1: describe Browser do)

Prism AST at offense line: CallNode > CallNode > ArgumentsNode > CallNode > ArgumentsNode > CallNode > CallNode > ArgumentsNode > SymbolNode > ArgumentsNode > TrueNode

Message: Do not stub methods of the object under test.

Ready-made test snippet (add to offense.rb, adjust ^ count):

          expect(subject).to receive(:open!).and_return true
^ RSpec/SubjectStub: Do not stub methods of the object under test.

Full file context (30 lines before offense):

       44:     context 'when the server is not running' do
       45:       it 'yields an error message' do
       46:         expect(subject).to receive(:fork) do |&block|
       47:           expect(subject).to receive(:server_running?).and_return false
       48:           block.call
       49:         end
       50: 
       51:         expect do |b|
       52:           subject.open(&b)
       53:         end.to yield_with_args('Failed connecting to http://localhost:3456. Is the server running?')
       54:       end
       55:     end
       56: 
       57:     context 'when the server is running but the launch command fails' do
       58:       it 'yields an error message' do
       59:         expect(subject).to receive(:fork) do |&block|
       60:           expect(subject).to receive(:server_running?).and_return true
       61:           expect(subject).to receive(:open!).and_return false
       62:           block.call
       63:         end
       64: 
       65:         regexp = Regexp.new 'Failed opening browser (.*open http://localhost:3456)'
       66:         expect { |b| subject.open(&b) }.to yield_with_args(regexp)
       67:       end
       68:     end
       69: 
       70:     context 'when the server is running and the launch command succeeds' do
       71:       it 'yields nil' do
       72:         expect(subject).to receive(:fork) do |&block|
       73:           expect(subject).to receive(:server_running?).and_return true
>>>    74:           expect(subject).to receive(:open!).and_return true
       75:           block.call
       76:         end
       77: 
       78:         expect { |b| subject.open(&b) }.to yield_with_args(nil)
       79:       end
       80:     end
       81:   end

FP #1: discourse__docker_manager__370ca86: spec/support/shared_examples/web_server_adapter.rb:10

NOT REPRODUCED — CONFIG/CONTEXT issue
nitrocop does not flag this in isolation or in the full file
(with default config). The FP is caused by the target repo's
config (e.g., different Max value, Include/Exclude patterns).

Source context:

RSpec.shared_examples "a web server adapter" do
  subject(:adapter) { described_class.new(upgrader) }

  let(:upgrader) { instance_double(DockerManager::Upgrader, log: nil) }

  describe "#workers" do
    before do
      allow(adapter).to receive(:master_pid).and_return(1001)
      allow_any_instance_of(Kernel).to receive(:`).with("pgrep -f -P 1001 worker").and_return(
        "2001\n2002\n2003\n",
      )
    end

    it "returns array of worker PIDs" do
      expect(adapter.workers).to contain_exactly(2001, 2002, 2003)

Message: Do not stub methods of the object under test.

Full file context (30 lines before offense):

        1: # frozen_string_literal: true
        2: 
        3: RSpec.shared_examples "a web server adapter" do
        4:   subject(:adapter) { described_class.new(upgrader) }
        5: 
        6:   let(:upgrader) { instance_double(DockerManager::Upgrader, log: nil) }
        7: 
        8:   describe "#workers" do
        9:     before do
>>>    10:       allow(adapter).to receive(:master_pid).and_return(1001)
       11:       allow_any_instance_of(Kernel).to receive(:`).with("pgrep -f -P 1001 worker").and_return(
       12:         "2001\n2002\n2003\n",
       13:       )
       14:     end
       15: 
       16:     it "returns array of worker PIDs" do
       17:       expect(adapter.workers).to contain_exactly(2001, 2002, 2003)

FP #2: discourse__docker_manager__370ca86: spec/support/shared_examples/web_server_adapter.rb:47

NOT REPRODUCED — CONFIG/CONTEXT issue
nitrocop does not flag this in isolation or in the full file
(with default config). The FP is caused by the target repo's
config (e.g., different Max value, Include/Exclude patterns).

Source context:

    end
  end

  describe "#scale_down_workers" do
    let(:master_pid) { 1234 }

    before do
      allow(adapter).to receive(:master_pid).and_return(master_pid)
      allow(Process).to receive(:kill)
    end

    it "sends TTOU signal to master for each worker to scale down" do
      adapter.scale_down_workers(3)
      expect(Process).to have_received(:kill).with("TTOU", master_pid).thrice
    end

Message: Do not stub methods of the object under test.

Full file context (30 lines before offense):

       17:       expect(adapter.workers).to contain_exactly(2001, 2002, 2003)
       18:     end
       19:   end
       20: 
       21:   describe "#min_workers" do
       22:     it "returns at least 1" do
       23:       expect(adapter.min_workers).to be >= 1
       24:     end
       25:   end
       26: 
       27:   describe "#local_web_url" do
       28:     context "when UNICORN_PORT is set" do
       29:       before { allow(ENV).to receive(:[]).with("UNICORN_PORT").and_return("8080") }
       30: 
       31:       it "uses the configured port" do
       32:         expect(adapter.local_web_url).to eq("http://127.0.0.1:8080/srv/status")
       33:       end
       34:     end
       35: 
       36:     context "when UNICORN_PORT is not set" do
       37:       it "defaults to port 3000" do
       38:         expect(adapter.local_web_url).to eq("http://127.0.0.1:3000/srv/status")
       39:       end
       40:     end
       41:   end
       42: 
       43:   describe "#scale_down_workers" do
       44:     let(:master_pid) { 1234 }
       45: 
       46:     before do
>>>    47:       allow(adapter).to receive(:master_pid).and_return(master_pid)
       48:       allow(Process).to receive(:kill)
       49:     end
       50: 
       51:     it "sends TTOU signal to master for each worker to scale down" do
       52:       adapter.scale_down_workers(3)
       53:       expect(Process).to have_received(:kill).with("TTOU", master_pid).thrice
       54:     end

FP #3: discourse__docker_manager__370ca86: spec/support/shared_examples/web_server_adapter.rb:61

NOT REPRODUCED — CONFIG/CONTEXT issue
nitrocop does not flag this in isolation or in the full file
(with default config). The FP is caused by the target repo's
config (e.g., different Max value, Include/Exclude patterns).

Source context:

    end
  end

  describe "#scale_up_workers" do
    let(:master_pid) { 1234 }

    before do
      allow(adapter).to receive(:master_pid).and_return(master_pid)
      allow(Process).to receive(:kill)
    end

    it "sends TTIN signal to master for each worker to scale up" do
      adapter.scale_up_workers(2)
      expect(Process).to have_received(:kill).with("TTIN", master_pid).twice
    end

Message: Do not stub methods of the object under test.

Full file context (30 lines before offense):

       31:       it "uses the configured port" do
       32:         expect(adapter.local_web_url).to eq("http://127.0.0.1:8080/srv/status")
       33:       end
       34:     end
       35: 
       36:     context "when UNICORN_PORT is not set" do
       37:       it "defaults to port 3000" do
       38:         expect(adapter.local_web_url).to eq("http://127.0.0.1:3000/srv/status")
       39:       end
       40:     end
       41:   end
       42: 
       43:   describe "#scale_down_workers" do
       44:     let(:master_pid) { 1234 }
       45: 
       46:     before do
       47:       allow(adapter).to receive(:master_pid).and_return(master_pid)
       48:       allow(Process).to receive(:kill)
       49:     end
       50: 
       51:     it "sends TTOU signal to master for each worker to scale down" do
       52:       adapter.scale_down_workers(3)
       53:       expect(Process).to have_received(:kill).with("TTOU", master_pid).thrice
       54:     end
       55:   end
       56: 
       57:   describe "#scale_up_workers" do
       58:     let(:master_pid) { 1234 }
       59: 
       60:     before do
>>>    61:       allow(adapter).to receive(:master_pid).and_return(master_pid)
       62:       allow(Process).to receive(:kill)
       63:     end
       64: 
       65:     it "sends TTIN signal to master for each worker to scale up" do
       66:       adapter.scale_up_workers(2)
       67:       expect(Process).to have_received(:kill).with("TTIN", master_pid).twice
       68:     end

FP #4: discourse__docker_manager__370ca86: spec/support/shared_examples/web_server_adapter.rb:95

NOT REPRODUCED — CONFIG/CONTEXT issue
nitrocop does not flag this in isolation or in the full file
(with default config). The FP is caused by the target repo's
config (e.g., different Max value, Include/Exclude patterns).

Source context:

  describe "#reload" do
    let(:launcher_pid) { 1000 }
    let(:master_pid) { 1001 }
    let(:server_name) { adapter.server_name }

    before do
      allow(adapter).to receive_messages(
        launcher_pid:,
        master_pid:,
        set_restart_flag: "OK",
        clear_restart_flag: nil,
      )
      allow(adapter).to receive(:sleep)
      allow(adapter).to receive(:`).and_return("ok")

Message: Do not stub methods of the object under test.

Full file context (30 lines before offense):

       65:     it "sends TTIN signal to master for each worker to scale up" do
       66:       adapter.scale_up_workers(2)
       67:       expect(Process).to have_received(:kill).with("TTIN", master_pid).twice
       68:     end
       69:   end
       70: 
       71:   describe "#set_restart_flag" do
       72:     it "sets the server restart flag through redis" do
       73:       expect { adapter.set_restart_flag }.to change {
       74:         Discourse.redis.get(DockerManager::WebServerAdapter::RESTART_FLAG_KEY)
       75:       }.to("1")
       76:     end
       77:   end
       78: 
       79:   describe "#clear_restart_flag" do
       80:     before { adapter.set_restart_flag }
       81: 
       82:     it "deletes the server restart flag in redis" do
       83:       expect { adapter.clear_restart_flag }.to change {
       84:         Discourse.redis.get(DockerManager::WebServerAdapter::RESTART_FLAG_KEY)
       85:       }.to be_nil
       86:     end
       87:   end
       88: 
       89:   describe "#reload" do
       90:     let(:launcher_pid) { 1000 }
       91:     let(:master_pid) { 1001 }
       92:     let(:server_name) { adapter.server_name }
       93: 
       94:     before do
>>>    95:       allow(adapter).to receive_messages(
       96:         launcher_pid:,
       97:         master_pid:,
       98:         set_restart_flag: "OK",
       99:         clear_restart_flag: nil,
      100:       )
      101:       allow(adapter).to receive(:sleep)
      102:       allow(adapter).to receive(:`).and_return("ok")

FP #5: discourse__docker_manager__370ca86: spec/support/shared_examples/web_server_adapter.rb:101

NOT REPRODUCED — CONFIG/CONTEXT issue
nitrocop does not flag this in isolation or in the full file
(with default config). The FP is caused by the target repo's
config (e.g., different Max value, Include/Exclude patterns).

Source context:

    before do
      allow(adapter).to receive_messages(
        launcher_pid:,
        master_pid:,
        set_restart_flag: "OK",
        clear_restart_flag: nil,
      )
      allow(adapter).to receive(:sleep)
      allow(adapter).to receive(:`).and_return("ok")
      allow(Process).to receive(:kill)
      allow(Process).to receive(:getpgid).and_raise(Errno::ESRCH)
    end

    it "sets the restart flag before reloading" do
      adapter.reload

Message: Do not stub methods of the object under test.

Full file context (30 lines before offense):

       71:   describe "#set_restart_flag" do
       72:     it "sets the server restart flag through redis" do
       73:       expect { adapter.set_restart_flag }.to change {
       74:         Discourse.redis.get(DockerManager::WebServerAdapter::RESTART_FLAG_KEY)
       75:       }.to("1")
       76:     end
       77:   end
       78: 
       79:   describe "#clear_restart_flag" do
       80:     before { adapter.set_restart_flag }
       81: 
       82:     it "deletes the server restart flag in redis" do
       83:       expect { adapter.clear_restart_flag }.to change {
       84:         Discourse.redis.get(DockerManager::WebServerAdapter::RESTART_FLAG_KEY)
       85:       }.to be_nil
       86:     end
       87:   end
       88: 
       89:   describe "#reload" do
       90:     let(:launcher_pid) { 1000 }
       91:     let(:master_pid) { 1001 }
       92:     let(:server_name) { adapter.server_name }
       93: 
       94:     before do
       95:       allow(adapter).to receive_messages(
       96:         launcher_pid:,
       97:         master_pid:,
       98:         set_restart_flag: "OK",
       99:         clear_restart_flag: nil,
      100:       )
>>>   101:       allow(adapter).to receive(:sleep)
      102:       allow(adapter).to receive(:`).and_return("ok")
      103:       allow(Process).to receive(:kill)
      104:       allow(Process).to receive(:getpgid).and_raise(Errno::ESRCH)
      105:     end
      106: 
      107:     it "sets the restart flag before reloading" do
      108:       adapter.reload

FP #6: discourse__docker_manager__370ca86: spec/support/shared_examples/web_server_adapter.rb:102

NOT REPRODUCED — CONFIG/CONTEXT issue
nitrocop does not flag this in isolation or in the full file
(with default config). The FP is caused by the target repo's
config (e.g., different Max value, Include/Exclude patterns).

Source context:

      allow(adapter).to receive_messages(
        launcher_pid:,
        master_pid:,
        set_restart_flag: "OK",
        clear_restart_flag: nil,
      )
      allow(adapter).to receive(:sleep)
      allow(adapter).to receive(:`).and_return("ok")
      allow(Process).to receive(:kill)
      allow(Process).to receive(:getpgid).and_raise(Errno::ESRCH)
    end

    it "sets the restart flag before reloading" do
      adapter.reload
      expect(adapter).to have_received(:set_restart_flag).ordered

Message: Do not stub methods of the object under test.

Full file context (30 lines before offense):

       72:     it "sets the server restart flag through redis" do
       73:       expect { adapter.set_restart_flag }.to change {
       74:         Discourse.redis.get(DockerManager::WebServerAdapter::RESTART_FLAG_KEY)
       75:       }.to("1")
       76:     end
       77:   end
       78: 
       79:   describe "#clear_restart_flag" do
       80:     before { adapter.set_restart_flag }
       81: 
       82:     it "deletes the server restart flag in redis" do
       83:       expect { adapter.clear_restart_flag }.to change {
       84:         Discourse.redis.get(DockerManager::WebServerAdapter::RESTART_FLAG_KEY)
       85:       }.to be_nil
       86:     end
       87:   end
       88: 
       89:   describe "#reload" do
       90:     let(:launcher_pid) { 1000 }
       91:     let(:master_pid) { 1001 }
       92:     let(:server_name) { adapter.server_name }
       93: 
       94:     before do
       95:       allow(adapter).to receive_messages(
       96:         launcher_pid:,
       97:         master_pid:,
       98:         set_restart_flag: "OK",
       99:         clear_restart_flag: nil,
      100:       )
      101:       allow(adapter).to receive(:sleep)
>>>   102:       allow(adapter).to receive(:`).and_return("ok")
      103:       allow(Process).to receive(:kill)
      104:       allow(Process).to receive(:getpgid).and_raise(Errno::ESRCH)
      105:     end
      106: 
      107:     it "sets the restart flag before reloading" do
      108:       adapter.reload
      109:       expect(adapter).to have_received(:set_restart_flag).ordered

FP #7: discourse__docker_manager__370ca86: spec/support/shared_examples/web_server_adapter.rb:109

NOT REPRODUCED — CONFIG/CONTEXT issue
nitrocop does not flag this in isolation or in the full file
(with default config). The FP is caused by the target repo's
config (e.g., different Max value, Include/Exclude patterns).

Source context:

      allow(adapter).to receive(:`).and_return("ok")
      allow(Process).to receive(:kill)
      allow(Process).to receive(:getpgid).and_raise(Errno::ESRCH)
    end

    it "sets the restart flag before reloading" do
      adapter.reload
      expect(adapter).to have_received(:set_restart_flag).ordered
      expect(Process).to have_received(:kill).with("USR2", launcher_pid).ordered
    end

    it "logs the restart action" do
      adapter.reload
      expect(upgrader).to have_received(:log).with("Restarting #{server_name} pid: #{launcher_pid}")
    end

Message: Do not stub methods of the object under test.

Full file context (30 lines before offense):

       79:   describe "#clear_restart_flag" do
       80:     before { adapter.set_restart_flag }
       81: 
       82:     it "deletes the server restart flag in redis" do
       83:       expect { adapter.clear_restart_flag }.to change {
       84:         Discourse.redis.get(DockerManager::WebServerAdapter::RESTART_FLAG_KEY)
       85:       }.to be_nil
       86:     end
       87:   end
       88: 
       89:   describe "#reload" do
       90:     let(:launcher_pid) { 1000 }
       91:     let(:master_pid) { 1001 }
       92:     let(:server_name) { adapter.server_name }
       93: 
       94:     before do
       95:       allow(adapter).to receive_messages(
       96:         launcher_pid:,
       97:         master_pid:,
       98:         set_restart_flag: "OK",
       99:         clear_restart_flag: nil,
      100:       )
      101:       allow(adapter).to receive(:sleep)
      102:       allow(adapter).to receive(:`).and_return("ok")
      103:       allow(Process).to receive(:kill)
      104:       allow(Process).to receive(:getpgid).and_raise(Errno::ESRCH)
      105:     end
      106: 
      107:     it "sets the restart flag before reloading" do
      108:       adapter.reload
>>>   109:       expect(adapter).to have_received(:set_restart_flag).ordered
      110:       expect(Process).to have_received(:kill).with("USR2", launcher_pid).ordered
      111:     end
      112: 
      113:     it "logs the restart action" do
      114:       adapter.reload
      115:       expect(upgrader).to have_received(:log).with("Restarting #{server_name} pid: #{launcher_pid}")
      116:     end

FP #8: discourse__docker_manager__370ca86: spec/support/shared_examples/web_server_adapter.rb:132

NOT REPRODUCED — CONFIG/CONTEXT issue
nitrocop does not flag this in isolation or in the full file
(with default config). The FP is caused by the target repo's
config (e.g., different Max value, Include/Exclude patterns).

Source context:

      allow(Process).to receive(:getpgid).with(master_pid) do
        call_count += 1
        raise Errno::ESRCH if call_count > 2
        true
      end

      adapter.reload
      expect(adapter).to have_received(:sleep).with(2).twice
    end

    it "waits for workers to respond to health check" do
      call_count = 0
      allow(adapter).to receive(:`).with(/curl/) do
        call_count += 1
        call_count > 3 ? "ok" : "not ready"

Message: Do not stub methods of the object under test.

Full file context (30 lines before offense):

      102:       allow(adapter).to receive(:`).and_return("ok")
      103:       allow(Process).to receive(:kill)
      104:       allow(Process).to receive(:getpgid).and_raise(Errno::ESRCH)
      105:     end
      106: 
      107:     it "sets the restart flag before reloading" do
      108:       adapter.reload
      109:       expect(adapter).to have_received(:set_restart_flag).ordered
      110:       expect(Process).to have_received(:kill).with("USR2", launcher_pid).ordered
      111:     end
      112: 
      113:     it "logs the restart action" do
      114:       adapter.reload
      115:       expect(upgrader).to have_received(:log).with("Restarting #{server_name} pid: #{launcher_pid}")
      116:     end
      117: 
      118:     it "sends USR2 signal to launcher" do
      119:       adapter.reload
      120:       expect(Process).to have_received(:kill).with("USR2", launcher_pid)
      121:     end
      122: 
      123:     it "waits for original master to exit" do
      124:       call_count = 0
      125:       allow(Process).to receive(:getpgid).with(master_pid) do
      126:         call_count += 1
      127:         raise Errno::ESRCH if call_count > 2
      128:         true
      129:       end
      130: 
      131:       adapter.reload
>>>   132:       expect(adapter).to have_received(:sleep).with(2).twice
      133:     end
      134: 
      135:     it "waits for workers to respond to health check" do
      136:       call_count = 0
      137:       allow(adapter).to receive(:`).with(/curl/) do
      138:         call_count += 1
      139:         call_count > 3 ? "ok" : "not ready"

Omitted 7 additional diagnosed FP example(s) for brevity.

@6 6 bot force-pushed the fix/rspec-subject_stub-23729875530 branch from d0be728 to 302077e Compare March 30, 2026 06:06
@6 6 bot marked this pull request as ready for review March 30, 2026 06:06
@6 6 bot enabled auto-merge (squash) March 30, 2026 06:06
@github-actions
Copy link
Copy Markdown
Contributor

Cop-check results (8 shards)

Cop Baseline FP Baseline FN Local FP Local FN FP Δ FN Δ Result
RSpec/SubjectStub 0 5 0 0 0 -5
RSpec/SubjectStub 2 0 2 0 0 0
RSpec/SubjectStub 0 0 0 0 0 0
RSpec/SubjectStub 4 0 4 0 0 0
RSpec/SubjectStub 0 0 0 0 0 0
RSpec/SubjectStub 11 0 0 0 -11 0
RSpec/SubjectStub 0 0 0 0 0 0
RSpec/SubjectStub 0 0 0 0 0 0

All shards passed — ready to merge.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants