saimonmoore-database_cleaner 0.5.0
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.
- data/History.txt +81 -0
- data/LICENSE +20 -0
- data/README.textile +127 -0
- data/Rakefile +46 -0
- data/TODO +0 -0
- data/VERSION.yml +5 -0
- data/cucumber.yml +1 -0
- data/examples/features/example.feature +11 -0
- data/examples/features/step_definitions/example_steps.rb +8 -0
- data/examples/features/support/env.rb +23 -0
- data/examples/lib/activerecord_models.rb +12 -0
- data/examples/lib/couchpotato_models.rb +21 -0
- data/examples/lib/couchrest_models.rb +23 -0
- data/examples/lib/datamapper_models.rb +16 -0
- data/examples/lib/mongomapper_models.rb +17 -0
- data/features/cleaning.feature +21 -0
- data/features/step_definitions/database_cleaner_steps.rb +25 -0
- data/features/support/env.rb +7 -0
- data/lib/database_cleaner.rb +3 -0
- data/lib/database_cleaner/active_record/transaction.rb +26 -0
- data/lib/database_cleaner/active_record/truncation.rb +79 -0
- data/lib/database_cleaner/configuration.rb +128 -0
- data/lib/database_cleaner/couch_potato/truncation.rb +26 -0
- data/lib/database_cleaner/couchrest/compatibility.rb +12 -0
- data/lib/database_cleaner/couchrest/truncation.rb +28 -0
- data/lib/database_cleaner/cucumber.rb +8 -0
- data/lib/database_cleaner/data_mapper/transaction.rb +23 -0
- data/lib/database_cleaner/data_mapper/truncation.rb +142 -0
- data/lib/database_cleaner/mongo_mapper/truncation.rb +30 -0
- data/lib/database_cleaner/truncation_base.rb +41 -0
- data/spec/database_cleaner/active_record/truncation_spec.rb +66 -0
- data/spec/database_cleaner/configuration_spec.rb +104 -0
- data/spec/database_cleaner/couch_potato/truncation_spec.rb +40 -0
- data/spec/database_cleaner/couchrest/truncation_spec.rb +56 -0
- data/spec/database_cleaner/mongo_mapper/truncation_spec.rb +81 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +12 -0
- metadata +104 -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,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, 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,104 @@
|
|
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
|
+
Object.send(:remove_const, 'MongoMapper') if defined?(::MongoMapper)
|
13
|
+
# need to add one for each ORM that we load in the spec helper...
|
14
|
+
end
|
15
|
+
after(:all) do
|
16
|
+
Object.send(:remove_const, 'ActiveRecord') if defined?(::ActiveRecord) #want to make sure we have the real one...
|
17
|
+
ActiveRecord = TempAR
|
18
|
+
MongoMapper = TempMM
|
19
|
+
end
|
20
|
+
|
21
|
+
before(:each) do
|
22
|
+
DatabaseCleaner::ActiveRecord::Transaction.stub!(:new).and_return(@strategy = mock('strategy'))
|
23
|
+
Object.const_set('ActiveRecord', "just mocking out the constant here...") unless defined?(::ActiveRecord)
|
24
|
+
DatabaseCleaner.strategy = nil
|
25
|
+
DatabaseCleaner.orm = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
describe ".create_strategy" do
|
29
|
+
it "should initialize and return the appropirate strategy" do
|
30
|
+
DatabaseCleaner::ActiveRecord::Transaction.should_receive(:new).with('options' => 'hash')
|
31
|
+
result = DatabaseCleaner.create_strategy(:transaction, {'options' => 'hash'})
|
32
|
+
result.should == @strategy
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe ".clean_with" do
|
37
|
+
it "should initialize the appropirate strategy and clean with it" do
|
38
|
+
DatabaseCleaner::ActiveRecord::Transaction.should_receive(:new).with('options' => 'hash')
|
39
|
+
@strategy.should_receive(:clean)
|
40
|
+
DatabaseCleaner.clean_with(:transaction, 'options' => 'hash')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe ".strategy=" do
|
45
|
+
it "should initialize the appropirate strategy based on the ORM adapter detected" do
|
46
|
+
DatabaseCleaner::ActiveRecord::Transaction.should_receive(:new).with('options' => 'hash')
|
47
|
+
DatabaseCleaner.strategy = :transaction, {'options' => 'hash'}
|
48
|
+
|
49
|
+
Object.send(:remove_const, 'ActiveRecord')
|
50
|
+
Object.const_set('DataMapper', "just mocking out the constant here...")
|
51
|
+
DatabaseCleaner.orm = nil
|
52
|
+
|
53
|
+
DatabaseCleaner::DataMapper::Transaction.should_receive(:new).with(no_args)
|
54
|
+
DatabaseCleaner.strategy = :transaction
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should raise an error when no ORM is detected" do
|
58
|
+
Object.send(:remove_const, 'ActiveRecord') if defined?(::ActiveRecord)
|
59
|
+
Object.send(:remove_const, 'DataMapper') if defined?(::DataMapper)
|
60
|
+
Object.send(:remove_const, 'CouchPotato') if defined?(::CouchPotato)
|
61
|
+
|
62
|
+
running { DatabaseCleaner.strategy = :transaction }.should raise_error(DatabaseCleaner::NoORMDetected)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should use the strategy version of the ORM specified with #orm=" do
|
66
|
+
DatabaseCleaner.orm = 'data_mapper'
|
67
|
+
DatabaseCleaner::DataMapper::Transaction.should_receive(:new)
|
68
|
+
|
69
|
+
DatabaseCleaner.strategy = :transaction
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should raise an error when multiple args is passed in and the first is not a symbol" do
|
73
|
+
running { DatabaseCleaner.strategy=Object.new, {:foo => 'bar'} }.should raise_error(ArgumentError)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should raise an error when the specified strategy is not found" do
|
77
|
+
running { DatabaseCleaner.strategy = :foo }.should raise_error(DatabaseCleaner::UnknownStrategySpecified)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should allow any object to be set as the strategy" do
|
81
|
+
mock_strategy = mock('strategy')
|
82
|
+
running { DatabaseCleaner.strategy = mock_strategy }.should_not raise_error
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
%w[start clean].each do |strategy_method|
|
89
|
+
describe ".#{strategy_method}" do
|
90
|
+
it "should be delgated to the strategy set with strategy=" do
|
91
|
+
DatabaseCleaner.strategy = :transaction
|
92
|
+
|
93
|
+
@strategy.should_receive(strategy_method)
|
94
|
+
|
95
|
+
DatabaseCleaner.send(strategy_method)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should raise en error when no strategy has been set" do
|
99
|
+
running { DatabaseCleaner.send(strategy_method) }.should raise_error(DatabaseCleaner::NoStrategySetError)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
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,56 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
require 'database_cleaner/couchrest/truncation'
|
3
|
+
require 'couchrest'
|
4
|
+
|
5
|
+
# DB_SERVER = CouchRest.new
|
6
|
+
# DB_SERVER.default_database = couchdb['database']
|
7
|
+
# TEST_DB = DB_SERVER.database(DB_SERVER.default_database.name) unless defined? TEST_DB
|
8
|
+
# TEST_DB.recreate!
|
9
|
+
|
10
|
+
|
11
|
+
module DatabaseCleaner
|
12
|
+
module CouchRest
|
13
|
+
DEFAULT_DB_NAME= 'default_test_db'
|
14
|
+
|
15
|
+
describe Truncation do
|
16
|
+
before(:each) do
|
17
|
+
::CouchRest.default_database_name = DEFAULT_DB_NAME
|
18
|
+
|
19
|
+
@database = mock('database')
|
20
|
+
::CouchRest.stub!(:database!).and_return(@database)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "it should raise an error if ::CouchRest.default_database_name has not been set" do
|
24
|
+
running {
|
25
|
+
::CouchRest.default_database_name = nil
|
26
|
+
Truncation.new.clean()
|
27
|
+
}.should raise_error(ArgumentError)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should re-create the database" do
|
31
|
+
@database.should_receive(:recreate!)
|
32
|
+
|
33
|
+
Truncation.new.clean()
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should raise an error when the :only option is used" do
|
37
|
+
running {
|
38
|
+
Truncation.new(:only => ['document-type'])
|
39
|
+
}.should raise_error(ArgumentError)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should raise an error when the :except option is used" do
|
43
|
+
running {
|
44
|
+
Truncation.new(:except => ['document-type'])
|
45
|
+
}.should raise_error(ArgumentError)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should raise an error when invalid options are provided" do
|
49
|
+
running {
|
50
|
+
Truncation.new(:foo => 'bar')
|
51
|
+
}.should raise_error(ArgumentError)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
require 'mongomapper'
|
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
|
data/spec/spec.opts
ADDED