with_model icon indicating copy to clipboard operation
with_model copied to clipboard

Error when used on MySQL with transactional_fixtures enabled in RSpec

Open Envek opened this issue 11 months ago • 1 comments

When Rails application is tested with RSpec and transactional_fixtures are enabled:

RSpec.configure do |config|
  config.extend WithModel
  config.use_transactional_fixtures = true
end

RSpec.describe "TestModel" do
  with_model :TestModel do
    table do |t|
      t.string :title
    end
  end

  it "works" do
    expect(TestModel.create!(title: "Whatever")).to be_present
  end
end

Following error is thrown when a with_model record is created:

SAVEPOINT active_record_1 does not exist

Test logs:

TRANSACTION (0.1ms)  BEGIN
 (21.5ms)  CREATE TABLE `with_model_test_models_178402_9300` (`id` bigint NOT NULL AUTO_INCREMENT PRIMARY KEY, `title` varchar(255))
TRANSACTION (0.3ms)  SAVEPOINT active_record_1
TestModel Create (2.0ms)  INSERT INTO `with_model_test_models_178402_9300` (`title`) VALUES ('Whatever')
TRANSACTION (0.3ms)  RELEASE SAVEPOINT active_record_1
TRANSACTION (0.1ms)  ROLLBACK TO SAVEPOINT active_record_1
 (4.2ms)  DROP TABLE `with_model_test_models_178402_9300`

Note that ROLLBACK TO SAVEPOINT active_record_1 used after releasing that same savepoint instead of just ROLLBACK of the whole transaction.

Correct logs from PostgreSQL example
TRANSACTION (0.2ms)  BEGIN
 (3.4ms)  CREATE TABLE "with_model_test_models_182673_6320" ("id" bigserial primary key, "title" character varying)
TRANSACTION (0.5ms)  SAVEPOINT active_record_1
TestModel Create (1.2ms)  INSERT INTO "with_model_test_models_182673_6320" ("title") VALUES ($1) RETURNING "id"  [["title", "Whatever"]]
TRANSACTION (0.4ms)  RELEASE SAVEPOINT active_record_1
 (0.8ms)  DROP TABLE "with_model_test_models_182673_6320"
TRANSACTION (0.4ms)  ROLLBACK

Environment:

Gem version: 2.2.0 Rails version: 7.2 and 8.0, both mysql2 and trilogy adapters.

Reproduction steps:

  1. Run MySQL server. E.g. with Docker:

    docker run --rm -e MYSQL_ALLOW_EMPTY_PASSWORD=yes -e MYSQL_DATABASE=test -p 3306:3306 mysql:latest
    
  2. Run reproduction executable test case: https://gist.github.com/Envek/1e152e913787554379ec2834cdeaf169

    wget https://gist.github.com/Envek/1e152e913787554379ec2834cdeaf169/raw/09e2bc0a4501322842e5462fdb222d7475ea551e/with_model_mysql_spec.rb
    ruby with_model_mysql_spec.rb  
    

Workaround:

Replace with_model with manual table and model creation.

Manual table and model creation.
before(:context) do # rubocop:disable RSpec/BeforeAfterAll
  ActiveRecord::Schema.define do
    drop_table :dynamodb_attrs, if_exists: true
    create_table :test_models do |t|
      t.string :title
    end
  end
end

after(:context) do # rubocop:disable RSpec/BeforeAfterAll
  ActiveRecord::Schema.define do
    drop_table :test_models, if_exists: true
  end
end

# Stub the constant for each example
before do
  stub_const("TestModel", Class.new(ActiveRecord::Base) do
    self.table_name = "test_models"
  end)
end

Envek avatar Feb 05 '25 12:02 Envek