Merge branch 'master' of github.com:bmabey/database_cleaner

This commit is contained in:
Ben Mabey 2013-05-13 15:50:31 -06:00
commit ee5cd622d0
33 changed files with 802 additions and 79 deletions

View file

@ -26,6 +26,8 @@ group :development do
gem 'mysql', '~> 2.8.1'
gem 'mysql2'
gem 'pg'
gem 'sqlite3'
gem 'ohm', '~> 0.1.3'
gem 'guard-rspec'
end

View file

@ -135,6 +135,10 @@ GEM
multi_json (1.5.0)
mysql (2.8.1)
mysql2 (0.3.11)
nest (1.1.2)
redis
ohm (0.1.5)
nest (~> 1.0)
origin (1.0.11)
pg (0.14.1)
plucky (0.5.2)
@ -160,6 +164,7 @@ GEM
rake (10.0.3)
rdoc (3.12)
json (~> 1.4)
redis (3.0.4)
rest-client (1.6.7)
mime-types (>= 1.16)
rspec (2.12.0)
@ -213,6 +218,7 @@ DEPENDENCIES
mongoid
mysql (~> 2.8.1)
mysql2
ohm (~> 0.1.3)
pg
rake
rspec-rails

View file

@ -5,7 +5,7 @@ Database Cleaner is a set of strategies for cleaning your database in Ruby.
The original use case was to ensure a clean state during tests.
Each strategy is a small amount of code but is code that is usually needed in any ruby app that is testing with a database.
ActiveRecord, DataMapper, Sequel, MongoMapper, Mongoid, and CouchPotato are supported.
ActiveRecord, DataMapper, Sequel, MongoMapper, Mongoid, CouchPotato, Ohm and Redis are supported.
[![Build Status](https://secure.travis-ci.org/bmabey/database_cleaner.png)](http://travis-ci.org/bmabey/database_cleaner)
@ -55,6 +55,18 @@ Here is an overview of the strategies supported for each library:
<td> Yes</td>
<td> No</td>
</tr>
<tr>
<td>Redis</td>
<td><b>Yes</b></td>
<td>No</td>
<td>No</td>
</tr>
<tr>
<td>Ohm</td>
<td><b>Yes</b></td>
<td>No</td>
<td>No</td>
</tr>
</tbody>
</table>
@ -72,6 +84,12 @@ Here is an overview of the strategies supported for each library:
<td> No</td>
<td> No</td>
</tr>
<tr>
<td> Moped</td>
<td> Yes</td>
<td> No</td>
<td> No</td>
</tr>
</tbody>
</table>
@ -88,11 +106,11 @@ For the SQL libraries the fastest option will be to use `:transaction` as transa
One common approach is to force all processes to use the same database connection ([common ActiveRecord hack](http://blog.plataformatec.com.br/2011/12/three-tips-to-improve-the-performance-of-your-test-suite/)) however this approach has been reported to result in non-deterministic failures.
Another approach is to have the transactions rolled back in the application's process and relax the isolation level of the database (so the tests can read the uncommited transactions).
Another approach is to have the transactions rolled back in the application's process and relax the isolation level of the database (so the tests can read the uncommitted transactions).
An easier, but slower, solution is to use the `:truncation` or `:deletion` strategy.
So what is fastest out of `:deletion` and `:truncation`? Well, it depends on your table structure and what percentage of tables you populate in an average test. The reasoning is out the the scope of this README but here is a [good SO answer on this topic for Postgres](http://stackoverflow.com/questions/11419536/postgresql-truncation-speed/11423886#11423886).
So what is fastest out of `:deletion` and `:truncation`? Well, it depends on your table structure and what percentage of tables you populate in an average test. The reasoning is out of the scope of this README but here is a [good SO answer on this topic for Postgres](http://stackoverflow.com/questions/11419536/postgresql-truncation-speed/11423886#11423886).
Some people report much faster speeds with `:deletion` while others say `:truncation` is faster for them. The best approach therefore is it try all options on your test suite and see what is faster.
@ -123,6 +141,9 @@ DatabaseCleaner.strategy = :truncation, {:only => %w[widgets dogs some_other_tab
DatabaseCleaner.strategy = :truncation, {:except => %w[widgets]}
```
With Ohm and Redis, `:only` and `:except` take a list of strings to be
passed to [`keys`](http://redis.io/commands/keys)).
(I should point out the truncation strategy will never truncate your schema_migrations table.)
Some strategies require that you call `DatabaseCleaner.start` before calling `clean` (for example the `:transaction` one needs to know to open up a transaction). So you would have:
@ -276,6 +297,11 @@ Usage beyond that remains the same with `DatabaseCleaner.start` calling any setu
<td> <code>DatabaseCleaner[:mongoid]</code></td>
<td> Multiple databases supported for Mongoid 3. Specify <code>DatabaseCleaner[:mongoid, {:connection =&gt; :db_name}]</code> </td>
</tr>
<tr>
<td> Moped</td>
<td> <code>DatabaseCleaner[:moped]</code></td>
<td> It is necessary to configure database name with <code>DatabaseCleaner[:moped].db = db_name</code> otherwise name `default` will be used.</td>
</tr>
<tr>
<td> Couch Potato</td>
<td> <code>DatabaseCleaner[:couch_potato]</code></td>
@ -286,6 +312,16 @@ Usage beyond that remains the same with `DatabaseCleaner.start` calling any setu
<td> <code>DatabaseCleaner[:sequel]</code></td>
<td> Multiple databases supported; specify <code>Databasecleaner[:sequel, {:connection =&gt; Sequel.connect(uri)}]</code></td>
</tr>
<tr>
<td>Redis</td>
<td><code>DatabaseCleaner[:redis]</code></td>
<td>Connection specified as Redis URI</td>
</tr>
<tr>
<td>Ohm</td>
<td><code>DatabaseCleaner[:ohm]</code></td>
<td>Connection specified as Redis URI</td>
</tr>
</tbody>
</table>

View file

@ -0,0 +1,8 @@
test:
url: 'redis://localhost:6379/0'
one:
url: 'redis://localhost:6379/1'
two:
url: 'redis://localhost:6379/2'

View file

@ -15,6 +15,10 @@ another_orm = ENV['ANOTHER_ORM']
strategy = ENV['STRATEGY']
multiple_db = ENV['MULTIPLE_DBS']
config = YAML::load(File.open("#{File.dirname(__FILE__)}/../../config/redis.yml"))
ENV['REDIS_URL'] = config['test']['url']
ENV['REDIS_URL_ONE'] = config['one']['url']
ENV['REDIS_URL_TWO'] = config['two']['url']
if orm && strategy
$:.unshift(File.dirname(__FILE__) + '/../../../lib')
@ -37,6 +41,9 @@ if orm && strategy
when :mongo_mapper
DatabaseCleaner[ orm_sym, {:connection => 'database_cleaner_test_one'} ].strategy = strategy.to_sym
DatabaseCleaner[ orm_sym, {:connection => 'database_cleaner_test_two'} ].strategy = strategy.to_sym
when :redis, :ohm
DatabaseCleaner[ orm_sym, {:connection => ENV['REDIS_URL_ONE']} ].strategy = strategy.to_sym
DatabaseCleaner[ orm_sym, {:connection => ENV['REDIS_URL_TWO']} ].strategy = strategy.to_sym
when :active_record
DatabaseCleaner[:active_record, {:model => ActiveRecordWidgetUsingDatabaseOne} ].strategy = strategy.to_sym
DatabaseCleaner[:active_record, {:model => ActiveRecordWidgetUsingDatabaseTwo} ].strategy = strategy.to_sym
@ -53,5 +60,5 @@ if orm && strategy
end
else
raise "Run 'ORM=ActiveRecord|DataMapper|MongoMapper|CouchPotato [ANOTHER_ORM=...] [MULTIPLE_DBS=true] STRATEGY=transaction|truncation|default cucumber examples/features'"
raise "Run 'ORM=ActiveRecord|DataMapper|MongoMapper|CouchPotato|Ohm|Redis [ANOTHER_ORM=...] [MULTIPLE_DBS=true] STRATEGY=transaction|truncation|default cucumber examples/features'"
end

View file

@ -0,0 +1,43 @@
require 'ohm'
Ohm.connect :url => ENV['REDIS_URL']
class OhmWidget < Ohm::Model
attribute :name
def self.create!(attrs = {})
new({:name => 'some widget'}.merge(attrs)).save
end
def self.count
all.count
end
end
class OhmWidgetUsingDatabaseOne < Ohm::Model
connect :url => ENV['REDIS_URL_ONE']
attribute :name
def self.create!(attrs = {})
new({:name => 'a widget using database one'}.merge(attrs)).save
end
def self.count
all.count
end
end
class OhmWidgetUsingDatabaseTwo < Ohm::Model
connect :url => ENV['REDIS_URL_TWO']
attribute :name
def self.create!(attrs = {})
new({:name => 'a widget using database two'}.merge(attrs)).save
end
def self.count
all.count
end
end

View file

@ -0,0 +1,65 @@
require 'redis'
class RedisWidget
def self.redis
threaded ||= Redis.connect
end
def self.redis=(connection)
threaded = connection
end
def self.threaded
Thread.current[self.class.to_s] ||= {}
end
def initialize(options = {})
options = options.dup
@name = options[:name]
end
def connection
self.class.redis
end
def save
unless connection.get(self.class.to_s + ':id')
@id = 0
connection.set(self.class.to_s + ':id', @id)
end
@id = connection.incr(self.class.to_s + ':id')
connection.set(self.class.to_s + ':%d:name' % @id, @name)
end
def self.count
self.redis.keys(self.to_s + '*name').size
end
def self.create!
new(:name => 'some widget').save
end
end
class RedisWidgetUsingDatabaseOne < RedisWidget
def self.redis
threaded[self.class.to_s] ||= Redis.connect :url => ENV['REDIS_URL_ONE']
end
def self.create!
new(:name => 'a widget using database one').save
end
end
class RedisWidgetUsingDatabaseTwo < RedisWidget
def self.redis
threaded[self.class.to_s] ||= Redis.connect :url => ENV['REDIS_URL_TWO']
end
def self.create!
new(:name => 'a widget using database two').save
end
end

View file

@ -11,12 +11,14 @@ Feature: database cleaning
Then I should see all green
Examples:
| ORM | Strategy |
| ActiveRecord | transaction |
| ActiveRecord | truncation |
| ActiveRecord | deletion |
| DataMapper | transaction |
| DataMapper | truncation |
| MongoMapper | truncation |
| Mongoid | truncation |
| CouchPotato | truncation |
| ORM | Strategy |
| ActiveRecord | transaction |
| ActiveRecord | truncation |
| ActiveRecord | deletion |
| DataMapper | transaction |
| DataMapper | truncation |
| MongoMapper | truncation |
| Mongoid | truncation |
| CouchPotato | truncation |
| Redis | truncation |
| Ohm | truncation |

View file

@ -17,3 +17,5 @@ Feature: database cleaning
| MongoMapper |
| Mongoid |
| CouchPotato |
| Redis |
| Ohm |

View file

@ -15,15 +15,34 @@ Feature: database cleaning using multiple ORMs
| ActiveRecord | MongoMapper |
| ActiveRecord | Mongoid |
| ActiveRecord | CouchPotato |
| ActiveRecord | Ohm |
| ActiveRecord | Redis |
| DataMapper | ActiveRecord |
| DataMapper | MongoMapper |
| DataMapper | Mongoid |
| DataMapper | CouchPotato |
| DataMapper | Ohm |
| DataMapper | Redis |
| MongoMapper | ActiveRecord |
| MongoMapper | DataMapper |
| MongoMapper | Mongoid |
| MongoMapper | CouchPotato |
| MongoMapper | Ohm |
| MongoMapper | Redis |
| CouchPotato | ActiveRecord |
| CouchPotato | DataMapper |
| CouchPotato | MongoMapper |
| CouchPotato | Mongoid |
| CouchPotato | Ohm |
| CouchPotato | Redis |
| Ohm | ActiveRecord |
| Ohm | DataMapper |
| Ohm | MongoMapper |
| Ohm | Mongoid |
| Ohm | CouchPotato |
| Redis | ActiveRecord |
| Redis | DataMapper |
| Redis | MongoMapper |
| Redis | Mongoid |
| Redis | CouchPotato |
| Redis | Ohm |

View file

@ -1,10 +1,11 @@
orms_pattern = /(ActiveRecord|DataMapper|MongoMapper|Mongoid|CouchPotato|Redis|Ohm)/
Given /^I am using (ActiveRecord|DataMapper|MongoMapper|Mongoid|CouchPotato)$/ do |orm|
Given /^I am using #{orms_pattern}$/ do |orm|
@feature_runner = FeatureRunner.new
@feature_runner.orm = orm
end
Given /^I am using (ActiveRecord|DataMapper|MongoMapper|CouchPotato|Mongoid) and (ActiveRecord|DataMapper|MongoMapper|CouchPotato|Mongoid)$/ do |orm1,orm2|
Given /^I am using #{orms_pattern} and #{orms_pattern}$/ do |orm1,orm2|
@feature_runner = FeatureRunner.new
@feature_runner.orm = orm1
@feature_runner.another_orm = orm2

View file

@ -0,0 +1,31 @@
Given /^I have setup database cleaner to clean multiple databases using ohm$/ do
#DatabaseCleaner
# require "#{File.dirname(__FILE__)}/../../../lib/ohm_models"
#
# DatabaseCleaner[:ohm, {:connection => ENV['REDIS_URL_ONE']} ].strategy = :truncation
# DatabaseCleaner[:ohm, {:connection => ENV['REDIS_URL_TWO']} ].strategy = :truncation
end
When /^I create a widget using ohm$/ do
OhmWidget.create!
end
Then /^I should see ([\d]+) widget using ohm$/ do |widget_count|
OhmWidget.count.should == widget_count.to_i
end
When /^I create a widget in one db using ohm$/ do
OhmWidgetUsingDatabaseOne.create!
end
When /^I create a widget in another db using ohm$/ do
OhmWidgetUsingDatabaseTwo.create!
end
Then /^I should see ([\d]+) widget in one db using ohm$/ do |widget_count|
OhmWidgetUsingDatabaseOne.count.should == widget_count.to_i
end
Then /^I should see ([\d]+) widget in another db using ohm$/ do |widget_count|
OhmWidgetUsingDatabaseTwo.count.should == widget_count.to_i
end

View file

@ -0,0 +1,31 @@
Given /^I have setup database cleaner to clean multiple databases using redis$/ do
#DatabaseCleaner
# require "#{File.dirname(__FILE__)}/../../../lib/redis_models"
#
# DatabaseCleaner[:redis, {:connection => ENV['REDIS_URL_ONE']} ].strategy = :truncation
# DatabaseCleaner[:redis, {:connection => ENV['REDIS_URL_TWO']} ].strategy = :truncation
end
When /^I create a widget using redis$/ do
RedisWidget.create!
end
Then /^I should see ([\d]+) widget using redis$/ do |widget_count|
RedisWidget.count.should == widget_count.to_i
end
When /^I create a widget in one db using redis$/ do
RedisWidgetUsingDatabaseOne.create!
end
When /^I create a widget in another db using redis$/ do
RedisWidgetUsingDatabaseTwo.create!
end
Then /^I should see ([\d]+) widget in one db using redis$/ do |widget_count|
RedisWidgetUsingDatabaseOne.count.should == widget_count.to_i
end
Then /^I should see ([\d]+) widget in another db using redis$/ do |widget_count|
RedisWidgetUsingDatabaseTwo.count.should == widget_count.to_i
end

View file

@ -38,10 +38,6 @@ module DatabaseCleaner
end
end
def create_connection_class
Class.new(::ActiveRecord::Base)
end
def connection_class
@connection_class ||= if @db && !@db.is_a?(Symbol)
@db
@ -63,9 +59,7 @@ module DatabaseCleaner
end
def establish_connection
strategy_class = create_connection_class
strategy_class.send :establish_connection, connection_hash
strategy_class
::ActiveRecord::Base.establish_connection(connection_hash)
end
end

View file

@ -7,6 +7,10 @@ module DatabaseCleaner::ActiveRecord
include ::DatabaseCleaner::Generic::Transaction
def start
# Hack to make sure that the connection is properly setup for
# the clean code.
connection_class.connection.transaction{ }
if connection_maintains_transaction_count?
if connection_class.connection.respond_to?(:increment_open_transactions)
connection_class.connection.increment_open_transactions

View file

@ -36,12 +36,22 @@ module DatabaseCleaner
def clean_with(*args)
strategy = create_strategy(*args)
set_strategy_db strategy, self.db
strategy.clean
strategy
end
alias clean_with! clean_with
def set_strategy_db(strategy, desired_db)
if strategy.respond_to? :db=
strategy.db = desired_db
elsif desired_db != :default
raise ArgumentError, "You must provide a strategy object that supports non default databases when you specify a database"
end
end
def strategy=(args)
strategy, *strategy_args = args
if strategy.is_a?(Symbol)
@ -52,7 +62,7 @@ module DatabaseCleaner
raise ArgumentError, "You must provide a strategy object, or a symbol for a known strategy along with initialization params."
end
self.strategy_db = self.db
set_strategy_db @strategy, self.db
@strategy
end
@ -120,8 +130,14 @@ module DatabaseCleaner
:couch_potato
elsif defined? ::Sequel
:sequel
elsif defined? ::Moped
:moped
elsif defined? ::Ohm
:ohm
elsif defined? ::Redis
:redis
else
raise NoORMDetected, "No known ORM was detected! Is ActiveRecord, DataMapper, Sequel, MongoMapper, Mongoid, or CouchPotato loaded?"
raise NoORMDetected, "No known ORM was detected! Is ActiveRecord, DataMapper, Sequel, MongoMapper, Mongoid, Moped, or CouchPotato, Redis or Ohm loaded?"
end
end
end
@ -130,7 +146,7 @@ module DatabaseCleaner
case orm
when :active_record, :data_mapper, :sequel
self.strategy = :transaction
when :mongo_mapper, :mongoid, :couch_potato
when :mongo_mapper, :mongoid, :couch_potato, :moped, :ohm, :redis
self.strategy = :truncation
end
end

View file

@ -11,7 +11,7 @@ module DatabaseCleaner
# ghetto ordered hash.. maintains 1.8 compat and old API
@connections ||= []
end
def [](orm,opts = {})
raise NoORMDetected unless orm
init_cleaners
@ -113,6 +113,10 @@ module DatabaseCleaner
DatabaseCleaner::CouchPotato
when :sequel
DatabaseCleaner::Sequel
when :ohm
DatabaseCleaner::Ohm
when :redis
DatabaseCleaner::Redis
end
end
end

View file

@ -1,7 +1,7 @@
require 'database_cleaner/mongoid/base'
require 'database_cleaner/generic/truncation'
require 'database_cleaner/mongo/truncation_mixin'
require 'database_cleaner/moped/truncation'
require 'database_cleaner/moped/truncation_base'
require 'mongoid/version'
module DatabaseCleaner
@ -22,7 +22,7 @@ module DatabaseCleaner
else
include ::DatabaseCleaner::Moped::Truncation
include ::DatabaseCleaner::Moped::TruncationBase
private

View file

@ -0,0 +1,35 @@
require 'database_cleaner/generic/base'
module DatabaseCleaner
module Moped
def self.available_strategies
%w[truncation]
end
module Base
include ::DatabaseCleaner::Generic::Base
def db=(desired_db)
@db = desired_db
end
def db
@db || :default
end
def host_port=(desired_host)
@host = desired_host
end
def host
@host || '127.0.0.1:27017'
end
private
def session
::Moped::Session.new([host], database: db)
end
end
end
end

View file

@ -1,29 +1,9 @@
require 'database_cleaner/moped/truncation_base'
module DatabaseCleaner
module Moped
module Truncation
def clean
if @only
collections.each { |c| session[c].find.remove_all if @only.include?(c) }
else
collections.each { |c| session[c].find.remove_all unless @tables_to_exclude.include?(c) }
end
true
end
private
def collections
if db != :default
session.use(db)
end
session['system.namespaces'].find(:name => { '$not' => /system|\$/ }).to_a.map do |collection|
_, name = collection['name'].split('.', 2)
name
end
end
class Truncation
include ::DatabaseCleaner::Moped::TruncationBase
end
end
end

View file

@ -0,0 +1,34 @@
require 'database_cleaner/moped/base'
require 'database_cleaner/generic/truncation'
module DatabaseCleaner
module Moped
module TruncationBase
include ::DatabaseCleaner::Moped::Base
include ::DatabaseCleaner::Generic::Truncation
def clean
if @only
collections.each { |c| session[c].find.remove_all if @only.include?(c) }
else
collections.each { |c| session[c].find.remove_all unless @tables_to_exclude.include?(c) }
end
true
end
private
def collections
if db != :default
session.use(db)
end
session['system.namespaces'].find(:name => { '$not' => /system|\$/ }).to_a.map do |collection|
_, name = collection['name'].split('.', 2)
name
end
end
end
end
end

View file

@ -0,0 +1,15 @@
require 'database_cleaner/redis/truncation'
module DatabaseCleaner
module Ohm
class Truncation < ::DatabaseCleaner::Redis::Truncation
private
def default_redis
::Ohm.redis
end
end
end
end

View file

@ -0,0 +1,31 @@
require 'database_cleaner/generic/base'
module DatabaseCleaner
module Redis
def self.available_strategies
%w{truncation}
end
module Base
include ::DatabaseCleaner::Generic::Base
def db=(desired_db)
@db = desired_db
end
def db
@db || :default
end
alias url db
private
def connection
@connection ||= url == :default ? ::Redis.connect : ::Redis.connect(:url => url)
end
end
end
end

View file

@ -0,0 +1,26 @@
require 'database_cleaner/redis/base'
require 'database_cleaner/generic/truncation'
module DatabaseCleaner
module Redis
class Truncation
include ::DatabaseCleaner::Redis::Base
include ::DatabaseCleaner::Generic::Truncation
def clean
if @only
@only.each do |term|
connection.keys(term).each { |k| connection.del k }
end
elsif @tables_to_exclude
keys_except = []
@tables_to_exclude.each { |term| keys_except += connection.keys(term) }
connection.keys.each { |k| connection.del(k) unless keys_except.include?(k) }
else
connection.flushdb
end
connection.quit unless url == :default
end
end
end
end

View file

@ -119,16 +119,6 @@ my_db:
end
end
describe "create_connection_class" do
it "should return a class" do
subject.create_connection_class.should be_a(Class)
end
it "should return a class extending ::ActiveRecord::Base" do
subject.create_connection_class.ancestors.should include(::ActiveRecord::Base)
end
end
describe "connection_class" do
it { expect { subject.connection_class }.to_not raise_error }
it "should default to ActiveRecord::Base" do
@ -158,16 +148,9 @@ my_db:
before { ::ActiveRecord::Base.stub!(:respond_to?).and_return(false) }
before { subject.stub(:connection_hash).and_return(hash) }
it "should create connection_class if it doesnt exist if connection_hash is set" do
subject.should_receive(:create_connection_class).and_return(mock('class').as_null_object)
subject.connection_class
end
it "establish a connection using ActiveRecord::Base" do
::ActiveRecord::Base.should_receive(:establish_connection).with(hash)
it "should configure the class from create_connection_class if connection_hash is set" do
strategy_class = mock('strategy_class')
strategy_class.should_receive(:establish_connection).with(hash)
subject.should_receive(:create_connection_class).and_return(strategy_class)
subject.connection_class
end
end

View file

@ -15,6 +15,7 @@ module DatabaseCleaner
[:begin_transaction, :begin_db_transaction].each do |begin_transaction_method|
context "using #{begin_transaction_method}" do
before do
connection.stub(:transaction)
connection.stub(begin_transaction_method)
connection.stub(:respond_to?).with(:begin_transaction).and_return(:begin_transaction == begin_transaction_method)
end
@ -35,6 +36,7 @@ module DatabaseCleaner
connection.stub(:respond_to?).with(:increment_open_transactions).and_return(true)
connection.stub(:increment_open_transactions)
connection.should_receive(begin_transaction_method)
connection.should_receive(:transaction)
Transaction.new.start
end
end

View file

@ -18,6 +18,9 @@ module DatabaseCleaner
Temp_MO = ::Mongoid if defined?(::Mongoid) and not defined?(Temp_MO)
Temp_CP = ::CouchPotato if defined?(::CouchPotato) and not defined?(Temp_CP)
Temp_SQ = ::Sequel if defined?(::Sequel) and not defined?(Temp_SQ)
Temp_MP = ::Moped if defined?(::Moped) and not defined?(Temp_MP)
Temp_RS = ::Redis if defined?(::Redis) and not defined?(Temp_RS)
Temp_OH = ::Ohm if defined?(::Ohm) and not defined?(Temp_OH)
end
#Remove all ORM mocks and restore from cache
@ -28,6 +31,9 @@ module DatabaseCleaner
Object.send(:remove_const, 'Mongoid') if defined?(::Mongoid)
Object.send(:remove_const, 'CouchPotato') if defined?(::CouchPotato)
Object.send(:remove_const, 'Sequel') if defined?(::Sequel)
Object.send(:remove_const, 'Moped') if defined?(::Moped)
Object.send(:remove_const, 'Ohm') if defined?(::Ohm)
Object.send(:remove_const, 'Redis') if defined?(::Redis)
# Restore ORMs
@ -36,6 +42,9 @@ module DatabaseCleaner
::MongoMapper = Temp_MM if defined? Temp_MM
::Mongoid = Temp_MO if defined? Temp_MO
::CouchPotato = Temp_CP if defined? Temp_CP
::Moped = Temp_MP if defined? Temp_MP
::Ohm = Temp_OH if defined? Temp_OH
::Redis = Temp_RS if defined? Temp_RS
end
#reset the orm mocks
@ -46,8 +55,11 @@ module DatabaseCleaner
Object.send(:remove_const, 'Mongoid') if defined?(::Mongoid)
Object.send(:remove_const, 'CouchPotato') if defined?(::CouchPotato)
Object.send(:remove_const, 'Sequel') if defined?(::Sequel)
Object.send(:remove_const, 'Moped') if defined?(::Moped)
Object.send(:remove_const, 'Ohm') if defined?(::Ohm)
Object.send(:remove_const, 'Redis') if defined?(::Redis)
end
let(:cleaner) { DatabaseCleaner::Base.new :autodetect }
it "should raise an error when no ORM is detected" do
@ -61,6 +73,9 @@ module DatabaseCleaner
Object.const_set('Mongoid', 'Mongoid mock')
Object.const_set('CouchPotato', 'Couching mock potatos')
Object.const_set('Sequel', 'Sequel mock')
Object.const_set('Moped', 'Moped mock')
Object.const_set('Ohm', 'Ohm mock')
Object.const_set('Redis', 'Redis mock')
cleaner.orm.should == :active_record
cleaner.should be_auto_detected
@ -72,6 +87,9 @@ module DatabaseCleaner
Object.const_set('Mongoid', 'Mongoid mock')
Object.const_set('CouchPotato', 'Couching mock potatos')
Object.const_set('Sequel', 'Sequel mock')
Object.const_set('Moped', 'Moped mock')
Object.const_set('Ohm', 'Ohm mock')
Object.const_set('Redis', 'Redis mock')
cleaner.orm.should == :data_mapper
cleaner.should be_auto_detected
@ -82,6 +100,9 @@ module DatabaseCleaner
Object.const_set('Mongoid', 'Mongoid mock')
Object.const_set('CouchPotato', 'Couching mock potatos')
Object.const_set('Sequel', 'Sequel mock')
Object.const_set('Moped', 'Moped mock')
Object.const_set('Ohm', 'Ohm mock')
Object.const_set('Redis', 'Redis mock')
cleaner.orm.should == :mongo_mapper
cleaner.should be_auto_detected
@ -91,6 +112,9 @@ module DatabaseCleaner
Object.const_set('Mongoid', 'Mongoid mock')
Object.const_set('CouchPotato', 'Couching mock potatos')
Object.const_set('Sequel', 'Sequel mock')
Object.const_set('Moped', 'Moped mock')
Object.const_set('Ohm', 'Ohm mock')
Object.const_set('Redis', 'Redis mock')
cleaner.orm.should == :mongoid
cleaner.should be_auto_detected
@ -99,17 +123,45 @@ module DatabaseCleaner
it "should detect CouchPotato fifth" do
Object.const_set('CouchPotato', 'Couching mock potatos')
Object.const_set('Sequel', 'Sequel mock')
Object.const_set('Moped', 'Moped mock')
Object.const_set('Ohm', 'Ohm mock')
Object.const_set('Redis', 'Redis mock')
cleaner.orm.should == :couch_potato
cleaner.should be_auto_detected
end
it "should detect Sequel last" do
it "should detect Sequel sixth" do
Object.const_set('Sequel', 'Sequel mock')
Object.const_set('Moped', 'Moped mock')
Object.const_set('Ohm', 'Ohm mock')
Object.const_set('Redis', 'Redis mock')
cleaner.orm.should == :sequel
cleaner.should be_auto_detected
end
it 'detects Ohm seventh' do
Object.const_set('Ohm', 'Ohm mock')
Object.const_set('Redis', 'Redis mock')
cleaner.orm.should == :ohm
cleaner.should be_auto_detected
end
it 'detects Redis last' do
Object.const_set('Redis', 'Redis mock')
cleaner.orm.should == :redis
cleaner.should be_auto_detected
end
it 'detects Moped seventh' do
Object.const_set('Moped', 'Moped mock')
cleaner.orm.should == :moped
cleaner.should be_auto_detected
end
end
describe "orm_module" do
@ -160,7 +212,7 @@ module DatabaseCleaner
cleaner = ::DatabaseCleaner::Base.new "mongoid"
cleaner.orm.should == :mongoid
end
it "is autodetected if orm is not provided" do
cleaner = ::DatabaseCleaner::Base.new
cleaner.should be_auto_detected
@ -317,7 +369,7 @@ module DatabaseCleaner
it "should attempt to set strategy db" do
subject.stub(:db).and_return(:my_db)
subject.should_receive(:strategy_db=).with(:my_db)
subject.should_receive(:set_strategy_db).with(mock_strategy, :my_db)
subject.strategy = mock_strategy
end
@ -330,8 +382,7 @@ module DatabaseCleaner
describe "strategy" do
subject { ::DatabaseCleaner::Base.new :a_orm }
it "returns a null strategy when strategy no set and undetectable" do
subject.instance_values["@strategy"] = nil
it "returns a null strategy when strategy is not set and undetectable" do
subject.strategy.should == DatabaseCleaner::NullStrategy
end
@ -487,6 +538,21 @@ module DatabaseCleaner
cleaner = DatabaseCleaner::Base.new(:couch_potato)
cleaner.strategy.should be_instance_of DatabaseCleaner::CouchPotato::Truncation
end
it 'sets strategy to :truncation for Moped' do
cleaner = DatabaseCleaner::Base.new(:moped)
cleaner.strategy.should be_instance_of DatabaseCleaner::Moped::Truncation
end
it 'sets strategy to :truncation for Ohm' do
cleaner = DatabaseCleaner::Base.new(:ohm)
cleaner.strategy.should be_instance_of DatabaseCleaner::Ohm::Truncation
end
it 'sets strategy to :truncation for Redis' do
cleaner = DatabaseCleaner::Base.new(:redis)
cleaner.strategy.should be_instance_of DatabaseCleaner::Redis::Truncation
end
end
end

View file

@ -55,6 +55,20 @@ describe ::DatabaseCleaner do
cleaner.orm.should == :couch_potato
::DatabaseCleaner.connections.size.should == 1
end
it "should accept :moped" do
cleaner = ::DatabaseCleaner[:moped]
cleaner.should be_a(::DatabaseCleaner::Base)
cleaner.orm.should == :moped
::DatabaseCleaner.connections.size.should == 1
end
it 'accepts :ohm' do
cleaner = ::DatabaseCleaner[:ohm]
cleaner.should be_a(::DatabaseCleaner::Base)
cleaner.orm.should == :ohm
::DatabaseCleaner.connections.size.should == 1
end
end
it "should accept multiple orm's" do
@ -119,7 +133,7 @@ describe ::DatabaseCleaner do
it "should give me a default (autodetection) databasecleaner by default" do
cleaner = mock("cleaner").as_null_object
::DatabaseCleaner::Base.stub!(:new).and_return(cleaner)
::DatabaseCleaner.connections.should == [cleaner]
end
end

View file

@ -0,0 +1,26 @@
module MopedTest
class ThingBase
def self.collection
@db ||= 'database_cleaner_specs'
@session ||= ::Moped::Session.new(['127.0.0.1:27017'], database: @db)
@collection ||= @session[name]
end
def self.count
@collection.find.count
end
def initialize(attrs={})
@attrs = attrs
end
def save!
self.class.collection.insert(@attrs)
end
end
class Widget < ThingBase
end
class Gadget < ThingBase
end
end

View file

@ -0,0 +1,75 @@
require File.dirname(__FILE__) + '/../../spec_helper'
require 'moped'
require 'database_cleaner/moped/truncation'
require File.dirname(__FILE__) + '/moped_examples'
module DatabaseCleaner
module Moped
describe Truncation do
let(:args) {{}}
let(:truncation) { described_class.new(args).tap { |t| t.db=@db } }
#doing this in the file root breaks autospec, doing it before(:all) just fails the specs
before(:all) do
@test_db = 'database_cleaner_specs'
@session = ::Moped::Session.new(['127.0.0.1:27017'], database: @test_db)
end
before(:each) do
truncation.db = @test_db
end
after(:each) do
@session.drop
end
def ensure_counts(expected_counts)
# I had to add this sanity_check garbage because I was getting non-determinisc results from mongo at times..
# very odd and disconcerting...
expected_counts.each do |model_class, expected_count|
model_class.count.should equal(expected_count), "#{model_class} expected to have a count of #{expected_count} but was #{model_class.count}"
end
end
def create_widget(attrs={})
MopedTest::Widget.new({:name => 'some widget'}.merge(attrs)).save!
end
def create_gadget(attrs={})
MopedTest::Gadget.new({:name => 'some gadget'}.merge(attrs)).save!
end
it "truncates all collections by default" do
create_widget
create_gadget
ensure_counts(MopedTest::Widget => 1, MopedTest::Gadget => 1)
truncation.clean
ensure_counts(MopedTest::Widget => 0, MopedTest::Gadget => 0)
end
context "when collections are provided to the :only option" do
let(:args) {{:only => ['MopedTest::Widget']}}
it "only truncates the specified collections" do
create_widget
create_gadget
ensure_counts(MopedTest::Widget => 1, MopedTest::Gadget => 1)
truncation.clean
ensure_counts(MopedTest::Widget => 0, MopedTest::Gadget => 1)
end
end
context "when collections are provided to the :except option" do
let(:args) {{:except => ['MopedTest::Widget']}}
it "truncates all but the specified collections" do
create_widget
create_gadget
ensure_counts(MopedTest::Widget => 1, MopedTest::Gadget => 1)
truncation.clean
ensure_counts(MopedTest::Widget => 1, MopedTest::Gadget => 0)
end
end
end
end
end

View file

@ -0,0 +1,70 @@
require File.dirname(__FILE__) + '/../../spec_helper'
require 'ohm'
require 'database_cleaner/ohm/truncation'
module DatabaseCleaner
module Ohm
class Widget < ::Ohm::Model
attribute :name
end
class Gadget < ::Ohm::Model
attribute :name
end
describe Truncation do
before(:all) do
config = YAML::load(File.open("#{File.dirname(__FILE__)}/../../../examples/config/redis.yml"))
::Ohm.connect :url => config['test']['url']
@redis = ::Ohm.redis
end
before(:each) do
@redis.flushdb
end
it "should flush the database" do
Truncation.new.clean
end
def create_widget(attrs={})
Widget.new({:name => 'some widget'}.merge(attrs)).save
end
def create_gadget(attrs={})
Gadget.new({:name => 'some gadget'}.merge(attrs)).save
end
it "truncates all keys by default" do
create_widget
create_gadget
@redis.keys.size.should == 6
Truncation.new.clean
@redis.keys.size.should == 0
end
context "when keys are provided to the :only option" do
it "only truncates the specified keys" do
create_widget
create_gadget
@redis.keys.size.should == 6
Truncation.new(:only => ['*Widget*']).clean
@redis.keys.size.should == 3
@redis.get('DatabaseCleaner::Ohm::Gadget:id').should == '1'
end
end
context "when keys are provided to the :except option" do
it "truncates all but the specified keys" do
create_widget
create_gadget
@redis.keys.size.should == 6
Truncation.new(:except => ['*Widget*']).clean
@redis.keys.size.should == 3
@redis.get('DatabaseCleaner::Ohm::Widget:id').should == '1'
end
end
end
end
end

View file

@ -0,0 +1,32 @@
require 'spec_helper'
require 'database_cleaner/redis/base'
require 'database_cleaner/shared_strategy'
module DatabaseCleaner
describe Redis do
it { should respond_to(:available_strategies) }
end
module Redis
class ExampleStrategy
include ::DatabaseCleaner::Redis::Base
end
describe ExampleStrategy do
it_should_behave_like "a generic strategy"
it { should respond_to(:db) }
it { should respond_to(:db=) }
it "should store my describe db" do
url = 'redis://localhost:6379/2'
subject.db = 'redis://localhost:6379/2'
subject.db.should == url
end
it "should default to :default" do
subject.db.should == :default
end
end
end
end

View file

@ -0,0 +1,63 @@
require File.dirname(__FILE__) + '/../../spec_helper'
require 'redis'
require 'database_cleaner/redis/truncation'
module DatabaseCleaner
module Redis
describe Truncation do
before(:all) do
config = YAML::load(File.open("#{File.dirname(__FILE__)}/../../../examples/config/redis.yml"))
@redis = ::Redis.connect :url => config['test']['url']
end
before(:each) do
@redis.flushdb
end
it "should flush the database" do
Truncation.new.clean
end
def create_widget(attrs={})
@redis.set 'Widget', 1
end
def create_gadget(attrs={})
@redis.set 'Gadget', 1
end
it "truncates all keys by default" do
create_widget
create_gadget
@redis.keys.size.should == 2
Truncation.new.clean
@redis.keys.size.should == 0
end
context "when keys are provided to the :only option" do
it "only truncates the specified keys" do
create_widget
create_gadget
@redis.keys.size.should == 2
Truncation.new(:only => ['Widge*']).clean
@redis.keys.size.should == 1
@redis.get('Gadget').should == '1'
end
end
context "when keys are provided to the :except option" do
it "truncates all but the specified keys" do
create_widget
create_gadget
@redis.keys.size.should == 2
Truncation.new(:except => ['Widg*']).clean
@redis.keys.size.should == 1
@redis.get('Widget').should == '1'
end
end
end
end
end