Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow enabling/disabling IronTrail in rspec #9

Merged
merged 8 commits into from
Dec 9, 2024
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
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
Comment on lines +3 to +6
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would raising here stop the application from booting?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep 👍


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!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's unlikely to happen since you need to require this, but one might do it in a rails console, I'd like iron_trail to protect me from doing this in production

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed. I'll add a protection if ENV['RAILS_ENV'] == 'production'

# 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!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: also add a test where it is enabled by default, to ensure that iron_trail: false is working properly

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


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
Loading