mongoid-spec 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +373 -0
  4. data/Rakefile +17 -0
  5. data/lib/matchers/accept_nested_attributes.rb +67 -0
  6. data/lib/matchers/allow_mass_assignment.rb +102 -0
  7. data/lib/matchers/associations.rb +330 -0
  8. data/lib/matchers/be_dynamic_document.rb +26 -0
  9. data/lib/matchers/be_mongoid_document.rb +26 -0
  10. data/lib/matchers/be_stored_in.rb +50 -0
  11. data/lib/matchers/have_field.rb +90 -0
  12. data/lib/matchers/have_index_for.rb +63 -0
  13. data/lib/matchers/have_timestamps.rb +61 -0
  14. data/lib/matchers/validations.rb +81 -0
  15. data/lib/matchers/validations/acceptance_of.rb +9 -0
  16. data/lib/matchers/validations/associated.rb +19 -0
  17. data/lib/matchers/validations/confirmation_of.rb +22 -0
  18. data/lib/matchers/validations/custom_validation_of.rb +47 -0
  19. data/lib/matchers/validations/exclusion_of.rb +49 -0
  20. data/lib/matchers/validations/format_of.rb +71 -0
  21. data/lib/matchers/validations/inclusion_of.rb +49 -0
  22. data/lib/matchers/validations/length_of.rb +147 -0
  23. data/lib/matchers/validations/numericality_of.rb +90 -0
  24. data/lib/matchers/validations/presence_of.rb +9 -0
  25. data/lib/matchers/validations/uniqueness_of.rb +82 -0
  26. data/lib/matchers/validations/with_message.rb +27 -0
  27. data/lib/mongoid-rspec.rb +1 -0
  28. data/lib/mongoid/rspec.rb +40 -0
  29. data/lib/mongoid/rspec/version.rb +5 -0
  30. data/spec/models/article.rb +29 -0
  31. data/spec/models/comment.rb +6 -0
  32. data/spec/models/log.rb +9 -0
  33. data/spec/models/movie_article.rb +8 -0
  34. data/spec/models/permalink.rb +5 -0
  35. data/spec/models/person.rb +10 -0
  36. data/spec/models/profile.rb +16 -0
  37. data/spec/models/record.rb +5 -0
  38. data/spec/models/site.rb +9 -0
  39. data/spec/models/user.rb +37 -0
  40. data/spec/spec_helper.rb +35 -0
  41. data/spec/unit/accept_nested_attributes_spec.rb +12 -0
  42. data/spec/unit/associations_spec.rb +42 -0
  43. data/spec/unit/be_dynamic_document_spec.rb +22 -0
  44. data/spec/unit/be_mongoid_document_spec.rb +25 -0
  45. data/spec/unit/be_stored_in.rb +54 -0
  46. data/spec/unit/document_spec.rb +16 -0
  47. data/spec/unit/have_index_for_spec.rb +46 -0
  48. data/spec/unit/have_timestamps_spec.rb +71 -0
  49. data/spec/unit/validations_spec.rb +54 -0
  50. data/spec/validators/ssn_validator.rb +16 -0
  51. 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,5 @@
1
+ module Mongoid
2
+ module RSpec
3
+ VERSION = '4.0.1'
4
+ end
5
+ 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
@@ -0,0 +1,6 @@
1
+ class Comment
2
+ include Mongoid::Document
3
+
4
+ embedded_in :article, inverse_of: :comments, polymorphic: true
5
+ belongs_to :user, inverse_of: :comments
6
+ end
@@ -0,0 +1,9 @@
1
+ class Log
2
+ include Mongoid::Document
3
+ include Mongoid::Timestamps
4
+ include Mongoid::Attributes::Dynamic
5
+
6
+ store_in collection: "logs"
7
+
8
+ index({ created_at: 1 }, { bucket_size: 100, expire_after_seconds: 3600 } )
9
+ end
@@ -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,5 @@
1
+ class Permalink
2
+ include Mongoid::Document
3
+
4
+ embedded_in :linkable, inverse_of: :link
5
+ end
@@ -0,0 +1,10 @@
1
+ require 'ssn_validator.rb'
2
+
3
+ class Person
4
+ include Mongoid::Document
5
+
6
+ field :name
7
+ field :ssn
8
+
9
+ validates :ssn, ssn: true
10
+ 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
@@ -0,0 +1,5 @@
1
+ class Record
2
+ include Mongoid::Document
3
+
4
+ belongs_to :user, inverse_of: :record
5
+ end
@@ -0,0 +1,9 @@
1
+ class Site
2
+ include Mongoid::Document
3
+
4
+ field :name
5
+
6
+ has_many :users, inverse_of: :site, order: :email.desc, counter_cache: true
7
+
8
+ validates :name, presence: true, uniqueness: true
9
+ end
@@ -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
@@ -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