jonrowe-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 (35) hide show
  1. data/History.txt +85 -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/mongomapper_models.rb +17 -0
  15. data/features/cleaning.feature +20 -0
  16. data/features/step_definitions/database_cleaner_steps.rb +25 -0
  17. data/features/support/env.rb +7 -0
  18. data/lib/database_cleaner.rb +3 -0
  19. data/lib/database_cleaner/active_record/transaction.rb +30 -0
  20. data/lib/database_cleaner/active_record/truncation.rb +110 -0
  21. data/lib/database_cleaner/configuration.rb +127 -0
  22. data/lib/database_cleaner/couch_potato/truncation.rb +26 -0
  23. data/lib/database_cleaner/cucumber.rb +8 -0
  24. data/lib/database_cleaner/data_mapper/transaction.rb +23 -0
  25. data/lib/database_cleaner/data_mapper/truncation.rb +142 -0
  26. data/lib/database_cleaner/mongo_mapper/truncation.rb +30 -0
  27. data/lib/database_cleaner/truncation_base.rb +41 -0
  28. data/spec/database_cleaner/active_record/transaction_spec.rb +165 -0
  29. data/spec/database_cleaner/active_record/truncation_spec.rb +136 -0
  30. data/spec/database_cleaner/configuration_spec.rb +118 -0
  31. data/spec/database_cleaner/couch_potato/truncation_spec.rb +40 -0
  32. data/spec/database_cleaner/mongo_mapper/truncation_spec.rb +81 -0
  33. data/spec/spec.opts +6 -0
  34. data/spec/spec_helper.rb +12 -0
  35. metadata +109 -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,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,165 @@
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
+ let (:another_connection) { mock("a different connection") }
11
+
12
+ let (:model_klass) do
13
+ model_klass = mock("klass")
14
+ model_klass.stub!(:connection).and_return(another_connection)
15
+ model_klass
16
+ end
17
+
18
+ before(:each) do
19
+ ::ActiveRecord::Base.stub!(:connection).and_return(connection)
20
+ end
21
+
22
+ describe "start" do
23
+ context "single connection" do
24
+ it "should increment open transactions if possible" do
25
+ connection.stub!(:respond_to?).with(:increment_open_transactions).and_return(true)
26
+ connection.stub!(:begin_db_transaction)
27
+
28
+ connection.should_receive(:increment_open_transactions)
29
+ Transaction.new.start
30
+ end
31
+
32
+ it "should tell ActiveRecord to increment connection if its not possible to increment current connection" do
33
+ connection.stub!(:respond_to?).with(:increment_open_transactions).and_return(false)
34
+ connection.stub!(:begin_db_transaction)
35
+
36
+ ::ActiveRecord::Base.should_receive(:increment_open_transactions)
37
+ Transaction.new.start
38
+ end
39
+
40
+ it "should start a transaction" do
41
+ connection.stub!(:increment_open_transactions)
42
+
43
+ connection.should_receive(:begin_db_transaction)
44
+ Transaction.new.start
45
+ end
46
+ end
47
+
48
+ context "multiple connection" do
49
+
50
+ before(:each) do
51
+ DatabaseCleaner::ActiveRecord.connection_klasses = [model_klass]
52
+ end
53
+
54
+ after(:each) do
55
+ DatabaseCleaner::ActiveRecord.connection_klasses = []
56
+ end
57
+
58
+ it "should increment open transactions on both connections if possible" do
59
+ connection.stub!(:respond_to?).with(:increment_open_transactions).and_return(true)
60
+ connection.stub!(:begin_db_transaction)
61
+
62
+ another_connection.stub!(:respond_to?).with(:increment_open_transactions).and_return(true)
63
+ another_connection.stub!(:begin_db_transaction)
64
+
65
+ connection.should_receive(:increment_open_transactions)
66
+ another_connection.should_receive(:increment_open_transactions)
67
+ Transaction.new.start
68
+ end
69
+
70
+ it "should tell ActiveRecord to increment connection if its not possible to increment current connection" do
71
+ connection.stub!(:respond_to?).with(:increment_open_transactions).and_return(false)
72
+ connection.stub!(:begin_db_transaction)
73
+
74
+ another_connection.stub!(:respond_to?).with(:increment_open_transactions).and_return(false)
75
+ another_connection.stub!(:begin_db_transaction)
76
+
77
+ ::ActiveRecord::Base.should_receive(:increment_open_transactions)
78
+ model_klass.should_receive(:increment_open_transactions)
79
+ Transaction.new.start
80
+ end
81
+
82
+ it "should start a transaction" do
83
+ connection.stub!(:increment_open_transactions)
84
+ another_connection.stub!(:increment_open_transactions)
85
+
86
+ connection.should_receive(:begin_db_transaction)
87
+ another_connection.should_receive(:begin_db_transaction)
88
+ Transaction.new.start
89
+ end
90
+ end
91
+ end
92
+
93
+ describe "clean" do
94
+ context "single connection" do
95
+ it "should finish a transaction" do
96
+ connection.stub!(:decrement_open_transactions)
97
+
98
+ connection.should_receive(:rollback_db_transaction)
99
+ Transaction.new.clean
100
+ end
101
+
102
+ it "should decrement open transactions if possible" do
103
+ connection.stub!(:respond_to?).with(:decrement_open_transactions).and_return(true)
104
+ connection.stub!(:rollback_db_transaction)
105
+
106
+ connection.should_receive(:decrement_open_transactions)
107
+ Transaction.new.clean
108
+ end
109
+
110
+ it "should decrement connection via ActiveRecord::Base if connection won't" do
111
+ connection.stub!(:respond_to?).with(:decrement_open_transactions).and_return(false)
112
+ connection.stub!(:rollback_db_transaction)
113
+
114
+ ::ActiveRecord::Base.should_receive(:decrement_open_transactions)
115
+ Transaction.new.clean
116
+ end
117
+ end
118
+
119
+ context "multiple connection" do
120
+ before(:each) do
121
+ DatabaseCleaner::ActiveRecord.connection_klasses = [model_klass]
122
+ end
123
+
124
+ after(:each) do
125
+ DatabaseCleaner::ActiveRecord.connection_klasses = []
126
+ end
127
+
128
+ it "should finish a transaction" do
129
+ connection.stub!(:decrement_open_transactions)
130
+ another_connection.stub!(:decrement_open_transactions)
131
+
132
+ connection.should_receive(:rollback_db_transaction)
133
+ another_connection.should_receive(:rollback_db_transaction)
134
+ Transaction.new.clean
135
+ end
136
+
137
+ it "should decrement open transactions if possible" do
138
+ connection.stub!(:respond_to?).with(:decrement_open_transactions).and_return(true)
139
+ connection.stub!(:rollback_db_transaction)
140
+
141
+ another_connection.stub!(:respond_to?).with(:decrement_open_transactions).and_return(true)
142
+ another_connection.stub!(:rollback_db_transaction)
143
+
144
+ connection.should_receive(:decrement_open_transactions)
145
+ another_connection.should_receive(:decrement_open_transactions)
146
+ Transaction.new.clean
147
+ end
148
+
149
+ it "should decrement connection via ActiveRecord::Base if connection won't" do
150
+ connection.stub!(:respond_to?).with(:decrement_open_transactions).and_return(false)
151
+ connection.stub!(:rollback_db_transaction)
152
+
153
+ another_connection.stub!(:respond_to?).with(:decrement_open_transactions).and_return(false)
154
+ another_connection.stub!(:rollback_db_transaction)
155
+
156
+ ::ActiveRecord::Base.should_receive(:decrement_open_transactions)
157
+ model_klass.should_receive(:decrement_open_transactions)
158
+ Transaction.new.clean
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ end
165
+ end
@@ -0,0 +1,136 @@
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, 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
+ let (:connection) { mock('connection') }
19
+
20
+ before(:each) do
21
+ connection.stub!(:disable_referential_integrity).and_yield
22
+ ::ActiveRecord::Base.stub!(:connection).and_return(connection)
23
+ end
24
+
25
+ context "single connection" do
26
+ it "should truncate all tables except for schema_migrations" do
27
+ connection.stub!(:tables).and_return(%w[schema_migrations widgets dogs])
28
+
29
+ connection.should_receive(:truncate_table).with('widgets')
30
+ connection.should_receive(:truncate_table).with('dogs')
31
+ connection.should_not_receive(:truncate_table).with('schema_migrations')
32
+
33
+ Truncation.new.clean
34
+ end
35
+
36
+ it "should only truncate the tables specified in the :only option when provided" do
37
+ connection.stub!(:tables).and_return(%w[schema_migrations widgets dogs])
38
+
39
+ connection.should_receive(:truncate_table).with('widgets')
40
+ connection.should_not_receive(:truncate_table).with('dogs')
41
+
42
+ Truncation.new(:only => ['widgets']).clean
43
+ end
44
+
45
+ it "should not truncate the tables specified in the :except option" do
46
+ connection.stub!(:tables).and_return(%w[schema_migrations widgets dogs])
47
+
48
+ connection.should_receive(:truncate_table).with('dogs')
49
+ connection.should_not_receive(:truncate_table).with('widgets')
50
+
51
+ Truncation.new(:except => ['widgets']).clean
52
+ end
53
+
54
+ it "should raise an error when :only and :except options are used" do
55
+ running {
56
+ Truncation.new(:except => ['widgets'], :only => ['widgets'])
57
+ }.should raise_error(ArgumentError)
58
+ end
59
+
60
+ it "should raise an error when invalid options are provided" do
61
+ running { Truncation.new(:foo => 'bar') }.should raise_error(ArgumentError)
62
+ end
63
+ end
64
+
65
+ context "multiple connection" do
66
+
67
+ let (:another_connection) { mock('another connection') }
68
+ let (:model_klass) { mock('klass') }
69
+
70
+ before(:each) do
71
+ another_connection.stub!(:disable_referential_integrity).and_yield
72
+ model_klass.stub!(:connection).and_return(another_connection)
73
+
74
+ DatabaseCleaner::ActiveRecord.connection_klasses = [model_klass]
75
+ end
76
+
77
+ after(:each) do
78
+ DatabaseCleaner::ActiveRecord.connection_klasses = []
79
+ end
80
+
81
+ it "should truncate all tables except for schema_migrations on both connections" do
82
+ connection.stub!(:tables).and_return(%w[schema_migrations widgets dogs])
83
+ another_connection.stub!(:tables).and_return(%w[schema_migrations widgets dogs])
84
+
85
+ connection.should_receive(:truncate_table).with('widgets')
86
+ connection.should_receive(:truncate_table).with('dogs')
87
+ connection.should_not_receive(:truncate_table).with('schema_migrations')
88
+
89
+ another_connection.should_receive(:truncate_table).with('widgets')
90
+ another_connection.should_receive(:truncate_table).with('dogs')
91
+ another_connection.should_not_receive(:truncate_table).with('schema_migrations')
92
+
93
+ Truncation.new.clean
94
+ end
95
+
96
+ it "should only truncate the tables specified in the :only option when provided" do
97
+ connection.stub!(:tables).and_return(%w[schema_migrations widgets dogs])
98
+ another_connection.stub!(:tables).and_return(%w[schema_migrations widgets dogs])
99
+
100
+ connection.should_receive(:truncate_table).with('widgets')
101
+ connection.should_not_receive(:truncate_table).with('dogs')
102
+
103
+ another_connection.should_receive(:truncate_table).with('widgets')
104
+ another_connection.should_not_receive(:truncate_table).with('dogs')
105
+
106
+ Truncation.new(:only => ['widgets']).clean
107
+ end
108
+
109
+ it "should not truncate the tables specified in the :except option" do
110
+ connection.stub!(:tables).and_return(%w[schema_migrations widgets dogs])
111
+ another_connection.stub!(:tables).and_return(%w[schema_migrations widgets dogs])
112
+
113
+ connection.should_receive(:truncate_table).with('dogs')
114
+ connection.should_not_receive(:truncate_table).with('widgets')
115
+
116
+ another_connection.should_receive(:truncate_table).with('dogs')
117
+ another_connection.should_not_receive(:truncate_table).with('widgets')
118
+
119
+ Truncation.new(:except => ['widgets']).clean
120
+ end
121
+
122
+ it "should raise an error when :only and :except options are used" do
123
+ running {
124
+ Truncation.new(:except => ['widgets'], :only => ['widgets'])
125
+ }.should raise_error(ArgumentError)
126
+ end
127
+
128
+ it "should raise an error when invalid options are provided" do
129
+ running { Truncation.new(:foo => 'bar') }.should raise_error(ArgumentError)
130
+ end
131
+ end
132
+
133
+ end
134
+
135
+ end
136
+ end
@@ -0,0 +1,118 @@
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
+ describe ActiveRecord do
9
+ describe "connections" do
10
+ it "should return an array of classes containing ActiveRecord::Base by default" do
11
+ ::DatabaseCleaner::ActiveRecord.connection_klasses.should == [::ActiveRecord::Base]
12
+ end
13
+ it "should merge in an array of classes to get connections from" do
14
+ model = mock("model")
15
+ ::DatabaseCleaner::ActiveRecord.connection_klasses = [model]
16
+ ::DatabaseCleaner::ActiveRecord.connection_klasses.should include model
17
+ ::DatabaseCleaner::ActiveRecord.connection_klasses.should include ::ActiveRecord::Base
18
+ end
19
+ end
20
+ end
21
+
22
+ # These examples muck around with the constants for autodetection so we need to clean up....
23
+ before(:all) do
24
+ TempAR = ActiveRecord unless defined?(TempAR)
25
+ TempMM = MongoMapper unless defined?(TempMM)
26
+ Object.send(:remove_const, 'MongoMapper') if defined?(::MongoMapper)
27
+ # need to add one for each ORM that we load in the spec helper...
28
+ end
29
+ after(:all) do
30
+ Object.send(:remove_const, 'ActiveRecord') if defined?(::ActiveRecord) #want to make sure we have the real one...
31
+ ActiveRecord = TempAR
32
+ MongoMapper = TempMM
33
+ end
34
+
35
+ before(:each) do
36
+ DatabaseCleaner::ActiveRecord::Transaction.stub!(:new).and_return(@strategy = mock('strategy'))
37
+ Object.const_set('ActiveRecord', "just mocking out the constant here...") unless defined?(::ActiveRecord)
38
+ DatabaseCleaner.strategy = nil
39
+ DatabaseCleaner.orm = nil
40
+ end
41
+
42
+ describe ".create_strategy" do
43
+ it "should initialize and return the appropirate strategy" do
44
+ DatabaseCleaner::ActiveRecord::Transaction.should_receive(:new).with('options' => 'hash')
45
+ result = DatabaseCleaner.create_strategy(:transaction, {'options' => 'hash'})
46
+ result.should == @strategy
47
+ end
48
+ end
49
+
50
+ describe ".clean_with" do
51
+ it "should initialize the appropirate strategy and clean with it" do
52
+ DatabaseCleaner::ActiveRecord::Transaction.should_receive(:new).with('options' => 'hash')
53
+ @strategy.should_receive(:clean)
54
+ DatabaseCleaner.clean_with(:transaction, 'options' => 'hash')
55
+ end
56
+ end
57
+
58
+ describe ".strategy=" do
59
+ it "should initialize the appropirate strategy based on the ORM adapter detected" do
60
+ DatabaseCleaner::ActiveRecord::Transaction.should_receive(:new).with('options' => 'hash')
61
+ DatabaseCleaner.strategy = :transaction, {'options' => 'hash'}
62
+
63
+ Object.send(:remove_const, 'ActiveRecord')
64
+ Object.const_set('DataMapper', "just mocking out the constant here...")
65
+ DatabaseCleaner.orm = nil
66
+
67
+ DatabaseCleaner::DataMapper::Transaction.should_receive(:new).with(no_args)
68
+ DatabaseCleaner.strategy = :transaction
69
+ end
70
+
71
+ it "should raise an error when no ORM is detected" do
72
+ Object.send(:remove_const, 'ActiveRecord') if defined?(::ActiveRecord)
73
+ Object.send(:remove_const, 'DataMapper') if defined?(::DataMapper)
74
+ Object.send(:remove_const, 'CouchPotato') if defined?(::CouchPotato)
75
+
76
+ running { DatabaseCleaner.strategy = :transaction }.should raise_error(DatabaseCleaner::NoORMDetected)
77
+ end
78
+
79
+ it "should use the strategy version of the ORM specified with #orm=" do
80
+ DatabaseCleaner.orm = 'data_mapper'
81
+ DatabaseCleaner::DataMapper::Transaction.should_receive(:new)
82
+
83
+ DatabaseCleaner.strategy = :transaction
84
+ end
85
+
86
+ it "should raise an error when multiple args is passed in and the first is not a symbol" do
87
+ running { DatabaseCleaner.strategy=Object.new, {:foo => 'bar'} }.should raise_error(ArgumentError)
88
+ end
89
+
90
+ it "should raise an error when the specified strategy is not found" do
91
+ running { DatabaseCleaner.strategy = :foo }.should raise_error(DatabaseCleaner::UnknownStrategySpecified)
92
+ end
93
+
94
+ it "should allow any object to be set as the strategy" do
95
+ mock_strategy = mock('strategy')
96
+ running { DatabaseCleaner.strategy = mock_strategy }.should_not raise_error
97
+ end
98
+
99
+ end
100
+
101
+
102
+ %w[start clean].each do |strategy_method|
103
+ describe ".#{strategy_method}" do
104
+ it "should be delgated to the strategy set with strategy=" do
105
+ DatabaseCleaner.strategy = :transaction
106
+
107
+ @strategy.should_receive(strategy_method)
108
+
109
+ DatabaseCleaner.send(strategy_method)
110
+ end
111
+
112
+ it "should raise en error when no strategy has been set" do
113
+ running { DatabaseCleaner.send(strategy_method) }.should raise_error(DatabaseCleaner::NoStrategySetError)
114
+ end
115
+ end
116
+ end
117
+
118
+ end