trailblazer-macro 2.1.11 → 2.1.13

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,730 @@
1
+ require "test_helper"
2
+
3
+ # Use {ComputeNested.method(:compute_nested)}
4
+ # Use #trace
5
+ # Use #assert_invoke
6
+
7
+ class DocsNestedStaticTest < Minitest::Spec
8
+ #@ {:auto_wire} without any other options
9
+ module A
10
+ class Song
11
+ end
12
+
13
+ #:id3
14
+ module Song::Activity
15
+ class Id3Tag < Trailblazer::Activity::Railway
16
+ step :parse
17
+ step :encode_id3
18
+ #~meths
19
+ include T.def_steps(:parse, :encode_id3)
20
+
21
+ # def parse(ctx, seq:, **)
22
+ # ctx[:seq] = seq + [:parse]
23
+ # end
24
+ #~meths end
25
+ end
26
+ end
27
+ #:id3 end
28
+
29
+ module Song::Activity
30
+ class VorbisComment < Trailblazer::Activity::Railway
31
+ step :prepare_metadata
32
+ step :encode_cover
33
+ #~meths
34
+ include T.def_steps(:prepare_metadata, :encode_cover)
35
+ #~meths end
36
+ end
37
+ end
38
+
39
+ #:create
40
+ module Song::Activity
41
+ class Create < Trailblazer::Activity::Railway
42
+ step :model
43
+ step Nested(:decide_file_type,
44
+ auto_wire: [Id3Tag, VorbisComment]) # explicitely define possible nested activities.
45
+ step :save
46
+ #~meths
47
+ include T.def_steps(:model, :save)
48
+ #~meths end
49
+
50
+ def decide_file_type(ctx, params:, **)
51
+ params[:type] == "mp3" ? Id3Tag : VorbisComment
52
+ end
53
+ end
54
+ end
55
+ #:create end
56
+
57
+ end # A
58
+
59
+ #@ and the same with a decider callable
60
+ module AA
61
+ module Song
62
+ module Activity
63
+ Id3Tag = A::Song::Activity::Id3Tag
64
+ VorbisComment = A::Song::Activity::VorbisComment
65
+
66
+ class Create < Trailblazer::Activity::Railway
67
+ class MyDecider
68
+ def self.call(ctx, params:, **)
69
+ params[:type] == "mp3" ? Id3Tag : VorbisComment
70
+ end
71
+ end
72
+
73
+ step :model
74
+ step Nested(MyDecider,
75
+ auto_wire: [Id3Tag, VorbisComment]) # explicitely define possible nested activities.
76
+ step :save
77
+ #~meths
78
+ include T.def_steps(:model, :save)
79
+ #~meths end
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ it "wires all nested termini to the outer tracks" do
86
+ #@ success for Id3Tag
87
+ assert_invoke A::Song::Activity::Create, seq: %{[:model, :parse, :encode_id3, :save]}, params: {type: "mp3"}
88
+ assert_invoke AA::Song::Activity::Create, seq: %{[:model, :parse, :encode_id3, :save]}, params: {type: "mp3"}
89
+
90
+ #@ failure for Id3Tag
91
+ assert_invoke A::Song::Activity::Create, seq: %{[:model, :parse]}, params: {type: "mp3"}, parse: false, terminus: :failure
92
+ assert_invoke AA::Song::Activity::Create, seq: %{[:model, :parse]}, params: {type: "mp3"}, parse: false, terminus: :failure
93
+
94
+ #@ success for VorbisComment
95
+ assert_invoke A::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover, :save]}, params: {type: "vorbis"}
96
+ assert_invoke AA::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover, :save]}, params: {type: "vorbis"}
97
+ #@ failure for VorbisComment
98
+ assert_invoke A::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover]}, params: {type: "vorbis"}, encode_cover: false, terminus: :failure
99
+ assert_invoke AA::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover]}, params: {type: "vorbis"}, encode_cover: false, terminus: :failure
100
+ end
101
+
102
+ #@ Additional terminus {End.invalid_metadata} in Nested activity
103
+ module B
104
+ class Song
105
+ end
106
+
107
+ module Song::Activity
108
+ class Id3Tag < Trailblazer::Activity::Railway
109
+ InvalidMetadata = Class.new(Trailblazer::Activity::Signal)
110
+
111
+ step :parse, Output(InvalidMetadata, :invalid_metadata) => End(:invalid_metadata) # We have a new terminus {End.invalid_metadata}
112
+ step :encode_id3
113
+ #~meths
114
+ include T.def_steps(:parse, :encode_id3)
115
+
116
+ def validate_metadata(params)
117
+ params[:is_valid]
118
+ end
119
+ #~meths end
120
+ def parse(ctx, params:, **)
121
+ unless validate_metadata(params)
122
+ return InvalidMetadata
123
+ end
124
+ #~body
125
+ ctx[:seq] << :parse
126
+ true
127
+ # TODO: also test returning false.
128
+ #~body end
129
+ end
130
+ end
131
+
132
+ VorbisComment = A::Song::Activity::VorbisComment
133
+
134
+ class Create < Trailblazer::Activity::Railway
135
+ step :model
136
+ step Nested(:decide_file_type,
137
+ auto_wire: [Id3Tag, VorbisComment]), # explicitely define possible nested activities.
138
+ Output(:invalid_metadata) => Track(:failure)
139
+
140
+ step :save
141
+ #~meths
142
+ include T.def_steps(:model, :save)
143
+ #~meths end
144
+
145
+ def decide_file_type(ctx, params:, **)
146
+ params[:type] == "mp3" ? Id3Tag : VorbisComment
147
+ end
148
+ end
149
+
150
+ end
151
+ end # B
152
+
153
+ # Add another End.ExeedsLimit to {VorbisComment}
154
+ module C
155
+ class Song
156
+ end
157
+
158
+ #:unsupported-terminus
159
+ module Song::Activity
160
+ class VorbisComment < Trailblazer::Activity::Railway
161
+ step :prepare_metadata
162
+ step :encode_cover, Output(:failure) => End(:unsupported_file_format)
163
+ #~meths
164
+ include T.def_steps(:prepare_metadata, :encode_cover)
165
+ #~meths end
166
+ end
167
+ end
168
+ #:unsupported-terminus end
169
+
170
+ module Song::Activity
171
+ Id3Tag = B::Song::Activity::Id3Tag
172
+
173
+ class Create < Trailblazer::Activity::Railway
174
+ step :model
175
+ step Nested(:decide_file_type,
176
+ auto_wire: [Id3Tag, VorbisComment]), # explicitely define possible nested activities.
177
+ Output(:invalid_metadata) => Track(:failure),
178
+ Output(:unsupported_file_format) => End(:internal_error)
179
+
180
+ step :save
181
+ include T.def_steps(:model, :save)
182
+
183
+ def decide_file_type(ctx, params:, **)
184
+ params[:type] == "mp3" ? Id3Tag : VorbisComment
185
+ end
186
+ end
187
+
188
+ end
189
+ end
190
+
191
+ it "handle {InvalidMetadata}" do
192
+ #@ Id3Tag with valid metadata
193
+ assert_invoke B::Song::Activity::Create, seq: %{[:model, :parse, :encode_id3, :save]}, params: {type: "mp3", is_valid: true}, terminus: :success
194
+
195
+ #@ InvalidMetadata returned from Id3Tag#parse
196
+ assert_invoke B::Song::Activity::Create, seq: %{[:model]}, params: {type: "mp3", is_valid: false}, terminus: :failure
197
+ end
198
+
199
+ it "handle {failure} from VorbisComment" do
200
+ assert_invoke C::Song::Activity::Create, seq: %{[:model, :prepare_metadata]}, params: {type: "vorbis"}, terminus: :failure, prepare_metadata: false
201
+
202
+ #@ UnsupportedFileFormat
203
+ assert_invoke C::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover]}, params: {type: "vorbis"}, terminus: :internal_error, encode_cover: false
204
+ end
205
+
206
+ module D
207
+ class Song; end
208
+
209
+ module Song::Activity
210
+ Id3Tag = A::Song::Activity::Id3Tag
211
+ VorbisComment = C::Song::Activity::VorbisComment
212
+ end
213
+
214
+ #:create-output
215
+ module Song::Activity
216
+ class Create < Trailblazer::Activity::Railway
217
+ step :model
218
+ step Nested(
219
+ :decide_file_type,
220
+ auto_wire: [Id3Tag, VorbisComment]
221
+ ),
222
+ # Output and friends are used *after* Nested().
223
+ # Connect VorbisComment's {unsupported_file_format} to our {failure} track:
224
+ Output(:unsupported_file_format) => Track(:failure)
225
+
226
+ step :save
227
+ #~meths
228
+ include T.def_steps(:model, :save)
229
+
230
+ def decide_file_type(ctx, params:, **)
231
+ params[:type] == "mp3" ? Id3Tag : VorbisComment
232
+ end
233
+ #~meths end
234
+ end
235
+ end
236
+ #:create-output end
237
+ end
238
+
239
+ it "Id3Tag's invalid_metadata goes to {End.failure}" do
240
+ assert_invoke D::Song::Activity::Create, seq: %{[:model, :prepare_metadata]}, params: {type: "vorbis"}, terminus: :failure, prepare_metadata: false
241
+ end
242
+
243
+
244
+ #@ FastTrack is mapped to outer FastTrack.
245
+ module E
246
+ class Song
247
+ end
248
+
249
+ module Song::Activity
250
+ class Id3Tag < Trailblazer::Activity::FastTrack
251
+ step :parse,
252
+ fail_fast: true,
253
+ pass_fast: true
254
+ step :encode_id3
255
+ include T.def_steps(:parse, :encode_id3)
256
+ end
257
+
258
+ VorbisComment = DocsNestedStaticTest::C::Song::Activity::VorbisComment # has an {End.unsupported_file_format} terminus.
259
+ end
260
+
261
+ #:static-fasttrack
262
+ module Song::Activity
263
+ class Create < Trailblazer::Activity::FastTrack
264
+ step :model
265
+ step Nested(:decide_file_type, auto_wire: [Id3Tag, VorbisComment]),
266
+ fast_track: true,
267
+ Output(:unsupported_file_format) => End(:unsupported_file_format)
268
+ step :save
269
+ #~meths
270
+ include T.def_steps(:model, :save)
271
+ def decide_file_type(ctx, params:, **)
272
+ params[:type] == "mp3" ? Id3Tag : VorbisComment
273
+ end
274
+ #~meths end
275
+ end
276
+ end
277
+ #:static-fasttrack end
278
+ # puts Trailblazer::Developer.render(Song::Activity::Create)
279
+ end # C
280
+
281
+ it "FastTrack from nested_activity are mapped to respective tracks" do
282
+ #@ {End.pass_fast} goes success for Id3Tag
283
+ assert_invoke E::Song::Activity::Create, seq: %{[:model, :parse]}, params: {type: "mp3"}, terminus: :pass_fast
284
+ #@ {End.fail_fast} goes failure for Id3Tag
285
+ assert_invoke E::Song::Activity::Create, seq: %{[:model, :parse]}, params: {type: "mp3"}, parse: false, terminus: :fail_fast
286
+
287
+ assert_invoke E::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover, :save]}, params: {type: "vorbis"}
288
+ assert_invoke E::Song::Activity::Create, seq: %{[:model, :prepare_metadata]}, params: {type: "vorbis"}, prepare_metadata: false, terminus: :failure
289
+ #@ VorbisComment :unsupported_file_format is mapped to :failure
290
+ assert_invoke E::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover]}, params: {type: "vorbis"}, encode_cover: false, terminus: :unsupported_file_format
291
+ end
292
+ end
293
+
294
+ class DocsNestedDynamicTest < Minitest::Spec
295
+ #@ dynamic without any other options
296
+ module A
297
+ class Song
298
+ end
299
+
300
+ module Song::Activity
301
+ Id3Tag = DocsNestedStaticTest::A::Song::Activity::Id3Tag
302
+ VorbisComment = DocsNestedStaticTest::A::Song::Activity::VorbisComment
303
+ end
304
+
305
+ #:dynamic
306
+ module Song::Activity
307
+ class Create < Trailblazer::Activity::Railway
308
+ step :model
309
+ step Nested(:decide_file_type) # Run either {Id3Tag} or {VorbisComment}
310
+ step :save
311
+ #~meths
312
+ include T.def_steps(:model, :save)
313
+ #~meths end
314
+ def decide_file_type(ctx, params:, **)
315
+ params[:type] == "mp3" ? Id3Tag : VorbisComment
316
+ end
317
+ end
318
+ end
319
+ #:dynamic end
320
+ end # A
321
+
322
+ #@ and the same with a decider callable
323
+ module AA
324
+ module Song
325
+ module Activity
326
+ Id3Tag = A::Song::Activity::Id3Tag
327
+ VorbisComment = A::Song::Activity::VorbisComment
328
+
329
+ class Create < Trailblazer::Activity::Railway
330
+ MyDecider = DocsNestedStaticTest::AA::Song::Activity::Create::MyDecider
331
+
332
+ step :model
333
+ step Nested(MyDecider,
334
+ auto_wire: [Id3Tag, VorbisComment]) # explicitely define possible nested activities.
335
+ step :save
336
+ #~meths
337
+ include T.def_steps(:model, :save)
338
+ #~meths end
339
+ end
340
+ end
341
+ end
342
+ end
343
+
344
+ it "nested {success} and {failure} are wired to respective tracks on the outside" do
345
+ #@ success for Id3Tag means success track on the outside
346
+ assert_invoke A::Song::Activity::Create, seq: %{[:model, :parse, :encode_id3, :save]}, params: {type: "mp3"}
347
+ assert_invoke AA::Song::Activity::Create, seq: %{[:model, :parse, :encode_id3, :save]}, params: {type: "mp3"}
348
+ assert_invoke A::Song::Activity::Create, seq: %{[:model, :parse, :encode_id3]}, terminus: :failure, params: {type: "mp3"}, encode_id3: false
349
+ assert_invoke AA::Song::Activity::Create, seq: %{[:model, :parse, :encode_id3]}, terminus: :failure, params: {type: "mp3"}, encode_id3: false
350
+
351
+ #@ success for {VorbisComment}
352
+ assert_invoke A::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover, :save]}, params: {type: "vorbis"}
353
+ assert_invoke AA::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover, :save]}, params: {type: "vorbis"}
354
+ assert_invoke A::Song::Activity::Create, seq: %{[:model, :prepare_metadata]}, terminus: :failure, params: {type: "vorbis"}, prepare_metadata: false
355
+ assert_invoke AA::Song::Activity::Create, seq: %{[:model, :prepare_metadata]}, terminus: :failure, params: {type: "vorbis"}, prepare_metadata: false
356
+ end
357
+
358
+ #@ raises RuntimeError if we try to wire special terminus.
359
+ it "raises when wiring special termini" do
360
+ exception = assert_raises RuntimeError do
361
+ module B
362
+ class Song; end
363
+ #:dynamic-output
364
+ module Song::Activity
365
+ class Create < Trailblazer::Activity::Railway
366
+ step :model
367
+ step Nested(:decide_file_type),
368
+ Output(:unsupported_file_format) => Track(:failure) # error!
369
+ step :save
370
+ end
371
+ end
372
+ #:dynamic-output end
373
+ end
374
+ end # B
375
+
376
+ assert_equal exception.message[0..34], %{No `unsupported_file_format` output}
377
+ end
378
+
379
+ #@ any internal "special" terminus is mapped to either failure or success.
380
+ #@ FastTrack is converted to Binary outcome.
381
+ module C
382
+ class Song
383
+ end
384
+
385
+ module Song::Activity
386
+ Id3Tag = DocsNestedStaticTest::E::Song::Activity::Id3Tag # has {fail_fast} and {pass_fast} termini.
387
+ VorbisComment = DocsNestedStaticTest::C::Song::Activity::VorbisComment # has an {End.unsupported_file_format} terminus.
388
+ end
389
+
390
+ #:dynamic-unsupported
391
+ module Song::Activity
392
+ class Create < Trailblazer::Activity::Railway
393
+ step :model
394
+ step Nested(:decide_file_type) # Run either {Id3Tag} or {VorbisComment}
395
+ step :save
396
+ #~meths
397
+ include T.def_steps(:model, :save)
398
+ def decide_file_type(ctx, params:, **)
399
+ params[:type] == "mp3" ? Id3Tag : VorbisComment
400
+ end
401
+ #~meths end
402
+ end
403
+ end
404
+ #:dynamic-unsupported end
405
+ end # C
406
+
407
+ it "{VorbisComment}'s {End.unsupported_file_format} is mapped to {:failure}" do
408
+ #@ {End.pass_fast} goes success for Id3Tag
409
+ assert_invoke C::Song::Activity::Create, seq: %{[:model, :parse, :save]}, params: {type: "mp3"}
410
+ #@ {End.fail_fast} goes failure for Id3Tag
411
+ assert_invoke C::Song::Activity::Create, seq: %{[:model, :parse]}, params: {type: "mp3"}, parse: false, terminus: :failure
412
+
413
+ assert_invoke C::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover, :save]}, params: {type: "vorbis"}
414
+ assert_invoke C::Song::Activity::Create, seq: %{[:model, :prepare_metadata]}, params: {type: "vorbis"}, prepare_metadata: false, terminus: :failure
415
+ #@ VorbisComment :unsupported_file_format is mapped to :failure
416
+ assert_invoke C::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover]}, params: {type: "vorbis"}, encode_cover: false, terminus: :failure
417
+ end
418
+
419
+ it "is compatible with Debugging API" do
420
+ output, _ = trace A::Song::Activity::Create, params: {type: "mp3"}, seq: []
421
+
422
+ assert_equal output, %{TOP
423
+ |-- Start.default
424
+ |-- model
425
+ |-- Nested/decide_file_type
426
+ | |-- Start.default
427
+ | |-- call_dynamic_nested_activity
428
+ | | `-- DocsNestedStaticTest::A::Song::Activity::Id3Tag
429
+ | | |-- Start.default
430
+ | | |-- parse
431
+ | | |-- encode_id3
432
+ | | `-- End.success
433
+ | `-- End.success
434
+ |-- save
435
+ `-- End.success}
436
+
437
+ #@ we can look into non-dynamic parts
438
+ assert_equal Trailblazer::Developer::Introspect.find_path(A::Song::Activity::Create,
439
+ ["Nested/decide_file_type", :call_dynamic_nested_activity])[0].task.class.inspect, %{Method}
440
+
441
+ #@ we can't look into the dynamic parts
442
+ assert_raises do
443
+ Trailblazer::Developer::Introspect.find_path(A::Song::Activity::Create,
444
+ ["Nested/decide_file_type", :call_dynamic_nested_activity, DocsNestedStaticTest::A::Song::Activity::Id3Tag])[0]
445
+ end
446
+ end
447
+ end
448
+
449
+ class GenericNestedUnitTest < Minitest::Spec
450
+ module ComputeNested
451
+ module_function
452
+
453
+ def compute_nested(ctx, what:, **)
454
+ what
455
+ end
456
+ end
457
+
458
+ it "shows warning if `Nested()` is being used instead of `Subprocess()`" do
459
+ activity_classes = [Trailblazer::Activity::Path, Trailblazer::Activity::Railway, Trailblazer::Activity::FastTrack, Trailblazer::Operation]
460
+
461
+ activity_classes.each do |activity_class|
462
+ activity = Class.new(activity_class) # the "nested" activity.
463
+
464
+ _, warnings = capture_io do
465
+ Class.new(Trailblazer::Activity::Railway) do
466
+ step Nested(activity)
467
+ end
468
+ end
469
+ line_number_for_nested = __LINE__ - 3
470
+ puts _
471
+
472
+ assert_equal warnings, %{[Trailblazer] #{File.realpath(__FILE__)}:#{line_number_for_nested} Using the `Nested()` macro without a dynamic decider is deprecated.
473
+ To simply nest an activity or operation, replace `Nested(#{activity})` with `Subprocess(#{activity})`.
474
+ Check the Subprocess API docs to learn more about nesting: https://trailblazer.to/2.1/docs/activity.html#activity-wiring-api-subprocess
475
+ }
476
+ end
477
+ end
478
+
479
+ it "allows using multiple Nested() per operation" do
480
+ activity = Class.new(Trailblazer::Activity::Railway) do
481
+ step :a
482
+ step Nested(:decide)
483
+ step Nested(:decide), id: "Nested(2)"
484
+ # TODO: with static, too!
485
+ step :b
486
+
487
+ def decide(ctx, **)
488
+ DocsNestedStaticTest::A::Song::Activity::Create
489
+ end
490
+
491
+ include T.def_steps(:a, :b)
492
+ end
493
+
494
+ assert_invoke activity, seq: %{[:a, :model, :parse, :encode_id3, :save, :model, :parse, :encode_id3, :save, :b]}, params: {type: "mp3"}
495
+ end
496
+
497
+ it "allows I/O when using Nested(Activity) in Subprocess mode" do
498
+ activity = Class.new(Trailblazer::Activity::Railway) do
499
+ nested_activity = Class.new(Trailblazer::Activity::Railway) do
500
+ step ->(ctx, **) { ctx[:message] = Object }
501
+ step ->(ctx, **) { ctx[:status] = Class }
502
+ end
503
+
504
+ step Nested(nested_activity),
505
+ Out() => [:status]
506
+ end
507
+
508
+ assert_invoke activity, seq: %{[]}, expected_ctx_variables: {status: Class}
509
+ end
510
+
511
+ # TODO: move this to some testing gem? We need it a lot of times.
512
+ def self.step_capturing_visible_variables(ctx, **)
513
+ ctx[:visible] = ctx.keys
514
+ end
515
+
516
+ def activity_with_visible_variable
517
+ Class.new(Trailblazer::Activity::Railway) do
518
+ step GenericNestedUnitTest.method(:step_capturing_visible_variables)
519
+ end
520
+ end
521
+
522
+ def decider_with_visible_variable(ctx, what:, **)
523
+ ctx[:visible_in_decider] << ctx.keys # this is horrible, we're bleeding through to a "global" variable.
524
+ what
525
+ end
526
+
527
+ it "nested activity can see everything Nested() can see" do
528
+ sub_activity = activity_with_visible_variable()
529
+
530
+ #@ nested can see everything.
531
+ activity = Class.new(Trailblazer::Activity::Railway)
532
+ activity.step Trailblazer::Activity::Railway.Nested(ComputeNested.method(:compute_nested),
533
+ auto_wire: [sub_activity])
534
+
535
+ assert_invoke activity, what: sub_activity, dont_look_at_me: true, expected_ctx_variables: {visible: [:seq, :what, :dont_look_at_me]}
536
+
537
+
538
+ #@ nested can only see {:what}.
539
+ activity = Class.new(Trailblazer::Activity::Railway)
540
+ activity.step Trailblazer::Activity::Railway.Nested(ComputeNested.method(:compute_nested),
541
+ auto_wire: [sub_activity]),
542
+ Trailblazer::Activity::Railway.In() => [:what]
543
+
544
+ assert_invoke activity, what: sub_activity, dont_look_at_me: true, expected_ctx_variables: {visible: [:what]}
545
+ end
546
+
547
+ it "decider's variables are not discarded" do
548
+ sub_activity = activity_with_visible_variable()
549
+ compute_nested = ->(ctx, what:, **) do
550
+ ctx[:please_discard_me] = true # this bleeds through to all descendents.
551
+ what # this however is discarded.
552
+ end
553
+
554
+ #@ for Static
555
+ activity = Class.new(Trailblazer::Activity::Railway)
556
+ activity.step Trailblazer::Activity::Railway.Nested(compute_nested,
557
+ auto_wire: [sub_activity])
558
+
559
+ #@ nested_activity and top activity can see things from decider.
560
+ expected_variables = {please_discard_me: true, visible: [:seq, :what, :please_discard_me]}
561
+
562
+ assert_invoke activity, what: sub_activity, expected_ctx_variables: expected_variables
563
+
564
+ #@ for dynamic
565
+ activity = Class.new(Trailblazer::Activity::Railway)
566
+ activity.step Trailblazer::Activity::Railway.Nested(compute_nested)
567
+
568
+ #@ nested_activity and top activity cannot see things from decider.
569
+ assert_invoke activity, what: sub_activity, expected_ctx_variables: expected_variables
570
+ end
571
+
572
+ it "without In/Out, decider and nested_activity see the same" do
573
+ sub_activity = activity_with_visible_variable()
574
+ compute_nested = method(:decider_with_visible_variable)
575
+
576
+ #@ for Static
577
+ activity = Class.new(Trailblazer::Activity::Railway)
578
+ activity.step Trailblazer::Activity::Railway.Nested(compute_nested,
579
+ auto_wire: [sub_activity])
580
+
581
+ #@ nested_activity and top activity cannot see things from decider.
582
+ options = {
583
+ what: sub_activity, expected_ctx_variables: {
584
+ visible: [:seq, :what, :visible_in_decider],
585
+ visible_in_decider: [[:seq, :what, :visible_in_decider]]
586
+ }
587
+ }
588
+
589
+ assert_invoke activity, **options, visible_in_decider: []
590
+
591
+ #@ for dynamic
592
+ activity = Class.new(Trailblazer::Activity::Railway)
593
+ activity.step Trailblazer::Activity::Railway.Nested(compute_nested)
594
+
595
+ #@ nested_activity and top activity cannot see things from decider.
596
+ assert_invoke activity, **options, visible_in_decider: []
597
+ end
598
+
599
+ it "with In/Out, decider sees original ctx, nested_activity sees filtered" do
600
+ # sees {:activity_to_nest}
601
+ decider_with_visible_variable = ->(ctx, activity_to_nest:, **) do
602
+ ctx[:visible_in_decider] << ctx.keys # this is horrible, we're bleeding through to a "global" variable.
603
+ activity_to_nest
604
+ end
605
+
606
+ sub_activity = activity_with_visible_variable()
607
+ compute_nested = decider_with_visible_variable
608
+ in_out_options = {
609
+ Trailblazer::Activity::Railway.In() => {:activity_to_nest => :what}#,
610
+ # Trailblazer::Activity::Railway.In() => [:visible_in_decider]
611
+ }
612
+
613
+ #@ for Static
614
+ activity = Class.new(Trailblazer::Activity::Railway)
615
+ activity.step Trailblazer::Activity::Railway.Nested(compute_nested,
616
+ auto_wire: [sub_activity]).merge(in_out_options)
617
+
618
+ #@ nested_activity and top activity cannot see things from decider.
619
+ options = {
620
+ please_discard_me: true,
621
+ activity_to_nest: sub_activity, # renamed to {:what}
622
+ expected_ctx_variables: {
623
+ visible: [:what],
624
+ visible_in_decider: [[:seq, :please_discard_me, :activity_to_nest, :visible_in_decider]]
625
+ }
626
+ }
627
+
628
+ assert_invoke activity, **options, visible_in_decider: []
629
+
630
+ #@ for dynamic
631
+ activity = Class.new(Trailblazer::Activity::Railway)
632
+ activity.step Trailblazer::Activity::Railway.Nested(compute_nested).
633
+ merge(in_out_options)
634
+
635
+ #@ nested_activity and top activity cannot see things from decider.
636
+ assert_invoke activity, **options, visible_in_decider: []
637
+ end
638
+ end
639
+
640
+ class NestedStrategyComplianceTest < Minitest::Spec
641
+ Song = DocsNestedStaticTest::A::Song
642
+
643
+ it "is compatible with Debugging API" do
644
+ trace = %{
645
+ #:create-trace
646
+ Song::Activity::Create
647
+ |-- Start.default
648
+ |-- model
649
+ |-- Nested/decide_file_type
650
+ | |-- Start.default
651
+ | |-- route_to_nested_activity
652
+ | |-- DocsNestedStaticTest::A::Song::Activity::VorbisComment
653
+ | | |-- Start.default
654
+ | | |-- prepare_metadata
655
+ | | |-- encode_cover
656
+ | | `-- End.success
657
+ | `-- End.success
658
+ |-- save
659
+ `-- End.success
660
+ #:create-trace end
661
+ }
662
+
663
+ Trailblazer::Developer.wtf?(Song::Activity::Create, [{params: {type: "vorbis"}, seq: []}])
664
+
665
+ output, _ = trace Song::Activity::Create, params: {type: "vorbis"}, seq: []
666
+ assert_equal output, trace.split("\n")[2..-2].join("\n").sub("Song::Activity::Create", "TOP")
667
+
668
+ output, _ = trace Song::Activity::Create, params: {type: "mp3"}, seq: []
669
+ assert_equal output, %{TOP
670
+ |-- Start.default
671
+ |-- model
672
+ |-- Nested/decide_file_type
673
+ | |-- Start.default
674
+ | |-- route_to_nested_activity
675
+ | |-- DocsNestedStaticTest::A::Song::Activity::Id3Tag
676
+ | | |-- Start.default
677
+ | | |-- parse
678
+ | | |-- encode_id3
679
+ | | `-- End.success
680
+ | `-- End.success
681
+ |-- save
682
+ `-- End.success}
683
+
684
+
685
+ #@ compile time
686
+ #@ make sure we can find tasks/compile-time artifacts in Each by using their {compile_id}.
687
+ assert_equal Trailblazer::Developer::Introspect.find_path(Song::Activity::Create,
688
+ ["Nested/decide_file_type", DocsNestedStaticTest::A::Song::Activity::Id3Tag, :encode_id3])[0].task.inspect,
689
+ %{#<Trailblazer::Activity::TaskBuilder::Task user_proc=encode_id3>}
690
+
691
+ output =
692
+ #:create-introspect
693
+ Trailblazer::Developer.render(Song::Activity::Create,
694
+ path: [
695
+ "Nested/decide_file_type", # ID of Nested()
696
+ Song::Activity::Id3Tag # ID of the nested {Id3Tag} activity.
697
+ ]
698
+ )
699
+ #:create-introspect end
700
+ assert_match /user_proc=encode_id3>/, output
701
+ end
702
+
703
+ # TODO: test more options.
704
+ it "assigns ID via {Macro.id_for}" do
705
+ activity = Class.new(Trailblazer::Activity::Railway) do
706
+ step Nested(:decide_file_type, auto_wire: [Trailblazer::Activity::Railway])
707
+ end
708
+
709
+ assert_equal Trailblazer::Developer::Introspect.find_path(activity,
710
+ ["Nested/decide_file_type"])[0].id, %{Nested/decide_file_type}
711
+ end
712
+
713
+ it do
714
+ skip
715
+
716
+ #:patch
717
+ faster_mp3 = Trailblazer::Activity::DSL::Linear.Patch(
718
+ Song::Activity::Create,
719
+ ["Nested/decide_file_type", Song::Activity::Id3Tag] => -> { step :fast_encode_id3, replace: :encode_id3 }
720
+ )
721
+ #:patch end
722
+ faster_mp3.include(T.def_steps(:fast_encode_id3))
723
+ # Trailblazer::Developer.wtf?(faster_mp3, [{params: {type: "mp3"}, seq: []}])
724
+
725
+ #@ Original class isn't changed.
726
+ assert_invoke Song::Activity::Create, params: {type: "mp3"}, seq: "[:model, :parse, :encode_id3, :save]"
727
+ #@ Patched class runs
728
+ assert_invoke faster_mp3, params: {type: "mp3"}, seq: "[:model, :parse, :fast_encode_id3, :save]"
729
+ end
730
+ end