From 39ee760b2abcc0d725acee46b79f2aabe3e87894 Mon Sep 17 00:00:00 2001 From: Stephen Rushe Date: Fri, 1 Apr 2016 11:23:40 +0100 Subject: [PATCH] Allow models to have thresholds for reporting --- README.md | 18 +++- lib/database_plumber/leak_finder.rb | 7 +- lib/database_plumber/version.rb | 2 +- spec/lib/database_plumber/leak_finder_spec.rb | 88 ++++++++++++++++++- 4 files changed, 107 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0886ba9..40e0101 100644 --- a/README.md +++ b/README.md @@ -133,8 +133,22 @@ config.after(:all) do end ``` -This is also useful if your test suite is problem-free and you would like ensure it -stays that way. +### Setting thresholds for Models + +You may have some models you would like to report on, but which should also have +entries in the database, for example a table that is seeded or loaded with fixtures. +In order to allow this you can provide a threshold for a Model, which is the +maximum number of entries allowed in the database for the Model before it is +regarded as leaky. + +To provide a threshold for a Model, you can can the following: + +```ruby +config.after(:all) do + # Perform the report after each example group + DatabasePlumber.inspect model_thresholds: { Bar => 3 } +end + ## Contributing diff --git a/lib/database_plumber/leak_finder.rb b/lib/database_plumber/leak_finder.rb index b85287e..1140de4 100644 --- a/lib/database_plumber/leak_finder.rb +++ b/lib/database_plumber/leak_finder.rb @@ -12,12 +12,13 @@ def self.inspect(options = {}) def initialize(options) @ignored_models = (options[:ignored_models] || []) + IGNORED_AR_INTERNALS @ignored_adapters = options[:ignored_adapters] || [] + @model_thresholds = options[:model_thresholds] || {} end def inspect filtered_models.each_with_object({}) do |model, results| records = count_for(model) - if records > 0 + if records > threshold_for(model) results[model.to_s] = records mop_up(model) end @@ -34,6 +35,10 @@ def count_for(model) raise InvalidModelError, "#{model} does not have a valid table definition" end + def threshold_for(model) + @model_thresholds.key?(model) ? @model_thresholds[model].to_i : 0 + end + def mop_up(model) model.destroy_all end diff --git a/lib/database_plumber/version.rb b/lib/database_plumber/version.rb index 57d6af1..2580633 100644 --- a/lib/database_plumber/version.rb +++ b/lib/database_plumber/version.rb @@ -1,3 +1,3 @@ module DatabasePlumber - VERSION = '1.0.0' + VERSION = '1.1.0' end diff --git a/spec/lib/database_plumber/leak_finder_spec.rb b/spec/lib/database_plumber/leak_finder_spec.rb index 3cf5c6e..0980632 100644 --- a/spec/lib/database_plumber/leak_finder_spec.rb +++ b/spec/lib/database_plumber/leak_finder_spec.rb @@ -3,7 +3,7 @@ let(:ignored_connection) { double(:connection, adapter_name: 'SQLite') } let(:happy_model) { double(:happy, name: 'Happy', abstract_class?: nil, connection: normal_connection, count: 0) } - let(:leaky_model) { double(:leaky, name: 'Leaky', abstract_class?: nil, connection: normal_connection, count: 1) } + let(:leaky_model) { double(:leaky, name: 'Leaky', abstract_class?: nil, connection: normal_connection, count: 2) } let(:abstract_model) { double(:abstract, name: 'Abstract', abstract_class?: true, connection: normal_connection, count: 2) } let(:ignored_model) { double(:ignored, name: 'Ignored', abstract_class?: nil, connection: normal_connection, count: 3) } let(:ignored_adapter_model) { double(:anon, name: 'Anon', abstract_class?: nil, connection: ignored_connection, count: 4) } @@ -24,7 +24,7 @@ context 'with no params' do let(:expected_leaks) do { - leaky_model.to_s => 1, + leaky_model.to_s => 2, ignored_model.to_s => 3, ignored_adapter_model.to_s => 4 } @@ -54,7 +54,7 @@ let(:expected_leaks) do { - leaky_model.to_s => 1, + leaky_model.to_s => 2, ignored_adapter_model.to_s => 4 } end @@ -83,7 +83,7 @@ let(:expected_leaks) do { - leaky_model.to_s => 1, + leaky_model.to_s => 2, ignored_model.to_s => 3 } end @@ -103,6 +103,86 @@ it { expect(join_model_stub).not_to have_received(:destroy_all) } end + context 'with a threshold' do + context 'with a leaky model at the threshold' do + let(:options_params) do + { + ignored_models: [ignored_model], + ignored_adapters: [:sqlite], + model_thresholds: { leaky_model => 2 } + } + end + + before(:each) { @leaks = described_class.inspect(options_params) } + + it { expect(@leaks).to be_empty } + + it { expect(ActiveRecord::SchemaMigration).not_to have_received(:destroy_all) } + + it { expect(happy_model).not_to have_received(:destroy_all) } + it { expect(leaky_model).not_to have_received(:destroy_all) } + + it { expect(ignored_model).not_to have_received(:destroy_all) } + it { expect(ignored_adapter_model).not_to have_received(:destroy_all) } + + it { expect(join_model_stub).not_to have_received(:destroy_all) } + end + + context 'with a leaky model below the threshold' do + let(:options_params) do + { + ignored_models: [ignored_model], + ignored_adapters: [:sqlite], + model_thresholds: { leaky_model => 5 } + } + end + + before(:each) { @leaks = described_class.inspect(options_params) } + + it { expect(@leaks).to be_empty } + + it { expect(ActiveRecord::SchemaMigration).not_to have_received(:destroy_all) } + + it { expect(happy_model).not_to have_received(:destroy_all) } + it { expect(leaky_model).not_to have_received(:destroy_all) } + + it { expect(ignored_model).not_to have_received(:destroy_all) } + it { expect(ignored_adapter_model).not_to have_received(:destroy_all) } + + it { expect(join_model_stub).not_to have_received(:destroy_all) } + end + + context 'with a leaky model above the threshold' do + let(:options_params) do + { + ignored_models: [ignored_model], + ignored_adapters: [:sqlite], + model_thresholds: { leaky_model => 1 } + } + end + + let(:expected_leaks) do + { + leaky_model.to_s => 2 + } + end + + before(:each) { @leaks = described_class.inspect(options_params) } + + it { expect(@leaks).to eql(expected_leaks) } + + it { expect(ActiveRecord::SchemaMigration).not_to have_received(:destroy_all) } + + it { expect(happy_model).not_to have_received(:destroy_all) } + it { expect(leaky_model).to have_received(:destroy_all) } + + it { expect(ignored_model).not_to have_received(:destroy_all) } + it { expect(ignored_adapter_model).not_to have_received(:destroy_all) } + + it { expect(join_model_stub).not_to have_received(:destroy_all) } + end + end + context 'with no leaking models in scope' do let(:options_params) do {