Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions lib/fixture_kit/coders/active_record_coder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ def mount(data)
# This should be revisited when Rails 8.2 makes it public.
connection.__send__(:execute_batch, statements, "FixtureKit Insert")
end

verify_foreign_keys!(connection)
end
end
end
Expand Down Expand Up @@ -93,6 +95,19 @@ def build_insert_sql(table_name, columns, rows, connection)
"INSERT INTO #{quoted_table} (#{quoted_columns.join(", ")}) VALUES #{rows.join(", ")}"
end

def verify_foreign_keys!(connection)
return unless ActiveRecord.verify_foreign_keys_for_fixtures

begin
connection.check_all_foreign_keys_valid!
rescue ActiveRecord::StatementInvalid => e
raise FixtureKit::Error,
"Foreign key violations found in cached fixture data. The cache may be " \
"stale relative to your current schema or fixture definitions. " \
"Original error:\n\n#{e.message}"
end
end

def models_by_pool(data)
seen = Set.new

Expand Down
65 changes: 65 additions & 0 deletions spec/unit/coders/active_record_coder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,71 @@ def exercise_user_write_operations(suffix)

coder.mount(records)
end

context "when ActiveRecord.verify_foreign_keys_for_fixtures is true" do
around do |example|
previous = ActiveRecord.verify_foreign_keys_for_fixtures
ActiveRecord.verify_foreign_keys_for_fixtures = true
example.run
ensure
ActiveRecord.verify_foreign_keys_for_fixtures = previous
end

it "calls check_all_foreign_keys_valid! after the batch executes" do
records = { User => "INSERT INTO users (id, name) VALUES (1, 'Alice')" }
fake_connection = stub_fake_connection
stub_pool(User, fake_connection)

coder.mount(records)

expect(fake_connection).to have_received(:check_all_foreign_keys_valid!).once
end

it "wraps an FK violation in a FixtureKit::Error with a hint about stale cache" do
records = { User => "INSERT INTO users (id, name) VALUES (1, 'Alice')" }
fake_connection = stub_fake_connection
allow(fake_connection).to receive(:check_all_foreign_keys_valid!)
.and_raise(ActiveRecord::StatementInvalid, "Foreign key violations found: orders")
stub_pool(User, fake_connection)

expect { coder.mount(records) }.to raise_error(FixtureKit::Error, /cached fixture data.*stale.*Foreign key violations found: orders/m)
end
end

context "when ActiveRecord.verify_foreign_keys_for_fixtures is false" do
around do |example|
previous = ActiveRecord.verify_foreign_keys_for_fixtures
ActiveRecord.verify_foreign_keys_for_fixtures = false
example.run
ensure
ActiveRecord.verify_foreign_keys_for_fixtures = previous
end

it "does not call check_all_foreign_keys_valid!" do
records = { User => "INSERT INTO users (id, name) VALUES (1, 'Alice')" }
fake_connection = stub_fake_connection
stub_pool(User, fake_connection)

coder.mount(records)

expect(fake_connection).not_to have_received(:check_all_foreign_keys_valid!)
end
end

def stub_fake_connection
fake_connection = double("connection")
allow(fake_connection).to receive(:disable_referential_integrity).and_yield
allow(fake_connection).to receive(:execute_batch)
allow(fake_connection).to receive(:quote_table_name) { |name| %("#{name}") }
allow(fake_connection).to receive(:check_all_foreign_keys_valid!)
fake_connection
end

def stub_pool(model, connection)
pool = double("pool-for-#{model.name}")
allow(pool).to receive(:with_connection).and_yield(connection)
allow(model).to receive(:connection_pool).and_return(pool)
end
end

describe "sql.active_record payload name format assumptions" do
Expand Down
Loading