trailblazer-macro 2.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,133 @@
1
+ require "test_helper"
2
+
3
+ #:policy
4
+ class MyPolicy
5
+ def initialize(user, model)
6
+ @user, @model = user, model
7
+ end
8
+
9
+ def create?
10
+ @user == Module && @model.id.nil?
11
+ end
12
+
13
+ def new?
14
+ @user == Class
15
+ end
16
+ end
17
+ #:policy end
18
+
19
+ #--
20
+ # with policy
21
+ class DocsPunditProcTest < Minitest::Spec
22
+ Song = Struct.new(:id)
23
+
24
+ #:pundit
25
+ class Create < Trailblazer::Operation
26
+ step Model( Song, :new )
27
+ step Policy::Pundit( MyPolicy, :create? )
28
+ # ...
29
+ end
30
+ #:pundit end
31
+
32
+ it { Trailblazer::Operation::Inspect.(Create).must_equal %{[>model.build,>policy.default.eval]} }
33
+ it { Create.(params: {}, current_user: Module).inspect(:model).must_equal %{<Result:true [#<struct DocsPunditProcTest::Song id=nil>] >} }
34
+ it { Create.(params: {} ).inspect(:model).must_equal %{<Result:false [#<struct DocsPunditProcTest::Song id=nil>] >} }
35
+
36
+ it do
37
+ #:pundit-result
38
+ result = Create.(params: {}, current_user: Module)
39
+ result["result.policy.default"].success? #=> true
40
+ result["result.policy.default"]["policy"] #=> #<MyPolicy ...>
41
+ #:pundit-result end
42
+ result["result.policy.default"].success?.must_equal true
43
+ result["result.policy.default"]["policy"].is_a?(MyPolicy).must_equal true
44
+ end
45
+
46
+ #---
47
+ #- override
48
+ class New < Create
49
+ step Policy::Pundit( MyPolicy, :new? ), override: true
50
+ end
51
+
52
+ it { Trailblazer::Operation::Inspect.(New).must_equal %{[>model.build,>policy.default.eval]} }
53
+ it { New.(params: {}, current_user: Class ).inspect(:model).must_equal %{<Result:true [#<struct DocsPunditProcTest::Song id=nil>] >} }
54
+ it { New.(params: {}, current_user: nil ).inspect(:model).must_equal %{<Result:false [#<struct DocsPunditProcTest::Song id=nil>] >} }
55
+
56
+ #---
57
+ #- override with :name
58
+ class Edit < Trailblazer::Operation
59
+ step Policy::Pundit( MyPolicy, :create?, name: "first" )
60
+ step Policy::Pundit( MyPolicy, :new?, name: "second" )
61
+ end
62
+
63
+ class Update < Edit
64
+ step Policy::Pundit( MyPolicy, :new?, name: "first" ), override: true
65
+ end
66
+
67
+ it { Trailblazer::Operation::Inspect.(Edit).must_equal %{[>policy.first.eval,>policy.second.eval]} }
68
+ it { Edit.(params: {}, current_user: Class).inspect(:model).must_equal %{<Result:false [nil] >} }
69
+ it { Trailblazer::Operation::Inspect.(Update).must_equal %{[>policy.first.eval,>policy.second.eval]} }
70
+ it { Update.(params: {}, current_user: Class).inspect(:model).must_equal %{<Result:true [nil] >} }
71
+
72
+ #---
73
+ # dependency injection
74
+ class AnotherPolicy < MyPolicy
75
+ def create?
76
+ true
77
+ end
78
+ end
79
+
80
+ it {
81
+ result =
82
+ #:di-call
83
+ Create.(params: {},
84
+ current_user: Module,
85
+ "policy.default.eval" => Trailblazer::Operation::Policy::Pundit.build(AnotherPolicy, :create?)
86
+ )
87
+ #:di-call end
88
+ result.inspect("").must_equal %{<Result:true [nil] >} }
89
+ end
90
+
91
+ #-
92
+ # with name:
93
+ class PunditWithNameTest < Minitest::Spec
94
+ Song = Struct.new(:id)
95
+
96
+ #:name
97
+ class Create < Trailblazer::Operation
98
+ step Model( Song, :new )
99
+ step Policy::Pundit( MyPolicy, :create?, name: "after_model" )
100
+ # ...
101
+ end
102
+ #:name end
103
+
104
+ it {
105
+ #:name-call
106
+ result = Create.(params: {}, current_user: Module)
107
+ result["result.policy.after_model"].success? #=> true
108
+ #:name-call end
109
+ result["result.policy.after_model"].success?.must_equal true }
110
+ end
111
+
112
+ #---
113
+ # class-level guard
114
+ # class DocsGuardClassLevelTest < Minitest::Spec
115
+ # #:class-level
116
+ # class Create < Trailblazer::Operation
117
+ # step Policy::Guard[ ->(options) { options["current_user"] == Module } ],
118
+ # before: "operation.new"
119
+ # #~pipe--only
120
+ # step ->(options) { options["x"] = true }
121
+ # #~pipe--only end
122
+ # end
123
+ # #:class-level end
124
+
125
+ # it { Create.(); Create["result.policy"].must_be_nil }
126
+ # it { Create.(params: {}, current_user: Module)["x"].must_equal true }
127
+ # it { Create.(params: {} )["x"].must_be_nil }
128
+ # end
129
+
130
+
131
+
132
+ # TODO:
133
+ #policy.default
@@ -0,0 +1,126 @@
1
+ require "test_helper"
2
+
3
+ class NestedRescueTest < Minitest::Spec
4
+ #---
5
+ # nested raise (i hope people won't use this but it works)
6
+ A = Class.new(RuntimeError)
7
+ Y = Class.new(RuntimeError)
8
+
9
+ class NestedInsanity < Trailblazer::Operation
10
+ step Rescue {
11
+ step ->(options, **) { options["a"] = true }
12
+ step Rescue {
13
+ step ->(options, **) { options["y"] = true }
14
+ success ->(options, **) { raise Y if options["raise-y"] }
15
+ step ->(options, **) { options["z"] = true }
16
+ }
17
+ step ->(options, **) { options["b"] = true }
18
+ success ->(options, **) { raise A if options["raise-a"] }
19
+ step ->(options, **) { options["c"] = true }
20
+ failure ->(options, **) { options["inner-err"] = true }
21
+ }
22
+ step ->(options, **) { options["e"] = true }, id: "nested/e"
23
+ failure ->(options, **) { options["outer-err"] = true }, id: "nested/failure"
24
+ end
25
+
26
+ it { Trailblazer::Operation::Inspect.(NestedInsanity).must_match /\[>Rescue\(\d+\),>nested/ } # FIXME: better introspect tests for all id-generating macros.
27
+ it { NestedInsanity.().inspect("a", "y", "z", "b", "c", "e", "inner-err", "outer-err").must_equal %{<Result:true [true, true, true, true, true, true, nil, nil] >} }
28
+ it { NestedInsanity.( "raise-y" => true).inspect("a", "y", "z", "b", "c", "e", "inner-err", "outer-err").must_equal %{<Result:false [true, true, nil, nil, nil, nil, true, true] >} }
29
+ it { NestedInsanity.( "raise-a" => true).inspect("a", "y", "z", "b", "c", "e", "inner-err", "outer-err").must_equal %{<Result:false [true, true, true, true, nil, nil, nil, true] >} }
30
+
31
+ #-
32
+ # inheritance
33
+ class UbernestedInsanity < NestedInsanity
34
+ end
35
+
36
+ it { UbernestedInsanity.().inspect("a", "y", "z", "b", "c", "e", "inner-err", "outer-err").must_equal %{<Result:true [true, true, true, true, true, true, nil, nil] >} }
37
+ it { UbernestedInsanity.( "raise-a" => true).inspect("a", "y", "z", "b", "c", "e", "inner-err", "outer-err").must_equal %{<Result:false [true, true, true, true, nil, nil, nil, true] >} }
38
+ end
39
+
40
+ class RescueTest < Minitest::Spec
41
+
42
+ =begin
43
+ plain Rescue()
44
+ =end
45
+ class RescueWithoutHandlerTest < Minitest::Spec
46
+ Memo = Class.new
47
+
48
+ class Memo::Create < Trailblazer::Operation
49
+ step :find_model
50
+ step Rescue() {
51
+ step :update
52
+ step :rehash
53
+ }
54
+ step :notify
55
+ fail :log_error
56
+ #~methods
57
+ include Test::Methods
58
+ #~methods end
59
+ end
60
+
61
+ it { Memo::Create.( { seq: [] } ).inspect(:seq, :exception_class).must_equal %{<Result:true [[:find_model, :update, :rehash, :notify], nil] >} }
62
+ it { Memo::Create.( { seq: [], rehash_raise: true } ).inspect(:seq).must_equal %{<Result:false [[:find_model, :update, :rehash, :log_error]] >} }
63
+ end
64
+
65
+ =begin
66
+ Rescue( handler: X )
67
+ =end
68
+ class RescueWithHandlerTest < Minitest::Spec
69
+ Memo = Class.new
70
+
71
+ #:rescue-handler
72
+ class MyHandler
73
+ def self.call(exception, (ctx), *)
74
+ ctx[:exception_class] = exception.class
75
+ end
76
+ end
77
+ #:rescue-handler end
78
+
79
+ #:rescue
80
+ class Memo::Create < Trailblazer::Operation
81
+ step :find_model
82
+ step Rescue( RuntimeError, handler: MyHandler ) {
83
+ step :update
84
+ step :rehash
85
+ }
86
+ step :notify
87
+ fail :log_error
88
+ #~methods
89
+ include Test::Methods
90
+ #~methods end
91
+ end
92
+ #:rescue end
93
+
94
+ it { Memo::Create.( { seq: [], } ).inspect(:seq, :exception_class).must_equal %{<Result:true [[:find_model, :update, :rehash, :notify], nil] >} }
95
+ it { Memo::Create.( { seq: [], rehash_raise: true } ).inspect(:seq, :exception_class).must_equal %{<Result:false [[:find_model, :update, :rehash, :log_error], RuntimeError] >} }
96
+ end
97
+
98
+ =begin
99
+ Rescue( handler: :instance_method )
100
+ =end
101
+ class RescueWithHandlerTest < Minitest::Spec
102
+ Memo = Class.new
103
+
104
+ #:rescue-method
105
+ class Memo::Create < Trailblazer::Operation
106
+ step :find_model
107
+ step Rescue( RuntimeError, handler: :my_handler ) {
108
+ step :update
109
+ step :rehash
110
+ }
111
+ step :notify
112
+ fail :log_error
113
+ #~methods
114
+ include Test::Methods
115
+ #~methods end
116
+
117
+ def my_handler(exception, (ctx), *)
118
+ ctx[:exception_class] = exception.class
119
+ end
120
+ end
121
+ #:rescue-method end
122
+
123
+ it { Memo::Create.( { seq: [], } ).inspect(:seq, :exception_class).must_equal %{<Result:true [[:find_model, :update, :rehash, :notify], nil] >} }
124
+ it { Memo::Create.( { seq: [], rehash_raise: true } ).inspect(:seq, :exception_class).must_equal %{<Result:false [[:find_model, :update, :rehash, :log_error], RuntimeError] >} }
125
+ end
126
+ end
@@ -0,0 +1,274 @@
1
+ require "test_helper"
2
+
3
+ class DocsWrapTest < Minitest::Spec
4
+ module Memo
5
+ end
6
+
7
+ =begin
8
+ When success: return the block's returns
9
+ When raise: return {Railway.fail!}
10
+ =end
11
+ #:wrap-handler
12
+ class HandleUnsafeProcess
13
+ def self.call((ctx, flow_options), *, &block)
14
+ begin
15
+ yield # calls the wrapped steps
16
+ rescue
17
+ [ Trailblazer::Operation::Railway.fail!, [ctx, flow_options] ]
18
+ end
19
+ end
20
+ end
21
+ #:wrap-handler end
22
+
23
+ #:wrap
24
+ class Memo::Create < Trailblazer::Operation
25
+ step :find_model
26
+ step Wrap( HandleUnsafeProcess ) {
27
+ step :update
28
+ step :rehash
29
+ }
30
+ step :notify
31
+ fail :log_error
32
+ #~methods
33
+ include Test::Methods
34
+ #~methods end
35
+ end
36
+ #:wrap end
37
+
38
+ it { Memo::Create.( { seq: [] } ).inspect(:seq).must_equal %{<Result:true [[:find_model, :update, :rehash, :notify]] >} }
39
+ it { Memo::Create.( { seq: [], rehash_raise: true } ).inspect(:seq).must_equal %{<Result:false [[:find_model, :update, :rehash, :log_error]] >} }
40
+
41
+ =begin
42
+ Tracing with Wrap()
43
+ =end
44
+ it do
45
+ options = { seq: [] }
46
+ #:trace-call
47
+ result = Memo::Create.trace( options )
48
+ #:trace-call end
49
+ result.wtf.gsub("\n", "").must_match /.*Start.*find_model.*Wrap.*update.*rehash.*success.*notify.*success/
50
+ =begin
51
+ #:trace-success
52
+ result.wtf? #=>
53
+ |-- #<Trailblazer::Activity::Start semantic=:default>
54
+ |-- find_model
55
+ |-- Wrap/85
56
+ | |-- #<Trailblazer::Activity::Start semantic=:default>
57
+ | |-- update
58
+ | |-- rehash
59
+ | `-- #<Trailblazer::Operation::Railway::End::Success semantic=:success>
60
+ |-- notify
61
+ `-- #<Trailblazer::Operation::Railway::End::Success semantic=:success>
62
+ #:trace-success end
63
+ =end
64
+ end
65
+
66
+ =begin
67
+ When success: return the block's returns
68
+ When raise: return {Railway.fail!}, but wire Wrap() to {fail_fast: true}
69
+ =end
70
+ class WrapGoesIntoFailFastTest < Minitest::Spec
71
+ Memo = Module.new
72
+
73
+ class Memo::Create < Trailblazer::Operation
74
+ class HandleUnsafeProcess
75
+ def self.call((ctx), *, &block)
76
+ begin
77
+ yield # calls the wrapped steps
78
+ rescue
79
+ [ Trailblazer::Operation::Railway.fail!, [ctx, {}] ]
80
+ end
81
+ end
82
+ end
83
+
84
+ step :find_model
85
+ step Wrap( HandleUnsafeProcess ) {
86
+ step :update
87
+ step :rehash
88
+ }, fail_fast: true
89
+ step :notify
90
+ fail :log_error
91
+
92
+ #~methods
93
+ include Test::Methods
94
+ #~methods end
95
+ end
96
+
97
+ it { Memo::Create.( { seq: [] } ).inspect(:seq).must_equal %{<Result:true [[:find_model, :update, :rehash, :notify]] >} }
98
+ it { Memo::Create.( { seq: [], rehash_raise: true } ).inspect(:seq).must_equal %{<Result:false [[:find_model, :update, :rehash]] >} }
99
+ end
100
+
101
+ =begin
102
+ When success: return the block's returns
103
+ When raise: return {Railway.fail_fast!} and configure Wrap() to {fast_track: true}
104
+ =end
105
+ class WrapGoesIntoFailFastViaFastTrackTest < Minitest::Spec
106
+ Memo = Module.new
107
+
108
+ #:fail-fast-handler
109
+ class HandleUnsafeProcess
110
+ def self.call((ctx), *, &block)
111
+ begin
112
+ yield # calls the wrapped steps
113
+ rescue
114
+ [ Trailblazer::Operation::Railway.fail_fast!, [ctx, {}] ]
115
+ end
116
+ end
117
+ end
118
+ #:fail-fast-handler end
119
+
120
+ #:fail-fast
121
+ class Memo::Create < Trailblazer::Operation
122
+ step :find_model
123
+ step Wrap( HandleUnsafeProcess ) {
124
+ step :update
125
+ step :rehash
126
+ }, fast_track: true
127
+ step :notify
128
+ fail :log_error
129
+ #~methods
130
+ include Test::Methods
131
+ #~methods end
132
+ end
133
+ #:fail-fast end
134
+
135
+ it { Memo::Create.( { seq: [] } ).inspect(:seq).must_equal %{<Result:true [[:find_model, :update, :rehash, :notify]] >} }
136
+ it { Memo::Create.( { seq: [], rehash_raise: true } ).inspect(:seq).must_equal %{<Result:false [[:find_model, :update, :rehash]] >} }
137
+ end
138
+
139
+ =begin
140
+ When success: return the block's returns
141
+ When raise: return {Railway.fail!} or {Railway.pass!}
142
+ =end
143
+ class WrapWithTransactionTest < Minitest::Spec
144
+ Memo = Module.new
145
+
146
+ module Sequel
147
+ def self.transaction
148
+ begin
149
+ end_event, (ctx, flow_options) = yield
150
+ true
151
+ rescue
152
+ false
153
+ end
154
+ end
155
+ end
156
+
157
+ #:transaction-handler
158
+ class MyTransaction
159
+ def self.call((ctx, flow_options), *, &block)
160
+ result = Sequel.transaction { yield }
161
+
162
+ signal = result ? Trailblazer::Operation::Railway.pass! : Trailblazer::Operation::Railway.fail!
163
+
164
+ [ signal, [ctx, flow_options] ]
165
+ end
166
+ end
167
+ #:transaction-handler end
168
+
169
+ #:transaction
170
+ class Memo::Create < Trailblazer::Operation
171
+ step :find_model
172
+ step Wrap( MyTransaction ) {
173
+ step :update
174
+ step :rehash
175
+ }
176
+ step :notify
177
+ fail :log_error
178
+ #~methods
179
+ include Test::Methods
180
+ #~methods end
181
+ end
182
+ #:transaction end
183
+
184
+ it { Memo::Create.( { seq: [] } ).inspect(:seq).must_equal %{<Result:true [[:find_model, :update, :rehash, :notify]] >} }
185
+ it { Memo::Create.( { seq: [], rehash_raise: true } ).inspect(:seq).must_equal %{<Result:false [[:find_model, :update, :rehash, :log_error]] >} }
186
+ end
187
+
188
+ =begin
189
+ When success: return the block's returns
190
+ When raise: return {Railway.fail!} or {Railway.pass!}
191
+ =end
192
+ class WrapWithCustomEndsTest < Minitest::Spec
193
+ Memo = Module.new
194
+ Sequel = WrapWithTransactionTest::Sequel
195
+
196
+ #:custom-handler
197
+ class MyTransaction
198
+ MyFailSignal = Class.new(Trailblazer::Activity::Signal)
199
+
200
+ def self.call((ctx, flow_options), *, &block)
201
+ result = Sequel.transaction { yield }
202
+
203
+ signal = result ? Trailblazer::Operation::Railway.pass! : MyFailSignal
204
+
205
+ [ signal, [ctx, flow_options] ]
206
+ end
207
+ end
208
+ #:custom-handler end
209
+
210
+ #:custom
211
+ class Memo::Create < Trailblazer::Operation
212
+ step :find_model
213
+ step Wrap( MyTransaction ) {
214
+ step :update
215
+ step :rehash
216
+ },
217
+ Output(:success) => End(:transaction_worked),
218
+ Output(MyTransaction::MyFailSignal, :failure) => End(:transaction_failed)
219
+ step :notify
220
+ fail :log_error
221
+ #~methods
222
+ include Test::Methods
223
+ #~methods end
224
+ end
225
+ #:custom end
226
+
227
+ it do
228
+ result = Memo::Create.( { seq: [] } )
229
+ result.inspect(:seq).must_equal %{<Result:false [[:find_model, :update, :rehash]] >}
230
+ result.event.inspect.must_equal %{#<Trailblazer::Activity::End semantic=:transaction_worked>}
231
+ end
232
+
233
+ it do
234
+ result = Memo::Create.( { seq: [], rehash_raise: true } )
235
+ result.inspect(:seq).must_equal %{<Result:false [[:find_model, :update, :rehash]] >}
236
+ result.event.inspect.must_equal %{#<Trailblazer::Activity::End semantic=:transaction_failed>}
237
+ end
238
+ end
239
+
240
+ =begin
241
+ When success: return the block's returns
242
+ When raise: return {Railway.pass!} and go "successful"
243
+ =end
244
+ class WrapGoesIntoPassFromRescueTest < Minitest::Spec
245
+ Memo = Module.new
246
+
247
+ class Memo::Create < Trailblazer::Operation
248
+ class HandleUnsafeProcess
249
+ def self.call((ctx), *, &block)
250
+ begin
251
+ yield # calls the wrapped steps
252
+ rescue
253
+ [ Trailblazer::Operation::Railway.pass!, [ctx, {}] ]
254
+ end
255
+ end
256
+ end
257
+
258
+ step :find_model
259
+ step Wrap( HandleUnsafeProcess ) {
260
+ step :update
261
+ step :rehash
262
+ }
263
+ step :notify
264
+ fail :log_error
265
+
266
+ #~methods
267
+ include Test::Methods
268
+ #~methods end
269
+ end
270
+
271
+ it { Memo::Create.( { seq: [] } ).inspect(:seq).must_equal %{<Result:true [[:find_model, :update, :rehash, :notify]] >} }
272
+ it { Memo::Create.( { seq: [], rehash_raise: true } ).inspect(:seq).must_equal %{<Result:true [[:find_model, :update, :rehash, :notify]] >} }
273
+ end
274
+ end