Skip to content

Commit 1fc1c16

Browse files
Merge pull request #370 from RailsEventStore/publish-matcher
Publish rspec matcher
2 parents d8f4511 + d74ed34 commit 1fc1c16

File tree

6 files changed

+295
-1
lines changed

6 files changed

+295
-1
lines changed

rails_event_store-rspec/Makefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ IGNORE = RailsEventStore::RSpec::Matchers\#differ \
55
RailsEventStore::RSpec::Matchers\#formatter \
66
RailsEventStore::RSpec::Matchers\#have_published \
77
RailsEventStore::RSpec::Matchers\#have_applied \
8-
RailsEventStore::RSpec::Matchers\#be_an_event
8+
RailsEventStore::RSpec::Matchers\#publish \
9+
RailsEventStore::RSpec::Matchers\#be_an_event \
10+
RailsEventStore::RSpec::Publish\#last_event
911
SUBJECT ?= RailsEventStore::RSpec*
1012

1113
install: ## Install gem dependencies

rails_event_store-rspec/lib/rails_event_store/rspec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module RSpec
1010
require "rails_event_store/rspec/be_event"
1111
require "rails_event_store/rspec/have_published"
1212
require "rails_event_store/rspec/have_applied"
13+
require "rails_event_store/rspec/publish"
1314
require "rails_event_store/rspec/matchers"
1415

1516
::RSpec.configure do |config|

rails_event_store-rspec/lib/rails_event_store/rspec/matchers.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ def have_applied(*expected)
3838
HaveApplied.new(*expected, differ: differ, phraser: phraser)
3939
end
4040

41+
def publish(*expected)
42+
Publish.new(*expected)
43+
end
44+
4145
private
4246

4347
def formatter
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
require 'rspec/matchers/built_in/base_matcher'
2+
3+
module RailsEventStore
4+
module RSpec
5+
class Publish
6+
def in(event_store)
7+
@event_store = event_store
8+
self
9+
end
10+
11+
def in_stream(stream)
12+
@stream = stream
13+
self
14+
end
15+
16+
def matches?(event_proc)
17+
raise_event_store_not_set unless @event_store
18+
spec = @event_store.read
19+
spec = spec.stream(@stream) if @stream
20+
last_event_before_block = last_event(spec)
21+
event_proc.call
22+
spec = spec.from(last_event_before_block.event_id) if last_event_before_block
23+
@published_events = spec.each.to_a
24+
if match_events?
25+
::RSpec::Matchers::BuiltIn::Include.new(*@expected).matches?(@published_events)
26+
else
27+
!@published_events.empty?
28+
end
29+
end
30+
31+
def failure_message
32+
if match_events?
33+
<<-EOS
34+
expected block to have published:
35+
36+
#{@expected}
37+
38+
#{"in stream #{@stream} " if @stream}but published:
39+
40+
#{@published_events}
41+
EOS
42+
else
43+
"expected block to have published any events"
44+
end
45+
end
46+
47+
def failure_message_when_negated
48+
if match_events?
49+
<<-EOS
50+
expected block not to have published:
51+
52+
#{@expected}
53+
54+
#{"in stream #{@stream} " if @stream}but published:
55+
56+
#{@published_events}
57+
EOS
58+
else
59+
"expected block not to have published any events"
60+
end
61+
end
62+
63+
def description
64+
"publish events"
65+
end
66+
67+
def supports_block_expectations?
68+
true
69+
end
70+
71+
private
72+
73+
def initialize(*expected)
74+
@expected = expected
75+
end
76+
77+
def match_events?
78+
!@expected.empty?
79+
end
80+
81+
def last_event(spec)
82+
spec.backward.limit(1).each.first
83+
end
84+
85+
def raise_event_store_not_set
86+
raise SyntaxError, "You have to set the event store instance with `in`, e.g. `expect { ... }.to publish(an_event(MyEvent)).in(event_store)`"
87+
end
88+
end
89+
end
90+
end

rails_event_store-rspec/spec/rails_event_store/rspec/matchers_spec.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ module RSpec
3434
expect(event_store).to matchers.have_published(matchers.an_event(FooEvent), matchers.an_event(BarEvent))
3535
end
3636

37+
specify do
38+
event_store = RailsEventStore::Client.new(repository: RailsEventStore::InMemoryRepository.new)
39+
event_store.publish_event(FooEvent.new)
40+
expect {
41+
event_store.publish_event(BarEvent.new)
42+
}.to publish(matchers.an_event(BarEvent)).in(event_store)
43+
end
44+
3745
specify { expect(matchers.have_applied(matchers.an_event(FooEvent))).to be_an(HaveApplied) }
3846

3947
specify do
@@ -60,6 +68,10 @@ module RSpec
6068
aggregate_root.bar
6169
expect(aggregate_root).to matchers.have_applied(matchers.an_event(FooEvent), matchers.an_event(BarEvent))
6270
end
71+
72+
specify do
73+
expect(matchers.publish).to be_a(Publish)
74+
end
6375
end
6476

6577
module Matchers
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
require "spec_helper"
2+
3+
module RailsEventStore
4+
module RSpec
5+
::RSpec.describe Publish do
6+
let(:matchers) { Object.new.tap { |o| o.extend(Matchers) } }
7+
let(:event_store) do
8+
RailsEventStore::Client.new(
9+
repository: RailsEventStore::InMemoryRepository.new,
10+
mapper: RubyEventStore::Mappers::NullMapper.new
11+
)
12+
end
13+
14+
def matcher(*expected)
15+
Publish.new(*expected)
16+
end
17+
18+
specify do
19+
expect {
20+
expect {
21+
true
22+
}.to matcher
23+
}.to raise_error(SyntaxError, "You have to set the event store instance with `in`, e.g. `expect { ... }.to publish(an_event(MyEvent)).in(event_store)`")
24+
end
25+
26+
specify do
27+
expect {
28+
true
29+
}.not_to matcher.in(event_store)
30+
end
31+
32+
specify do
33+
expect {
34+
event_store.publish_event(FooEvent.new)
35+
}.to matcher.in(event_store)
36+
end
37+
38+
specify do
39+
expect {
40+
event_store.publish_event(FooEvent.new, stream_name: 'Foo$1')
41+
}.to matcher.in(event_store).in_stream('Foo$1')
42+
end
43+
44+
specify do
45+
expect {
46+
event_store.publish_event(FooEvent.new, stream_name: 'Foo$1')
47+
}.not_to matcher.in(event_store).in_stream('Bar$1')
48+
end
49+
50+
specify do
51+
expect {
52+
event_store.publish_event(FooEvent.new)
53+
}.not_to matcher(matchers.an_event(BarEvent)).in(event_store)
54+
end
55+
56+
specify do
57+
expect {
58+
event_store.publish_event(FooEvent.new)
59+
}.to matcher(matchers.an_event(FooEvent)).in(event_store)
60+
end
61+
62+
specify do
63+
expect {
64+
event_store.publish_event(FooEvent.new, stream_name: "Foo$1")
65+
}.to matcher(matchers.an_event(FooEvent)).in(event_store).in_stream("Foo$1")
66+
end
67+
68+
specify do
69+
expect {
70+
event_store.publish_event(FooEvent.new)
71+
}.not_to matcher(matchers.an_event(FooEvent)).in(event_store).in_stream("Foo$1")
72+
end
73+
74+
specify do
75+
foo_event = FooEvent.new
76+
bar_event = BarEvent.new
77+
expect {
78+
event_store.publish_event(foo_event, stream_name: "Foo$1")
79+
event_store.publish_event(bar_event, stream_name: "Bar$1")
80+
}.to matcher(matchers.an_event(FooEvent), matchers.an_event(BarEvent)).in(event_store)
81+
end
82+
83+
specify do
84+
foo_event = FooEvent.new
85+
bar_event = BarEvent.new
86+
87+
event_store.publish_event(foo_event)
88+
expect {
89+
event_store.publish_event(bar_event)
90+
}.not_to matcher(matchers.an_event(FooEvent)).in(event_store)
91+
end
92+
93+
specify do
94+
expect {
95+
true
96+
}.not_to matcher.in(event_store)
97+
end
98+
99+
specify do
100+
_matcher = matcher.in(event_store)
101+
_matcher.matches?(Proc.new { })
102+
103+
expect(_matcher.failure_message_when_negated.to_s).to eq(<<~EOS.strip)
104+
expected block not to have published any events
105+
EOS
106+
end
107+
108+
specify do
109+
_matcher = matcher.in(event_store)
110+
_matcher.matches?(Proc.new { })
111+
112+
expect(_matcher.failure_message.to_s).to eq(<<~EOS.strip)
113+
expected block to have published any events
114+
EOS
115+
end
116+
117+
specify do
118+
_matcher = matcher(actual = matchers.an_event(FooEvent)).in(event_store)
119+
_matcher.matches?(Proc.new { })
120+
121+
expect(_matcher.failure_message.to_s).to eq(<<~EOS)
122+
expected block to have published:
123+
124+
#{[actual].inspect}
125+
126+
but published:
127+
128+
[]
129+
EOS
130+
end
131+
132+
specify do
133+
_matcher = matcher(actual = matchers.an_event(FooEvent)).in_stream('foo').in(event_store)
134+
_matcher.matches?(Proc.new { })
135+
136+
expect(_matcher.failure_message.to_s).to eq(<<~EOS)
137+
expected block to have published:
138+
139+
#{[actual].inspect}
140+
141+
in stream foo but published:
142+
143+
[]
144+
EOS
145+
end
146+
147+
specify do
148+
foo_event = FooEvent.new
149+
_matcher = matcher(actual = matchers.an_event(FooEvent)).in(event_store)
150+
_matcher.matches?(Proc.new { event_store.publish_event(foo_event) })
151+
152+
expect(_matcher.failure_message_when_negated.to_s).to eq(<<~EOS)
153+
expected block not to have published:
154+
155+
#{[actual].inspect}
156+
157+
but published:
158+
159+
#{[foo_event].inspect}
160+
EOS
161+
end
162+
163+
specify do
164+
foo_event = FooEvent.new
165+
_matcher = matcher(actual = matchers.an_event(FooEvent)).in_stream('foo').in(event_store)
166+
_matcher.matches?(Proc.new { event_store.publish_event(foo_event, stream_name: 'foo') })
167+
168+
expect(_matcher.failure_message_when_negated.to_s).to eq(<<~EOS)
169+
expected block not to have published:
170+
171+
#{[actual].inspect}
172+
173+
in stream foo but published:
174+
175+
#{[foo_event].inspect}
176+
EOS
177+
end
178+
179+
specify do
180+
_matcher = matcher
181+
expect(_matcher.description).to eq("publish events")
182+
end
183+
end
184+
end
185+
end

0 commit comments

Comments
 (0)