rails_event_store-rspec 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/LICENSE +22 -0
- data/Makefile +20 -0
- data/README.md +217 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/rails_event_store/rspec.rb +12 -0
- data/lib/rails_event_store/rspec/be_event.rb +171 -0
- data/lib/rails_event_store/rspec/have_applied.rb +42 -0
- data/lib/rails_event_store/rspec/have_published.rb +47 -0
- data/lib/rails_event_store/rspec/matchers.rb +26 -0
- data/lib/rails_event_store/rspec/version.rb +5 -0
- data/rails_event_store-rspec.gemspec +29 -0
- metadata +158 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: '0384d5116d56586a7f7da3b03855609bb2ea9a05'
|
4
|
+
data.tar.gz: 6caf1a937e31986e93658bbc93faa187f93bc625
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 97dac1b86a4963e8548755d2fe8255ead37bec13696aa5f214474538b121750d950dd6280423b5130fed4c5d1c58d3e78921ce305c2726cffdea180abd7b721e
|
7
|
+
data.tar.gz: 0432ec0d4534a94de83ed3b500bcea0ad9ac367fba24d565a01e2274394f385348c63ab69c8aace7c8a4ecc8443c6da170a0eae8de76faf0b95a3111af530e61
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Arkency
|
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.
|
22
|
+
|
data/Makefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
install-bundler: ## Install gem dependencies
|
2
|
+
@echo "Installing gem dependencies"
|
3
|
+
@bundle install
|
4
|
+
|
5
|
+
install: install-bundler ## Prepare current development environment
|
6
|
+
|
7
|
+
test: ## Run tests
|
8
|
+
@echo "Running basic tests - beware: this won't guarantee build pass"
|
9
|
+
@bundle exec rspec
|
10
|
+
|
11
|
+
mutate: test ## Run mutation tests
|
12
|
+
@echo "Running mutation tests - only 100% free mutation will be accepted"
|
13
|
+
@bundle exec mutant --include lib --require rails_event_store/rspec --use rspec --ignore-subject "RailsEventStore::RSpec::Matchers#differ" "RailsEventStore::RSpec*"
|
14
|
+
|
15
|
+
.PHONY: help
|
16
|
+
|
17
|
+
help:
|
18
|
+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
19
|
+
|
20
|
+
.DEFAULT_GOAL := help
|
data/README.md
ADDED
@@ -0,0 +1,217 @@
|
|
1
|
+
# RailsEventStore::RSpec
|
2
|
+
|
3
|
+
## Installation
|
4
|
+
|
5
|
+
Add this line to your application's Gemfile:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
gem 'rails_event_store-rspec'
|
9
|
+
```
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install rails_event_store-rspec
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
Configure your rspec to include matchers in all examples first:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
RSpec.configure do |config|
|
25
|
+
config.include ::RailsEventStore::RSpec::Matchers
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
You can as well choose to have RES matches in particular test file only:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
RSpec.describe MySubject do
|
33
|
+
include ::RailsEventStore::RSpec::Matchers
|
34
|
+
|
35
|
+
specify do
|
36
|
+
# matchers available here
|
37
|
+
end
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
### be_event
|
42
|
+
|
43
|
+
The `be_event` matcher enables you to make expectations on a domain event. It exposes fluent interface.
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
OrderPlaced = Class.new(RailsEventStore::Event)
|
47
|
+
domain_event = OrderPlaced.new(
|
48
|
+
data: {
|
49
|
+
order_id: 42,
|
50
|
+
net_value: BigDecimal.new("1999.0")
|
51
|
+
},
|
52
|
+
metadata: {
|
53
|
+
remote_ip: '1.2.3.4'
|
54
|
+
}
|
55
|
+
)
|
56
|
+
|
57
|
+
expect(domain_event)
|
58
|
+
.to(be_an_event(OrderPlaced)
|
59
|
+
.with_data(order_id: 42, net_value: BigDecimal.new("1999.0"))
|
60
|
+
.with_metadata(remote_ip: '1.2.3.4'))
|
61
|
+
```
|
62
|
+
|
63
|
+
By default the behaviour of `with_data` and `with_metadata` is not strict, that is the expectation is met when all specified values for keys match. Additional data or metadata that is not specified to be expected does not change the outcome.
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
domain_event = OrderPlaced.new(
|
67
|
+
data: {
|
68
|
+
order_id: 42,
|
69
|
+
net_value: BigDecimal.new("1999.0")
|
70
|
+
}
|
71
|
+
)
|
72
|
+
|
73
|
+
# this would pass even though data contains also net_value
|
74
|
+
expect(domain_event).to be_an_event(OrderPlaced).with_data(order_id: 42)
|
75
|
+
```
|
76
|
+
|
77
|
+
This matcher is both [composable](http://rspec.info/blog/2014/01/new-in-rspec-3-composable-matchers/) and accepting [built-in matchers](https://relishapp.com/rspec/rspec-expectations/v/3-6/docs/built-in-matchers) as a part of an expectation.
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
expect(domain_event).to be_an_event(OrderPlaced).with_data(order_id: kind_of(Integer))
|
81
|
+
expect([domain_event]).to include(an_event(OrderPlaced))
|
82
|
+
```
|
83
|
+
|
84
|
+
If you depend on matching the exact data or metadata, there's a `strict` modifier.
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
domain_event = OrderPlaced.new(
|
88
|
+
data: {
|
89
|
+
order_id: 42,
|
90
|
+
net_value: BigDecimal.new("1999.0")
|
91
|
+
}
|
92
|
+
)
|
93
|
+
|
94
|
+
# this would fail as data contains unexpected net_value
|
95
|
+
expect(domain_event).to be_an_event(OrderPlaced).with_data(order_id: 42).strict
|
96
|
+
```
|
97
|
+
|
98
|
+
Mind that `strict` makes both `with_data` and `with_metadata` behave in a stricter way. If you need to mix both, i.e. strict data but non-strict metadata then consider composing matchers.
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
expect(domain_event)
|
102
|
+
.to(be_event(OrderPlaced).with_data(order_id: 42, net_value: BigDecimal.new("1999.0")).strict
|
103
|
+
.and(an_event(OrderPlaced).with_metadata(timestamp: kind_of(Time)))
|
104
|
+
```
|
105
|
+
|
106
|
+
You may have noticed the same matcher being referenced as `be_event`, `be_an_event` and `an_event`. There's also just `event`. Use whichever reads better grammatically.
|
107
|
+
|
108
|
+
### have_published
|
109
|
+
|
110
|
+
Use this matcher to target `event_store` and reading from streams specifically.
|
111
|
+
In a simplest form it would read all streams backward up to a page limit (100 events) and check whether the expectation holds true. Its behaviour can be best compared to the `include` matcher — it is satisfied by at least one element present in the collection. You're encouraged to compose it with `be_event`.
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
event_store = RailsEventStore::Client.new
|
115
|
+
event_store.publish_event(OrderPlaced.new(data: { order_id: 42 }))
|
116
|
+
|
117
|
+
expect(event_store).to have_published(an_event(OrderPlaced))
|
118
|
+
```
|
119
|
+
|
120
|
+
Expectation can be narrowed to the specific stream.
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
event_store = RailsEventStore::Client.new
|
124
|
+
event_store.publish_event(OrderPlaced.new(data: { order_id: 42 }), stream_name: "Order$42")
|
125
|
+
|
126
|
+
expect(event_store).to have_published(an_event(OrderPlaced)).in_stream("Order$42")
|
127
|
+
```
|
128
|
+
|
129
|
+
It is sometimes important to ensure no additional events have been published. Luckliy there's a modifier to cover that usecase.
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
expect(event_store).not_to have_published(an_event(OrderPlaced)).once
|
133
|
+
expect(event_store).to have_published(an_event(OrderPlaced)).exactly(2).times
|
134
|
+
```
|
135
|
+
|
136
|
+
Finally you can make expectation on several events at once.
|
137
|
+
```ruby
|
138
|
+
expect(event_store).to have_published(
|
139
|
+
an_event(OrderPlaced),
|
140
|
+
an_event(OrderExpired).with_data(expired_at: be_between(Date.yesterday, Date.tomorrow))
|
141
|
+
)
|
142
|
+
```
|
143
|
+
|
144
|
+
If there's a usecase not covered by examples above or you need a different set of events to make expectations on you can always resort to a more verbose approach and skip `have_published`.
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
expect(event_store.read_events_forward("OrderAuditLog$42", count: 2)).to eq([
|
148
|
+
an_event(OrderPlaced),
|
149
|
+
an_event(OrderExpired)
|
150
|
+
])
|
151
|
+
```
|
152
|
+
|
153
|
+
### have_applied
|
154
|
+
|
155
|
+
This matcher is intended to be used on [aggregate root](https://github.com/RailsEventStore/rails_event_store/tree/master/aggregate_root#usage). Behaviour is almost identical to `have_published` counterpart, except the concept of stream. Expecations are made against internal unpublished events collection.
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
class Order
|
159
|
+
include AggregateRoot
|
160
|
+
HasBeenAlreadySubmitted = Class.new(StandardError)
|
161
|
+
HasExpired = Class.new(StandardError)
|
162
|
+
|
163
|
+
def initialize
|
164
|
+
self.state = :new
|
165
|
+
# any other code here
|
166
|
+
end
|
167
|
+
|
168
|
+
def submit
|
169
|
+
raise HasBeenAlreadySubmitted if state == :submitted
|
170
|
+
raise HasExpired if state == :expired
|
171
|
+
apply OrderSubmitted.new(data: {delivery_date: Time.now + 24.hours})
|
172
|
+
end
|
173
|
+
|
174
|
+
def expire
|
175
|
+
apply OrderExpired.new
|
176
|
+
end
|
177
|
+
|
178
|
+
private
|
179
|
+
attr_accessor :state
|
180
|
+
|
181
|
+
def apply_order_submitted(event)
|
182
|
+
self.state = :submitted
|
183
|
+
end
|
184
|
+
|
185
|
+
def apply_order_expired(event)
|
186
|
+
self.state = :expired
|
187
|
+
end
|
188
|
+
end
|
189
|
+
```
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
aggregate_root = Order.new
|
193
|
+
aggregate_root.submit
|
194
|
+
|
195
|
+
expect(aggregate_root).to have_applied(event(OrderSubmitted)).once
|
196
|
+
```
|
197
|
+
## Code status
|
198
|
+
|
199
|
+
[![Build Status](https://travis-ci.org/RailsEventStore/rails_event_store-rspec.svg?branch=master)](https://travis-ci.org/RailsEventStore/rails_event_store-rspec)
|
200
|
+
[![Gem Version](https://badge.fury.io/rb/rails_event_store-rspec.svg)](http://badge.fury.io/rb/rails_event_store-rspec)
|
201
|
+
|
202
|
+
We're aiming for 100% mutation coverage in this project. This is why:
|
203
|
+
|
204
|
+
* [Why I want to introduce mutation testing to the rails_event_store gem](http://blog.arkency.com/2015/04/why-i-want-to-introduce-mutation-testing-to-the-rails-event-store-gem/)
|
205
|
+
* [Mutation testing and continuous integration](http://blog.arkency.com/2015/05/mutation-testing-and-continuous-integration/)
|
206
|
+
|
207
|
+
Whenever you fix a bug or add a new feature, we require that the coverage doesn't go down.
|
208
|
+
|
209
|
+
## Development
|
210
|
+
|
211
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
212
|
+
|
213
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
214
|
+
|
215
|
+
## Contributing
|
216
|
+
|
217
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/RailsEventStore/rails_event_store-rspec.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "rails_event_store/rspec"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
module RailsEventStore
|
2
|
+
module RSpec
|
3
|
+
NotSupported = Class.new(StandardError)
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
require "rails_event_store/rspec/version"
|
8
|
+
require "rails_event_store/rspec/be_event"
|
9
|
+
require "rails_event_store/rspec/have_published"
|
10
|
+
require "rails_event_store/rspec/have_applied"
|
11
|
+
require "rails_event_store/rspec/matchers"
|
12
|
+
|
@@ -0,0 +1,171 @@
|
|
1
|
+
module RailsEventStore
|
2
|
+
module RSpec
|
3
|
+
class BeEvent
|
4
|
+
class KindMatcher
|
5
|
+
def initialize(expected)
|
6
|
+
@expected = expected
|
7
|
+
end
|
8
|
+
|
9
|
+
def matches?(actual)
|
10
|
+
@expected === actual
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class DataMatcher
|
15
|
+
def initialize(expected, strict:)
|
16
|
+
@strict = strict
|
17
|
+
@expected = expected
|
18
|
+
end
|
19
|
+
|
20
|
+
def matches?(actual)
|
21
|
+
return true unless @expected
|
22
|
+
matcher = @strict ? ::RSpec::Matchers::BuiltIn::Match : ::RSpec::Matchers::BuiltIn::Include
|
23
|
+
matcher.new(@expected).matches?(actual)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class FailureMessage
|
28
|
+
class ExpectedLine
|
29
|
+
def initialize(expected_klass, expected_metadata, expected_data)
|
30
|
+
@expected_klass = expected_klass
|
31
|
+
@expected_metadata = expected_metadata
|
32
|
+
@expected_data = expected_data
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
["\nexpected: ", @expected_klass, with, metadata, data]
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def with
|
42
|
+
" with" if [@expected_data, @expected_metadata].any?
|
43
|
+
end
|
44
|
+
|
45
|
+
def data
|
46
|
+
[" data: ", @expected_data] if @expected_data
|
47
|
+
end
|
48
|
+
|
49
|
+
def metadata
|
50
|
+
[" metadata: ", @expected_metadata] if @expected_metadata
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class ActualLine
|
55
|
+
def initialize(actual_klass, actual_metadata, actual_data, expected_metadata, expected_data)
|
56
|
+
@actual_klass = actual_klass
|
57
|
+
@actual_metadata = actual_metadata
|
58
|
+
@actual_data = actual_data
|
59
|
+
@expected_metadata = expected_metadata
|
60
|
+
@expected_data = expected_data
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_s
|
64
|
+
["\n got: ", @actual_klass, with, metadata, data, "\n"]
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def with
|
70
|
+
" with" if [@expected_data, @expected_metadata].any?
|
71
|
+
end
|
72
|
+
|
73
|
+
def data
|
74
|
+
[" data: ", @actual_data] if @expected_data
|
75
|
+
end
|
76
|
+
|
77
|
+
def metadata
|
78
|
+
[" metadata: ", @actual_metadata] if @expected_metadata
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class Diff
|
83
|
+
def initialize(actual, expected, label, differ:)
|
84
|
+
@actual = actual
|
85
|
+
@expected = expected
|
86
|
+
@label = label
|
87
|
+
@differ = differ
|
88
|
+
end
|
89
|
+
|
90
|
+
def to_s
|
91
|
+
@expected && ["\n#{@label} diff:", @differ.diff_as_string(@actual.to_s, @expected.to_s)]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def initialize(expected_klass, actual_klass, expected_data, actual_data, expected_metadata, actual_metadata, differ:)
|
96
|
+
@expected_klass = expected_klass
|
97
|
+
@actual_klass = actual_klass
|
98
|
+
@expected_data = expected_data
|
99
|
+
@actual_data = actual_data
|
100
|
+
@expected_metadata = expected_metadata
|
101
|
+
@actual_metadata = actual_metadata
|
102
|
+
@differ = differ
|
103
|
+
end
|
104
|
+
|
105
|
+
def to_s
|
106
|
+
[
|
107
|
+
ExpectedLine.new(@expected_klass, @expected_metadata, @expected_data),
|
108
|
+
ActualLine.new(@actual_klass, @actual_metadata, @actual_data, @expected_metadata, @expected_data),
|
109
|
+
Diff.new(@actual_metadata, @expected_metadata, "Metadata", differ: @differ),
|
110
|
+
Diff.new(@actual_data, @expected_data, "Data", differ: @differ)
|
111
|
+
].map(&:to_s).join
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
include ::RSpec::Matchers::Composable
|
116
|
+
|
117
|
+
def initialize(expected, differ:)
|
118
|
+
@differ = differ
|
119
|
+
@expected = expected
|
120
|
+
end
|
121
|
+
|
122
|
+
def matches?(actual)
|
123
|
+
@actual = actual
|
124
|
+
[matches_kind, matches_data, matches_metadata].all?
|
125
|
+
end
|
126
|
+
|
127
|
+
def with_data(expected_data)
|
128
|
+
@expected_data = expected_data
|
129
|
+
self
|
130
|
+
end
|
131
|
+
|
132
|
+
def with_metadata(expected_metadata)
|
133
|
+
@expected_metadata = expected_metadata
|
134
|
+
self
|
135
|
+
end
|
136
|
+
|
137
|
+
def failure_message
|
138
|
+
FailureMessage.new(@expected, @actual.class, @expected_data, @actual.data, @expected_metadata, @actual.metadata, differ: differ).to_s
|
139
|
+
end
|
140
|
+
|
141
|
+
def failure_message_when_negated
|
142
|
+
%Q{
|
143
|
+
expected: not a kind of #{@expected}
|
144
|
+
got: #{@actual.class}
|
145
|
+
}
|
146
|
+
end
|
147
|
+
|
148
|
+
def strict
|
149
|
+
@strict = true
|
150
|
+
self
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
attr_reader :differ
|
156
|
+
|
157
|
+
def matches_kind
|
158
|
+
KindMatcher.new(@expected).matches?(@actual)
|
159
|
+
end
|
160
|
+
|
161
|
+
def matches_data
|
162
|
+
DataMatcher.new(@expected_data, strict: @strict).matches?(@actual.data)
|
163
|
+
end
|
164
|
+
|
165
|
+
def matches_metadata
|
166
|
+
DataMatcher.new(@expected_metadata, strict: @strict).matches?(@actual.metadata)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module RailsEventStore
|
2
|
+
module RSpec
|
3
|
+
class HaveApplied
|
4
|
+
def initialize(*expected)
|
5
|
+
raise ArgumentError if expected.empty?
|
6
|
+
@expected = expected
|
7
|
+
@matcher = ::RSpec::Matchers::BuiltIn::Include.new(*@expected)
|
8
|
+
end
|
9
|
+
|
10
|
+
def matches?(aggregate_root)
|
11
|
+
events = aggregate_root.__send__(:unpublished_events)
|
12
|
+
@matcher.matches?(events) && matches_count(events, @expected, @count)
|
13
|
+
end
|
14
|
+
|
15
|
+
def exactly(count)
|
16
|
+
@count = count
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def times
|
21
|
+
self
|
22
|
+
end
|
23
|
+
alias :time :times
|
24
|
+
|
25
|
+
def once
|
26
|
+
exactly(1)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def matches_count(events, expected, count)
|
32
|
+
return true unless count
|
33
|
+
raise NotSupported if expected.size > 1
|
34
|
+
|
35
|
+
expected.all? do |event_or_matcher|
|
36
|
+
events.select { |e| event_or_matcher === e }.size.equal?(count)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module RailsEventStore
|
2
|
+
module RSpec
|
3
|
+
class HavePublished
|
4
|
+
def initialize(*expected)
|
5
|
+
raise ArgumentError if expected.empty?
|
6
|
+
@expected = expected
|
7
|
+
@matcher = ::RSpec::Matchers::BuiltIn::Include.new(*@expected)
|
8
|
+
end
|
9
|
+
|
10
|
+
def matches?(event_store)
|
11
|
+
events = @stream_name ? event_store.read_events_backward(@stream_name)
|
12
|
+
: event_store.read_all_streams_backward
|
13
|
+
@matcher.matches?(events) && matches_count(events, @expected, @count)
|
14
|
+
end
|
15
|
+
|
16
|
+
def exactly(count)
|
17
|
+
@count = count
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def in_stream(stream_name)
|
22
|
+
@stream_name = stream_name
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def times
|
27
|
+
self
|
28
|
+
end
|
29
|
+
alias :time :times
|
30
|
+
|
31
|
+
def once
|
32
|
+
exactly(1)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def matches_count(events, expected, count)
|
38
|
+
return true unless count
|
39
|
+
raise NotSupported if expected.size > 1
|
40
|
+
|
41
|
+
expected.all? do |event_or_matcher|
|
42
|
+
events.select { |e| event_or_matcher === e }.size.equal?(count)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module RailsEventStore
|
2
|
+
module RSpec
|
3
|
+
module Matchers
|
4
|
+
def be_an_event(expected)
|
5
|
+
BeEvent.new(expected, differ: differ)
|
6
|
+
end
|
7
|
+
alias :be_event :be_an_event
|
8
|
+
alias :an_event :be_an_event
|
9
|
+
alias :event :be_an_event
|
10
|
+
|
11
|
+
def have_published(*expected)
|
12
|
+
HavePublished.new(*expected)
|
13
|
+
end
|
14
|
+
|
15
|
+
def have_applied(*expected)
|
16
|
+
HaveApplied.new(*expected)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def differ
|
22
|
+
::RSpec::Support::Differ.new(color: ::RSpec::Matchers.configuration.color?)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "rails_event_store/rspec/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rails_event_store-rspec"
|
8
|
+
spec.version = RailsEventStore::RSpec::VERSION
|
9
|
+
spec.authors = ["Arkency"]
|
10
|
+
spec.email = ["dev@arkency.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{RSpec matchers for RailsEventStore}
|
13
|
+
|
14
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
15
|
+
f.match(%r{^(test|spec|features)/})
|
16
|
+
end
|
17
|
+
spec.bindir = "exe"
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.15"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
24
|
+
spec.add_development_dependency "mutant-rspec", "~> 0.8.11"
|
25
|
+
spec.add_development_dependency "rails", "~> 4.2"
|
26
|
+
spec.add_development_dependency "rails_event_store", "~> 0.15.0"
|
27
|
+
|
28
|
+
spec.add_dependency "rspec", "~> 3.0"
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails_event_store-rspec
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Arkency
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-09-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.15'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.15'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: mutant-rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.8.11
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.8.11
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rails
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '4.2'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '4.2'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rails_event_store
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.15.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.15.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.0'
|
111
|
+
description:
|
112
|
+
email:
|
113
|
+
- dev@arkency.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".gitignore"
|
119
|
+
- ".rspec"
|
120
|
+
- ".travis.yml"
|
121
|
+
- Gemfile
|
122
|
+
- LICENSE
|
123
|
+
- Makefile
|
124
|
+
- README.md
|
125
|
+
- Rakefile
|
126
|
+
- bin/console
|
127
|
+
- bin/setup
|
128
|
+
- lib/rails_event_store/rspec.rb
|
129
|
+
- lib/rails_event_store/rspec/be_event.rb
|
130
|
+
- lib/rails_event_store/rspec/have_applied.rb
|
131
|
+
- lib/rails_event_store/rspec/have_published.rb
|
132
|
+
- lib/rails_event_store/rspec/matchers.rb
|
133
|
+
- lib/rails_event_store/rspec/version.rb
|
134
|
+
- rails_event_store-rspec.gemspec
|
135
|
+
homepage:
|
136
|
+
licenses: []
|
137
|
+
metadata: {}
|
138
|
+
post_install_message:
|
139
|
+
rdoc_options: []
|
140
|
+
require_paths:
|
141
|
+
- lib
|
142
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ">="
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
requirements: []
|
153
|
+
rubyforge_project:
|
154
|
+
rubygems_version: 2.6.11
|
155
|
+
signing_key:
|
156
|
+
specification_version: 4
|
157
|
+
summary: RSpec matchers for RailsEventStore
|
158
|
+
test_files: []
|