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
2 changes: 0 additions & 2 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ jobs:
RSPEC_JUNIT_FORMATTER: true
RSPEC_JUNIT_OUTPUT: junit.xml
run: |
bundle exec rails db:migrate
bundle exec rails parallel:prepare
bundle exec rake coverage:parallel

- name: Upload coverage to Codecov
Expand Down
9 changes: 3 additions & 6 deletions .github/workflows/test-ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,9 @@ jobs:
- name: Setup environment
uses: ./.github/actions/setup-environment

- name: Prepare database
env:
RAILS_ENV: test
run: |
bundle exec rails db:migrate
bundle exec rails parallel:prepare
# No database prep: the test suite uses an in-memory SQLite database and
# loads db/schema.rb into it at boot (see spec/rails_helper.rb), so there
# is nothing to migrate or prepare on disk.

- name: Run tests
env:
Expand Down
7 changes: 5 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,17 @@ This is useful for production environments where PDF generation is expensive. Se
- **Run parallel tests with coverage**: `bundle exec rake coverage:parallel`
- Run single test: `bundle exec rspec spec/path/to/file_spec.rb:LINE_NUMBER`
- Run with verbose output: `bundle exec rspec --format documentation`
- Prepare parallel test databases: `bundle exec rails parallel:prepare`
- **Test database is in-memory**: the test env uses shared-cache in-memory
SQLite and loads `db/schema.rb` at boot (see `spec/rails_helper.rb`), so
there is no `parallel:prepare`/`db:migrate` step - each parallel worker and
each mutant kill-fork gets its own isolated in-memory database

## Environment Notes

- **ripgrep (rg) is NOT installed** - use `grep` command instead of `rg` for searching
- **Full test suite is SLOW** - only run `bundle exec rspec` when explicitly requested
- Prefer running individual test files or specific tests during development
- **Database locking**: If tests fail with "database is locked", just inform the user and wait for them to confirm it's unlocked
- **Database locking**: The test database is in-memory (no file lock). If a run still reports "database is locked", inform the user and wait for them to confirm it's unlocked
- **NEVER paste code into Rails console** - it never works. Instead write very specific RSpec tests
- **Active Storage cleanup**: Test suite automatically cleans tmp/storage before and after test runs

Expand Down
16 changes: 14 additions & 2 deletions config/database.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,21 @@ development:
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
# In-memory, shared-cache SQLite. A URI database with cache=shared lets every
# connection in a process (the test thread and Capybara's Puma server thread)
# see the same schema and data, with no file on disk to lock under mutant's
# parallel kill-forks. Shared cache is per-process, so parallel_test workers are
# isolated automatically - TEST_ENV_NUMBER is no longer needed to separate them.
# WAL/mmap pragmas are dropped because they are meaningless for an in-memory DB.
test:
<<: *default
database: storage/test<%= ENV["TEST_ENV_NUMBER"] %>.sqlite3
adapter: sqlite3
database: "file::memory:?cache=shared"
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000
pragmas:
journal_mode: memory
synchronous: "off"
busy_timeout: 5000
# SQLite3 write its data on the local filesystem, as such it requires
# persistent disks. If you are deploying to a managed service, you should
# make sure it provides disk persistence, as many don't.
Expand Down
25 changes: 20 additions & 5 deletions spec/rails_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,22 @@

Rails.root.glob("spec/support/**/*.rb").sort_by(&:to_s).each { |f| require f }

begin
ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
abort e.to_s.strip
# An in-memory database starts empty on every boot, so load the schema
# straight into the live connection (keeping it in the shared cache) rather
# than the file-based maintain_test_schema! dance.
def in_memory_database?
ActiveRecord::Base.connection_db_config.database.to_s.include?(":memory:")
end

if in_memory_database?
ActiveRecord::Schema.verbose = false
load Rails.root.join("db/schema.rb")
else
begin
ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
abort e.to_s.strip
end
end

# Configure ActiveStorage for test environment
Expand Down Expand Up @@ -68,7 +80,10 @@

config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
if ENV["TEST_ENV_NUMBER"]
# Re-establishing the connection for a file-based parallel worker is
# harmless, but for an in-memory DB it would drop the shared cache (and
# the schema loaded above) - each worker already has its own in-memory DB.
if ENV["TEST_ENV_NUMBER"] && !in_memory_database?
ActiveRecord::Base.establish_connection(
ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).first
)
Expand Down
Loading