From 438731be82d93562f905f05e1fae2ea481f76c00 Mon Sep 17 00:00:00 2001 From: Bill Kirtley Date: Mon, 25 Aug 2025 12:37:59 -0400 Subject: [PATCH] Port existing test from rspec to minitest It's the new hottness. --- .github/workflows/ci.yml | 2 +- .rubocop.yml | 15 +--- Gemfile | 4 +- Rakefile | 10 ++- lib/safety_mailer/safety_mailer.rb | 2 +- spec/safety_mailer/carrier_spec.rb | 115 -------------------------- spec/safety_mailer_spec.rb | 9 -- spec/spec_helper.rb | 20 ----- test/safety_mailer/carrier_test.rb | 112 +++++++++++++++++++++++++ test/safety_mailer_test.rb | 9 ++ {spec => test}/support/fake_mailer.rb | 0 test/test_helper.rb | 9 ++ 12 files changed, 145 insertions(+), 162 deletions(-) delete mode 100644 spec/safety_mailer/carrier_spec.rb delete mode 100644 spec/safety_mailer_spec.rb delete mode 100644 spec/spec_helper.rb create mode 100644 test/safety_mailer/carrier_test.rb create mode 100644 test/safety_mailer_test.rb rename {spec => test}/support/fake_mailer.rb (100%) create mode 100644 test/test_helper.rb diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 62fa64e..88bd6bd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,4 +23,4 @@ jobs: bundler-cache: true - name: Run tests - run: bundle exec rake spec + run: bundle exec rake test diff --git a/.rubocop.yml b/.rubocop.yml index 5f1f27f..794bb17 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,19 +1,12 @@ plugins: - rubocop-rake -require: - - rubocop-rspec + - rubocop-minitest AllCops: NewCops: disable Metrics/MethodLength: - Max: 20 + Max: 30 -Capybara/RSpec/PredicateMatcher: - Enabled: false - -RSpec/ExampleLength: - Max: 15 - -RSpec/MultipleExpectations: - Max: 5 +Metrics/AbcSize: + Max: 25 diff --git a/Gemfile b/Gemfile index 63c0b8d..373f4b9 100644 --- a/Gemfile +++ b/Gemfile @@ -11,8 +11,8 @@ gem 'pry' gem 'rake', '~> 13.0' gem 'mail' -gem 'rspec', '~> 3.0' +gem 'minitest', '~> 5.0' gem 'rubocop', '~> 1.21' +gem 'rubocop-minitest', '~> 0.35', require: false gem 'rubocop-rake', '~> 0.6', require: false -gem 'rubocop-rspec', '~> 2.5', require: false diff --git a/Rakefile b/Rakefile index 4964751..272c87a 100644 --- a/Rakefile +++ b/Rakefile @@ -1,12 +1,16 @@ # frozen_string_literal: true require 'bundler/gem_tasks' -require 'rspec/core/rake_task' +require 'rake/testtask' -RSpec::Core::RakeTask.new(:spec) +Rake::TestTask.new(:test) do |t| + t.libs << 'test' + t.libs << 'lib' + t.test_files = FileList['test/**/*_test.rb'] +end require 'rubocop/rake_task' RuboCop::RakeTask.new -task default: %i[spec rubocop] +task default: %i[test rubocop] diff --git a/lib/safety_mailer/safety_mailer.rb b/lib/safety_mailer/safety_mailer.rb index 2c2aa0f..2118d57 100644 --- a/lib/safety_mailer/safety_mailer.rb +++ b/lib/safety_mailer/safety_mailer.rb @@ -91,7 +91,7 @@ def prepare_sendgrid_delivery(addresses) end def log(msg) - Rails.logger.warn(msg) if defined?(Rails) + Rails.logger.warn(msg) if defined?(Rails) && Rails.respond_to?(:logger) end end end diff --git a/spec/safety_mailer/carrier_spec.rb b/spec/safety_mailer/carrier_spec.rb deleted file mode 100644 index 0f586b1..0000000 --- a/spec/safety_mailer/carrier_spec.rb +++ /dev/null @@ -1,115 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require 'mail' - -RSpec.describe SafetyMailer::Carrier do - # rubocop:disable RSpec/VariableDefinition, RSpec/VariableName - describe 'stubbing delivery class' do - describe 'no recipient is whitelisted' do - let(:mailer) { instance_double(Mail::SMTP) } - - before do - message = Mail.new do - from 'safety@example.com' - to 'mailer@example.com, pop@oozou.com' - subject 'Angry Birds Star Wars' - body 'Lorem ipsum dolor sit amet' - end - allow(mailer).to receive(:deliver!) - allow(Mail::SMTP).to receive(:new).and_return(mailer) - described_class.new({ allowed_matchers: [/@unrelated\.com$/] }).deliver!(message) - end - - it 'suppresses delivery altogether' do - expect(mailer).not_to have_received(:deliver!) - end - end - - describe 'allowing mail to go only to some recipients' do - let(:mailer) { instance_double(Mail::SMTP) } - - before do - allow(mailer).to receive(:deliver!) - allow(Mail::SMTP).to receive(:new).and_return(mailer) - message = Mail.new do - from 'safety@example.com' - to 'mailer@example.com, pop@oozou.com' - subject 'Angry Birds Star Wars' - body 'Lorem ipsum dolor sit amet' - end - - described_class.new({ allowed_matchers: [/@oozou\.com$/] }).deliver!(message) - end - - it 'allows mail to go only to whitelisted recipients' do - expect(mailer).to have_received(:deliver!) do |mail| - expect(mail.to).not_to include 'mailer@example.com' - expect(mail.to).to include 'pop@oozou.com' - end - end - end - - context 'with irrelevant SendGrid headers' do - let(:mailer) { instance_double(Mail::SMTP) } - - before do - allow(mailer).to receive(:deliver!) - allow(Mail::SMTP).to receive(:new).and_return(mailer) - message = Mail.new do - from 'safety@example.com' - to 'mailer@example.com, pop@oozou.com' - subject 'Angry Birds Star Wars' - body 'Lorem ipsum dolor sit amet' - end - message['X-SMTPAPI'] = '{}' - described_class.new(allowed_matchers: [/@oozou\.com$/]).deliver!(message) - end - - it 'ignores SendGrid headers, strips out unsafe recipients' do - expect(mailer).to have_received(:deliver!) do |mail| - expect(mail.to).not_to include 'mailer@example.com' - expect(mail.to).to include 'pop@oozou.com' - end - end - end - - context 'with SendGrid batch mailing' do - let(:mailer) { instance_double(Mail::SMTP) } - - before do - allow(mailer).to receive(:deliver!).once - allow(Mail::SMTP).to receive(:new).and_return(mailer) - message = Mail.new do - from 'safety@example.com' - subject 'Lunch Order' - body 'You like -food-' - end - sendgrid = { - 'to' => ['mailer@example.com', 'pop@oozou.com'], - 'sub' => { '-food-' => %w[bagels sushi] } - } - message['X-SMTPAPI'] = JSON.generate(sendgrid) - described_class.new(allowed_matchers: [/@oozou\.com$/]).deliver!(message) - end - - it 'strips out unsafe recipients and corresponding substitutions' do - expect(mailer).to have_received(:deliver!) do |mail| - parsed = JSON.parse(mail['X-SMTPAPI'].value) - recipients = parsed['to'] - expect(recipients).not_to include 'mailer@example.com' - expect(recipients).to include 'pop@oozou.com' - - substitutions = parsed['sub'] - expect(substitutions['-food-']).not_to include 'bagels' - expect(substitutions['-food-']).to include 'sushi' - end - end - end - end - - it 'allows initialization with a custom delivery method' do - expect(described_class.new(delivery_method: :faker)).to be_a described_class - end - # rubocop:enable RSpec/VariableDefinition, RSpec/VariableName -end diff --git a/spec/safety_mailer_spec.rb b/spec/safety_mailer_spec.rb deleted file mode 100644 index 5354f51..0000000 --- a/spec/safety_mailer_spec.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -require 'safety_mailer' - -RSpec.describe SafetyMailer do - it 'has a version number' do - expect(SafetyMailer::VERSION).not_to be_nil - end -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb deleted file mode 100644 index 4c8f709..0000000 --- a/spec/spec_helper.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -require 'safety_mailer' -require 'pry' -require 'support/fake_mailer' -require 'action_mailer' - -ActionMailer::Base.add_delivery_method :faker, FakeMailer - -RSpec.configure do |config| - # Enable flags like --only-failures and --next-failure - config.example_status_persistence_file_path = '.rspec_status' - - # Disable RSpec exposing methods globally on `Module` and `main` - config.disable_monkey_patching! - - config.expect_with :rspec do |c| - c.syntax = :expect - end -end diff --git a/test/safety_mailer/carrier_test.rb b/test/safety_mailer/carrier_test.rb new file mode 100644 index 0000000..361f9e2 --- /dev/null +++ b/test/safety_mailer/carrier_test.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +require 'test_helper' +require 'mail' +require 'json' + +module SafetyMailer + class CarrierTest < Minitest::Test + class MockDeliveryMethod + attr_reader :delivered_mail, :called + + def initialize + @delivered_mail = nil + @called = false + end + + def deliver!(mail) + @delivered_mail = mail + @called = true + end + end + + def test_no_recipient_is_whitelisted_suppresses_delivery + mock_delivery = MockDeliveryMethod.new + Mail::SMTP.stub(:new, mock_delivery) do + message = Mail.new do + from 'safety@example.com' + to 'mailer@example.com, pop@oozou.com' + subject 'Angry Birds Star Wars' + body 'Lorem ipsum dolor sit amet' + end + + SafetyMailer::Carrier.new({ allowed_matchers: [/@unrelated\.com$/] }).deliver!(message) + + refute mock_delivery.called, 'Expected delivery to be suppressed' + end + end + + def test_allows_mail_to_go_only_to_whitelisted_recipients + mock_delivery = MockDeliveryMethod.new + + Mail::SMTP.stub(:new, mock_delivery) do + message = Mail.new do + from 'safety@example.com' + to 'mailer@example.com, pop@oozou.com' + subject 'Angry Birds Star Wars' + body 'Lorem ipsum dolor sit amet' + end + + SafetyMailer::Carrier.new({ allowed_matchers: [/@oozou\.com$/] }).deliver!(message) + + assert mock_delivery.called, 'Expected delivery to be called' + refute_includes mock_delivery.delivered_mail.to, 'mailer@example.com' + assert_includes mock_delivery.delivered_mail.to, 'pop@oozou.com' + end + end + + def test_ignores_sendgrid_headers_strips_out_unsafe_recipients + mock_delivery = MockDeliveryMethod.new + + Mail::SMTP.stub(:new, mock_delivery) do + message = Mail.new do + from 'safety@example.com' + to 'mailer@example.com, pop@oozou.com' + subject 'Angry Birds Star Wars' + body 'Lorem ipsum dolor sit amet' + end + message['X-SMTPAPI'] = '{}' + + SafetyMailer::Carrier.new(allowed_matchers: [/@oozou\.com$/]).deliver!(message) + + assert mock_delivery.called, 'Expected delivery to be called' + refute_includes mock_delivery.delivered_mail.to, 'mailer@example.com' + assert_includes mock_delivery.delivered_mail.to, 'pop@oozou.com' + end + end + + def test_strips_out_unsafe_recipients_and_corresponding_substitutions + mock_delivery = MockDeliveryMethod.new + + Mail::SMTP.stub(:new, mock_delivery) do + message = Mail.new do + from 'safety@example.com' + subject 'Lunch Order' + body 'You like -food-' + end + sendgrid = { + 'to' => ['mailer@example.com', 'pop@oozou.com'], + 'sub' => { '-food-' => %w[bagels sushi] } + } + message['X-SMTPAPI'] = JSON.generate(sendgrid) + + SafetyMailer::Carrier.new(allowed_matchers: [/@oozou\.com$/]).deliver!(message) + + assert mock_delivery.called, 'Expected delivery to be called' + parsed = JSON.parse(mock_delivery.delivered_mail['X-SMTPAPI'].value) + recipients = parsed['to'] + refute_includes recipients, 'mailer@example.com' + assert_includes recipients, 'pop@oozou.com' + + substitutions = parsed['sub'] + refute_includes substitutions['-food-'], 'bagels' + assert_includes substitutions['-food-'], 'sushi' + end + end + + def test_allows_initialization_with_custom_delivery_method + carrier = SafetyMailer::Carrier.new(delivery_method: :faker) + assert_instance_of SafetyMailer::Carrier, carrier + end + end +end diff --git a/test/safety_mailer_test.rb b/test/safety_mailer_test.rb new file mode 100644 index 0000000..bad36b9 --- /dev/null +++ b/test/safety_mailer_test.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'test_helper' + +class SafetyMailerTest < Minitest::Test + def test_has_version_number + refute_nil SafetyMailer::VERSION + end +end diff --git a/spec/support/fake_mailer.rb b/test/support/fake_mailer.rb similarity index 100% rename from spec/support/fake_mailer.rb rename to test/support/fake_mailer.rb diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 0000000..b674e75 --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'minitest/autorun' +require 'safety_mailer' +require 'pry' +require 'support/fake_mailer' +require 'action_mailer' + +ActionMailer::Base.add_delivery_method :faker, FakeMailer