mongoid-spec 4.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.md +373 -0
- data/Rakefile +17 -0
- data/lib/matchers/accept_nested_attributes.rb +67 -0
- data/lib/matchers/allow_mass_assignment.rb +102 -0
- data/lib/matchers/associations.rb +330 -0
- data/lib/matchers/be_dynamic_document.rb +26 -0
- data/lib/matchers/be_mongoid_document.rb +26 -0
- data/lib/matchers/be_stored_in.rb +50 -0
- data/lib/matchers/have_field.rb +90 -0
- data/lib/matchers/have_index_for.rb +63 -0
- data/lib/matchers/have_timestamps.rb +61 -0
- data/lib/matchers/validations.rb +81 -0
- data/lib/matchers/validations/acceptance_of.rb +9 -0
- data/lib/matchers/validations/associated.rb +19 -0
- data/lib/matchers/validations/confirmation_of.rb +22 -0
- data/lib/matchers/validations/custom_validation_of.rb +47 -0
- data/lib/matchers/validations/exclusion_of.rb +49 -0
- data/lib/matchers/validations/format_of.rb +71 -0
- data/lib/matchers/validations/inclusion_of.rb +49 -0
- data/lib/matchers/validations/length_of.rb +147 -0
- data/lib/matchers/validations/numericality_of.rb +90 -0
- data/lib/matchers/validations/presence_of.rb +9 -0
- data/lib/matchers/validations/uniqueness_of.rb +82 -0
- data/lib/matchers/validations/with_message.rb +27 -0
- data/lib/mongoid-rspec.rb +1 -0
- data/lib/mongoid/rspec.rb +40 -0
- data/lib/mongoid/rspec/version.rb +5 -0
- data/spec/models/article.rb +29 -0
- data/spec/models/comment.rb +6 -0
- data/spec/models/log.rb +9 -0
- data/spec/models/movie_article.rb +8 -0
- data/spec/models/permalink.rb +5 -0
- data/spec/models/person.rb +10 -0
- data/spec/models/profile.rb +16 -0
- data/spec/models/record.rb +5 -0
- data/spec/models/site.rb +9 -0
- data/spec/models/user.rb +37 -0
- data/spec/spec_helper.rb +35 -0
- data/spec/unit/accept_nested_attributes_spec.rb +12 -0
- data/spec/unit/associations_spec.rb +42 -0
- data/spec/unit/be_dynamic_document_spec.rb +22 -0
- data/spec/unit/be_mongoid_document_spec.rb +25 -0
- data/spec/unit/be_stored_in.rb +54 -0
- data/spec/unit/document_spec.rb +16 -0
- data/spec/unit/have_index_for_spec.rb +46 -0
- data/spec/unit/have_timestamps_spec.rb +71 -0
- data/spec/unit/validations_spec.rb +54 -0
- data/spec/validators/ssn_validator.rb +16 -0
- metadata +171 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Matchers
|
3
|
+
module Validations
|
4
|
+
module WithMessage
|
5
|
+
def with_message(message)
|
6
|
+
@expected_message = message
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def check_expected_message
|
13
|
+
actual_message = @validator.options[:message]
|
14
|
+
if actual_message.nil?
|
15
|
+
@negative_result_message << " with no custom message"
|
16
|
+
@result = false
|
17
|
+
elsif actual_message == @expected_message
|
18
|
+
@positive_result_message << " with custom message '#{@expected_message}'"
|
19
|
+
else
|
20
|
+
@negative_result_message << " got message '#{actual_message}'"
|
21
|
+
@result = false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'mongoid/rspec'
|
@@ -0,0 +1,40 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require 'mongoid'
|
4
|
+
require 'rspec/core'
|
5
|
+
require 'rspec/expectations'
|
6
|
+
require 'rspec/mocks'
|
7
|
+
require 'active_support/core_ext/hash/keys'
|
8
|
+
require 'active_support/core_ext/hash/transform_values'
|
9
|
+
require 'active_support/core_ext/object/blank'
|
10
|
+
require 'active_support/core_ext/string/inflections'
|
11
|
+
|
12
|
+
require 'matchers/associations'
|
13
|
+
require 'matchers/allow_mass_assignment'
|
14
|
+
require 'matchers/accept_nested_attributes'
|
15
|
+
require 'matchers/validations'
|
16
|
+
require 'matchers/validations/with_message'
|
17
|
+
require 'matchers/validations/associated'
|
18
|
+
require 'matchers/validations/confirmation_of'
|
19
|
+
require 'matchers/validations/exclusion_of'
|
20
|
+
require 'matchers/validations/format_of'
|
21
|
+
require 'matchers/validations/inclusion_of'
|
22
|
+
require 'matchers/validations/length_of'
|
23
|
+
require 'matchers/validations/numericality_of'
|
24
|
+
require 'matchers/validations/presence_of'
|
25
|
+
require 'matchers/validations/uniqueness_of'
|
26
|
+
require 'matchers/validations/acceptance_of'
|
27
|
+
require 'matchers/validations/custom_validation_of'
|
28
|
+
require 'matchers/be_mongoid_document'
|
29
|
+
require 'matchers/be_dynamic_document'
|
30
|
+
require 'matchers/be_stored_in'
|
31
|
+
require 'matchers/have_field'
|
32
|
+
require 'matchers/have_index_for'
|
33
|
+
require 'matchers/have_timestamps'
|
34
|
+
|
35
|
+
module Mongoid
|
36
|
+
module Matchers
|
37
|
+
include Mongoid::Matchers::Associations
|
38
|
+
include Mongoid::Matchers::Validations
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Article
|
2
|
+
include Mongoid::Document
|
3
|
+
include Mongoid::Timestamps
|
4
|
+
|
5
|
+
field :title, localize: true
|
6
|
+
field :content
|
7
|
+
field :published, type: Boolean, default: false
|
8
|
+
field :allow_comments, type: Boolean, default: true
|
9
|
+
field :number_of_comments, type: Integer
|
10
|
+
field :status, type: Symbol
|
11
|
+
|
12
|
+
embeds_many :comments, cascade_callbacks: true, inverse_of: :article
|
13
|
+
embeds_one :permalink, inverse_of: :linkable
|
14
|
+
belongs_to :author, class_name: 'User', inverse_of: :articles, index: true
|
15
|
+
|
16
|
+
validates :title, presence: true
|
17
|
+
|
18
|
+
validates_inclusion_of :status, in: [:pending], on: :create
|
19
|
+
validates_inclusion_of :status, in: [:approved, :rejected ], on: :update
|
20
|
+
|
21
|
+
validates_length_of :title, within: 8..16
|
22
|
+
validates_length_of :content, minimum: 200
|
23
|
+
|
24
|
+
index({ title: 1 }, { unique: true, background: true, drop_dups: true })
|
25
|
+
index({ published: 1 })
|
26
|
+
index({ 'permalink._id' => 1 })
|
27
|
+
|
28
|
+
accepts_nested_attributes_for :permalink
|
29
|
+
end
|
data/spec/models/log.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
class MovieArticle < Article
|
2
|
+
|
3
|
+
field :rating, type: Float
|
4
|
+
field :classification, type: Integer
|
5
|
+
|
6
|
+
validates :rating, numericality: { greater_than: 0, less_than_or_equal_to: 5 }
|
7
|
+
validates :classification, numericality: { even: true, only_integer: true, allow_nil: false }
|
8
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Profile
|
2
|
+
include Mongoid::Document
|
3
|
+
|
4
|
+
field :first_name
|
5
|
+
field :last_name
|
6
|
+
field :age
|
7
|
+
field :hobbies, type: Array, default: []
|
8
|
+
|
9
|
+
embedded_in :user, inverse_of: :profile
|
10
|
+
|
11
|
+
validates :age, numericality: { greater_than: 0 }
|
12
|
+
validates :terms_of_service, acceptance: true
|
13
|
+
validates :hobbies, length: { minimum: 1, message: "requires at least one hobby" }
|
14
|
+
|
15
|
+
index({ first_name: 1, last_name: 1 })
|
16
|
+
end
|
data/spec/models/site.rb
ADDED
data/spec/models/user.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
class User
|
2
|
+
include Mongoid::Document
|
3
|
+
include Mongoid::Timestamps::Created
|
4
|
+
|
5
|
+
field :login
|
6
|
+
field :email
|
7
|
+
field :role
|
8
|
+
field :age, type: Integer
|
9
|
+
field :password, type: String
|
10
|
+
field :provider_uid
|
11
|
+
field :locale
|
12
|
+
|
13
|
+
belongs_to :site, inverse_of: :users
|
14
|
+
has_many :articles, foreign_key: :author_id, order: :title
|
15
|
+
has_many :comments, dependent: :destroy, autosave: true
|
16
|
+
has_and_belongs_to_many :children, class_name: "User"
|
17
|
+
has_one :record, autobuild: true, inverse_of: :user
|
18
|
+
|
19
|
+
embeds_one :profile, inverse_of: :user
|
20
|
+
|
21
|
+
validates :login, presence: true, uniqueness: { scope: :site }, format: { with: /\A[\w\-]+\z/ }, exclusion: { in: ["super", "index", "edit"] }
|
22
|
+
validates :email, uniqueness: { case_sensitive: false, scope: :site, message: "is already taken" }, confirmation: true
|
23
|
+
validates :role, presence: true, inclusion: { in: ["admin", "moderator", "member"] }
|
24
|
+
validates :profile, presence: true, associated: true
|
25
|
+
validates :age, presence: true, numericality: true, inclusion: { in: 23..42 }, on: [:create, :update]
|
26
|
+
validates :password, presence: true, on: [:create, :update]
|
27
|
+
validates :password, exclusion: { in: ->(user) { ['password'] } }
|
28
|
+
validates :password, confirmation: { message: "Password confirmation must match given password" }
|
29
|
+
validates :provider_uid, presence: true
|
30
|
+
validates :locale, inclusion: { in: ->(user) { [:en, :ru] } }
|
31
|
+
|
32
|
+
accepts_nested_attributes_for :articles, :comments
|
33
|
+
|
34
|
+
def admin?
|
35
|
+
false
|
36
|
+
end
|
37
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "lib"))
|
3
|
+
MODELS = File.join(File.dirname(__FILE__), "models")
|
4
|
+
$LOAD_PATH.unshift(MODELS)
|
5
|
+
VALIDATORS = File.join(File.dirname(__FILE__), "validators")
|
6
|
+
$LOAD_PATH.unshift(VALIDATORS)
|
7
|
+
|
8
|
+
require "rubygems"
|
9
|
+
require "bundler"
|
10
|
+
Bundler.setup
|
11
|
+
|
12
|
+
require 'mongoid'
|
13
|
+
require 'rspec'
|
14
|
+
require 'rspec/core'
|
15
|
+
require 'rspec/expectations'
|
16
|
+
|
17
|
+
Mongoid::Config.connect_to('mongoid-rspec-test')
|
18
|
+
Mongo::Logger.logger.level = ::Logger::INFO
|
19
|
+
|
20
|
+
Dir[ File.join(MODELS, "*.rb") ].sort.each { |file| require File.basename(file) }
|
21
|
+
|
22
|
+
require 'mongoid-rspec'
|
23
|
+
|
24
|
+
RSpec.configure do |config|
|
25
|
+
config.include RSpec::Matchers
|
26
|
+
config.include Mongoid::Matchers
|
27
|
+
config.mock_with :rspec
|
28
|
+
config.after :all do
|
29
|
+
Mongoid::Config.purge!
|
30
|
+
end
|
31
|
+
config.after :suite do
|
32
|
+
print "\n# Mongoid v#{Mongoid::VERSION}"
|
33
|
+
end
|
34
|
+
config.disable_monkey_patching!
|
35
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe "AcceptsNestedAttributes" do
|
4
|
+
describe User do
|
5
|
+
it { is_expected.to accept_nested_attributes_for(:articles) }
|
6
|
+
it { is_expected.to accept_nested_attributes_for(:comments) }
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Article do
|
10
|
+
it { is_expected.to accept_nested_attributes_for(:permalink) }
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe "Associations" do
|
4
|
+
describe User do
|
5
|
+
it { is_expected.to have_many(:articles).with_foreign_key(:author_id).ordered_by(:title) }
|
6
|
+
|
7
|
+
it { is_expected.to have_one(:record).as_inverse_of(:user).with_autobuild }
|
8
|
+
|
9
|
+
it { is_expected.to have_many(:comments).with_dependent(:destroy).with_autosave }
|
10
|
+
|
11
|
+
it { is_expected.to embed_one(:profile).as_inverse_of(:user) }
|
12
|
+
|
13
|
+
it { is_expected.to have_and_belong_to_many(:children).of_type(User) }
|
14
|
+
end
|
15
|
+
|
16
|
+
describe Profile do
|
17
|
+
it { is_expected.to be_embedded_in(:user).as_inverse_of(:profile) }
|
18
|
+
end
|
19
|
+
|
20
|
+
describe Article do
|
21
|
+
it { is_expected.to belong_to(:author).of_type(User).as_inverse_of(:articles).with_index }
|
22
|
+
it { is_expected.to embed_many(:comments).as_inverse_of(:article).with_cascading_callbacks }
|
23
|
+
it { is_expected.to embed_one(:permalink).as_inverse_of(:linkable) }
|
24
|
+
end
|
25
|
+
|
26
|
+
describe Comment do
|
27
|
+
it { is_expected.to be_embedded_in(:article).as_inverse_of(:comments).with_polymorphism }
|
28
|
+
it { is_expected.to belong_to(:user).as_inverse_of(:comments) }
|
29
|
+
end
|
30
|
+
|
31
|
+
describe Record do
|
32
|
+
it { is_expected.to belong_to(:user).as_inverse_of(:record) }
|
33
|
+
end
|
34
|
+
|
35
|
+
describe Permalink do
|
36
|
+
it { is_expected.to be_embedded_in(:linkable).as_inverse_of(:link) }
|
37
|
+
end
|
38
|
+
|
39
|
+
describe Site do
|
40
|
+
it { is_expected.to have_many(:users).as_inverse_of(:site).ordered_by(:email.desc).with_counter_cache }
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Mongoid::Matchers::BeDynamicDocument do
|
4
|
+
context 'when model does\'t include Mongoid::Document' do
|
5
|
+
subject do
|
6
|
+
Class.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it { is_expected.not_to be_mongoid_document }
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
context 'when model doesn\'t include Mongoid::Document' do
|
14
|
+
subject do
|
15
|
+
Class.new do
|
16
|
+
include Mongoid::Document
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it { is_expected.to be_mongoid_document }
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Mongoid::Matchers::BeMongoidDocument do
|
4
|
+
context 'when model does\'t include Mongoid::Attributes::Dynamic' do
|
5
|
+
subject do
|
6
|
+
Class.new do
|
7
|
+
include Mongoid::Document
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it { is_expected.not_to be_dynamic_document }
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
context 'when model doesn\'t include Mongoid::Attributes::Dynamic' do
|
16
|
+
subject do
|
17
|
+
Class.new do
|
18
|
+
include Mongoid::Document
|
19
|
+
include Mongoid::Attributes::Dynamic
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it { is_expected.to be_dynamic_document }
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Mongoid::Matchers::BeStoredIn do
|
4
|
+
subject do
|
5
|
+
Class.new do
|
6
|
+
include Mongoid::Document
|
7
|
+
store_in collection: 'citizens', database: 'other', client: 'secondary'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'detects storage options' do
|
12
|
+
is_expected.to be_stored_in(collection: 'citizens', database: 'other', client: 'secondary')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'detects even part of storage options' do
|
16
|
+
is_expected.to be_stored_in(database: 'other')
|
17
|
+
is_expected.to be_stored_in(client: 'secondary')
|
18
|
+
is_expected.to be_stored_in(collection: 'citizens')
|
19
|
+
is_expected.to be_stored_in(collection: 'citizens', database: 'other')
|
20
|
+
is_expected.to be_stored_in(database: 'other', client: 'secondary')
|
21
|
+
is_expected.to be_stored_in(collection: 'citizens', client: 'secondary')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'detects differences' do
|
25
|
+
is_expected.not_to be_stored_in(collection: 'aliens')
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when models has storage options defined via blocks, procs or lambdas' do
|
29
|
+
subject do
|
30
|
+
Class.new do
|
31
|
+
include Mongoid::Document
|
32
|
+
store_in database: ->{ Thread.current[:database] }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
before do
|
37
|
+
Thread.current[:database] = 'db1981'
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'detects storage options' do
|
41
|
+
is_expected.to be_stored_in(database: 'db1981')
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'reflects changes in storage options' do
|
45
|
+
is_expected.to be_stored_in(database: 'db1981')
|
46
|
+
Thread.current[:database] = 'db2200'
|
47
|
+
is_expected.to be_stored_in(database: 'db2200')
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'detects differences' do
|
51
|
+
is_expected.not_to be_stored_in(database: 'other')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe "Document" do
|
4
|
+
describe User do
|
5
|
+
it { is_expected.to have_fields(:email, :login) }
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Article do
|
9
|
+
it { is_expected.to have_field(:published).of_type(Mongoid::Boolean).with_default_value_of(false) }
|
10
|
+
it { is_expected.to have_field(:allow_comments).of_type(Mongoid::Boolean).with_default_value_of(true) }
|
11
|
+
it { is_expected.to belong_to(:author) }
|
12
|
+
it { is_expected.to have_field(:title).localized }
|
13
|
+
it { is_expected.not_to have_field(:allow_comments).of_type(Mongoid::Boolean).with_default_value_of(false) }
|
14
|
+
it { is_expected.not_to have_field(:number_of_comments).of_type(Integer).with_default_value_of(1) }
|
15
|
+
end
|
16
|
+
end
|