nulldb 0.3.7.pre.alpha.1
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.
- checksums.yaml +7 -0
- data/.gitignore +28 -0
- data/.travis.yml +49 -0
- data/Appraisals +36 -0
- data/CHANGES.md +75 -0
- data/Gemfile +14 -0
- data/LICENSE +21 -0
- data/README.rdoc +147 -0
- data/Rakefile +14 -0
- data/VERSION +1 -0
- data/gemfiles/activerecord_2.3.gemfile +17 -0
- data/gemfiles/activerecord_3.0.gemfile +16 -0
- data/gemfiles/activerecord_3.1.gemfile +16 -0
- data/gemfiles/activerecord_3.2.gemfile +16 -0
- data/gemfiles/activerecord_4.0.gemfile +16 -0
- data/gemfiles/activerecord_4.1.gemfile +16 -0
- data/gemfiles/activerecord_4.2.gemfile +16 -0
- data/gemfiles/activerecord_5.0.gemfile +16 -0
- data/gemfiles/activerecord_5.1.gemfile +16 -0
- data/gemfiles/activerecord_master.gemfile +18 -0
- data/lib/active_record/connection_adapters/nulldb_adapter.rb +21 -0
- data/lib/active_record/connection_adapters/nulldb_adapter/checkpoint.rb +13 -0
- data/lib/active_record/connection_adapters/nulldb_adapter/column.rb +20 -0
- data/lib/active_record/connection_adapters/nulldb_adapter/configuration.rb +5 -0
- data/lib/active_record/connection_adapters/nulldb_adapter/core.rb +324 -0
- data/lib/active_record/connection_adapters/nulldb_adapter/empty_result.rb +26 -0
- data/lib/active_record/connection_adapters/nulldb_adapter/index_definition.rb +5 -0
- data/lib/active_record/connection_adapters/nulldb_adapter/null_object.rb +13 -0
- data/lib/active_record/connection_adapters/nulldb_adapter/statement.rb +15 -0
- data/lib/active_record/connection_adapters/nulldb_adapter/table_definition.rb +5 -0
- data/lib/activerecord-nulldb-adapter.rb +1 -0
- data/lib/nulldb.rb +2 -0
- data/lib/nulldb/arel_compiler.rb +6 -0
- data/lib/nulldb/core.rb +39 -0
- data/lib/nulldb/extensions.rb +42 -0
- data/lib/nulldb/rails.rb +4 -0
- data/lib/nulldb_rspec.rb +104 -0
- data/lib/tasks/database.rake +32 -0
- data/nulldb.gemspec +27 -0
- data/spec/nulldb_spec.rb +345 -0
- data/spec/spec.opts +1 -0
- metadata +172 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
class ActiveRecord::ConnectionAdapters::NullDBAdapter
|
2
|
+
|
3
|
+
class EmptyResult < Array
|
4
|
+
attr_writer :columns
|
5
|
+
def rows
|
6
|
+
[]
|
7
|
+
end
|
8
|
+
|
9
|
+
def column_types
|
10
|
+
columns.map{|col| col.type}
|
11
|
+
end
|
12
|
+
|
13
|
+
def columns
|
14
|
+
@columns ||= []
|
15
|
+
end
|
16
|
+
|
17
|
+
def cast_values(type_overrides = nil)
|
18
|
+
rows
|
19
|
+
end
|
20
|
+
|
21
|
+
def >(num)
|
22
|
+
rows.size > num
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class ActiveRecord::ConnectionAdapters::NullDBAdapter
|
2
|
+
|
3
|
+
class Statement
|
4
|
+
attr_reader :entry_point, :content
|
5
|
+
|
6
|
+
def initialize(entry_point, content = "")
|
7
|
+
@entry_point, @content = entry_point, content
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(other)
|
11
|
+
self.entry_point == other.entry_point
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'nulldb'
|
data/lib/nulldb.rb
ADDED
data/lib/nulldb/core.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_support/deprecation'
|
3
|
+
require 'active_record/connection_adapters/nulldb_adapter'
|
4
|
+
|
5
|
+
module NullDB
|
6
|
+
class Configuration < Struct.new(:project_root); end
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def configure
|
10
|
+
@configuration = Configuration.new.tap {|c| yield c}
|
11
|
+
end
|
12
|
+
|
13
|
+
def configuration
|
14
|
+
if @configuration.nil?
|
15
|
+
raise "NullDB not configured. Require a framework, ex 'nulldb/rails'"
|
16
|
+
end
|
17
|
+
|
18
|
+
@configuration
|
19
|
+
end
|
20
|
+
|
21
|
+
def nullify(options={})
|
22
|
+
begin
|
23
|
+
@prev_connection = ActiveRecord::Base.connection_pool.try(:spec)
|
24
|
+
rescue ActiveRecord::ConnectionNotEstablished
|
25
|
+
end
|
26
|
+
ActiveRecord::Base.establish_connection(options.merge(:adapter => :nulldb))
|
27
|
+
end
|
28
|
+
|
29
|
+
def restore
|
30
|
+
if @prev_connection
|
31
|
+
ActiveRecord::Base.establish_connection(@prev_connection.config)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def checkpoint
|
36
|
+
ActiveRecord::Base.connection.checkpoint!
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
unless respond_to?(:tap)
|
2
|
+
class Object
|
3
|
+
def tap
|
4
|
+
yield self
|
5
|
+
self
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
unless respond_to?(:try)
|
11
|
+
class Object
|
12
|
+
def try(*a, &b)
|
13
|
+
if a.empty? && block_given?
|
14
|
+
yield self
|
15
|
+
else
|
16
|
+
__send__(*a, &b)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class NilClass
|
22
|
+
def try(*args); nil; end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class ActiveRecord::Base
|
27
|
+
# Instantiate a new NullDB connection. Used by ActiveRecord internally.
|
28
|
+
def self.nulldb_connection(config)
|
29
|
+
ActiveRecord::ConnectionAdapters::NullDBAdapter.new(config)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
module ActiveRecord
|
35
|
+
# Just make sure you have the latest version of your schema
|
36
|
+
superclass = ActiveRecord::VERSION::MAJOR == 5 ? Migration.public_send(:[], "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}".to_f) : Migration
|
37
|
+
class Schema < superclass
|
38
|
+
def self.define(info={}, &block)
|
39
|
+
instance_eval(&block)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/nulldb/rails.rb
ADDED
data/lib/nulldb_rspec.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'active_record/connection_adapters/nulldb_adapter'
|
2
|
+
|
3
|
+
module NullDB
|
4
|
+
module RSpec
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
module NullDB::RSpec::NullifiedDatabase
|
9
|
+
NullDBAdapter = ActiveRecord::ConnectionAdapters::NullDBAdapter
|
10
|
+
|
11
|
+
class HaveExecuted
|
12
|
+
|
13
|
+
def initialize(entry_point)
|
14
|
+
@entry_point = entry_point
|
15
|
+
end
|
16
|
+
|
17
|
+
def matches?(connection)
|
18
|
+
log = connection.execution_log_since_checkpoint
|
19
|
+
if @entry_point == :anything
|
20
|
+
not log.empty?
|
21
|
+
else
|
22
|
+
log.include?(NullDBAdapter::Statement.new(@entry_point))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def description
|
27
|
+
"connection should execute #{@entry_point} statement"
|
28
|
+
end
|
29
|
+
|
30
|
+
def failure_message
|
31
|
+
" did not execute #{@entry_point} statement when it should have"
|
32
|
+
end
|
33
|
+
|
34
|
+
def negative_failure_message
|
35
|
+
" executed #{@entry_point} statement when it should not have"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.globally_nullify_database
|
40
|
+
block = lambda { |config| nullify_database(config) }
|
41
|
+
if defined?(RSpec)
|
42
|
+
RSpec.configure(&block)
|
43
|
+
else
|
44
|
+
Spec::Runner.configure(&block)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.contextually_nullify_database(context)
|
49
|
+
nullify_database(context)
|
50
|
+
end
|
51
|
+
|
52
|
+
# A matcher for asserting that database statements have (or have not) been
|
53
|
+
# executed. Usage:
|
54
|
+
#
|
55
|
+
# ActiveRecord::Base.connection.should have_executed(:insert)
|
56
|
+
#
|
57
|
+
# The types of statement that can be matched mostly mirror the public
|
58
|
+
# operations available in
|
59
|
+
# ActiveRecord::ConnectionAdapters::DatabaseStatements:
|
60
|
+
# - :select_one
|
61
|
+
# - :select_all
|
62
|
+
# - :select_value
|
63
|
+
# - :insert
|
64
|
+
# - :update
|
65
|
+
# - :delete
|
66
|
+
# - :execute
|
67
|
+
#
|
68
|
+
# There is also a special :anything symbol that will match any operation.
|
69
|
+
def have_executed(entry_point)
|
70
|
+
HaveExecuted.new(entry_point)
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def self.included(other)
|
76
|
+
if nullify_contextually?(other)
|
77
|
+
contextually_nullify_database(other)
|
78
|
+
else
|
79
|
+
globally_nullify_database
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.nullify_contextually?(other)
|
84
|
+
if defined?(RSpec)
|
85
|
+
other < RSpec::Core::ExampleGroup
|
86
|
+
else
|
87
|
+
other.is_a? Spec::ExampleGroup
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.nullify_database(receiver)
|
92
|
+
receiver.before :all do
|
93
|
+
ActiveRecord::Base.establish_connection(:adapter => :nulldb)
|
94
|
+
end
|
95
|
+
|
96
|
+
receiver.before :each do
|
97
|
+
ActiveRecord::Base.connection.checkpoint!
|
98
|
+
end
|
99
|
+
|
100
|
+
receiver.after :all do
|
101
|
+
ActiveRecord::Base.establish_connection(:test)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Sadly, we have to monkeypatch Rake because all of the Rails database tasks are
|
2
|
+
# hardcoded for specific adapters, with no extension points (!)
|
3
|
+
Rake::TaskManager.class_eval do
|
4
|
+
def remove_task(task_name)
|
5
|
+
@tasks.delete(task_name.to_s)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def remove_task(task_name)
|
10
|
+
Rake.application.remove_task(task_name)
|
11
|
+
end
|
12
|
+
|
13
|
+
def wrap_task(task_name, &wrapper)
|
14
|
+
wrapped_task = Rake::Task[task_name]
|
15
|
+
remove_task(Rake::Task.scope_name(Rake.application.current_scope,
|
16
|
+
task_name))
|
17
|
+
task(task_name) do
|
18
|
+
wrapper.call(wrapped_task)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
namespace :db do
|
23
|
+
namespace :test do
|
24
|
+
wrap_task :purge do |wrapped_task|
|
25
|
+
if ActiveRecord::Base.configurations["test"]["adapter"] == "nulldb"
|
26
|
+
# NO-OP
|
27
|
+
else
|
28
|
+
wrapped_task.invoke
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/nulldb.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "nulldb"
|
5
|
+
s.version = "0.3.7-alpha.1"
|
6
|
+
|
7
|
+
s.require_paths = ["lib"]
|
8
|
+
s.authors = ["Avdi Grimm", "Myron Marston"]
|
9
|
+
s.summary = "The Null Object pattern as applied to ActiveRecord database adapters"
|
10
|
+
s.description = "A database backend that translates database interactions into no-ops. Using NullDB enables you to test your model business logic - including after_save hooks - without ever touching a real database."
|
11
|
+
s.email = "myron.marston@gmail.com"
|
12
|
+
s.extra_rdoc_files = [
|
13
|
+
"LICENSE",
|
14
|
+
"README.rdoc"
|
15
|
+
]
|
16
|
+
s.files = `git ls-files`.split($/)
|
17
|
+
s.homepage = "http://github.com/nulldb/nulldb"
|
18
|
+
s.licenses = ["MIT"]
|
19
|
+
|
20
|
+
s.add_runtime_dependency 'activerecord', '>= 2.0.0'
|
21
|
+
s.add_development_dependency 'spec'
|
22
|
+
s.add_development_dependency 'rspec'
|
23
|
+
s.add_development_dependency 'rake'
|
24
|
+
s.add_development_dependency 'appraisal'
|
25
|
+
s.add_development_dependency 'simplecov'
|
26
|
+
end
|
27
|
+
|
data/spec/nulldb_spec.rb
ADDED
@@ -0,0 +1,345 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
# Optional simplecov loading
|
4
|
+
begin
|
5
|
+
require 'simplecov'
|
6
|
+
SimpleCov.start
|
7
|
+
rescue LoadError
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'active_record'
|
11
|
+
require 'active_record/version'
|
12
|
+
$: << File.join(File.dirname(__FILE__), "..", "lib")
|
13
|
+
|
14
|
+
if ActiveRecord::VERSION::MAJOR > 2
|
15
|
+
require 'rspec' # rspec 2
|
16
|
+
else
|
17
|
+
require 'spec' # rspec 1
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'nulldb_rspec'
|
21
|
+
|
22
|
+
class Employee < ActiveRecord::Base
|
23
|
+
after_save :on_save_finished
|
24
|
+
|
25
|
+
def on_save_finished
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class TablelessModel < ActiveRecord::Base
|
30
|
+
end
|
31
|
+
|
32
|
+
NullDB.configure {|ndb| ndb.project_root = 'Rails.root'}
|
33
|
+
|
34
|
+
describe "NullDB with no schema pre-loaded" do
|
35
|
+
before :each do
|
36
|
+
allow( Kernel ).to receive :load
|
37
|
+
allow( ActiveRecord::Migration ).to receive :verbose=
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should load Rails.root/db/schema.rb if no alternate is specified" do
|
41
|
+
ActiveRecord::Base.establish_connection :adapter => :nulldb
|
42
|
+
expect( Kernel ).to receive(:load).with("Rails.root/db/schema.rb")
|
43
|
+
ActiveRecord::Base.connection.columns('schema_info')
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should load the specified schema relative to Rails.root" do
|
47
|
+
expect( Kernel ).to receive(:load).with("Rails.root/foo/myschema.rb")
|
48
|
+
ActiveRecord::Base.establish_connection :adapter => :nulldb,
|
49
|
+
:schema => "foo/myschema.rb"
|
50
|
+
ActiveRecord::Base.connection.columns('schema_info')
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should suppress migration output" do
|
54
|
+
expect( ActiveRecord::Migration).to receive(:verbose=).with(false)
|
55
|
+
ActiveRecord::Base.establish_connection :adapter => :nulldb,
|
56
|
+
:schema => "foo/myschema.rb"
|
57
|
+
ActiveRecord::Base.connection.columns('schema_info')
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should allow creating a table without passing a block" do
|
61
|
+
ActiveRecord::Base.establish_connection :adapter => :nulldb
|
62
|
+
ActiveRecord::Schema.define do
|
63
|
+
create_table(:employees)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "NullDB" do
|
69
|
+
before :all do
|
70
|
+
ActiveRecord::Base.establish_connection :adapter => :nulldb
|
71
|
+
ActiveRecord::Migration.verbose = false
|
72
|
+
ActiveRecord::Schema.define do
|
73
|
+
create_table(:employees) do |t|
|
74
|
+
t.string :name, null: false, limit: 50
|
75
|
+
t.date :hire_date
|
76
|
+
t.integer :employee_number
|
77
|
+
t.decimal :salary
|
78
|
+
end
|
79
|
+
|
80
|
+
create_table(:employees_widgets, :id => false, :force => true) do |t|
|
81
|
+
t.integer :employee_id
|
82
|
+
t.integer :widget_id
|
83
|
+
end
|
84
|
+
|
85
|
+
add_index "employees", :name, :name => "index_employees_on_name"
|
86
|
+
add_index "employees", ["employee_number"], :name => "index_employees_on_employee_number", :unique => true
|
87
|
+
add_index "employees_widgets", ["employee_id", "widget_id"], :name => "my_index"
|
88
|
+
|
89
|
+
add_fk_constraint "foo", "bar", "baz", "buz", "bungle"
|
90
|
+
add_pk_constraint "foo", "bar", {}, "baz", "buz"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
before :each do
|
95
|
+
@employee = Employee.new(:name => "John Smith",
|
96
|
+
:hire_date => Date.civil(2000, 1, 1),
|
97
|
+
:employee_number => 42,
|
98
|
+
:salary => 56000.00)
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should set the @config instance variable so plugins that assume its there can use it" do
|
102
|
+
expect( Employee.connection.instance_variable_get(:@config)[:adapter]).to eq :nulldb
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should enable instantiation of AR objects without a database" do
|
106
|
+
expect( @employee ).to be_a_kind_of(ActiveRecord::Base)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should remember columns defined in migrations" do
|
110
|
+
should_have_column(Employee, :name, :string)
|
111
|
+
should_have_column(Employee, :hire_date, :date)
|
112
|
+
should_have_column(Employee, :employee_number, :integer)
|
113
|
+
should_have_column(Employee, :salary, :decimal)
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'should have limit on name' do
|
117
|
+
expect(Employee.columns_hash['name'].limit).to eq 50
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should return true on nullable field" do
|
121
|
+
expect(Employee.columns_hash['salary'].null).to be true
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should return false on non-nullable field" do
|
125
|
+
expect(Employee.columns_hash['name'].null).to be false
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should return the appropriate primary key" do
|
129
|
+
expect( ActiveRecord::Base.connection.primary_key('employees') ).to eq 'id'
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should return a nil primary key on habtm" do
|
133
|
+
expect( ActiveRecord::Base.connection.primary_key('employees_widgets') ).to eq nil
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should return an empty array of columns for a table-less model" do
|
137
|
+
expect( TablelessModel.columns).to eq []
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should enable simulated saving of AR objects" do
|
141
|
+
expect{ @employee.save! }.to_not raise_error
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should enable AR callbacks during simulated save" do
|
145
|
+
expect( @employee ).to receive :on_save_finished
|
146
|
+
@employee.save
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should enable simulated deletes of AR objects" do
|
150
|
+
expect{ @employee.destroy }.to_not raise_error
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should enable simulated creates of AR objects" do
|
154
|
+
emp = Employee.create(:name => "Bob Jones")
|
155
|
+
expect( emp.name ).to eq "Bob Jones"
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should generate new IDs when inserting unsaved objects" do
|
159
|
+
cxn = Employee.connection
|
160
|
+
id1 = cxn.insert("some sql", "SomeClass Create", "id", nil, nil)
|
161
|
+
id2 = cxn.insert("some sql", "SomeClass Create", "id", nil, nil)
|
162
|
+
expect( id2 ).to eq (id1 + 1)
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should re-use object ID when inserting saved objects" do
|
166
|
+
cxn = Employee.connection
|
167
|
+
id1 = cxn.insert("some sql", "SomeClass Create", "id", 23, nil)
|
168
|
+
expect( id1 ).to eq 23
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should log executed SQL statements" do
|
172
|
+
cxn = Employee.connection
|
173
|
+
exec_count = cxn.execution_log.size
|
174
|
+
@employee.save!
|
175
|
+
expect( cxn.execution_log.size ).to eq (exec_count + 1)
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should have the adapter name 'NullDB'" do
|
179
|
+
expect( Employee.connection.adapter_name ).to eq "NullDB"
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should support migrations" do
|
183
|
+
expect( Employee.connection.supports_migrations? ).to eq true
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should always have a schema_info table definition" do
|
187
|
+
expect( Employee.connection.tables ).to include "schema_info"
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should return an empty array from #select" do
|
191
|
+
result = Employee.connection.select_all("who cares", "blah")
|
192
|
+
expect( result ).to eq []
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should provide a way to set log checkpoints" do
|
196
|
+
cxn = Employee.connection
|
197
|
+
@employee.save!
|
198
|
+
expect( cxn.execution_log_since_checkpoint.size ).to be > 0
|
199
|
+
|
200
|
+
cxn.checkpoint!
|
201
|
+
expect( cxn.execution_log_since_checkpoint.size ).to eq 0
|
202
|
+
|
203
|
+
@employee.salary = @employee.salary + 1
|
204
|
+
@employee.save!
|
205
|
+
expect( cxn.execution_log_since_checkpoint.size ).to eq 1
|
206
|
+
end
|
207
|
+
|
208
|
+
def should_contain_statement(cxn, entry_point)
|
209
|
+
expect( cxn.execution_log_since_checkpoint).to \
|
210
|
+
include(ActiveRecord::ConnectionAdapters::NullDBAdapter::Statement.new(entry_point))
|
211
|
+
end
|
212
|
+
|
213
|
+
def should_not_contain_statement(cxn, entry_point)
|
214
|
+
expect( cxn.execution_log_since_checkpoint ).to_not \
|
215
|
+
include(ActiveRecord::ConnectionAdapters::NullDBAdapter::Statement.new(entry_point))
|
216
|
+
end
|
217
|
+
|
218
|
+
it "should tag logged statements with their entry point" do
|
219
|
+
cxn = Employee.connection
|
220
|
+
|
221
|
+
should_not_contain_statement(cxn, :insert)
|
222
|
+
@employee.save
|
223
|
+
should_contain_statement(cxn, :insert)
|
224
|
+
|
225
|
+
cxn.checkpoint!
|
226
|
+
should_not_contain_statement(cxn, :update)
|
227
|
+
@employee.salary = @employee.salary + 1
|
228
|
+
@employee.save
|
229
|
+
should_contain_statement(cxn, :update)
|
230
|
+
|
231
|
+
cxn.checkpoint!
|
232
|
+
should_not_contain_statement(cxn, :delete)
|
233
|
+
@employee.destroy
|
234
|
+
should_contain_statement(cxn, :delete)
|
235
|
+
|
236
|
+
cxn.checkpoint!
|
237
|
+
should_not_contain_statement(cxn, :select_all)
|
238
|
+
Employee.all.each do |emp|; end
|
239
|
+
should_contain_statement(cxn, :select_all)
|
240
|
+
|
241
|
+
cxn.checkpoint!
|
242
|
+
should_not_contain_statement(cxn, :select_value)
|
243
|
+
Employee.count_by_sql("frobozz")
|
244
|
+
should_contain_statement(cxn, :select_value)
|
245
|
+
|
246
|
+
cxn.checkpoint!
|
247
|
+
should_not_contain_statement(cxn, :select_values)
|
248
|
+
cxn.select_values("")
|
249
|
+
should_contain_statement(cxn, :select_values)
|
250
|
+
end
|
251
|
+
|
252
|
+
it "should allow #finish to be called on the result of #execute" do
|
253
|
+
Employee.connection.execute("blah").finish
|
254
|
+
end
|
255
|
+
|
256
|
+
it "should #to_a return empty array on the result of #execute" do
|
257
|
+
result = Employee.connection.execute("blah")
|
258
|
+
expect( result.to_a ).to be_a Array
|
259
|
+
expect( result.to_a ).to be_empty
|
260
|
+
end
|
261
|
+
|
262
|
+
def should_have_column(klass, col_name, col_type)
|
263
|
+
col = klass.columns_hash[col_name.to_s]
|
264
|
+
expect(col.sql_type.to_s.gsub(/\([0-9]+\)/, "").to_sym).to eq col_type
|
265
|
+
end
|
266
|
+
|
267
|
+
|
268
|
+
it "should support adding indexes" do
|
269
|
+
expect( Employee.connection.indexes('employees').size ).to eq 2
|
270
|
+
expect( Employee.connection.indexes('employees_widgets').size ).to eq 1
|
271
|
+
end
|
272
|
+
|
273
|
+
it "should support unique indexes" do
|
274
|
+
expect( Employee.connection.indexes('employees').detect{|idx| idx.columns == ["name"]}.unique ).to eq false
|
275
|
+
expect( Employee.connection.indexes('employees').detect{|idx| idx.columns == ["employee_number"]}.unique ).to eq true
|
276
|
+
end
|
277
|
+
|
278
|
+
it "should support multi-column indexes" do
|
279
|
+
expect( Employee.connection.indexes('employees_widgets').first.columns).to eq ["employee_id", "widget_id"]
|
280
|
+
end
|
281
|
+
|
282
|
+
it "should support custom index names" do
|
283
|
+
expect( Employee.connection.indexes('employees_widgets').first.name ).to eq 'my_index'
|
284
|
+
end
|
285
|
+
|
286
|
+
it 'should handle ActiveRecord::ConnectionNotEstablished' do
|
287
|
+
expect( ActiveRecord::Base ).to receive(:connection_pool).and_raise(ActiveRecord::ConnectionNotEstablished)
|
288
|
+
expect { NullDB.nullify }.to_not raise_error
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
# need a fallback db for contextual nullification
|
293
|
+
ActiveRecord::Base.configurations['test'] = {'adapter' => 'nulldb'}
|
294
|
+
|
295
|
+
describe NullDB::RSpec::NullifiedDatabase do
|
296
|
+
describe 'have_executed rspec matcher' do
|
297
|
+
before(:all) do
|
298
|
+
ActiveRecord::Schema.define do
|
299
|
+
create_table(:employees)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
include NullDB::RSpec::NullifiedDatabase
|
304
|
+
|
305
|
+
before { NullDB.checkpoint }
|
306
|
+
|
307
|
+
it 'passes if an execution was made' do
|
308
|
+
expect( Employee.connection ).to receive(:insert)
|
309
|
+
allow( Kernel ).to receive :load
|
310
|
+
Employee.create
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
describe '.globally_nullify_database' do
|
315
|
+
it 'nullifies the database' do
|
316
|
+
expect( NullDB::RSpec::NullifiedDatabase ).to respond_to(:nullify_database)
|
317
|
+
expect( NullDB::RSpec::NullifiedDatabase ).to receive(:nullify_database)
|
318
|
+
NullDB::RSpec::NullifiedDatabase.globally_nullify_database
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
|
324
|
+
describe 'adapter-specific extensions' do
|
325
|
+
before(:all) do
|
326
|
+
ActiveRecord::Base.establish_connection :adapter => :nulldb
|
327
|
+
ActiveRecord::Migration.verbose = false
|
328
|
+
end
|
329
|
+
|
330
|
+
it "supports 'enable_extension' in the schema definition" do
|
331
|
+
expect{
|
332
|
+
ActiveRecord::Schema.define do
|
333
|
+
enable_extension "plpgsql"
|
334
|
+
end
|
335
|
+
}.to_not raise_error
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
describe ActiveRecord::ConnectionAdapters::NullDBAdapter::EmptyResult do
|
340
|
+
it "should return an empty array from #cast_values" do
|
341
|
+
result = described_class.new
|
342
|
+
expect( result.cast_values ).to be_a Array
|
343
|
+
expect( result.cast_values ).to be_empty
|
344
|
+
end
|
345
|
+
end
|