Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions .github/workflows/rspec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,18 @@ jobs:
entry:
- { ruby: '2.6', mongodb: '4.4', gemfile: 'mongoid_4', bundler: '1.17.3' }
- { ruby: '2.6', mongodb: '4.4', gemfile: 'mongoid_5', bundler: '1.17.3' }
- { ruby: '2.7', mongodb: '4.4', gemfile: 'mongoid_6' }
- { ruby: '2.7', mongodb: '4.4', gemfile: 'mongoid_6', coverage: true }
- { ruby: '2.7', mongodb: '5.0', gemfile: 'mongoid_7' }
- { ruby: '2.7', mongodb: '5.0', gemfile: 'mongoid_8' }
- { ruby: '3.0', mongodb: '6.0', gemfile: 'mongoid_8' }
- { ruby: '3.1', mongodb: '6.0', gemfile: 'mongoid_8' }
- { ruby: '3.2', mongodb: '7.0', gemfile: 'mongoid_8' }
- { ruby: '3.3', mongodb: '7.0', gemfile: 'mongoid_9' }
- { ruby: '3.4', mongodb: '8.0', gemfile: 'mongoid_9' }
- { ruby: '3.4', mongodb: '8.0', gemfile: 'mongoid_9', coverage: true }
- { ruby: '4.0', mongodb: '8.0', gemfile: 'mongoid_9' }
name: test (ruby=${{ matrix.entry.ruby }}, mongodb=${{ matrix.entry.mongodb }}), gemfile=${{ matrix.entry.gemfile }})
env:
COVERAGE: ${{ matrix.entry.ruby == '3.4' }}
COVERAGE: ${{ matrix.entry.coverage }}
BUNDLE_GEMFILE: gemfiles/${{ matrix.entry.gemfile }}.gemfile
steps:
- name: Set up MongoDB ${{ matrix.entry.mongodb }}
Expand All @@ -59,9 +59,22 @@ jobs:
- name: Run tests
run: bundle exec rake
- name: Coveralls
if: matrix.entry.ruby == '3.4'
if: matrix.entry.coverage
uses: coverallsapp/github-action@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
file: coverage/.resultset.json
format: simplecov
parallel: true
flag-name: ${{ matrix.entry.gemfile }}
coverage:
name: Finalize coverage
needs: test
if: always() && needs.test.result == 'success'
runs-on: ubuntu-latest
steps:
- name: Finish parallel Coveralls reporting
uses: coverallsapp/github-action@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel-finished: true
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
### 4.4.1 (Next)

* [#263](https://github.com/mongoid/mongoid-rspec/pull/263): Expand matcher behavior and diagnostic coverage, run the storage matcher specs in the default suite, combine Mongoid 6 and 9 coverage in Coveralls, and document isolated MongoDB test databases - [@dblock](https://github.com/dblock).
* Your contribution here.

### 4.4.0 (2026/10/15)
Expand Down
8 changes: 8 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ bundle install
bundle exec rake
```

To use a dedicated MongoDB database (including a server on a nondefault port), set `MONGODB_URI`. The suite purges its configured database after each example group, so never point it at a database containing data you need.

```
MONGODB_URI=mongodb://localhost:27017/mongoid_rspec_coverage COVERAGE=true bundle exec rake
```

`COVERAGE=true` enables SimpleCov for every file under `lib/`; the HTML report is written to `coverage/index.html`.

#### Write Tests

Try to write a test that reproduces the problem you're trying to fix or describes a feature that you want to build. Add to [spec/mongoid-rspec](spec/mongoid-rspec).
Expand Down
13 changes: 12 additions & 1 deletion spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,18 @@
require 'rspec/expectations'
require 'mongoid/compatibility'

Mongoid::Config.connect_to('mongoid-rspec-test') if Mongoid::Compatibility::Version.mongoid3_or_newer?
if ENV['MONGODB_URI']
Mongoid.configure do |config|
connection = { uri: ENV.fetch('MONGODB_URI') }
if Mongoid::Compatibility::Version.mongoid5_or_newer?
config.clients[:default] = connection
else
config.sessions[:default] = connection
end
end
elsif Mongoid::Compatibility::Version.mongoid3_or_newer?
Mongoid::Config.connect_to('mongoid-rspec-test')
end
Mongo::Logger.logger.level = ::Logger::INFO if Mongoid::Compatibility::Version.mongoid5_or_newer?

Dir[File.join(MODELS, '*.rb')].sort.each { |file| require File.basename(file) }
Expand Down
65 changes: 65 additions & 0 deletions spec/unit/association_diagnostics_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe 'Association matcher diagnostics' do
[
[User, :have_many, :missing, nil, [], 'no association named missing'],
[User, :have_one, :articles, nil, [], 'references many'],
[User, :have_many, :articles, :of_type, [Profile], 'of type Article'],
[Article, :belong_to, :author, :as_inverse_of, [:other], 'inverse of articles'],
[User, :have_many, :articles, :ordered_by, [:missing], 'ordered by title'],
[User, :have_many, :comments, :with_dependent, [:delete], 'dependent as destroy'],
[User, :have_many, :messages, :with_autosave, [], 'did not set autosave'],
[User, :embed_one, :profile, :with_autobuild, [], 'did not set autobuild'],
[Article, :belong_to, :author, :with_polymorphism, [], 'did not set polymorphic'],
[User, :embed_one, :profile, :with_cascading_callbacks, [], 'did not set cascade_callbacks'],
[Article, :embed_many, :comments, :cyclic, [], 'did not set cyclic'],
[Article, :embed_many, :comments, :stored_as, [:other], 'stored as comments'],
[User, :belong_to, :site, :with_index, [], 'did not set index'],
[Article, :belong_to, :author, :with_foreign_key, [:other_id], 'foreign key "author_id"'],
[Article, :belong_to, :author, :with_touch, [false], 'sets touch as true'],
[Article, :belong_to, :author, :with_optional, [], 'did not set optional']
].each do |model, factory, relation, modifier, arguments, message|
it "explains #{model}.#{relation} mismatches for #{modifier || factory}" do
matcher = public_send(factory, relation)
matcher.public_send(modifier, *arguments) if modifier
expect(matcher.matches?(model)).to be false
expect(matcher.failure_message).to include(message)
expect(matcher.description).not_to be_empty
end
end

it 'describes successful matches when negated' do
matcher = have_many(:comments).of_type(Comment).with_autosave
expect(matcher.matches?(User.new)).to be true
expect(matcher.failure_message_when_negated).to include('not', 'of type Comment', 'which set autosave')
end

it 'checks cyclic and stored association metadata on real embedded relations' do
model = Class.new do
include Mongoid::Document
embeds_many :comments, cyclic: true, store_as: :notes
end
expect(model).to embed_many(:comments).cyclic.stored_as(:notes)
end

it 'rejects unknown association types and ordering a singular association' do
expect { have_one(:record).ordered_by(:name) }.to raise_error(RuntimeError, /does not respond to :order/)
expect { have_many(:articles).type_description(String) }.to raise_error(RuntimeError, /Unknown association type/)
end

if Mongoid::Compatibility::Version.mongoid6_or_older?
it 'reports associations without a counter cache' do
matcher = have_many(:articles).with_counter_cache
expect(matcher.matches?(User)).to be false
expect(matcher.failure_message).to include('did not set counter_cache')
end

it 'explains an ordering direction mismatch on legacy metadata' do
matcher = have_many(:users).ordered_by(:email.asc)
expect(matcher.matches?(Site)).to be false
expect(matcher.failure_message).to include('descending')
end
end
end
56 changes: 0 additions & 56 deletions spec/unit/be_stored_in.rb

This file was deleted.

82 changes: 82 additions & 0 deletions spec/unit/be_stored_in_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Mongoid::Matchers::BeStoredIn do
let(:connection_option) { Mongoid::Compatibility::Version.mongoid5_or_newer? ? :client : :session }
let(:connection_storage) { { connection_option => 'secondary' } }

subject do
storage_options = { collection: 'citizens', database: 'other' }.merge(connection_storage)
Class.new do
include Mongoid::Document
store_in storage_options
end
end

it 'detects storage options' do
is_expected.to be_stored_in({ collection: 'citizens', database: 'other' }.merge(connection_storage))
end

it 'detects even part of storage options' do
is_expected.to be_stored_in(database: 'other')
is_expected.to be_stored_in(connection_storage)
is_expected.to be_stored_in(collection: 'citizens')
is_expected.to be_stored_in(collection: 'citizens', database: 'other')
is_expected.to be_stored_in({ database: 'other' }.merge(connection_storage))
is_expected.to be_stored_in({ collection: 'citizens' }.merge(connection_storage))
end

it 'detects differences' do
is_expected.not_to be_stored_in(collection: 'aliens')
end

# Dynamic storage matching uses persistence_context, introduced in Mongoid 6.
if Mongoid::Compatibility::Version.mongoid6_or_newer?
context 'when models have storage options defined via blocks, procs or lambdas' do
subject do
Class.new do
include Mongoid::Document
store_in database: -> { Thread.current[:database] }
end
end

before do
Thread.current[:database] = 'db1981'
end

after do
Thread.current[:database] = nil
end

it 'detects storage options' do
is_expected.to be_stored_in(database: 'db1981')
end

it 'reflects changes in storage options' do
is_expected.to be_stored_in(database: 'db1981')
Thread.current[:database] = 'db2200'
is_expected.to be_stored_in(database: 'db2200')
end

it 'detects differences' do
is_expected.not_to be_stored_in(database: 'other')
end
end
end

it 'describes mismatched storage and negated expectations' do
matcher = be_stored_in(collection: 'aliens')
expect(matcher.matches?(subject.new)).to be false
expect(matcher.failure_message).to include('be stored in', 'aliens', 'citizens')
expect(matcher.failure_message_when_negated).to include('not to be stored in', 'citizens')
end

it 'compares non-symbolizable storage options without changing their values' do
model = Class.new do
include Mongoid::Document
store_in collection: nil
end
expect(model).to be_stored_in(collection: nil)
end
end
Loading
Loading