bitfluent-database_cleaner 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. data/History.txt +100 -0
  2. data/LICENSE +20 -0
  3. data/README.textile +118 -0
  4. data/Rakefile +46 -0
  5. data/TODO +0 -0
  6. data/VERSION.yml +5 -0
  7. data/cucumber.yml +1 -0
  8. data/examples/features/example.feature +11 -0
  9. data/examples/features/step_definitions/example_steps.rb +8 -0
  10. data/examples/features/support/env.rb +23 -0
  11. data/examples/lib/activerecord_models.rb +12 -0
  12. data/examples/lib/couchpotato_models.rb +21 -0
  13. data/examples/lib/datamapper_models.rb +16 -0
  14. data/examples/lib/mongoid_models.rb +23 -0
  15. data/examples/lib/mongomapper_models.rb +17 -0
  16. data/features/cleaning.feature +21 -0
  17. data/features/step_definitions/database_cleaner_steps.rb +25 -0
  18. data/features/support/env.rb +7 -0
  19. data/lib/database_cleaner.rb +3 -0
  20. data/lib/database_cleaner/active_record/transaction.rb +26 -0
  21. data/lib/database_cleaner/active_record/truncation.rb +106 -0
  22. data/lib/database_cleaner/configuration.rb +129 -0
  23. data/lib/database_cleaner/couch_potato/truncation.rb +26 -0
  24. data/lib/database_cleaner/cucumber.rb +8 -0
  25. data/lib/database_cleaner/data_mapper/transaction.rb +23 -0
  26. data/lib/database_cleaner/data_mapper/truncation.rb +142 -0
  27. data/lib/database_cleaner/mongo_mapper/truncation.rb +30 -0
  28. data/lib/database_cleaner/mongoid/truncation.rb +23 -0
  29. data/lib/database_cleaner/truncation_base.rb +41 -0
  30. data/spec/database_cleaner/active_record/transaction_spec.rb +65 -0
  31. data/spec/database_cleaner/active_record/truncation_spec.rb +66 -0
  32. data/spec/database_cleaner/configuration_spec.rb +107 -0
  33. data/spec/database_cleaner/couch_potato/truncation_spec.rb +40 -0
  34. data/spec/database_cleaner/mongo_mapper/truncation_spec.rb +81 -0
  35. data/spec/database_cleaner/mongoid/truncation_spec.rb +84 -0
  36. data/spec/spec.opts +6 -0
  37. data/spec/spec_helper.rb +12 -0
  38. metadata +118 -0
@@ -0,0 +1,30 @@
1
+ require 'database_cleaner/truncation_base'
2
+
3
+ module DatabaseCleaner
4
+ module MongoMapper
5
+ class Truncation < DatabaseCleaner::TruncationBase
6
+ def clean
7
+ if @only
8
+ collections.each { |c| c.remove if @only.include?(c.name) }
9
+ else
10
+ collections.each { |c| c.remove unless @tables_to_exclude.include?(c.name) }
11
+ end
12
+ true
13
+ end
14
+
15
+ private
16
+
17
+ def connection
18
+ ::MongoMapper.connection
19
+ end
20
+
21
+ def collections
22
+ connection.db(database).collections
23
+ end
24
+
25
+ def database
26
+ ::MongoMapper.database.name
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,23 @@
1
+ require 'database_cleaner/truncation_base'
2
+
3
+ module DatabaseCleaner
4
+ module Mongoid
5
+ class Truncation < DatabaseCleaner::TruncationBase
6
+ def clean
7
+ if @only
8
+ collections.each { |c| c.remove if @only.include?(c.name) }
9
+ else
10
+ collections.each { |c| c.remove unless @tables_to_exclude.include?(c.name) }
11
+ end
12
+ true
13
+ end
14
+
15
+ private
16
+
17
+ def collections
18
+ ::Mongoid.database.collections
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,41 @@
1
+ module DatabaseCleaner
2
+ class TruncationBase
3
+
4
+ def initialize(options = {})
5
+ if !options.empty? && !(options.keys - [:only, :except]).empty?
6
+ raise ArgumentError, "The only valid options are :only and :except. You specified #{options.keys.join(',')}."
7
+ end
8
+ if options.has_key?(:only) && options.has_key?(:except)
9
+ raise ArgumentError, "You may only specify either :only or :either. Doing both doesn't really make sense does it?"
10
+ end
11
+
12
+ @only = options[:only]
13
+ @tables_to_exclude = (options[:except] || [])
14
+ if migration_storage = migration_storage_name
15
+ @tables_to_exclude << migration_storage
16
+ end
17
+ end
18
+
19
+ def start
20
+ # no-op
21
+ end
22
+
23
+ def clean
24
+ raise NotImplementedError
25
+ end
26
+
27
+
28
+ private
29
+
30
+ def tables_to_truncate
31
+ raise NotImplementedError
32
+ end
33
+
34
+ # overwrite in subclasses
35
+ # default implementation given because migration storage need not be present
36
+ def migration_storage_name
37
+ nil
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,65 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+ require 'database_cleaner/active_record/transaction'
3
+ require 'active_record'
4
+
5
+ module DatabaseCleaner
6
+ module ActiveRecord
7
+
8
+ describe Transaction do
9
+ let (:connection) { mock("connection") }
10
+ before(:each) do
11
+ ::ActiveRecord::Base.stub!(:connection).and_return(connection)
12
+ end
13
+
14
+ describe "#start" do
15
+ it "should increment open transactions if possible" do
16
+ connection.stub!(:respond_to?).with(:increment_open_transactions).and_return(true)
17
+ connection.stub!(:begin_db_transaction)
18
+
19
+ connection.should_receive(:increment_open_transactions)
20
+ Transaction.new.start
21
+ end
22
+
23
+ it "should tell ActiveRecord to increment connection if its not possible to increment current connection" do
24
+ connection.stub!(:respond_to?).with(:increment_open_transactions).and_return(false)
25
+ connection.stub!(:begin_db_transaction)
26
+
27
+ ::ActiveRecord::Base.should_receive(:increment_open_transactions)
28
+ Transaction.new.start
29
+ end
30
+
31
+ it "should start a transaction" do
32
+ connection.stub!(:increment_open_transactions)
33
+
34
+ connection.should_receive(:begin_db_transaction)
35
+ Transaction.new.start
36
+ end
37
+ end
38
+
39
+ describe "#clean" do
40
+ it "should start a transaction" do
41
+ connection.stub!(:decrement_open_transactions)
42
+
43
+ connection.should_receive(:rollback_db_transaction)
44
+ Transaction.new.clean
45
+ end
46
+ it "should decrement open transactions if possible" do
47
+ connection.stub!(:respond_to?).with(:decrement_open_transactions).and_return(true)
48
+ connection.stub!(:rollback_db_transaction)
49
+
50
+ connection.should_receive(:decrement_open_transactions)
51
+ Transaction.new.clean
52
+ end
53
+
54
+ it "should decrement connection via ActiveRecord::Base if connection won't" do
55
+ connection.stub!(:respond_to?).with(:decrement_open_transactions).and_return(false)
56
+ connection.stub!(:rollback_db_transaction)
57
+
58
+ ::ActiveRecord::Base.should_receive(:decrement_open_transactions)
59
+ Transaction.new.clean
60
+ end
61
+ end
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,66 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+ require 'database_cleaner/active_record/truncation'
3
+ require 'active_record'
4
+ module ActiveRecord
5
+ module ConnectionAdapters
6
+ [MysqlAdapter, Mysql2Adapter, SQLite3Adapter, JdbcAdapter, PostgreSQLAdapter].each do |adapter|
7
+ describe adapter, "#truncate_table" do
8
+ it "should truncate the table"
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ module DatabaseCleaner
15
+ module ActiveRecord
16
+
17
+ describe Truncation do
18
+ before(:each) do
19
+ @connection = mock('connection')
20
+ @connection.stub!(:disable_referential_integrity).and_yield
21
+ ::ActiveRecord::Base.stub!(:connection).and_return(@connection)
22
+ end
23
+
24
+ it "should truncate all tables except for schema_migrations" do
25
+ @connection.stub!(:tables).and_return(%w[schema_migrations widgets dogs])
26
+
27
+ @connection.should_receive(:truncate_table).with('widgets')
28
+ @connection.should_receive(:truncate_table).with('dogs')
29
+ @connection.should_not_receive(:truncate_table).with('schema_migrations')
30
+
31
+ Truncation.new.clean
32
+ end
33
+
34
+ it "should only truncate the tables specified in the :only option when provided" do
35
+ @connection.stub!(:tables).and_return(%w[schema_migrations widgets dogs])
36
+
37
+ @connection.should_receive(:truncate_table).with('widgets')
38
+ @connection.should_not_receive(:truncate_table).with('dogs')
39
+
40
+ Truncation.new(:only => ['widgets']).clean
41
+ end
42
+
43
+ it "should not truncate the tables specified in the :except option" do
44
+ @connection.stub!(:tables).and_return(%w[schema_migrations widgets dogs])
45
+
46
+ @connection.should_receive(:truncate_table).with('dogs')
47
+ @connection.should_not_receive(:truncate_table).with('widgets')
48
+
49
+ Truncation.new(:except => ['widgets']).clean
50
+ end
51
+
52
+ it "should raise an error when :only and :except options are used" do
53
+ running {
54
+ Truncation.new(:except => ['widgets'], :only => ['widgets'])
55
+ }.should raise_error(ArgumentError)
56
+ end
57
+
58
+ it "should raise an error when invalid options are provided" do
59
+ running { Truncation.new(:foo => 'bar') }.should raise_error(ArgumentError)
60
+ end
61
+
62
+
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,107 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require 'database_cleaner/active_record/transaction'
3
+ require 'database_cleaner/data_mapper/transaction'
4
+
5
+
6
+ describe DatabaseCleaner do
7
+
8
+ # These examples muck around with the constants for autodetection so we need to clean up....
9
+ before(:all) do
10
+ TempAR = ActiveRecord unless defined?(TempAR)
11
+ TempMM = MongoMapper unless defined?(TempMM)
12
+ TempMI = Mongoid unless defined?(TempMI)
13
+ Object.send(:remove_const, 'MongoMapper') if defined?(::MongoMapper)
14
+ Object.send(:remove_const, 'Mongoid') if defined?(::Mongoid)
15
+ # need to add one for each ORM that we load in the spec helper...
16
+ end
17
+ after(:all) do
18
+ Object.send(:remove_const, 'ActiveRecord') if defined?(::ActiveRecord) #want to make sure we have the real one...
19
+ ActiveRecord = TempAR
20
+ MongoMapper = TempMM
21
+ Mongoid = TempMI
22
+ end
23
+
24
+ before(:each) do
25
+ DatabaseCleaner::ActiveRecord::Transaction.stub!(:new).and_return(@strategy = mock('strategy'))
26
+ Object.const_set('ActiveRecord', "just mocking out the constant here...") unless defined?(::ActiveRecord)
27
+ DatabaseCleaner.strategy = nil
28
+ DatabaseCleaner.orm = nil
29
+ end
30
+
31
+ describe ".create_strategy" do
32
+ it "should initialize and return the appropirate strategy" do
33
+ DatabaseCleaner::ActiveRecord::Transaction.should_receive(:new).with('options' => 'hash')
34
+ result = DatabaseCleaner.create_strategy(:transaction, {'options' => 'hash'})
35
+ result.should == @strategy
36
+ end
37
+ end
38
+
39
+ describe ".clean_with" do
40
+ it "should initialize the appropirate strategy and clean with it" do
41
+ DatabaseCleaner::ActiveRecord::Transaction.should_receive(:new).with('options' => 'hash')
42
+ @strategy.should_receive(:clean)
43
+ DatabaseCleaner.clean_with(:transaction, 'options' => 'hash')
44
+ end
45
+ end
46
+
47
+ describe ".strategy=" do
48
+ it "should initialize the appropirate strategy based on the ORM adapter detected" do
49
+ DatabaseCleaner::ActiveRecord::Transaction.should_receive(:new).with('options' => 'hash')
50
+ DatabaseCleaner.strategy = :transaction, {'options' => 'hash'}
51
+
52
+ Object.send(:remove_const, 'ActiveRecord')
53
+ Object.const_set('DataMapper', "just mocking out the constant here...")
54
+ DatabaseCleaner.orm = nil
55
+
56
+ DatabaseCleaner::DataMapper::Transaction.should_receive(:new).with(no_args)
57
+ DatabaseCleaner.strategy = :transaction
58
+ end
59
+
60
+ it "should raise an error when no ORM is detected" do
61
+ Object.send(:remove_const, 'ActiveRecord') if defined?(::ActiveRecord)
62
+ Object.send(:remove_const, 'DataMapper') if defined?(::DataMapper)
63
+ Object.send(:remove_const, 'CouchPotato') if defined?(::CouchPotato)
64
+
65
+ running { DatabaseCleaner.strategy = :transaction }.should raise_error(DatabaseCleaner::NoORMDetected)
66
+ end
67
+
68
+ it "should use the strategy version of the ORM specified with #orm=" do
69
+ DatabaseCleaner.orm = 'data_mapper'
70
+ DatabaseCleaner::DataMapper::Transaction.should_receive(:new)
71
+
72
+ DatabaseCleaner.strategy = :transaction
73
+ end
74
+
75
+ it "should raise an error when multiple args is passed in and the first is not a symbol" do
76
+ running { DatabaseCleaner.strategy=Object.new, {:foo => 'bar'} }.should raise_error(ArgumentError)
77
+ end
78
+
79
+ it "should raise an error when the specified strategy is not found" do
80
+ running { DatabaseCleaner.strategy = :foo }.should raise_error(DatabaseCleaner::UnknownStrategySpecified)
81
+ end
82
+
83
+ it "should allow any object to be set as the strategy" do
84
+ mock_strategy = mock('strategy')
85
+ running { DatabaseCleaner.strategy = mock_strategy }.should_not raise_error
86
+ end
87
+
88
+ end
89
+
90
+
91
+ %w[start clean].each do |strategy_method|
92
+ describe ".#{strategy_method}" do
93
+ it "should be delgated to the strategy set with strategy=" do
94
+ DatabaseCleaner.strategy = :transaction
95
+
96
+ @strategy.should_receive(strategy_method)
97
+
98
+ DatabaseCleaner.send(strategy_method)
99
+ end
100
+
101
+ it "should raise en error when no strategy has been set" do
102
+ running { DatabaseCleaner.send(strategy_method) }.should raise_error(DatabaseCleaner::NoStrategySetError)
103
+ end
104
+ end
105
+ end
106
+
107
+ end
@@ -0,0 +1,40 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+ require 'database_cleaner/couch_potato/truncation'
3
+ require 'couch_potato'
4
+
5
+ module DatabaseCleaner
6
+ module CouchPotato
7
+
8
+ describe Truncation do
9
+ before(:each) do
10
+ @database = mock('database')
11
+ ::CouchPotato.stub!(:couchrest_database).and_return(@database)
12
+ end
13
+
14
+ it "should re-create the database" do
15
+ @database.should_receive(:recreate!)
16
+
17
+ Truncation.new.clean
18
+ end
19
+
20
+ it "should raise an error when the :only option is used" do
21
+ running {
22
+ Truncation.new(:only => ['document-type'])
23
+ }.should raise_error(ArgumentError)
24
+ end
25
+
26
+ it "should raise an error when the :except option is used" do
27
+ running {
28
+ Truncation.new(:except => ['document-type'])
29
+ }.should raise_error(ArgumentError)
30
+ end
31
+
32
+ it "should raise an error when invalid options are provided" do
33
+ running {
34
+ Truncation.new(:foo => 'bar')
35
+ }.should raise_error(ArgumentError)
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,81 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+ require 'mongo_mapper'
3
+ require 'database_cleaner/mongo_mapper/truncation'
4
+
5
+ MongoMapper.connection = Mongo::Connection.new('127.0.0.1')
6
+ TEST_DATABASE = 'database_cleaner_specs'
7
+ MongoMapper.database = TEST_DATABASE
8
+
9
+ class Widget
10
+ include MongoMapper::Document
11
+ key :name, String
12
+ end
13
+ class Gadget
14
+ include MongoMapper::Document
15
+ key :name, String
16
+ end
17
+
18
+
19
+ module DatabaseCleaner
20
+ module MongoMapper
21
+
22
+ describe Truncation do
23
+ before(:each) do
24
+ ::MongoMapper.connection.drop_database(TEST_DATABASE)
25
+ #::MongoMapper.connection.db(TEST_DATABASE).collections.each {|c| c.remove }
26
+ #::MongoMapper.database = TEST_DATABASE
27
+ end
28
+
29
+ def ensure_counts(expected_counts)
30
+ # I had to add this sanity_check garbage because I was getting non-determinisc results from mongomapper at times..
31
+ # very odd and disconcerting...
32
+ sanity_check = expected_counts.delete(:sanity_check)
33
+ begin
34
+ expected_counts.each do |model_class, expected_count|
35
+ model_class.count.should equal(expected_count), "#{model_class} expected to have a count of #{expected_count} but was #{model_class.count}"
36
+ end
37
+ rescue Spec::Expectations::ExpectationNotMetError => e
38
+ raise !sanity_check ? e : Spec::ExpectationNotMetError::ExpectationNotMetError.new("SANITY CHECK FAILURE! This should never happen here: #{e.message}")
39
+ end
40
+ end
41
+
42
+ def create_widget(attrs={})
43
+ Widget.new({:name => 'some widget'}.merge(attrs)).save!
44
+ end
45
+
46
+ def create_gadget(attrs={})
47
+ Gadget.new({:name => 'some gadget'}.merge(attrs)).save!
48
+ end
49
+
50
+ it "truncates all collections by default" do
51
+ create_widget
52
+ create_gadget
53
+ ensure_counts(Widget => 1, Gadget => 1, :sanity_check => true)
54
+ Truncation.new.clean
55
+ ensure_counts(Widget => 0, Gadget => 0)
56
+ end
57
+
58
+ context "when collections are provided to the :only option" do
59
+ it "only truncates the specified collections" do
60
+ create_widget
61
+ create_gadget
62
+ ensure_counts(Widget => 1, Gadget => 1, :sanity_check => true)
63
+ Truncation.new(:only => ['widgets']).clean
64
+ ensure_counts(Widget => 0, Gadget => 1)
65
+ end
66
+ end
67
+
68
+ context "when collections are provided to the :except option" do
69
+ it "truncates all but the specified collections" do
70
+ create_widget
71
+ create_gadget
72
+ ensure_counts(Widget => 1, Gadget => 1, :sanity_check => true)
73
+ Truncation.new(:except => ['widgets']).clean
74
+ ensure_counts(Widget => 1, Gadget => 0)
75
+ end
76
+ end
77
+
78
+ end
79
+
80
+ end
81
+ end