Skip to content

Commit

Permalink
Merge pull request #9 from trusted/rspec-helpers
Browse files Browse the repository at this point in the history
Allow enabling/disabling IronTrail in rspec
  • Loading branch information
andrepiske authored Dec 9, 2024
2 parents 54f2aa1 + a3d9cc6 commit 2c85696
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 2 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ jobs:
POSTGRES_HOST: localhost
POSTGRES_PORT: 5432
- name: Run specs
run: bundle exec rake install_database_yml spec
run:
bundle exec rake install_database_yml spec && bundle exec rake testing_spec
env:
BACKTRACE: 1
BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Added means to disable tracking ignored tables
- Allow enabling/disabling IronTrail in rspec

## 0.0.1 - 2024-11-26

Expand Down
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,32 @@ RSpec.configure do |config|
end
```

You'll likely also want to require [lib/iron_trail/testing/rspec.rb](lib/iron_trail/testing/rspec.rb)
in your `rails_helper.rb`, then explicitly either disable or enable IronTrail in tests:

```ruby
require 'iron_trail/testing/rspec'
IronTrail::Testing.enable! # to have it enabled by default in specs
IronTrail::Testing.disable! # to have it disabled by default in specs
```

You don't make it explicit, IronTrail will be enabled by default, which will
likely impact your test suite performance slightly.

In case you disable it by default, you can enable it per rspec context with:

```ruby
describe 'in a "describe" block', iron_trail: true do
it 'or also in an "it" block', iron_trail: true do
# ...
end
end
```

Enabling/disabling IronTrail in specs works by replacing the trigger function in Postgres
with a dummy no-op function or with the real function and it won't add or drop triggers from
any tables.

## Rake tasks

IronTrail comes with a few handy rake tasks you can use in your dev, test and
Expand Down
10 changes: 9 additions & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,12 @@ require 'rspec/core/rake_task'
task(:spec).clear
RSpec::Core::RakeTask.new(:spec)

task default: %i[prepare spec]
# Loading the testing/rspec file will affect RSpec globally. Because of that,
# we want to test it in a separate scope. We could also always require it,
# but that wouldn't be true in a real rails app and could make all tests
# farther apart from reality, thus less reliable.
RSpec::Core::RakeTask.new(:testing_spec).tap do |task|
task.pattern = 'spec/testing_itself.rb'
end

task default: %i[prepare spec testing_spec]
63 changes: 63 additions & 0 deletions lib/iron_trail/testing/rspec.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,66 @@
# frozen_string_literal: true

if ENV['RAILS_ENV'] == 'production'
raise 'This file should not be required in production. ' \
'Change the RAILS_ENV env var temporarily to override this.'
end

require 'iron_trail'

module IronTrail
module Testing
class << self
attr_accessor :enabled

def enable!
DbFunctions.new(ActiveRecord::Base.connection).install_functions
@enabled = true
end

def disable!
# We "disable" it by replacing the trigger function by a no-op one.
# This should be faster than adding/removing triggers from several
# tables every time.
sql = <<~SQL
CREATE OR REPLACE FUNCTION irontrail_log_row()
RETURNS TRIGGER AS $$
BEGIN
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
SQL

ActiveRecord::Base.connection.execute(sql)
@enabled = false
end

def with_iron_trail(want_enabled:, &block)
was_enabled = IronTrail::Testing.enabled

if want_enabled
::IronTrail::Testing.enable! unless was_enabled
else
::IronTrail::Testing.disable! if was_enabled
end

block.call
ensure
if want_enabled && !was_enabled
::IronTrail::Testing.disable!
elsif !want_enabled && was_enabled
::IronTrail::Testing.enable!
end
end
end
end
end

RSpec.configure do |config|
config.around(:each, iron_trail: true) do |example|
IronTrail::Testing.with_iron_trail(want_enabled: true) { example.run }
end
config.around(:each, iron_trail: false) do |example|
raise "Using iron_trail: false does not do what you might think it does. To disable iron_trail, " \
"use IronTrail::Testing.with_iron_trail(want_enabled: false) { ... } instead."
end
end
52 changes: 52 additions & 0 deletions spec/testing_itself.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# frozen_string_literal: true

require 'iron_trail/testing/rspec'

IronTrail::Testing.disable!

RSpec.describe 'lib/iron_trail/testing/rspec.rb' do
let(:person) { Person.create!(first_name: 'Arthur', last_name: 'Schopenhauer') }

subject(:do_some_changes!) do
person.update!(first_name: 'Jim')
person.update!(first_name: 'Jane')
end

describe 'IronTrail::Testing#with_iron_trail' do
context 'when IronTrail is disabled but we enable it for a while' do
it 'tracks only while enabled' do
person.update!(first_name: 'Jim')

expect(person.reload.iron_trails.length).to be(0)

IronTrail::Testing.with_iron_trail(want_enabled: true) do
person.update!(first_name: 'Jane')
end

expect(person.reload.iron_trails.length).to be(1)

person.update!(first_name: 'Joe')

expect(person.reload.iron_trails.length).to be(1)
end
end
end

describe 'rspec helpers' do
context 'with IronTrail disabled' do
it 'does not track anything' do
do_some_changes!

expect(person.reload.iron_trails.length).to be(0)
end
end

context 'with IronTrail enabled through the helper', iron_trail: true do
it 'does not track anything' do
do_some_changes!

expect(person.reload.iron_trails.length).to be(3)
end
end
end
end

0 comments on commit 2c85696

Please sign in to comment.