From 0f85a37259e23efb18c8202895fd0a540d2a5b84 Mon Sep 17 00:00:00 2001 From: David Corson-Knowles Date: Fri, 14 Aug 2026 02:36:46 -0700 Subject: [PATCH] Stop a fake team inheriting a real same-named team's plugin data Plugin's registry is keyed by team name, and the around hook installed by Testing.enable! only busts it after an example that built a fake team. An example that reads only real teams therefore leaves real-team plugin instances in the registry, and a later fake team sharing a real team's name silently receives the real team's cached plugin data instead of its own. This makes suites order-dependent in a way that is hard to trace: the spec passes in isolation and fails only when some earlier example happened to touch a real team of the same name. Bust the plugin registry as each fake team is registered so the fake always registers first. Only Plugin.bust_caches! is called, not CodeTeams.bust_caches!, so CodeTeams.all stays memoized and no team YAML is re-read. Co-Authored-By: Claude Opus 5 --- lib/code_teams/testing.rb | 5 +++++ spec/lib/code_teams/testing_spec.rb | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/lib/code_teams/testing.rb b/lib/code_teams/testing.rb index f28886d..f3ecc57 100644 --- a/lib/code_teams/testing.rb +++ b/lib/code_teams/testing.rb @@ -51,6 +51,11 @@ def self.create_code_team(attributes) ) code_teams << code_team + # Plugin's registry is keyed by team name, and the hook above only busts it after an + # example that built a fake team. An example reading only real teams therefore leaves + # real-team plugin instances behind, and a fake team sharing a real team's name would + # inherit that cached data. Drop the registry so the fake registers first. + Plugin.bust_caches! code_team end diff --git a/spec/lib/code_teams/testing_spec.rb b/spec/lib/code_teams/testing_spec.rb index 53b6e0e..4e8611d 100644 --- a/spec/lib/code_teams/testing_spec.rb +++ b/spec/lib/code_teams/testing_spec.rb @@ -11,5 +11,23 @@ expect(CodeTeams.find('Temp Team')).to eq(team) expect(team.raw_hash.dig('extra_data', 'foo', 'bar')).to eq(1) end + + it 'does not let a fake team inherit a real same-named team\'s cached plugin data' do + test_plugin_class = Class.new(CodeTeams::Plugin) do + def test_plugin + Data.define(:source).new(@team.raw_hash['extra_data']['source']) + end + end + stub_const('TestPlugin', test_plugin_class) + CodeTeams.bust_caches! + write_team_yml(extra_data: { 'source' => 'real' }) + + # Reading the real team caches its plugin instance under the name 'My Team'. + expect(CodeTeams.find('My Team').test_plugin.source).to eq('real') + + fake = described_class.create_code_team({ name: 'My Team', extra_data: { 'source' => 'fake' } }) + + expect(fake.test_plugin.source).to eq('fake') + end end end