dry-transaction 0.10.2 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +1 -1
  3. data/Gemfile.lock +20 -14
  4. data/lib/dry/transaction/builder.rb +2 -4
  5. data/lib/dry/transaction/callable.rb +38 -0
  6. data/lib/dry/transaction/errors.rb +27 -0
  7. data/lib/dry/transaction/instance_methods.rb +22 -5
  8. data/lib/dry/transaction/operation.rb +4 -4
  9. data/lib/dry/transaction/operation_resolver.rb +5 -1
  10. data/lib/dry/transaction/result_matcher.rb +3 -3
  11. data/lib/dry/transaction/stack.rb +24 -0
  12. data/lib/dry/transaction/step.rb +37 -21
  13. data/lib/dry/transaction/step_adapter.rb +49 -0
  14. data/lib/dry/transaction/step_adapters/around.rb +25 -0
  15. data/lib/dry/transaction/step_adapters/check.rb +18 -0
  16. data/lib/dry/transaction/step_adapters/map.rb +3 -3
  17. data/lib/dry/transaction/step_adapters/raw.rb +6 -12
  18. data/lib/dry/transaction/step_adapters/tee.rb +4 -4
  19. data/lib/dry/transaction/step_adapters/try.rb +11 -8
  20. data/lib/dry/transaction/step_adapters.rb +2 -0
  21. data/lib/dry/transaction/version.rb +1 -1
  22. data/lib/dry/transaction.rb +14 -11
  23. data/spec/examples.txt +81 -65
  24. data/spec/integration/around_spec.rb +81 -0
  25. data/spec/integration/custom_step_adapters_spec.rb +6 -4
  26. data/spec/integration/operation_spec.rb +3 -3
  27. data/spec/integration/passing_step_arguments_spec.rb +1 -1
  28. data/spec/integration/publishing_step_events_spec.rb +36 -17
  29. data/spec/integration/transaction_spec.rb +165 -37
  30. data/spec/integration/transaction_without_steps_spec.rb +101 -0
  31. data/spec/spec_helper.rb +14 -5
  32. data/spec/support/container.rb +10 -0
  33. data/spec/support/database.rb +12 -0
  34. data/spec/support/db_transactions.rb +45 -0
  35. data/spec/support/result_mixin.rb +3 -0
  36. data/spec/unit/step_adapters/around_spec.rb +46 -0
  37. data/spec/unit/step_adapters/check_spec.rb +43 -0
  38. data/spec/unit/step_adapters/map_spec.rb +5 -12
  39. data/spec/unit/step_adapters/raw_spec.rb +16 -32
  40. data/spec/unit/step_adapters/tee_spec.rb +4 -10
  41. data/spec/unit/step_adapters/try_spec.rb +24 -33
  42. data/spec/unit/step_spec.rb +41 -10
  43. metadata +39 -21
  44. data/spec/support/either_mixin.rb +0 -3
@@ -1,15 +1,16 @@
1
1
  RSpec.describe "Transactions" do
2
+ include_context "database"
3
+
4
+ include Dry::Monads::Result::Mixin
5
+
2
6
  let(:dependencies) { {} }
3
7
 
4
8
  before do
5
- Test::NotValidError = Class.new(StandardError)
6
- Test::DB = []
7
- class Test::Container
8
- extend Dry::Container::Mixin
9
+ container.instance_exec do
9
10
  register :process, -> input { {name: input["name"], email: input["email"]} }
10
- register :verify, -> input { Dry::Monads::Right(input) }
11
+ register :verify, -> input { Success(input) }
11
12
  register :validate, -> input { input[:email].nil? ? raise(Test::NotValidError, "email required") : input }
12
- register :persist, -> input { Test::DB << input and true }
13
+ register :persist, -> input { self[:database] << input and true }
13
14
  end
14
15
  end
15
16
 
@@ -17,33 +18,33 @@ RSpec.describe "Transactions" do
17
18
  let(:transaction) {
18
19
  Class.new do
19
20
  include Dry::Transaction(container: Test::Container)
20
- map :process
21
- step :verify
22
- try :validate, catch: Test::NotValidError
23
- tee :persist
21
+ map :process
22
+ step :verify
23
+ try :validate, catch: Test::NotValidError
24
+ tee :persist
24
25
  end.new(**dependencies)
25
26
  }
26
27
  let(:input) { {"name" => "Jane", "email" => "jane@doe.com"} }
27
28
 
28
29
  it "calls the operations" do
29
30
  transaction.call(input)
30
- expect(Test::DB).to include(name: "Jane", email: "jane@doe.com")
31
+ expect(database).to include(name: "Jane", email: "jane@doe.com")
31
32
  end
32
33
 
33
34
  it "returns a success" do
34
- expect(transaction.call(input)).to be_a Dry::Monads::Either::Right
35
+ expect(transaction.call(input)).to be_a Dry::Monads::Result::Success
35
36
  end
36
37
 
37
38
  it "wraps the result of the final operation" do
38
- expect(transaction.call(input).value).to eq(name: "Jane", email: "jane@doe.com")
39
+ expect(transaction.call(input).value!).to eq(name: "Jane", email: "jane@doe.com")
39
40
  end
40
41
 
41
42
  it "can be called multiple times to the same effect" do
42
43
  transaction.call(input)
43
44
  transaction.call(input)
44
45
 
45
- expect(Test::DB[0]).to eq(name: "Jane", email: "jane@doe.com")
46
- expect(Test::DB[1]).to eq(name: "Jane", email: "jane@doe.com")
46
+ expect(database[0]).to eql(name: "Jane", email: "jane@doe.com")
47
+ expect(database[1]).to eql(name: "Jane", email: "jane@doe.com")
47
48
  end
48
49
 
49
50
  it "supports matching on success" do
@@ -66,7 +67,7 @@ RSpec.describe "Transactions" do
66
67
  class Test::ContainerNames
67
68
  extend Dry::Container::Mixin
68
69
  register :process_step, -> input { {name: input["name"], email: input["email"]} }
69
- register :verify_step, -> input { Dry::Monads::Right(input) }
70
+ register :verify_step, -> input { Dry::Monads::Success(input) }
70
71
  register :persist_step, -> input { Test::DB << input and true }
71
72
  end
72
73
  end
@@ -83,7 +84,7 @@ RSpec.describe "Transactions" do
83
84
 
84
85
  it "supports steps using differently named container operations" do
85
86
  transaction.call("name" => "Jane", "email" => "jane@doe.com")
86
- expect(Test::DB).to include(name: "Jane", email: "jane@doe.com")
87
+ expect(database).to include(name: "Jane", email: "jane@doe.com")
87
88
  end
88
89
  end
89
90
 
@@ -98,13 +99,13 @@ RSpec.describe "Transactions" do
98
99
  }
99
100
 
100
101
  let(:dependencies) {
101
- {verify_step: -> input { Dry::Monads.Right(input.merge(foo: :bar)) }}
102
+ {verify_step: -> input { Success(input.merge(foo: :bar)) }}
102
103
  }
103
104
 
104
105
  it "calls injected operations" do
105
106
  transaction.call("name" => "Jane", "email" => "jane@doe.com")
106
107
 
107
- expect(Test::DB).to include(name: "Jane", email: "jane@doe.com", foo: :bar)
108
+ expect(database).to include(name: "Jane", email: "jane@doe.com", foo: :bar)
108
109
  end
109
110
  end
110
111
 
@@ -129,7 +130,32 @@ RSpec.describe "Transactions" do
129
130
  it "allows local methods to run operations via super" do
130
131
  transaction.call("name" => "Jane", "email" => "jane@doe.com")
131
132
 
132
- expect(Test::DB).to include(name: "Jane", email: "jane@doe.com", greeting: "hello!")
133
+ expect(database).to include(name: "Jane", email: "jane@doe.com", greeting: "hello!")
134
+ end
135
+ end
136
+
137
+ context "wrapping operations with private local methods" do
138
+ let(:transaction) do
139
+ Class.new do
140
+ include Dry::Transaction(container: Test::Container)
141
+
142
+ map :process, with: :process
143
+ step :verify, with: :verify
144
+ tee :persist, with: :persist
145
+
146
+ private
147
+
148
+ def verify(input)
149
+ new_input = input.merge(greeting: "hello!")
150
+ super(new_input)
151
+ end
152
+ end.new(**dependencies)
153
+ end
154
+
155
+ it "allows local methods to run operations via super" do
156
+ transaction.call("name" => "Jane", "email" => "jane@doe.com")
157
+
158
+ expect(database).to include(name: "Jane", email: "jane@doe.com", greeting: "hello!")
133
159
  end
134
160
  end
135
161
 
@@ -143,7 +169,7 @@ RSpec.describe "Transactions" do
143
169
  tee :persist, with: :persist
144
170
 
145
171
  def verify(input)
146
- Right(input.keys)
172
+ Success(input.keys)
147
173
  end
148
174
  end.new
149
175
  end
@@ -151,7 +177,7 @@ RSpec.describe "Transactions" do
151
177
  it "execute step only defined as local method" do
152
178
  transaction.call("name" => "Jane", "email" => "jane@doe.com")
153
179
 
154
- expect(Test::DB).to include([:name, :email])
180
+ expect(database).to include([:name, :email])
155
181
  end
156
182
  end
157
183
 
@@ -165,7 +191,7 @@ RSpec.describe "Transactions" do
165
191
  tee :persist, with: :persist
166
192
 
167
193
  def verify_only_local(input)
168
- Right(input.keys)
194
+ Success(input.keys)
169
195
  end
170
196
  end.new
171
197
  end
@@ -173,7 +199,7 @@ RSpec.describe "Transactions" do
173
199
  it "execute step only defined as local method" do
174
200
  transaction.call("name" => "Jane", "email" => "jane@doe.com")
175
201
 
176
- expect(Test::DB).to include([:name, :email])
202
+ expect(database).to include([:name, :email])
177
203
  end
178
204
  end
179
205
 
@@ -192,18 +218,18 @@ RSpec.describe "Transactions" do
192
218
  end
193
219
 
194
220
  def verify(input)
195
- Dry::Monads.Right(input)
221
+ Success(input)
196
222
  end
197
223
 
198
224
  def persist(input)
199
- Test::DB << input and true
225
+ Test::Container[:database] << input and true
200
226
  end
201
227
  end.new
202
228
  end
203
229
 
204
230
  it "executes succesfully" do
205
231
  transaction.call("name" => "Jane", "email" => "jane@doe.com")
206
- expect(Test::DB).to include([["name", "Jane"], ["email", "jane@doe.com"]])
232
+ expect(database).to include([["name", "Jane"], ["email", "jane@doe.com"]])
207
233
  end
208
234
  end
209
235
 
@@ -221,15 +247,15 @@ RSpec.describe "Transactions" do
221
247
 
222
248
  it "does not run subsequent operations" do
223
249
  transaction.call(input)
224
- expect(Test::DB).to be_empty
250
+ expect(database).to be_empty
225
251
  end
226
252
 
227
253
  it "returns a failure" do
228
- expect(transaction.call(input)).to be_a Dry::Monads::Either::Left
254
+ expect(transaction.call(input)).to be_a Dry::Monads::Result::Failure
229
255
  end
230
256
 
231
257
  it "wraps the result of the failing operation" do
232
- expect(transaction.call(input).value).to be_a Test::NotValidError
258
+ expect(transaction.call(input).left).to be_a Test::NotValidError
233
259
  end
234
260
 
235
261
  it "supports matching on failure" do
@@ -285,9 +311,10 @@ RSpec.describe "Transactions" do
285
311
  before do
286
312
  class Test::ContainerRaw
287
313
  extend Dry::Container::Mixin
314
+ extend Dry::Monads::Result::Mixin
288
315
  register :process_step, -> input { {name: input["name"], email: input["email"]} }
289
- register :verify_step, -> input { Dry::Monads::Left("raw failure") }
290
- register :persist_step, -> input { Test::DB << input and true }
316
+ register :verify_step, -> input { Failure("raw failure") }
317
+ register :persist_step, -> input { self[:database] << input and true }
291
318
  end
292
319
  end
293
320
 
@@ -303,25 +330,25 @@ RSpec.describe "Transactions" do
303
330
 
304
331
  it "does not run subsequent operations" do
305
332
  transaction.call(input)
306
- expect(Test::DB).to be_empty
333
+ expect(database).to be_empty
307
334
  end
308
335
 
309
336
  it "returns a failure" do
310
- expect(transaction.call(input)).to be_a Dry::Monads::Either::Left
337
+ expect(transaction.call(input)).to be_a_failure
311
338
  end
312
339
 
313
340
  it "returns the failing value from the operation" do
314
- expect(transaction.call(input).value).to eq "raw failure"
341
+ expect(transaction.call(input).left).to eq "raw failure"
315
342
  end
316
343
 
317
344
  it "returns an object that quacks like expected" do
318
- result = transaction.call(input).value
345
+ result = transaction.call(input).left
319
346
 
320
347
  expect(Array(result)).to eq(['raw failure'])
321
348
  end
322
349
 
323
350
  it "does not allow to call private methods on the result accidently" do
324
- result = transaction.call(input).value
351
+ result = transaction.call(input).left
325
352
 
326
353
  expect { result.print('') }.to raise_error(NoMethodError)
327
354
  end
@@ -352,4 +379,105 @@ RSpec.describe "Transactions" do
352
379
  expect { transaction.call(input) }.to raise_error(ArgumentError)
353
380
  end
354
381
  end
382
+
383
+ context "keyword arguments" do
384
+ let(:input) { { name: 'jane', age: 20 } }
385
+
386
+ let(:upcaser) do
387
+ Class.new {
388
+ include Dry::Monads::Result::Mixin
389
+
390
+ def call(name: 'John', **rest)
391
+ Success(name: name[0].upcase + name[1..-1], **rest)
392
+ end
393
+ }.new
394
+ end
395
+
396
+ let(:transaction) do
397
+ Class.new {
398
+ include Dry::Transaction
399
+
400
+ step :camelize
401
+
402
+ }.new(camelize: upcaser)
403
+ end
404
+
405
+ it "calls the operations" do
406
+ expect(transaction.(input).value).to eql(name: 'Jane', age: 20)
407
+ end
408
+ end
409
+
410
+ context "invalid steps" do
411
+ context "non-callable step" do
412
+ context "with container" do
413
+ let(:input) { {} }
414
+
415
+ let(:transaction) {
416
+ Class.new do
417
+ include Dry::Transaction(container: Test::ContainerRaw)
418
+ map :not_a_proc
419
+ end.new
420
+ }
421
+
422
+ before do
423
+ class Test::ContainerRaw
424
+ extend Dry::Container::Mixin
425
+
426
+ register :not_a_proc, "definitely not a proc"
427
+ end
428
+ end
429
+
430
+ it "raises an exception" do
431
+ expect { transaction.call(input) }.to raise_error(Dry::Transaction::InvalidStepError)
432
+ end
433
+ end
434
+ end
435
+
436
+ context "missing steps" do
437
+ context "no container" do
438
+ let(:input) { {} }
439
+
440
+ let(:transaction) {
441
+ Class.new do
442
+ include Dry::Transaction
443
+ map :noop
444
+ map :i_am_missing
445
+
446
+ def noop
447
+ Success(input)
448
+ end
449
+ end.new
450
+ }
451
+
452
+ it "raises an exception" do
453
+ expect { transaction.call(input) }.to raise_error(Dry::Transaction::MissingStepError)
454
+ end
455
+ end
456
+
457
+ context "with container" do
458
+ let(:input) { {} }
459
+
460
+ let(:transaction) {
461
+ Class.new do
462
+ include Dry::Transaction(container: Test::ContainerRaw)
463
+ map :noop
464
+ map :i_am_missing
465
+
466
+ end.new
467
+ }
468
+
469
+ before do
470
+ class Test::ContainerRaw
471
+ extend Dry::Container::Mixin
472
+
473
+ register :noop, -> input { Success(input) }
474
+ end
475
+ end
476
+
477
+ it "raises an exception" do
478
+ expect { transaction.call(input) }.to raise_error(Dry::Transaction::MissingStepError)
479
+ end
480
+ end
481
+ end
482
+ end
355
483
  end
@@ -0,0 +1,101 @@
1
+ RSpec.describe "Transactions steps without arguments" do
2
+ let(:dependencies) { {} }
3
+
4
+ before do
5
+ Test::NotValidError = Class.new(StandardError)
6
+ Test::DB = [{"name" => "Jane", "email" => "jane@doe.com"}]
7
+ Test::Http = Class.new do
8
+ def self.get
9
+ "pong"
10
+ end
11
+
12
+ def self.post(value)
13
+ Test::DB << value
14
+ end
15
+ end
16
+ class Test::Container
17
+ extend Dry::Container::Mixin
18
+ register :fetch_data, -> { Test::DB.delete_at(0) }, call: false
19
+ register :call_outside, -> { Test::Http.get }, call: false
20
+ register :external_store, -> input { Test::Http.post(input) }
21
+ register :process, -> input { { name: input["name"], email: input["email"] } }
22
+ register :validate, -> input { input[:email].nil? ? raise(Test::NotValidError, "email required") : input }
23
+ register :persist, -> input { Test::DB << input and true }
24
+ end
25
+ end
26
+
27
+ context "successful" do
28
+ let(:transaction) {
29
+ Class.new do
30
+ include Dry::Transaction(container: Test::Container)
31
+ map :fetch_data
32
+ map :process
33
+ try :validate, catch: Test::NotValidError
34
+ tee :persist
35
+ end.new(**dependencies)
36
+ }
37
+
38
+ it "calls the operations" do
39
+ transaction.call
40
+ expect(Test::DB).to include(name: "Jane", email: "jane@doe.com")
41
+ end
42
+
43
+ it "returns a success" do
44
+ expect(transaction.call()).to be_a Dry::Monads::Result::Success
45
+ end
46
+
47
+ it "wraps the result of the final operation" do
48
+ expect(transaction.call().value!).to eq(name: "Jane", email: "jane@doe.com")
49
+ end
50
+
51
+ it "supports matching on success" do
52
+ results = []
53
+
54
+ transaction.call() do |m|
55
+ m.success do |value|
56
+ results << "success for #{value[:email]}"
57
+ end
58
+
59
+ m.failure { }
60
+ end
61
+
62
+ expect(results.first).to eq "success for jane@doe.com"
63
+ end
64
+ end
65
+
66
+ context "using multiple tee step operators" do
67
+ let(:transaction) {
68
+ Class.new do
69
+ include Dry::Transaction(container: Test::Container)
70
+ tee :call_outside
71
+ map :fetch_data
72
+ map :process
73
+ try :validate, catch: Test::NotValidError
74
+ tee :external_store
75
+ end.new(**dependencies)
76
+ }
77
+
78
+ it "calls the operations" do
79
+ transaction.call
80
+ expect(Test::DB).to include(name: "Jane", email: "jane@doe.com")
81
+ end
82
+ end
83
+
84
+ context "not needing arguments in the middle of the transaction" do
85
+ let(:transaction) {
86
+ Class.new do
87
+ include Dry::Transaction(container: Test::Container)
88
+ map :process
89
+ try :validate, catch: Test::NotValidError
90
+ tee :call_outside
91
+ tee :external_store
92
+ end.new(**dependencies)
93
+ }
94
+ let(:input) { {"name" => "Jane", "email" => "jane@doe.com"} }
95
+
96
+ it "calls the operations" do
97
+ transaction.call(input)
98
+ expect(Test::DB).to include(name: "Jane", email: "jane@doe.com")
99
+ end
100
+ end
101
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,12 +1,19 @@
1
- if RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.4"
2
- require "simplecov"
3
- SimpleCov.start do
4
- add_filter "/spec/"
1
+ if RUBY_ENGINE == 'ruby' && ENV['COVERAGE'] == 'true'
2
+ require 'yaml'
3
+ rubies = YAML.load(File.read(File.join(__dir__, '..', '.travis.yml')))['rvm']
4
+ latest_mri = rubies.select { |v| v =~ /\A\d+\.\d+.\d+\z/ }.max
5
+
6
+ if RUBY_VERSION == latest_mri
7
+ require 'simplecov'
8
+ SimpleCov.start do
9
+ add_filter '/spec/'
10
+ end
5
11
  end
6
12
  end
7
13
 
8
14
  begin
9
- require "byebug"
15
+ require "pry"
16
+ require "pry-byebug"
10
17
  rescue LoadError; end
11
18
 
12
19
  require "dry-transaction"
@@ -104,4 +111,6 @@ RSpec.configure do |config|
104
111
  # test failures related to randomization by passing the same `--seed` value
105
112
  # as the one that triggered the failure.
106
113
  Kernel.srand config.seed
114
+
115
+ config.include Dry::Monads::Result::Mixin, adapter: true
107
116
  end
@@ -0,0 +1,10 @@
1
+ RSpec.shared_context "container" do
2
+ before do
3
+ class Test::Container
4
+ extend Dry::Container::Mixin
5
+ extend Dry::Monads::Result::Mixin
6
+ end
7
+ end
8
+
9
+ let(:container) { Test::Container }
10
+ end
@@ -0,0 +1,12 @@
1
+ RSpec.shared_context "database" do
2
+ include_context "container"
3
+
4
+ before do
5
+ Test::NotValidError = Class.new(StandardError)
6
+ Test::DB = []
7
+
8
+ Test::Container.register(:database, Test::DB)
9
+ end
10
+
11
+ let(:database) { Test::DB }
12
+ end
@@ -0,0 +1,45 @@
1
+ RSpec.shared_context "db transactions" do
2
+ include_context "database"
3
+
4
+ before do
5
+ Test::Rollback = Class.new(StandardError)
6
+
7
+ class << Test::DB
8
+ attr_accessor :in_transaction, :rolled_back, :committed
9
+ alias_method :in_transaction?, :in_transaction
10
+ alias_method :rolled_back?, :rolled_back
11
+ alias_method :committed?, :committed
12
+
13
+ def transaction
14
+ self.in_transaction = true
15
+ self.rolled_back = false
16
+ self.committed = false
17
+
18
+ yield.tap do
19
+ self.committed = true
20
+ end
21
+ rescue => error
22
+ self.rolled_back = true
23
+ clear
24
+
25
+ raise error
26
+ ensure
27
+ self.in_transaction = false
28
+ end
29
+ end
30
+
31
+ container.register(:transaction) do |input, &block|
32
+ result = nil
33
+
34
+ begin
35
+ Test::DB.transaction do
36
+ result = block.(Success(input))
37
+ raise Test::Rollback if result.failure?
38
+ result
39
+ end
40
+ rescue Test::Rollback
41
+ result
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,3 @@
1
+ RSpec.configure do |config|
2
+ config.include Dry::Monads::Result::Mixin
3
+ end
@@ -0,0 +1,46 @@
1
+ RSpec.describe Dry::Transaction::StepAdapters::Around, :adapter do
2
+ subject { described_class.new }
3
+
4
+ let(:operation) {
5
+ -> (input, &block) { block.(Success(input.upcase)) }
6
+ }
7
+
8
+ let(:options) { { step_name: "unit" } }
9
+
10
+ let(:continue) do
11
+ -> (input) { input.fmap { |v| v + " terminated" } }
12
+ end
13
+
14
+ describe "#call" do
15
+ context "when the result of the operation is NOT a Dry::Monads::Result" do
16
+ let(:continue) do
17
+ -> (input) { "plain string" }
18
+ end
19
+
20
+ it "raises an InvalidResultError" do
21
+ expect {
22
+ subject.(operation, options, ["input"], &continue)
23
+ }.to raise_error(
24
+ Dry::Transaction::InvalidResultError,
25
+ "step +unit+ must return a Result object"
26
+ )
27
+ end
28
+ end
29
+
30
+ context "passing a block" do
31
+ it "returns a Success value with result from block" do
32
+ expect(subject.(operation, options, ["input"], &continue)).to eql(Success("INPUT terminated"))
33
+ end
34
+ end
35
+
36
+ context "when the result of the operation is a Failure value" do
37
+ let(:operation) {
38
+ -> (input, &block) { block.(Failure(input.upcase)) }
39
+ }
40
+
41
+ it "return a Failure value" do
42
+ expect(subject.(operation, options, ["input"], &continue)).to eql(Failure("INPUT"))
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,43 @@
1
+ RSpec.describe Dry::Transaction::StepAdapters::Check, :adapter do
2
+
3
+ subject { described_class.new }
4
+
5
+ let(:operation) {
6
+ -> (input) { input == "right" }
7
+ }
8
+
9
+ let(:options) { { step_name: "unit" } }
10
+
11
+ describe "#call" do
12
+
13
+ it "returns the result of the operation as output" do
14
+ expect(subject.(operation, options, ["right"])).to eql(Success("right"))
15
+ end
16
+
17
+ context "when check fails" do
18
+ it "return a Failure" do
19
+ expect(subject.(operation, options, ["wrong"])).to eql(Failure("wrong"))
20
+ end
21
+ end
22
+
23
+ context "when operation return right monad" do
24
+ let(:operation) {
25
+ -> (input) { Success(true) }
26
+ }
27
+
28
+ it "return a Success" do
29
+ expect(subject.(operation, options, ["input"])).to eql(Success("input"))
30
+ end
31
+ end
32
+
33
+ context "when operation return left monad" do
34
+ let(:operation) {
35
+ -> (input) { Failure(true) }
36
+ }
37
+
38
+ it "return a Failure" do
39
+ expect(subject.(operation, options, ["input"])).to eql(Failure("input"))
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,23 +1,16 @@
1
- RSpec.describe Dry::Transaction::StepAdapters::Map do
1
+ RSpec.describe Dry::Transaction::StepAdapters::Map, :adapter do
2
2
 
3
3
  subject { described_class.new }
4
4
 
5
+ let(:options) { {} }
6
+
5
7
  let(:operation) {
6
8
  -> (input) { input.upcase }
7
9
  }
8
10
 
9
- let(:step) {
10
- Dry::Transaction::Step.new(subject, :step, :step, operation, {})
11
- }
12
-
13
11
  describe "#call" do
14
-
15
- it "return a Right Monad" do
16
- expect(subject.call(step, 'input')).to be_a Dry::Monads::Either::Right
17
- end
18
-
19
- it "return the result of the operation as output" do
20
- expect(subject.call(step, 'input').value).to eql 'INPUT'
12
+ it "return a Success value" do
13
+ expect(subject.(operation, options, 'input')).to eql(Success('INPUT'))
21
14
  end
22
15
  end
23
16
  end