k2nr/ulid-rails

Fixtures cannot be loaded

gjtorikian opened this issue · 11 comments

This is a fork of this earlier comment I made.

I finally found some time to set up a reproducible test case: https://github.com/gjtorikian/ulid-fixtures-bug

Run bin/rails db:create && bin/rails db:migrate && bin/rails test to see the issue:

E

Error:
ProductTest#test_it_can_find_a_product:
ActiveRecord::RecordNotFound: Couldn't find Product with [WHERE "products"."id" = $1]
    test/models/products_test.rb:9:in `block in <class:ProductTest>'

It seems that, for fixtures which are backed by ULID, the Rails test runner cannot find them.

In ulid-rails < 2.0, I was able to work around this with:

module ULID
  module Generator
    extend T::Sig
    # encode is a private method, so we monkey patch in
    # a new method that calls the private method
    def encode_wo_random(input)
      encode(input, ULID::Generator::ENCODED_LENGTH)
    end
  end
end

module FixtureSetExtensions
  extend T::Sig

  def identify(label, column_type = :integer)
    # Need to override default ID generation for fixtures,
    # because it wholly assumes that the fixture IDs are integers
    if column_type == :binary && !ULID::Rails::Validator.is_valid?(label)
      input = super # use default ID generation
      value = ULID.encode_wo_random(input)
      ULID::Rails::Type.new.serialize(value)
    else
      super
    end
  end
end

module FixtureExtensions
  def find
    primary_key_type = model_class.columns.find { |c| c.name == model_class.primary_key }.type
    super unless primary_key_type == :binary

    deserialized_primary_key = ULID::Rails::Type.new.deserialize(fixture[model_class.primary_key])
    # all of the lines below are from the original `find` method
    object = model_class.unscoped do
      model_class.find(deserialized_primary_key)
    end
    object.instance_variable_set(:@strict_loading, false)
    object
  end
end

module ActiveRecord
  class FixtureSet
    class << self
      prepend FixtureSetExtensions
    end
  end

  class Fixture
    prepend FixtureExtensions
  end
end

To begin with, I think this is a Rails problem, but I am not entirely sure. Rails converts a fixture's YAML key name (product_one) into an integer (here). That integer does not exist in the DB, because we're using ULIDs. And, from what I remember, the ULID ID autogeneration inserts some randomness; so I rewrote the methods to allow for Rails to look up fixtures by their keyname turned into a ULID.

If this is a Rails problem, their identify code should probably support bytea, just as they already support uuid. But I am not sure they will accept that solution, and so I think this lib should have a solution by default. What I cannot figure out is why no one else seems to have encountered this issue, and what I've done to trigger it.

👋 @bquorning just seeing if there's any more info I can provide here.

Thank you for the bug report and the repository with reproduction code @gjtorikian. That made the bug very easy to replicate for me 🙏🏼

Rails’ fixtures seem to depend on the column type and ignores the sql_type.

(ruby) ActiveRecord::FixtureSet::ModelMetadata.new(Product).primary_key_type
:binary
(ruby) Product.connection.schema_cache.columns(:products)[0].type
:binary
(ruby) Product.connection.schema_cache.columns(:products)[0].sql_type
"bytea"

The code you linked to is switching on this type and adding a special case if its value is :uuid. Since the value in our case is :binary, an integer is created.

The only solution I could find was to change the type returned by ULID::Rails::Type, from :binary to :ulid (I am not 100% sure that this won’t have bad consequences), and then patch the ActiveRecord::FixtureSet.identify method to add special handling for the :ulid type.

Could you please check if #70 fixes the issue?

Sadly, it. did not. 😦 Here's the stack trace from that branch:

NoMethodError: undefined method `encoding' for nil
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activemodel-7.1.3.2/lib/active_model/type/binary.rb:43:in `initialize'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activemodel-7.1.3.2/lib/active_model/type/binary.rb:32:in `new'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activemodel-7.1.3.2/lib/active_model/type/binary.rb:32:in `serialize'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:555:in `block (2 levels) in build_fixture_sql'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:552:in `each'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:552:in `map'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:552:in `block in build_fixture_sql'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:544:in `map'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:544:in `build_fixture_sql'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:586:in `block in build_fixture_statements'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:584:in `each'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:584:in `filter_map'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:584:in `build_fixture_statements'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:455:in `insert_fixtures_set'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/fixtures.rb:681:in `block in insert'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/fixtures.rb:672:in `each'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/fixtures.rb:672:in `insert'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/fixtures.rb:658:in `read_and_insert'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/fixtures.rb:603:in `create_fixtures'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/test_fixtures.rb:255:in `load_fixtures'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/test_fixtures.rb:142:in `setup_fixtures'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/isolator-1.0.1/lib/isolator/railtie.rb:7:in `setup_fixtures'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/test_fixtures.rb:10:in `before_setup'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/testing/setup_and_teardown.rb:40:in `before_setup'

Error:
ConversationPolicyTest#test_for_a_reader_without_corresponding_message_permisions:
NoMethodError: undefined method `pop' for nil
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/mocha-2.1.0/lib/mocha/mockery.rb:54:in `ensure in teardown'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/mocha-2.1.0/lib/mocha/mockery.rb:54:in `teardown'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/mocha-2.1.0/lib/mocha/hooks.rb:39:in `mocha_teardown'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/mocha-2.1.0/lib/mocha/integration/mini_test/adapter.rb:47:in `after_teardown'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/testing/time_helpers.rb:71:in `after_teardown'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/actioncable-7.1.3.2/lib/action_cable/test_helper.rb:17:in `after_teardown'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/test_fixtures.rb:15:in `after_teardown'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/testing/setup_and_teardown.rb:51:in `after_teardown'

bin/rails test test/policies/conversation_policy_test.rb:90

Let me know how else I can help! I tried the branch out in the test case repo I created earlier, and it didn't work there either.

Thanks for giving it a try.

Let me know how else I can help! I tried the branch out in the test case repo I created earlier, and it didn't work there either.

That is strange. When I use gem "ulid-rails", git: "https://github.com/k2nr/ulid-rails.git", branch: "patch-fixtures" with your test case repo, the test is passing.

Could you try again, and perhaps push up your changes to ulid-fixtures-bug if it’s still failing?

Sure! I pushed my commit up; I'm also running the test using bin/rails test (curious if you ran some other command that worked?).

I run bin/rails db:create && bin/rails db:migrate && bin/rails test – and the test passes on my machine. 😕

Can you share which error you get, plus stacktrace?

Sure, here it is:

bin/rails db:create && bin/rails db:migrate && bin/rails test
Database 'test_development' already exists
Database 'test_test' already exists
/rails dbRunning 1 tests in a single process (parallelization threshold is 50)
Run options: --seed 29343

# Running:

E

Error:
ProductTest#test_it_can_find_a_product:
ActiveRecord::NotNullViolation: PG::NotNullViolation: ERROR:  null value in column "id" of relation "products" violates not-null constraint
DETAIL:  Failing row contains (null, Name, Description, 1984-01-01 00:00:00, 1994-01-01 00:00:00).

    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/postgresql/database_statements.rb:55:in `exec'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/postgresql/database_statements.rb:55:in `block (2 levels) in raw_execute'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract_adapter.rb:1028:in `block in with_raw_connection'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/concurrency/null_lock.rb:9:in `synchronize'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract_adapter.rb:1000:in `with_raw_connection'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/postgresql/database_statements.rb:54:in `block in raw_execute'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/notifications/instrumenter.rb:58:in `instrument'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract_adapter.rb:1143:in `log'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/postgresql/database_statements.rb:53:in `raw_execute'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:521:in `internal_execute'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:131:in `execute'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/query_cache.rb:25:in `execute'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/postgresql/database_statements.rb:47:in `execute'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/postgresql/database_statements.rb:151:in `execute_batch'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:462:in `block (3 levels) in insert_fixtures_set'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:535:in `block in within_new_transaction'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/concurrency/null_lock.rb:9:in `synchronize'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:532:in `within_new_transaction'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:344:in `transaction'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:461:in `block (2 levels) in insert_fixtures_set'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/postgresql/referential_integrity.rb:19:in `disable_referential_integrity'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:460:in `block in insert_fixtures_set'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:601:in `with_multi_statements'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:459:in `insert_fixtures_set'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/fixtures.rb:681:in `block in insert'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/fixtures.rb:672:in `each'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/fixtures.rb:672:in `insert'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/fixtures.rb:658:in `read_and_insert'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/fixtures.rb:603:in `create_fixtures'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/test_fixtures.rb:255:in `load_fixtures'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/test_fixtures.rb:142:in `setup_fixtures'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/test_fixtures.rb:10:in `before_setup'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/testing/setup_and_teardown.rb:40:in `before_setup'


bin/rails test test/models/products_test.rb:8



Finished in 0.027135s, 36.8528 runs/s, 0.0000 assertions/s.
1 runs, 0 assertions, 0 failures, 1 errors, 0 skips

~/Developer/ulid-fixtures-bug main ⇡
❯ bin/rails db:reset
Dropped database 'test_development'
Dropped database 'test_test'
Created database 'test_development'
Created database 'test_test'

~/Developer/ulid-fixtures-bug main ⇡
❯ bin/rails db:migrate && bin/rails test
Running 1 tests in a single process (parallelization threshold is 50)
Run options: --seed 12539

# Running:

E

Error:
ProductTest#test_it_can_find_a_product:
ActiveRecord::NotNullViolation: PG::NotNullViolation: ERROR:  null value in column "id" of relation "products" violates not-null constraint
DETAIL:  Failing row contains (null, Name, Description, 1984-01-01 00:00:00, 1994-01-01 00:00:00).

    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/postgresql/database_statements.rb:55:in `exec'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/postgresql/database_statements.rb:55:in `block (2 levels) in raw_execute'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract_adapter.rb:1028:in `block in with_raw_connection'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/concurrency/null_lock.rb:9:in `synchronize'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract_adapter.rb:1000:in `with_raw_connection'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/postgresql/database_statements.rb:54:in `block in raw_execute'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/notifications/instrumenter.rb:58:in `instrument'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract_adapter.rb:1143:in `log'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/postgresql/database_statements.rb:53:in `raw_execute'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:521:in `internal_execute'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:131:in `execute'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/query_cache.rb:25:in `execute'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/postgresql/database_statements.rb:47:in `execute'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/postgresql/database_statements.rb:151:in `execute_batch'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:462:in `block (3 levels) in insert_fixtures_set'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:535:in `block in within_new_transaction'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/concurrency/null_lock.rb:9:in `synchronize'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:532:in `within_new_transaction'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:344:in `transaction'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:461:in `block (2 levels) in insert_fixtures_set'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/postgresql/referential_integrity.rb:19:in `disable_referential_integrity'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:460:in `block in insert_fixtures_set'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:601:in `with_multi_statements'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:459:in `insert_fixtures_set'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/fixtures.rb:681:in `block in insert'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/fixtures.rb:672:in `each'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/fixtures.rb:672:in `insert'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/fixtures.rb:658:in `read_and_insert'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/fixtures.rb:603:in `create_fixtures'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/test_fixtures.rb:255:in `load_fixtures'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/test_fixtures.rb:142:in `setup_fixtures'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activerecord-7.1.3.2/lib/active_record/test_fixtures.rb:10:in `before_setup'
    /Users/gjtorikian/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/testing/setup_and_teardown.rb:40:in `before_setup'


bin/rails test test/models/products_test.rb:8

FWIW I will also try this branch in my actual (real) app and see if I can pinpoint what isn't working. Thank you for your help with all of this!

I set up GitHub Actions on that test repo, and the tests failed in the same way I posted above: https://github.com/gjtorikian/ulid-fixtures-bug/actions/runs/8225587319/job/22490802305

I'm not sure why this would work on your machine, but at least I confirmed it's not mine! 😅

Ok, this is weird. I got the error once locally, but then I never saw it again. I will try to dig deeper into this over the coming days.

I think I found it! I am not sure how, but it seems the directive id: :binary did not make its way from db/migrate/20240103172229_create_products.rb into db/schema.rb. If I change that first line, the test is passing.

diff --git a/db/schema.rb b/db/schema.rb
index ba62d9b..f090cca 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -14,7 +14,7 @@ ActiveRecord::Schema[7.1].define(version: 2024_01_03_172229) do
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
 
-  create_table "products", force: :cascade do |t|
+  create_table "products", id: :binary, force: :cascade do |t|
     t.string "name"
     t.text "description"
     t.datetime "created_at", null: false

Good news, your suggested change fixed my sample bug! But, I still can't seem to get that branch to work for my project. There must be something I am setting up incorrectly. I am reluctant to do so, but I'll close this issue out since I think the problem is on my end. Will report back if I find out what is going on.