delivery_matchers 0.1.0 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 362f6ed9e967c5664e98c668b7cb0b13125ea63d
4
- data.tar.gz: a9574d94b314f3de76936ffcbf2f754b4bb8c0b4
3
+ metadata.gz: 0e78f4296323201bb6b45d5d38d1c6097e707d47
4
+ data.tar.gz: 4f3a1cb764fe4d9611ce5c6be03c5692d52e785a
5
5
  SHA512:
6
- metadata.gz: 9957b3743f9505b41d52b3a4b49bc38747d3d79b1ea380ce7ddf05e1a189bf5f53196b842daa964aab7f4e9451bf30b1557127f600b46e166c8f4e86e82f9227
7
- data.tar.gz: e04c8ccc43d9aa738dc2868dd39a357abfd3875fe7026495d6e44c87d60811455f25e783c97afd0c3a481bec47b1244486f8c4acd0e2a0f0fbcbae2f236a8dbe
6
+ metadata.gz: 096a255d6cda92bda89e1ca0e271809aadf78b2482b66fbae7be2b4c3d9a54c81f5da66d7e93280235b5fb3f391868b6bbb86be1bd86929d3baf8f277f51475e
7
+ data.tar.gz: 4f4ab701739ba5d96659151263b5d3fb15bf0f651646810b1c444217da9fecf927087afbd08bdb7abd6a7917e350115bd95bfff6a4849133805d3c2ad143fdc6
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /Gemfile.lock
7
+ /gemfiles/*.gemfile.lock
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+ /vendor
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ -I ./lib
3
+ -r spec_helper
@@ -0,0 +1,7 @@
1
+ appraise "actionmailer-4" do
2
+ gem "actionmailer", "4.2.8"
3
+ end
4
+
5
+ appraise "actionmailer-5" do
6
+ gem "actionmailer", "5.0.0"
7
+ end
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in delivery_matchers.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 General Assembly
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,145 @@
1
+ # DeliveryMatchers
2
+
3
+ [![Build Status](https://semaphoreci.com/api/v1/generalassembly/delivery_matchers/branches/master/badge.svg)](https://semaphoreci.com/generalassembly/delivery_matchers)
4
+
5
+ An RSpec custom matcher for ActionMailer's `deliver_later` method.
6
+
7
+ This matcher was extracted from a General Assembly product with a considerable amount of logic to determine when to send certain transactional emails to different subsets of students.
8
+
9
+ We wanted to ensure this code had bulletproof test coverage. Inspecting the in-memory ActiveJob queue let us test our delivery logic, but it also allowed a lot of low-level details to leak into our tests, making them harder to read.
10
+
11
+ We rolled this custom matcher to keep our test code beautiful. We hope you find it as useful as we did!
12
+
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ ```ruby
19
+ gem 'delivery_matchers'
20
+ ```
21
+
22
+ and include the `DeliveryMatchers` module in your RSpec config:
23
+
24
+ ```ruby
25
+ RSpec.configure do |config|
26
+ config.include DeliveryMatchers
27
+ end
28
+ ```
29
+
30
+
31
+ ## Usage
32
+
33
+ ### Basics
34
+
35
+ Let's assume you have a garden-variety ActionMailer class:
36
+
37
+ ```ruby
38
+ class UserMailer < ActionMailer::Base
39
+ def welcome(user)
40
+ @user = user
41
+ end
42
+ end
43
+ ```
44
+
45
+ Let's further assume you have a controller action that enqueues a welcome email for delivery with `deliver_later`, but only under certain circumstances:
46
+
47
+
48
+ ```ruby
49
+ class UsersController < ApplicationController
50
+ def create
51
+ ...
52
+ if user.emailable?
53
+ UserMailer(user).welcome.deliver_later
54
+ end
55
+ ...
56
+ end
57
+ end
58
+ ```
59
+
60
+ With this custom matcher, you can write tests to assert that the controller enqueues an email for delivery.
61
+
62
+ ```ruby
63
+ expect( UserMailer.welcome(emailable_user) ).to be_delivered
64
+ ```
65
+
66
+ You can also assert that an email is **not** delivered, in cases where that is the expected behavior.
67
+
68
+ ```ruby
69
+ expect( UserMailer.welcome(non_emailable_user) ).not_to be_delivered
70
+ ```
71
+
72
+ ### Delivery options
73
+
74
+ #### wait_until
75
+
76
+ If you schedule an email for delivery on a future date with `wait_until`
77
+
78
+ ```ruby
79
+ UserMailer.welcome(user).deliver_later(wait_until: 1.day.from_now)
80
+ ```
81
+
82
+ you can test it with
83
+
84
+ ```ruby
85
+ let(:email) { UserMailer.welcome(user) }
86
+ expect(email).to be_delivered 1.day.from_now
87
+ ```
88
+
89
+ or with the more explicit (and sometimes more readable) form
90
+
91
+ ```ruby
92
+ expect(email).to be_delivered on: expected_date
93
+ ```
94
+
95
+ #### wait
96
+
97
+ If you use `wait` to schedule an email for delivery after a certain interval
98
+
99
+ ```ruby
100
+ UserMailer.welcome(user).deliver_later(wait: 2.days)
101
+ ```
102
+
103
+ you can test it like this
104
+
105
+ ```ruby
106
+ expect(email).to be_delivered in: 2.days
107
+ ```
108
+
109
+ #### queue
110
+
111
+ You can also test that a delivery job was put into a particular queue
112
+
113
+ ```ruby
114
+ UserMailer.welcome(user).deliver_later(queue: "priority")
115
+ ```
116
+
117
+ like this
118
+
119
+ ```ruby
120
+ expect(email).to be_delivered via_queue: "priority"
121
+ ```
122
+
123
+ `via_queue` can also be combined with either the `on` or `in` options
124
+
125
+ ```ruby
126
+ expect(email).to be_delivered 3.days.from_now, via_queue: "priority"
127
+
128
+ expect(email).to be_delivered in: 2.days, via_queue: "priority"
129
+ ```
130
+
131
+ ### Alternative interface
132
+
133
+ `be_delivered` supports `in`, `on`, and `via_queue` as a convenience for readability. But if you prefer consistency with the code being tested, you can also use the same keys provided to `deliver_later`.
134
+
135
+ ```ruby
136
+ expect(email).to be_delivered wait_until: expected_date
137
+
138
+ expect(email).to be_delivered wait: 2.days
139
+
140
+ expect(email).to be_delivered queue: "priority"
141
+ ```
142
+
143
+ ### A note on precision
144
+
145
+ This matcher performs all time comparisons with a precision of 1 second. If you see intermittent errors as a result, consider using [ActiveSupport's TimeHelpers](http://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html) or the [Timecop gem](https://github.com/travisjeffery/timecop) to freeze time in your tests.
@@ -0,0 +1,9 @@
1
+ require "appraisal"
2
+ require "bundler/gem_tasks"
3
+ require "rspec/core/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ default_task = ENV["APPRAISAL_INITIALIZED"] ? :spec : :appraisal
8
+
9
+ task default: default_task
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'delivery_matchers/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "delivery_matchers"
8
+ spec.version = DeliveryMatchers::VERSION
9
+ spec.authors = ["General Assembly"]
10
+ spec.email = ["opensource@generalassemb.ly"]
11
+
12
+ spec.summary = "RSpec custom matchers for ActionMailer's deliver_later"
13
+ spec.homepage = "https://github.com/generalassembly/delivery_matchers"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ spec.require_paths = ["lib"]
17
+
18
+ spec.add_dependency "rspec", ">= 3.0"
19
+ spec.add_dependency "actionmailer", ">= 4.2", "< 5.1"
20
+ spec.add_dependency "activejob", ">= 4.2", "< 5.1"
21
+
22
+ spec.add_development_dependency "appraisal"
23
+ spec.add_development_dependency "bundler"
24
+ spec.add_development_dependency "rake"
25
+ end
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "actionmailer", "4.2.8"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "actionmailer", "5.0.0"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,11 @@
1
+ require "action_mailer"
2
+ require "active_job"
3
+
4
+ require "delivery_matchers/be_delivered"
5
+ require "delivery_matchers/version"
6
+
7
+ module DeliveryMatchers
8
+ def be_delivered(*args)
9
+ BeDelivered.new(*args)
10
+ end
11
+ end
@@ -0,0 +1,158 @@
1
+ module DeliveryMatchers
2
+ class BeDelivered
3
+ attr_reader :options, :email
4
+
5
+ def initialize(first={}, second={})
6
+ case first
7
+ when Time
8
+ # Allow user to specify the :on date as the first argument:
9
+ # be_delivered 1.day.from_now, via_queue: 'priority'
10
+ options = second
11
+ options[:on] = first
12
+ when Hash
13
+ options = first
14
+ end
15
+
16
+ # Rename the hash keys used by this matcher to match the keys used by
17
+ # ActionMailer::MessageDelivery#deliver_later
18
+ options[:wait] ||= options[:in]
19
+ options[:wait_until] ||= options[:on]
20
+ options[:queue] ||= options[:via_queue]
21
+
22
+ @options = options
23
+ end
24
+
25
+ def matches?(email)
26
+ @email = email
27
+ enqueued_jobs.any? { |job| match_expected?(job) }
28
+ end
29
+
30
+ def failure_message
31
+ enqueued = enqueued_jobs.map(&:inspect).join("\n ")
32
+
33
+ [
34
+ "expected to find this mail delivery job in queue:",
35
+ " #{expected_job}",
36
+ "instead found these jobs:",
37
+ " #{enqueued}"
38
+ ].join("\n")
39
+ end
40
+
41
+ def failure_message_when_negated
42
+ [
43
+ "expected NOT to find this mail delivery job in queue:",
44
+ " #{expected_job}"
45
+ ].join("\n")
46
+ end
47
+
48
+ private
49
+
50
+ def match_expected?(job)
51
+ args_match?(job) && options_match?(job)
52
+ end
53
+
54
+ def args_match?(job)
55
+ job[:args] == expected_args
56
+ end
57
+
58
+ def options_match?(job)
59
+ class_matches?(job) && queue_matches?(job) && time_matches?(job)
60
+ end
61
+
62
+ def class_matches?(job)
63
+ job[:job] == expected_class
64
+ end
65
+
66
+ def queue_matches?(job)
67
+ return true if options[:queue].nil?
68
+ job[:queue] == options[:queue]
69
+ end
70
+
71
+ def time_matches?(job)
72
+ return true unless options[:wait] || options[:wait_until]
73
+
74
+ if options[:wait]
75
+ expected = Time.current + options[:wait]
76
+ elsif options[:wait_until]
77
+ expected = options[:wait_until].to_time
78
+ end
79
+
80
+ # Difference between expected and actual must be less than 1 second
81
+ (job[:at].to_i - expected.to_i).abs <= 1
82
+ end
83
+
84
+ def expected_job
85
+ job = {
86
+ job: expected_class,
87
+ args: expected_args,
88
+ queue: expected_queue
89
+ }
90
+
91
+ if expected_delivery_time
92
+ job[:at] = expected_delivery_time
93
+ end
94
+
95
+ job
96
+ end
97
+
98
+ def expected_class
99
+ ActionMailer::DeliveryJob
100
+ end
101
+
102
+ def expected_queue
103
+ options[:queue] || 'mailers'
104
+ end
105
+
106
+ def expected_delivery_time
107
+ if options[:wait_until]
108
+ options[:wait_until].to_time.to_f
109
+ elsif options[:wait]
110
+ (Time.current + options[:wait]).to_f
111
+ end
112
+ end
113
+
114
+ def expected_args
115
+ mailer = expected_mailer
116
+ method = expected_method
117
+ args = email.instance_variable_get('@args')
118
+
119
+ [mailer, method, 'deliver_now', *global_ids(args)]
120
+ end
121
+
122
+ def expected_mailer
123
+ if rails5?
124
+ email.instance_variable_get(:@mailer_class).to_s
125
+ else
126
+ email.instance_variable_get(:@mailer).to_s
127
+ end
128
+ end
129
+
130
+ def expected_method
131
+ if rails5?
132
+ email.instance_variable_get(:@action).to_s
133
+ else
134
+ email.instance_variable_get(:@mail_method).to_s
135
+ end
136
+ end
137
+
138
+ def global_ids(array)
139
+ Array(array).map { |obj| global_id(obj) }
140
+ end
141
+
142
+ def global_id(obj)
143
+ if obj.respond_to? :to_global_id
144
+ { "_aj_globalid" => obj.to_global_id.to_s }
145
+ else
146
+ obj
147
+ end
148
+ end
149
+
150
+ def enqueued_jobs
151
+ ActiveJob::Base.queue_adapter.enqueued_jobs
152
+ end
153
+
154
+ def rails5?
155
+ ActionMailer.version >= Gem::Version.new("5.0.0")
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,3 @@
1
+ module DeliveryMatchers
2
+ VERSION = "0.1.1"
3
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: delivery_matchers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - General Assembly
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-06-01 00:00:00.000000000 Z
11
+ date: 2017-04-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -64,6 +64,20 @@ dependencies:
64
64
  - - "<"
65
65
  - !ruby/object:Gem::Version
66
66
  version: '5.1'
67
+ - !ruby/object:Gem::Dependency
68
+ name: appraisal
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
67
81
  - !ruby/object:Gem::Dependency
68
82
  name: bundler
69
83
  requirement: !ruby/object:Gem::Requirement
@@ -98,7 +112,20 @@ email:
98
112
  executables: []
99
113
  extensions: []
100
114
  extra_rdoc_files: []
101
- files: []
115
+ files:
116
+ - ".gitignore"
117
+ - ".rspec"
118
+ - Appraisals
119
+ - Gemfile
120
+ - LICENSE.md
121
+ - README.md
122
+ - Rakefile
123
+ - delivery_matchers.gemspec
124
+ - gemfiles/actionmailer_4.gemfile
125
+ - gemfiles/actionmailer_5.gemfile
126
+ - lib/delivery_matchers.rb
127
+ - lib/delivery_matchers/be_delivered.rb
128
+ - lib/delivery_matchers/version.rb
102
129
  homepage: https://github.com/generalassembly/delivery_matchers
103
130
  licenses: []
104
131
  metadata: {}
@@ -118,8 +145,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
118
145
  version: '0'
119
146
  requirements: []
120
147
  rubyforge_project:
121
- rubygems_version: 2.4.5
148
+ rubygems_version: 2.5.1
122
149
  signing_key:
123
150
  specification_version: 4
124
151
  summary: RSpec custom matchers for ActionMailer's deliver_later
125
152
  test_files: []
153
+ has_rdoc: