ruby_event_store-rspec 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e1362df53ba19bf0a2194492d627ab17cd22ee658c9fefa47b5c61c0b8eec562
4
- data.tar.gz: c665c377cf751768cb1ec6abd8f486231ae382c6dafaef36e8cf0aaf01bc3800
3
+ metadata.gz: 2824fc38cdb13ea93b60cc332617ae107281ca075102897cf3b3627b5455bd07
4
+ data.tar.gz: 2ad9ac851fb63a5707d679679a86aa294bc80754c054d4b1210005212cb395d9
5
5
  SHA512:
6
- metadata.gz: 94e802c2bbed41e35aa8f8a2507cc127afe2e853e2266d60a56ea240f98c09f357bf7c664e7489de95a3f69d0b4892cd7f50a1c261fe2e7c995b531b45518fe4
7
- data.tar.gz: bf4cc3ec24389daf273187565731d7dc460e2d2ba9621605dafbbce46b19df9324f27cc98deb8c749955e15b10a3c6dd8167f36ae07fbea0592a2c2b8e884261
6
+ metadata.gz: 6756a69b71fb2c42cb484988a85f365134026f7fc8a4f9dde87cbac934d9190234a2788790fb89a6f9588e4b3ff9aa4b799ca893524c8614482aa76ec592dc5c
7
+ data.tar.gz: 5f9c80aff6dffe7c16ec7da698e7f1d84af2554121d51b0bcefae0ff113fc785cf97162d811441a7e29ba375ad4fee208c263c2ba9ac6b8c56d5711189f896cd
@@ -8,14 +8,32 @@ module RubyEventStore
8
8
  end
9
9
  end
10
10
 
11
- require "ruby_event_store/rspec/version"
12
- require "ruby_event_store/rspec/be_event"
13
- require "ruby_event_store/rspec/have_published"
14
- require "ruby_event_store/rspec/have_applied"
15
- require "ruby_event_store/rspec/have_subscribed_to_events"
16
- require "ruby_event_store/rspec/publish"
17
- require "ruby_event_store/rspec/apply"
18
- require "ruby_event_store/rspec/matchers"
11
+ require_relative "rspec/version"
12
+ require_relative "rspec/be_event"
13
+ require_relative "rspec/expected_collection"
14
+ require_relative "rspec/fetch_events"
15
+ require_relative "rspec/fetch_unpublished_events"
16
+ require_relative "rspec/match_events"
17
+ require_relative "rspec/have_published"
18
+ require_relative "rspec/have_applied"
19
+ require_relative "rspec/have_subscribed_to_events"
20
+ require_relative "rspec/publish"
21
+ require_relative "rspec/apply"
22
+ require_relative "rspec/crude_failure_message_formatter"
23
+ require_relative "rspec/step_by_step_failure_message_formatter"
24
+ require_relative "rspec/matchers"
25
+
26
+ module RubyEventStore
27
+ module RSpec
28
+ def self.default_formatter=(new_formatter)
29
+ @@default_formatter = new_formatter
30
+ end
31
+
32
+ def self.default_formatter
33
+ @@default_formatter ||= CrudeFailureMessageFormatter.new
34
+ end
35
+ end
36
+ end
19
37
 
20
38
  ::RSpec.configure do |config|
21
39
  config.include ::RubyEventStore::RSpec::Matchers
@@ -3,58 +3,51 @@
3
3
  module RubyEventStore
4
4
  module RSpec
5
5
  class Apply
6
+ def initialize(*expected, failure_message_formatter:)
7
+ @expected = ExpectedCollection.new(expected)
8
+ @failure_message_formatter = failure_message_formatter
9
+ @fetch_events = FetchUnpublishedEvents.new
10
+ end
11
+
6
12
  def in(aggregate)
7
- @aggregate = aggregate
13
+ fetch_events.in(aggregate)
8
14
  self
9
15
  end
10
16
 
11
17
  def strict
12
- @matcher = ::RSpec::Matchers::BuiltIn::Match.new(@expected)
18
+ expected.strict
13
19
  self
14
20
  end
15
21
 
16
- def matches?(event_proc)
17
- raise_aggregate_not_set unless @aggregate
18
- before = @aggregate.unpublished_events.to_a
19
- event_proc.call
20
- @applied_events = @aggregate.unpublished_events.to_a - before
21
- if match_events?
22
- @matcher.matches?(@applied_events)
23
- else
24
- !@applied_events.empty?
25
- end
22
+ def exactly(count)
23
+ expected.exactly(count)
24
+ self
26
25
  end
27
26
 
28
- def failure_message
29
- if match_events?
30
- <<-EOS
31
- expected block to have applied:
27
+ def times
28
+ self
29
+ end
30
+ alias :time :times
32
31
 
33
- #{@expected}
32
+ def once
33
+ expected.once
34
+ self
35
+ end
34
36
 
35
- but applied:
37
+ def matches?(event_proc)
38
+ raise_aggregate_not_set unless fetch_events.aggregate?
39
+ before = fetch_events.aggregate.unpublished_events.to_a
40
+ event_proc.call
41
+ @applied_events = fetch_events.aggregate.unpublished_events.to_a - before
42
+ MatchEvents.new.call(expected, applied_events)
43
+ end
36
44
 
37
- #{@applied_events}
38
- EOS
39
- else
40
- "expected block to have applied any events"
41
- end
45
+ def failure_message
46
+ failure_message_formatter.failure_message(expected, applied_events)
42
47
  end
43
48
 
44
49
  def failure_message_when_negated
45
- if match_events?
46
- <<-EOS
47
- expected block not to have applied:
48
-
49
- #{@expected}
50
-
51
- but applied:
52
-
53
- #{@applied_events}
54
- EOS
55
- else
56
- "expected block not to have applied any events"
57
- end
50
+ failure_message_formatter.failure_message_when_negated(expected, applied_events)
58
51
  end
59
52
 
60
53
  def description
@@ -67,18 +60,11 @@ EOS
67
60
 
68
61
  private
69
62
 
70
- def initialize(*expected)
71
- @expected = expected
72
- @matcher = ::RSpec::Matchers::BuiltIn::Include.new(*expected)
73
- end
74
-
75
- def match_events?
76
- !@expected.empty?
77
- end
78
-
79
63
  def raise_aggregate_not_set
80
- raise SyntaxError, "You have to set the aggregate instance with `in`, e.g. `expect { ... }.to apply(an_event(MyEvent)).in(aggregate)`"
64
+ raise "You have to set the aggregate instance with `in`, e.g. `expect { ... }.to apply(an_event(MyEvent)).in(aggregate)`"
81
65
  end
66
+
67
+ attr_reader :expected, :applied_events, :failure_message_formatter, :fetch_events
82
68
  end
83
69
  end
84
70
  end
@@ -90,7 +90,7 @@ module RubyEventStore
90
90
  end
91
91
 
92
92
  def to_s
93
- @expected && ["\n#{@label} diff:", @differ.diff_as_string(@actual.to_s, @expected.to_s)]
93
+ @expected && ["\n#{@label} diff:", @differ.diff(@actual.to_s + "\n", @expected.to_s)]
94
94
  end
95
95
  end
96
96
 
@@ -124,7 +124,7 @@ module RubyEventStore
124
124
 
125
125
  def matches?(actual)
126
126
  @actual = actual
127
- matches_kind && matches_data && matches_metadata
127
+ matches_kind?(actual) && matches_data?(actual) && matches_metadata?(actual)
128
128
  end
129
129
 
130
130
  def with_data(expected_data)
@@ -167,25 +167,27 @@ expected: not a kind of #{expected}
167
167
  " (#{expectation_list.join(" and ")})" if expectation_list.any?
168
168
  end
169
169
 
170
- private
170
+ attr_reader :expected, :expected_data, :expected_metadata
171
171
 
172
- def matches_kind
173
- KindMatcher.new(expected).matches?(actual)
172
+ def strict?
173
+ @strict
174
174
  end
175
175
 
176
- def matches_data
177
- DataMatcher.new(expected_data, strict: strict?).matches?(actual.data)
176
+ def matches_kind?(actual_event)
177
+ KindMatcher.new(expected).matches?(actual_event)
178
178
  end
179
179
 
180
- def matches_metadata
181
- DataMatcher.new(expected_metadata, strict: strict?).matches?(actual.metadata.to_h)
182
- end
180
+ private
183
181
 
184
- attr_reader :expected_metadata, :expected_data, :actual, :expected, :differ, :formatter
182
+ def matches_data?(actual_event)
183
+ DataMatcher.new(expected_data, strict: strict?).matches?(actual_event.data)
184
+ end
185
185
 
186
- def strict?
187
- @strict
186
+ def matches_metadata?(actual_event)
187
+ DataMatcher.new(expected_metadata, strict: strict?).matches?(actual_event.metadata.to_h)
188
188
  end
189
+
190
+ attr_reader :actual, :differ, :formatter
189
191
  end
190
192
  end
191
193
  end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyEventStore
4
+ module RSpec
5
+ class CrudeFailureMessageFormatter
6
+ class HavePublished
7
+ def initialize(differ)
8
+ @differ = differ
9
+ end
10
+
11
+ def failure_message(expected, events, stream_name)
12
+ stream_expectation = " in stream #{stream_name}" unless stream_name.nil?
13
+ "expected #{expected.events} to be published#{stream_expectation}, diff:" +
14
+ differ.diff(expected.events.to_s + "\n", events)
15
+ end
16
+
17
+ def failure_message_when_negated(expected, events, stream_name)
18
+ stream_expectation = " in stream #{stream_name}" unless stream_name.nil?
19
+ "expected #{expected.events} not to be published#{stream_expectation}, diff:" +
20
+ differ.diff(expected.events.to_s + "\n", events)
21
+ end
22
+
23
+ private
24
+ attr_reader :differ
25
+ end
26
+
27
+ class Publish
28
+ def failure_message(expected, events, stream)
29
+ if !expected.empty?
30
+ <<~EOS
31
+ expected block to have published:
32
+
33
+ #{expected.events}
34
+
35
+ #{"in stream #{stream} " if stream}but published:
36
+
37
+ #{events}
38
+ EOS
39
+ else
40
+ "expected block to have published any events"
41
+ end
42
+ end
43
+
44
+ def failure_message_when_negated(expected, events, stream)
45
+ if !expected.empty?
46
+ <<~EOS
47
+ expected block not to have published:
48
+
49
+ #{expected.events}
50
+
51
+ #{"in stream #{stream} " if stream}but published:
52
+
53
+ #{events}
54
+ EOS
55
+ else
56
+ "expected block not to have published any events"
57
+ end
58
+ end
59
+ end
60
+
61
+ class HaveApplied
62
+ def initialize(differ)
63
+ @differ = differ
64
+ end
65
+
66
+ def failure_message(expected, events)
67
+ "expected #{expected.events} to be applied, diff:" +
68
+ differ.diff(expected.events.to_s + "\n", events)
69
+ end
70
+
71
+ def failure_message_when_negated(expected, events)
72
+ "expected #{expected.events} not to be applied, diff:" +
73
+ differ.diff(expected.events.inspect + "\n", events)
74
+ end
75
+
76
+ attr_reader :differ
77
+ end
78
+
79
+ class Apply
80
+ def failure_message(expected, applied_events)
81
+ if !expected.empty?
82
+ <<~EOS
83
+ expected block to have applied:
84
+
85
+ #{expected.events}
86
+
87
+ but applied:
88
+
89
+ #{applied_events}
90
+ EOS
91
+ else
92
+ "expected block to have applied any events"
93
+ end
94
+ end
95
+
96
+ def failure_message_when_negated(expected, applied_events)
97
+ if !expected.empty?
98
+ <<~EOS
99
+ expected block not to have applied:
100
+
101
+ #{expected.events}
102
+
103
+ but applied:
104
+
105
+ #{applied_events}
106
+ EOS
107
+ else
108
+ "expected block not to have applied any events"
109
+ end
110
+ end
111
+ end
112
+
113
+ def have_published(differ)
114
+ HavePublished.new(differ)
115
+ end
116
+
117
+ def publish(_differ)
118
+ Publish.new
119
+ end
120
+
121
+ def have_applied(differ)
122
+ HaveApplied.new(differ)
123
+ end
124
+
125
+ def apply(_differ)
126
+ Apply.new
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,43 @@
1
+ module RubyEventStore
2
+ module RSpec
3
+ class ExpectedCollection
4
+ def initialize(events)
5
+ @events = events
6
+ @strict = false
7
+ end
8
+
9
+ def exactly(count)
10
+ raise NotSupported if !events.size.equal?(1)
11
+ raise NotSupported if count < 1
12
+ @count = count
13
+ end
14
+
15
+ def empty?
16
+ events.empty?
17
+ end
18
+
19
+ def once
20
+ exactly(1)
21
+ end
22
+
23
+ def specified_count?
24
+ !count.nil?
25
+ end
26
+
27
+ def strict
28
+ @strict = true
29
+ end
30
+
31
+ def strict?
32
+ @strict
33
+ end
34
+
35
+ def event
36
+ raise NotSupported if !events.size.equal?(1)
37
+ events.first
38
+ end
39
+
40
+ attr_reader :events, :count
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,33 @@
1
+ module RubyEventStore
2
+ module RSpec
3
+ class FetchEvents
4
+ MissingEventStore = Class.new(StandardError)
5
+
6
+ def from(event_id)
7
+ @start = event_id
8
+ end
9
+
10
+ def stream(stream_name)
11
+ @stream_name = stream_name
12
+ end
13
+
14
+ def in(event_store)
15
+ @event_store = event_store
16
+ end
17
+
18
+ def from_last
19
+ @start = call.last&.event_id
20
+ end
21
+
22
+ def call
23
+ raise MissingEventStore if event_store.nil?
24
+ events = event_store.read
25
+ events = events.stream(stream_name) if stream_name
26
+ events = events.from(start) if start
27
+ events
28
+ end
29
+
30
+ attr_reader :start, :stream_name, :event_store
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,19 @@
1
+ module RubyEventStore
2
+ module RSpec
3
+ class FetchUnpublishedEvents
4
+ def in(aggregate)
5
+ @aggregate = aggregate
6
+ end
7
+
8
+ def call
9
+ aggregate.unpublished_events.to_a
10
+ end
11
+
12
+ def aggregate?
13
+ !aggregate.nil?
14
+ end
15
+
16
+ attr_reader :aggregate
17
+ end
18
+ end
19
+ end
@@ -3,20 +3,21 @@
3
3
  module RubyEventStore
4
4
  module RSpec
5
5
  class HaveApplied
6
- def initialize(mandatory_expected, *optional_expected, differ:, phraser:)
7
- @expected = [mandatory_expected, *optional_expected]
8
- @matcher = ::RSpec::Matchers::BuiltIn::Include.new(*expected)
9
- @differ = differ
6
+ def initialize(*expected, phraser:, failure_message_formatter:)
7
+ @expected = ExpectedCollection.new(expected)
8
+ @failure_message_formatter = failure_message_formatter
10
9
  @phraser = phraser
10
+ @fetch_events = FetchUnpublishedEvents.new
11
11
  end
12
12
 
13
13
  def matches?(aggregate_root)
14
- @events = aggregate_root.unpublished_events.to_a
15
- matcher.matches?(events) && matches_count?
14
+ fetch_events.in(aggregate_root)
15
+ @events = fetch_events.call
16
+ MatchEvents.new.call(expected, events)
16
17
  end
17
18
 
18
19
  def exactly(count)
19
- @count = count
20
+ expected.exactly(count)
20
21
  self
21
22
  end
22
23
 
@@ -26,38 +27,30 @@ module RubyEventStore
26
27
  alias :time :times
27
28
 
28
29
  def once
29
- exactly(1)
30
+ expected.once
31
+ self
30
32
  end
31
33
 
32
34
  def strict
33
- @matcher = ::RSpec::Matchers::BuiltIn::Match.new(expected)
35
+ expected.strict
34
36
  self
35
37
  end
36
38
 
37
39
  def failure_message
38
- "expected #{expected} to be applied, diff:" +
39
- differ.diff_as_string(expected.to_s, events.to_s)
40
+ failure_message_formatter.failure_message(expected, events)
40
41
  end
41
42
 
42
43
  def failure_message_when_negated
43
- "expected #{expected} not to be applied, diff:" +
44
- differ.diff_as_string(expected.inspect, events.inspect)
44
+ failure_message_formatter.failure_message_when_negated(expected, events)
45
45
  end
46
46
 
47
47
  def description
48
- "have applied events that have to (#{phraser.(expected)})"
48
+ "have applied events that have to (#{phraser.(expected.events)})"
49
49
  end
50
50
 
51
51
  private
52
52
 
53
- def matches_count?
54
- return true unless count
55
- raise NotSupported if expected.size > 1
56
- events.select { |e| expected.first === e }.size.equal?(count)
57
- end
58
-
59
- attr_reader :differ, :phraser, :expected, :events, :count, :matcher
53
+ attr_reader :phraser, :expected, :events, :failure_message_formatter, :fetch_events
60
54
  end
61
55
  end
62
56
  end
63
-
@@ -3,28 +3,35 @@
3
3
  module RubyEventStore
4
4
  module RSpec
5
5
  class HavePublished
6
- def initialize(mandatory_expected, *optional_expected, differ:, phraser:)
7
- @expected = [mandatory_expected, *optional_expected]
8
- @matcher = ::RSpec::Matchers::BuiltIn::Include.new(*expected)
9
- @differ = differ
6
+ def initialize(*expected, phraser:, failure_message_formatter:)
7
+ @expected = ExpectedCollection.new(expected)
10
8
  @phraser = phraser
9
+ @failure_message_formatter = failure_message_formatter
10
+ @fetch_events = FetchEvents.new
11
11
  end
12
12
 
13
13
  def matches?(event_store)
14
- @events = event_store.read
15
- @events = events.stream(stream_name) if stream_name
16
- @events = events.from(start) if start
17
- @events = events.each
18
- @matcher.matches?(events) && matches_count?
14
+ stream_names.all? do |stream_name|
15
+ fetch_events.stream(stream_name)
16
+ fetch_events.in(event_store)
17
+ @published_events = fetch_events.call.to_a
18
+ @failed_on_stream = stream_name
19
+ MatchEvents.new.call(expected, published_events)
20
+ end
19
21
  end
20
22
 
21
23
  def exactly(count)
22
- @count = count
24
+ expected.exactly(count)
23
25
  self
24
26
  end
25
27
 
26
28
  def in_stream(stream_name)
27
- @stream_name = stream_name
29
+ @stream_names = [stream_name]
30
+ self
31
+ end
32
+
33
+ def in_streams(stream_names)
34
+ @stream_names = Array(stream_names)
28
35
  self
29
36
  end
30
37
 
@@ -34,42 +41,39 @@ module RubyEventStore
34
41
  alias :time :times
35
42
 
36
43
  def from(event_id)
37
- @start = event_id
44
+ fetch_events.from(event_id)
38
45
  self
39
46
  end
40
47
 
41
48
  def once
42
- exactly(1)
49
+ expected.once
50
+ self
43
51
  end
44
52
 
45
53
  def failure_message
46
- "expected #{expected} to be published, diff:" +
47
- differ.diff_as_string(expected.to_s, events.to_a.to_s)
54
+ failure_message_formatter.failure_message(expected, published_events, failed_on_stream)
48
55
  end
49
56
 
50
57
  def failure_message_when_negated
51
- "expected #{expected} not to be published, diff:" +
52
- differ.diff_as_string(expected.to_s, events.to_a.to_s)
58
+ failure_message_formatter.failure_message_when_negated(expected, published_events, failed_on_stream)
53
59
  end
54
60
 
55
61
  def description
56
- "have published events that have to (#{phraser.(expected)})"
62
+ "have published events that have to (#{phraser.(expected.events)})"
57
63
  end
58
64
 
59
65
  def strict
60
- @matcher = ::RSpec::Matchers::BuiltIn::Match.new(expected)
66
+ expected.strict
61
67
  self
62
68
  end
63
69
 
64
70
  private
65
71
 
66
- def matches_count?
67
- return true unless count
68
- raise NotSupported if expected.size > 1
69
- events.select { |e| expected.first === e }.size.equal?(count)
72
+ def stream_names
73
+ @stream_names || [nil]
70
74
  end
71
75
 
72
- attr_reader :differ, :phraser, :stream_name, :expected, :count, :events, :start
76
+ attr_reader :phraser, :expected, :published_events, :failed_on_stream, :failure_message_formatter, :fetch_events
73
77
  end
74
78
  end
75
79
  end
@@ -26,12 +26,12 @@ module RubyEventStore
26
26
 
27
27
  def failure_message
28
28
  "expected #{handler} to be subscribed to events, diff:" +
29
- differ.diff_as_string(expected.to_s, subscribed_to.to_s)
29
+ differ.diff(expected.to_s + "\n", subscribed_to)
30
30
  end
31
31
 
32
32
  def failure_message_when_negated
33
33
  "expected #{handler} not to be subscribed to events, diff:" +
34
- differ.diff_as_string(expected.to_s, subscribed_to.to_s)
34
+ differ.diff(expected.to_s + "\n", subscribed_to)
35
35
  end
36
36
 
37
37
  def description
@@ -0,0 +1,32 @@
1
+ module RubyEventStore
2
+ module RSpec
3
+ class MatchEvents
4
+ def call(expected, events)
5
+ if match_events?(expected)
6
+ matcher(expected).matches?(events) && matches_count?(expected, events)
7
+ else
8
+ !events.empty?
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ def matches_count?(expected, events)
15
+ return true unless expected.specified_count?
16
+ events.select { |e| expected.event === e }.size.equal?(expected.count)
17
+ end
18
+
19
+ def matcher(expected)
20
+ if expected.strict?
21
+ ::RSpec::Matchers::BuiltIn::Match.new(expected.events)
22
+ else
23
+ ::RSpec::Matchers::BuiltIn::Include.new(*expected.events)
24
+ end
25
+ end
26
+
27
+ def match_events?(expected)
28
+ !expected.empty?
29
+ end
30
+ end
31
+ end
32
+ end
@@ -39,11 +39,11 @@ module RubyEventStore
39
39
  alias :event :be_an_event
40
40
 
41
41
  def have_published(*expected)
42
- HavePublished.new(*expected, differ: differ, phraser: phraser)
42
+ HavePublished.new(*expected, phraser: phraser, failure_message_formatter: RSpec.default_formatter.have_published(differ))
43
43
  end
44
44
 
45
45
  def have_applied(*expected)
46
- HaveApplied.new(*expected, differ: differ, phraser: phraser)
46
+ HaveApplied.new(*expected, phraser: phraser, failure_message_formatter: RSpec.default_formatter.have_applied(differ))
47
47
  end
48
48
 
49
49
  def have_subscribed_to_events(*expected)
@@ -51,11 +51,11 @@ module RubyEventStore
51
51
  end
52
52
 
53
53
  def publish(*expected)
54
- Publish.new(*expected)
54
+ Publish.new(*expected, failure_message_formatter: RSpec.default_formatter.publish(differ))
55
55
  end
56
56
 
57
57
  def apply(*expected)
58
- Apply.new(*expected)
58
+ Apply.new(*expected, failure_message_formatter: RSpec.default_formatter.apply(differ))
59
59
  end
60
60
 
61
61
  private
@@ -65,7 +65,7 @@ module RubyEventStore
65
65
  end
66
66
 
67
67
  def differ
68
- ::RSpec::Support::Differ.new(color: ::RSpec::Matchers.configuration.color?)
68
+ ::RSpec::Expectations.differ
69
69
  end
70
70
 
71
71
  def phraser
@@ -3,61 +3,66 @@
3
3
  module RubyEventStore
4
4
  module RSpec
5
5
  class Publish
6
+ def initialize(*expected, failure_message_formatter:)
7
+ @expected = ExpectedCollection.new(expected)
8
+ @failure_message_formatter = failure_message_formatter
9
+ @fetch_events = FetchEvents.new
10
+ end
11
+
6
12
  def in(event_store)
7
- @event_store = event_store
13
+ fetch_events.in(event_store)
8
14
  self
9
15
  end
10
16
 
11
- def in_stream(stream)
12
- @stream = stream
17
+ def in_stream(stream_name)
18
+ @stream_names = [stream_name]
13
19
  self
14
20
  end
15
21
 
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 = spec.last
21
- event_proc.call
22
- spec = spec.from(last_event_before_block.event_id) if last_event_before_block
23
- @published_events = spec.to_a
24
- if match_events?
25
- ::RSpec::Matchers::BuiltIn::Include.new(*@expected).matches?(@published_events)
26
- else
27
- !@published_events.empty?
28
- end
22
+ def in_streams(stream_names)
23
+ @stream_names = Array(stream_names)
24
+ self
29
25
  end
30
26
 
31
- def failure_message
32
- if match_events?
33
- <<-EOS
34
- expected block to have published:
35
-
36
- #{@expected}
27
+ def exactly(count)
28
+ expected.exactly(count)
29
+ self
30
+ end
37
31
 
38
- #{"in stream #{@stream} " if @stream}but published:
32
+ def once
33
+ expected.once
34
+ self
35
+ end
39
36
 
40
- #{@published_events}
41
- EOS
42
- else
43
- "expected block to have published any events"
44
- end
37
+ def times
38
+ self
45
39
  end
40
+ alias :time :times
46
41
 
47
- def failure_message_when_negated
48
- if match_events?
49
- <<-EOS
50
- expected block not to have published:
42
+ def strict
43
+ expected.strict
44
+ self
45
+ end
51
46
 
52
- #{@expected}
47
+ def matches?(event_proc)
48
+ fetch_events.from_last
49
+ event_proc.call
50
+ stream_names.all? do |stream_name|
51
+ fetch_events.stream(stream_name)
52
+ @published_events = fetch_events.call.to_a
53
+ @failed_on_stream = stream_name
54
+ MatchEvents.new.call(expected, published_events)
55
+ end
56
+ rescue FetchEvents::MissingEventStore
57
+ raise "You have to set the event store instance with `in`, e.g. `expect { ... }.to publish(an_event(MyEvent)).in(event_store)`"
58
+ end
53
59
 
54
- #{"in stream #{@stream} " if @stream}but published:
60
+ def failure_message
61
+ failure_message_formatter.failure_message(expected, published_events, failed_on_stream)
62
+ end
55
63
 
56
- #{@published_events}
57
- EOS
58
- else
59
- "expected block not to have published any events"
60
- end
64
+ def failure_message_when_negated
65
+ failure_message_formatter.failure_message_when_negated(expected, published_events, fetch_events.stream_name)
61
66
  end
62
67
 
63
68
  def description
@@ -70,17 +75,11 @@ EOS
70
75
 
71
76
  private
72
77
 
73
- def initialize(*expected)
74
- @expected = expected
78
+ def stream_names
79
+ @stream_names || [nil]
75
80
  end
76
81
 
77
- def match_events?
78
- !@expected.empty?
79
- end
80
-
81
- def raise_event_store_not_set
82
- raise SyntaxError, "You have to set the event store instance with `in`, e.g. `expect { ... }.to publish(an_event(MyEvent)).in(event_store)`"
83
- end
82
+ attr_reader :fetch_events, :expected, :failure_message_formatter, :published_events, :failed_on_stream
84
83
  end
85
84
  end
86
85
  end
@@ -0,0 +1,218 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyEventStore
4
+ module RSpec
5
+ class StepByStepFailureMessageFormatter
6
+ class Lingo
7
+ def initialize(published)
8
+ @published = published
9
+ end
10
+ attr_reader :published
11
+ end
12
+
13
+ class HavePublished
14
+ def initialize(differ, lingo)
15
+ @differ = differ
16
+ @lingo = lingo
17
+ end
18
+
19
+ def failure_message(expected, events, stream_name = nil)
20
+ return failure_message_strict(expected, events) if expected.strict?
21
+ return failure_message_no_events if expected.empty?
22
+ expected.events.each do |expected_event|
23
+ correct_event_count = 0
24
+ events_with_correct_type = []
25
+ events.each do |actual_event|
26
+ if expected_event.matches?(actual_event)
27
+ correct_event_count += 1
28
+ elsif expected_event.matches_kind?(actual_event)
29
+ events_with_correct_type << actual_event
30
+ end
31
+ end
32
+
33
+ expectations = expected_message(expected, expected_event, stream_name)
34
+
35
+ if expected.specified_count?
36
+ if correct_event_count >= 1
37
+ reality = failure_message_incorrect_count(expected_event, events_with_correct_type, correct_event_count)
38
+ elsif !events_with_correct_type.empty?
39
+ reality = failure_message_correct_type_incorrect_payload(expected_event, events_with_correct_type)
40
+ else
41
+ reality = failure_message_incorrect_type
42
+ end
43
+ else
44
+ if correct_event_count >= 1
45
+ next
46
+ else
47
+ if !events_with_correct_type.empty?
48
+ reality = failure_message_correct_type_incorrect_payload(expected_event, events_with_correct_type)
49
+ else
50
+ reality = failure_message_incorrect_type
51
+ end
52
+ end
53
+ end
54
+
55
+ return (expectations + reality)
56
+ end
57
+ end
58
+
59
+ def failure_message_when_negated(expected, events, _stream_name = nil)
60
+ return failure_message_when_negated_no_events if expected.empty?
61
+ if expected.specified_count?
62
+ <<~EOS
63
+ expected
64
+ #{expected.events.first.description}
65
+ not to be #{lingo.published} exactly #{expected.count} times
66
+
67
+ #{actual_events_list(events)}
68
+ EOS
69
+ else
70
+ <<~EOS
71
+ expected #{expected_events_list(expected.events)} not to #{"exactly " if expected.strict?}be #{lingo.published}
72
+
73
+ #{actual_events_list(events)}
74
+ EOS
75
+ end
76
+ end
77
+
78
+ private
79
+ attr_reader :differ, :lingo
80
+
81
+ def failure_message_no_events
82
+ "expected anything to be #{lingo.published}\n"
83
+ end
84
+
85
+ def failure_message_when_negated_no_events
86
+ "expected something to be #{lingo.published}\n"
87
+ end
88
+
89
+ def failure_message_incorrect_count(expected_event, events_with_correct_type, correct_event_count)
90
+ [
91
+ <<~EOS,
92
+
93
+ but was #{lingo.published} #{correct_event_count} times
94
+ EOS
95
+
96
+ if !events_with_correct_type.empty?
97
+ [
98
+ <<~EOS.strip,
99
+ There are events of correct type but with incorrect payload:
100
+ EOS
101
+ events_with_correct_type.each_with_index.map {|event_with_correct_type, index| event_diff(expected_event, event_with_correct_type, index) },
102
+ nil
103
+ ]
104
+ end
105
+ ].compact.join("\n")
106
+ end
107
+
108
+ def failure_message_correct_type_incorrect_payload(expected_event, events_with_correct_type)
109
+ <<~EOS
110
+ , but it was not #{lingo.published}
111
+
112
+ There are events of correct type but with incorrect payload:
113
+ #{events_with_correct_type.each_with_index.map {|event_with_correct_type, index| event_diff(expected_event, event_with_correct_type, index) }.join("\n")}
114
+ EOS
115
+ end
116
+
117
+ def event_diff(expected_event, event_with_correct_type, index)
118
+ [
119
+ "#{index + 1}) #{event_with_correct_type.inspect}",
120
+ indent(data_diff(expected_event, event_with_correct_type), 4),
121
+ indent(metadata_diff(expected_event, event_with_correct_type), 4),
122
+ ].reject(&:empty?)
123
+ end
124
+
125
+ def indent(str, count)
126
+ str.to_s.split("\n").map {|l| l.sub(//, " " * count) }
127
+ end
128
+
129
+ def failure_message_incorrect_type
130
+ <<~EOS
131
+ , but there is no event with such type
132
+ EOS
133
+ end
134
+
135
+ def failure_message_strict(expected, events)
136
+ if expected.specified_count?
137
+ <<~EOS
138
+ expected only
139
+ #{expected.events.first.description}
140
+ to be #{lingo.published} #{expected.count} times
141
+
142
+ #{actual_events_list(events)}
143
+ EOS
144
+ else
145
+ <<~EOS
146
+ expected only #{expected_events_list(expected.events)} to be #{lingo.published}
147
+
148
+ #{actual_events_list(events)}
149
+ EOS
150
+ end
151
+ end
152
+
153
+ def data_diff(expected_event, event_with_correct_type)
154
+ if !expected_event.expected_data.nil?
155
+ "data diff:#{differ.diff(expected_event.expected_data, event_with_correct_type.data)}"
156
+ end
157
+ end
158
+
159
+ def metadata_diff(expected_event, event_with_correct_type)
160
+ if !expected_event.expected_metadata.nil?
161
+ "metadata diff:#{differ.diff(expected_event.expected_metadata, event_with_correct_type.metadata.to_h)}"
162
+ end
163
+ end
164
+
165
+ def expected_message(expected, expected_event, stream_name)
166
+ expected_stream = " in stream #{stream_name}" if stream_name
167
+ if expected.specified_count?
168
+ <<~EOS
169
+ expected event
170
+ #{expected_event.description}
171
+ to be #{lingo.published} #{expected.count} times#{expected_stream}
172
+ EOS
173
+ else
174
+ <<~EOS
175
+ expected #{expected_events_list(expected.events)} to be #{lingo.published}#{expected_stream}
176
+
177
+ i.e. expected event
178
+ #{expected_event.description}
179
+ to be #{lingo.published}
180
+ EOS
181
+ end.strip
182
+ end
183
+
184
+ def expected_events_list(expected)
185
+ <<~EOS.strip
186
+ [
187
+ #{expected.map(&:description).map {|d| indent(d, 2) }.join("\n")}
188
+ ]
189
+ EOS
190
+ end
191
+
192
+ def actual_events_list(actual)
193
+ <<~EOS.strip
194
+ but the following was #{lingo.published}: [
195
+ #{actual.map(&:inspect).map {|d| indent(d, 2) }.join("\n")}
196
+ ]
197
+ EOS
198
+ end
199
+ end
200
+
201
+ def have_published(differ)
202
+ HavePublished.new(differ, Lingo.new("published"))
203
+ end
204
+
205
+ def publish(differ)
206
+ HavePublished.new(differ, Lingo.new("published"))
207
+ end
208
+
209
+ def have_applied(differ)
210
+ HavePublished.new(differ, Lingo.new("applied"))
211
+ end
212
+
213
+ def apply(differ)
214
+ HavePublished.new(differ, Lingo.new("applied"))
215
+ end
216
+ end
217
+ end
218
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RubyEventStore
4
4
  module RSpec
5
- VERSION = "2.1.0"
5
+ VERSION = "2.2.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_event_store-rspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arkency
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-07 00:00:00.000000000 Z
11
+ date: 2021-04-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -35,11 +35,17 @@ files:
35
35
  - lib/ruby_event_store/rspec.rb
36
36
  - lib/ruby_event_store/rspec/apply.rb
37
37
  - lib/ruby_event_store/rspec/be_event.rb
38
+ - lib/ruby_event_store/rspec/crude_failure_message_formatter.rb
39
+ - lib/ruby_event_store/rspec/expected_collection.rb
40
+ - lib/ruby_event_store/rspec/fetch_events.rb
41
+ - lib/ruby_event_store/rspec/fetch_unpublished_events.rb
38
42
  - lib/ruby_event_store/rspec/have_applied.rb
39
43
  - lib/ruby_event_store/rspec/have_published.rb
40
44
  - lib/ruby_event_store/rspec/have_subscribed_to_events.rb
45
+ - lib/ruby_event_store/rspec/match_events.rb
41
46
  - lib/ruby_event_store/rspec/matchers.rb
42
47
  - lib/ruby_event_store/rspec/publish.rb
48
+ - lib/ruby_event_store/rspec/step_by_step_failure_message_formatter.rb
43
49
  - lib/ruby_event_store/rspec/version.rb
44
50
  homepage: https://railseventstore.org
45
51
  licenses:
@@ -57,7 +63,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
57
63
  requirements:
58
64
  - - ">="
59
65
  - !ruby/object:Gem::Version
60
- version: '2.5'
66
+ version: '2.6'
61
67
  required_rubygems_version: !ruby/object:Gem::Requirement
62
68
  requirements:
63
69
  - - ">="