trailblazer-test 0.1.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +20 -0
- data/CHANGES.md +4 -0
- data/Gemfile +7 -0
- data/README.md +23 -118
- data/Rakefile +0 -3
- data/lib/trailblazer/test/assertion/assert_exposes.rb +73 -0
- data/lib/trailblazer/test/assertion/assert_fail.rb +58 -0
- data/lib/trailblazer/test/assertion/assert_pass.rb +101 -0
- data/lib/trailblazer/test/assertion.rb +114 -0
- data/lib/trailblazer/test/context.rb +5 -0
- data/lib/trailblazer/test/endpoint.rb +42 -0
- data/lib/trailblazer/test/helper/mock_step.rb +19 -0
- data/lib/trailblazer/test/suite/assert.rb +90 -0
- data/lib/trailblazer/test/suite/ctx.rb +51 -0
- data/lib/trailblazer/test/suite.rb +58 -0
- data/lib/trailblazer/test/testing.rb +62 -0
- data/lib/trailblazer/test/version.rb +1 -1
- data/lib/trailblazer/test.rb +13 -4
- data/trailblazer-test.gemspec +9 -4
- metadata +65 -20
- data/.rubocop-https---raw-githubusercontent-com-trailblazer-meta-master-rubocop-yml +0 -115
- data/.rubocop.yml +0 -17
- data/.travis.yml +0 -14
- data/lib/trailblazer/test/assertions.rb +0 -37
- data/lib/trailblazer/test/deprecation/operation/assertions.rb +0 -42
- data/lib/trailblazer/test/deprecation/operation/helper.rb +0 -28
- data/lib/trailblazer/test/operation/assertions.rb +0 -89
- data/lib/trailblazer/test/operation/helper.rb +0 -54
- data/lib/trailblazer/test/operation/policy_assertions.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 25326dbdb940038e49c6830ac6c4c833583c13db2d5f11782cac2b457f0836cb
|
4
|
+
data.tar.gz: e0639ac0cacc534a105138840e15f1019c9136c30042d8ef5d28c80eeb4800fa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 00332de32685126953eff6bfc0f1384f497e431e4ec1038a2f33a04cd3b37f762c8612c6834757a81095633ceaf55c748a799bf925ce27c923b5a391e8d2481d
|
7
|
+
data.tar.gz: a09347b703bf1eb6298f63b3e0c3b4481590e92b29b3d8abfb7e192e28a1986a94e2eafb1ff1d18792fc9693c94bcc4cad0cbec922ff9aff637e8ac2cc12fb4f
|
@@ -0,0 +1,20 @@
|
|
1
|
+
## This file is managed by Terraform.
|
2
|
+
## Do not modify this file directly, as it may be overwritten.
|
3
|
+
## Please open an issue instead.
|
4
|
+
name: CI
|
5
|
+
on: [push, pull_request]
|
6
|
+
jobs:
|
7
|
+
test:
|
8
|
+
strategy:
|
9
|
+
fail-fast: false
|
10
|
+
matrix:
|
11
|
+
# commenting out 2.7 because of dry.
|
12
|
+
ruby: ['3.0', '3.1', '3.2', "3.3", "head", "jruby"]
|
13
|
+
runs-on: ubuntu-latest
|
14
|
+
steps:
|
15
|
+
- uses: actions/checkout@v3
|
16
|
+
- uses: ruby/setup-ruby@v1
|
17
|
+
with:
|
18
|
+
ruby-version: ${{ matrix.ruby }}
|
19
|
+
bundler-cache: true
|
20
|
+
- run: bundle exec rake
|
data/CHANGES.md
CHANGED
data/Gemfile
CHANGED
@@ -2,3 +2,10 @@ source "https://rubygems.org"
|
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in trailblazer-test.gemspec
|
4
4
|
gemspec
|
5
|
+
|
6
|
+
# gem "reform-rails", path: "../reform-rails"
|
7
|
+
|
8
|
+
gem "trailblazer-endpoint", path: "../trailblazer-endpoint"
|
9
|
+
# gem "trailblazer-core-utils", path: "../trailblazer-core-utils"
|
10
|
+
# gem "trailblazer-endpoint", github: "trailblazer/trailblazer-endpoint"
|
11
|
+
gem "ostruct"
|
data/README.md
CHANGED
@@ -1,140 +1,45 @@
|
|
1
1
|
# Trailblazer::Test
|
2
2
|
|
3
|
-
|
4
|
-
[](http://badge.fury.io/rb/trailblazer-test)
|
3
|
+
_Assertions and helpers for operation unit tests._
|
5
4
|
|
6
|
-
|
5
|
+
The [comprehensive docs are here](https://trailblazer.to/2.1/docs/test/).
|
7
6
|
|
8
|
-
|
7
|
+
Read our introducing blog post for a better overview.
|
9
8
|
|
10
|
-
|
11
|
-
2. **Integration tests for controllers**: These Smoke tests only test the wiring between controller, operation and presentation layer. Usually, a coded click path simulates you manually clicking through your app and testing if it works. The preferred way here is using Rack-test and Capybara.
|
12
|
-
3. **Unit tests for cells** By invoking your cells with arbitrary data you functionally test the rendered markup using Capybara.
|
13
|
-
|
14
|
-
All the up to date details on the available assertions and helpers is available at [official documentation](http://2019.trailblazer.to/2.1/docs/trailblazer.html#trailblazer-test).
|
15
|
-
|
16
|
-
### Assertions
|
17
|
-
|
18
|
-
To use available assertions, add in your test `_helper` the following modules:
|
19
|
-
|
20
|
-
```ruby
|
21
|
-
include Trailblazer::Test::Assertions
|
22
|
-
include Trailblazer::Test::Operation::Assertions
|
23
|
-
```
|
24
|
-
|
25
|
-
If you are using Trailblazer v2.0 you need to add also:
|
26
|
-
|
27
|
-
```ruby
|
28
|
-
require "trailblazer/test/deprecation/operation/assertions"
|
29
|
-
|
30
|
-
include Trailblazer::Test::Deprecation::Operation::Assertions # in your test class
|
31
|
-
```
|
32
|
-
|
33
|
-
[Learn more](http://2019.trailblazer.to/2.1/docs/trailblazer.html#trailblazer-test-assertions)
|
34
|
-
|
35
|
-
#### assert_pass
|
36
|
-
|
37
|
-
Use `assert_pass` to run an operation and assert it was successful, while checking if the attributes of the operation's `model` are what you're expecting.
|
38
|
-
|
39
|
-
```ruby
|
40
|
-
it { assert_pass Blog::Operation::Create, { params: { title: "Ruby Soho" } }, title: "Ruby Soho" }
|
41
|
-
```
|
42
|
-
|
43
|
-
#### assert_fail
|
44
|
-
|
45
|
-
To test an unsuccessful outcome of an operation, use `assert_fail`. This is used for testing all kinds of validations. By passing insufficient or wrong data to the operation, it will fail and mark errors on the errors object.
|
46
|
-
|
47
|
-
```ruby
|
48
|
-
it { assert_fail Blog::Operation::Update, { params: { band: nil } }, expected_errors: [:band] }
|
49
|
-
```
|
50
|
-
|
51
|
-
#### assert_policy_fail
|
52
|
-
|
53
|
-
This will test that the operation fails due to a policy failure.
|
54
|
-
|
55
|
-
```ruby
|
56
|
-
it { assert_policy_fail Blog::Operation::Delete, ctx({title: "Ruby Soho"}, current_user: not_allowed_user) }
|
57
|
-
```
|
58
|
-
|
59
|
-
#### assert_exposes
|
60
|
-
|
61
|
-
Test attributes of an arbitrary object.
|
62
|
-
|
63
|
-
```ruby
|
64
|
-
it { assert_exposes model, title: "Timebomb", band: "Rancid" }
|
65
|
-
```
|
66
|
-
|
67
|
-
### Helpers
|
9
|
+
## Installation
|
68
10
|
|
69
|
-
|
70
|
-
Add this in your `_helper.rb` file to use all available helpers.
|
11
|
+
Add the following line to your project's `Gemfile`.
|
71
12
|
|
72
13
|
```ruby
|
73
|
-
|
14
|
+
gem "trailblazer-test", ">= 1.0.0", "< 2.0.0"
|
74
15
|
```
|
75
16
|
|
76
|
-
|
17
|
+
## Overview
|
77
18
|
|
78
|
-
|
19
|
+
This gem adds the following assertions and helpers:
|
79
20
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
it do
|
84
|
-
result = call Blog::Operation::Create, params: {title: "Shipwreck", band: "Rancid"}
|
85
|
-
# use `result` object however you want
|
86
|
-
end
|
87
|
-
```
|
21
|
+
* `#assert_pass` to test an operation terminating with success.
|
22
|
+
* `#assert_fail` to assert validation errors and the like.
|
23
|
+
* `#mock_step` helping the replace steps with stubs.
|
88
24
|
|
89
|
-
|
25
|
+
## Example
|
90
26
|
|
91
|
-
|
27
|
+
An example test case checking if an operation passed and created a model could look as follows.
|
92
28
|
|
93
29
|
```ruby
|
94
|
-
|
95
|
-
assert_raises do
|
96
|
-
factory Blog::Operation::Create, params: {title: "Shipwreck", band: "The Chats"}
|
97
|
-
end
|
98
|
-
end
|
99
|
-
```
|
100
|
-
|
101
|
-
#### mock_step
|
30
|
+
# test/operation/memo_test.rb
|
102
31
|
|
103
|
-
|
32
|
+
require "test_helper"
|
104
33
|
|
105
|
-
|
106
|
-
|
107
|
-
step :load_user
|
108
|
-
...
|
109
|
-
end
|
110
|
-
```
|
34
|
+
class MemoOperationTest < Minitest::Spec
|
35
|
+
Trailblazer::Test.module!(self) # install our helpers.
|
111
36
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
end
|
119
|
-
|
120
|
-
assert_pass new_activity, {}, {} do |ctx|
|
121
|
-
assert_equal ctx[:user].name, 'Mocky'
|
37
|
+
it "passes with valid input" do
|
38
|
+
# ...
|
39
|
+
assert_pass Memo::Operation::Create, input,
|
40
|
+
content: "Stock up beer",
|
41
|
+
persisted?: true,
|
42
|
+
id: ->(asserted:, **) { asserted.id > 0 }
|
122
43
|
end
|
123
44
|
end
|
124
45
|
```
|
125
|
-
|
126
|
-
## Installation
|
127
|
-
|
128
|
-
Add this line to your application's Gemfile:
|
129
|
-
|
130
|
-
```ruby
|
131
|
-
gem 'trailblazer-test'
|
132
|
-
```
|
133
|
-
|
134
|
-
And then execute:
|
135
|
-
|
136
|
-
$ bundle
|
137
|
-
|
138
|
-
Or install it yourself as:
|
139
|
-
|
140
|
-
$ gem install trailblazer-test
|
data/Rakefile
CHANGED
@@ -0,0 +1,73 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
module Test
|
3
|
+
module Assertion
|
4
|
+
module AssertExposes
|
5
|
+
# Test if all `tuples` values on `asserted` match the expected values.
|
6
|
+
# @param asserted Object Object that exposes attributes to test
|
7
|
+
# @param tuples Hash Key/value attribute pairs to test
|
8
|
+
# @param options Hash Default :reader is `asserted.{name}`,
|
9
|
+
# TODO: test err msgs!
|
10
|
+
def assert_exposes(asserted, expected=nil, reader: nil, **options)
|
11
|
+
expected = options.any? ? options : expected # allow passing {expected} as kwargs, too.
|
12
|
+
|
13
|
+
_assert_exposes_for(asserted, expected, reader: reader)
|
14
|
+
end
|
15
|
+
|
16
|
+
# def assert_exposes_hash(asserted, expected)
|
17
|
+
# _assert_exposes_for(asserted, expected, reader: :[])
|
18
|
+
# end
|
19
|
+
|
20
|
+
# @private
|
21
|
+
def _assert_exposes_for(asserted, expected, **options)
|
22
|
+
passed, matches, last_failed = Assert.assert_attributes(asserted, expected, **options) do |_matches, last_failed|
|
23
|
+
name, expected_value, actual_value, _passed, is_eq, error_msg = last_failed
|
24
|
+
|
25
|
+
is_eq ? assert_equal(expected_value, actual_value, error_msg) : assert(expected_value, error_msg)
|
26
|
+
|
27
|
+
return false
|
28
|
+
end
|
29
|
+
|
30
|
+
return true
|
31
|
+
end
|
32
|
+
|
33
|
+
module Assert
|
34
|
+
module_function
|
35
|
+
|
36
|
+
# Yields {block} if tuples don't match/failed.
|
37
|
+
def assert_attributes(asserted, expected, reader: false, &block)
|
38
|
+
passed, matches, last_failed = match_tuples(asserted, expected, reader: reader)
|
39
|
+
|
40
|
+
yield matches, last_failed unless passed
|
41
|
+
|
42
|
+
return passed, matches, last_failed
|
43
|
+
end
|
44
|
+
|
45
|
+
# Test if all properties match using our own {#test_equal}.
|
46
|
+
# @private
|
47
|
+
def match_tuples(asserted, expected, reader:)
|
48
|
+
passed = true # goes {false} if one or more attributes didn't match.
|
49
|
+
|
50
|
+
matches = expected.collect do |k, v|
|
51
|
+
actual = Test::Assertion.actual(asserted, reader, k)
|
52
|
+
expected, is_eq = Test::Assertion.expected(asserted, v, actual)
|
53
|
+
|
54
|
+
is_eq ?
|
55
|
+
[k, expected, actual, passed &= test_equal(expected, actual), is_eq, "Property [#{k}] mismatch"] :
|
56
|
+
[k, expected, actual, passed &= test_true(expected, actual), is_eq, "Actual: #{actual.inspect}."]
|
57
|
+
end
|
58
|
+
|
59
|
+
[passed, matches, matches.find { |k, v, actual, passed, *| !passed }]
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_equal(expected, actual)
|
63
|
+
expected == actual
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_true(expected, actual)
|
67
|
+
!! expected
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
module Test
|
3
|
+
module Assertion
|
4
|
+
module AssertFail
|
5
|
+
module_function
|
6
|
+
|
7
|
+
extend AssertPass::Utils
|
8
|
+
|
9
|
+
# {expected_errors} can be nil when using the {#assert_fail} block syntax.
|
10
|
+
def call(activity, ctx, expected_errors=nil, test:, invoke:, **kws)
|
11
|
+
signal, ctx, _ = invoke.(activity, ctx)
|
12
|
+
|
13
|
+
assert_fail_with_model(signal, ctx, expected_errors: expected_errors, test: test, operation: activity, **kws)
|
14
|
+
end
|
15
|
+
|
16
|
+
# @private
|
17
|
+
def assert_fail_with_model(signal, ctx, test:, **options)
|
18
|
+
assert_after_call(ctx, **options) do |ctx|
|
19
|
+
|
20
|
+
test.assert_equal *arguments_for_assert_fail(signal), error_message_for_assert_fail_after_call(signal, ctx, **options)
|
21
|
+
|
22
|
+
if options[:expected_errors]
|
23
|
+
# TODO: allow error messages from somewhere else.
|
24
|
+
# only test _if_ errors are present, not the content.
|
25
|
+
colored_errors = AssertPass::Errors.colored_errors_for(ctx)
|
26
|
+
|
27
|
+
test.assert_equal *arguments_for_assert_contract_errors(signal, ctx, contract_name: :default, **options), "Actual contract errors: #{colored_errors}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def arguments_for_assert_fail(signal)
|
33
|
+
return false, Assertion::SUCCESS_TERMINI.include?(signal.to_h[:semantic]) # FIXME: same logic as in {#assert_pass}.
|
34
|
+
end
|
35
|
+
|
36
|
+
def arguments_for_assert_contract_errors(signal, ctx, contract_name:, expected_errors:, **)
|
37
|
+
with_messages = expected_errors.is_a?(Hash)
|
38
|
+
|
39
|
+
raise ExpectedErrorsTypeError, "expected_errors has to be an Array or Hash" unless expected_errors.is_a?(Array) || with_messages # TODO: test me!
|
40
|
+
|
41
|
+
errors = ctx[:"contract.#{contract_name}"].errors.messages # TODO: this will soon change with the operation Errors object.
|
42
|
+
|
43
|
+
if with_messages
|
44
|
+
expected_errors = expected_errors.collect { |k, v| [k, Array(v)] }.to_h
|
45
|
+
|
46
|
+
return expected_errors, errors
|
47
|
+
else
|
48
|
+
return expected_errors.sort, errors.keys.sort
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def error_message_for_assert_fail_after_call(signal, ctx, operation:, **)
|
53
|
+
%{{#{operation}} didn't fail, it passed}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
module Test
|
3
|
+
module Assertion
|
4
|
+
module AssertPass
|
5
|
+
module_function
|
6
|
+
|
7
|
+
def call(activity, ctx, invoke:, **options)
|
8
|
+
signal, ctx, _ = invoke.(activity, ctx)
|
9
|
+
|
10
|
+
assert_pass_with_model(signal, ctx, operation: activity, **options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def assert_pass_with_model(signal, ctx, **options)
|
14
|
+
assert_after_call(ctx, **options) do |ctx|
|
15
|
+
Passed.new.call(signal, ctx, **options)
|
16
|
+
PassedWithAttributes.new.call(signal, ctx, **options)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Passed
|
21
|
+
# Check if the operation terminates on {:success}.
|
22
|
+
# @semi-public Used in rspec-trailblazer
|
23
|
+
def call(signal, ctx, **options)
|
24
|
+
expected_outcome, actual_outcome = arguments_for_assertion(signal)
|
25
|
+
error_msg = error_message(signal, ctx, **options) # DISCUSS: compute error message before there was an error?
|
26
|
+
|
27
|
+
outcome = assertion(expected_outcome, actual_outcome, error_msg, **options)
|
28
|
+
return outcome, error_msg
|
29
|
+
end
|
30
|
+
|
31
|
+
# What needs to be compared?
|
32
|
+
def arguments_for_assertion(signal)
|
33
|
+
return true, Assertion::SUCCESS_TERMINI.include?(signal.to_h[:semantic])
|
34
|
+
end
|
35
|
+
|
36
|
+
def error_message(signal, ctx, operation:, **)
|
37
|
+
colored_errors = Errors.colored_errors_for(ctx)
|
38
|
+
|
39
|
+
%{{#{operation}} failed: #{colored_errors}} # FIXME: only if contract's there!
|
40
|
+
end
|
41
|
+
|
42
|
+
def assertion(expected_outcome, actual_outcome, error_msg, test:, **)
|
43
|
+
test.assert_equal(
|
44
|
+
expected_outcome,
|
45
|
+
actual_outcome,
|
46
|
+
error_msg
|
47
|
+
)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# @semi-public Used in rspec-trailblazer
|
52
|
+
class PassedWithAttributes
|
53
|
+
def call(signal, ctx, **options)
|
54
|
+
model = model_for(ctx, **options)
|
55
|
+
|
56
|
+
outcome, error_msg = assertion(ctx, **options, model: model)
|
57
|
+
return outcome, error_msg
|
58
|
+
end
|
59
|
+
|
60
|
+
# DISCUSS: should we default options like {:model_at} here?
|
61
|
+
def model_for(ctx, model_at: :model, **)
|
62
|
+
ctx[model_at]
|
63
|
+
end
|
64
|
+
|
65
|
+
def assertion(ctx, model:, expected_model_attributes:, test:, **)
|
66
|
+
test.assert_exposes(model, expected_model_attributes)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
module Utils
|
71
|
+
# @private
|
72
|
+
def assert_after_call(ctx, user_block:, **kws)
|
73
|
+
yield(ctx)
|
74
|
+
|
75
|
+
user_block.call(ctx) if user_block
|
76
|
+
|
77
|
+
ctx
|
78
|
+
end
|
79
|
+
end # Utils
|
80
|
+
|
81
|
+
module Errors
|
82
|
+
module_function
|
83
|
+
|
84
|
+
def colored_errors_for(ctx)
|
85
|
+
# TODO: generic errors object "finding"
|
86
|
+
errors =
|
87
|
+
if ctx[:"contract.default"]
|
88
|
+
ctx[:"contract.default"].errors.messages.inspect
|
89
|
+
else
|
90
|
+
""
|
91
|
+
end
|
92
|
+
|
93
|
+
colored_errors = %{\e[33m#{errors}\e[0m}
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
extend Utils
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
module Test
|
3
|
+
# Top-level entry points for end users.
|
4
|
+
# These methods expose the end user syntax, not the logic.
|
5
|
+
module Assertion
|
6
|
+
def self.module!(receiver, activity: false, suite: false, spec: true)
|
7
|
+
modules = [Helper::MockStep, AssertExposes]
|
8
|
+
if suite
|
9
|
+
modules += [Suite, Suite::Spec] if spec
|
10
|
+
modules += [Suite, Suite::Test] if suite && !spec
|
11
|
+
else
|
12
|
+
modules += [Assertion]
|
13
|
+
end
|
14
|
+
|
15
|
+
modules += [Assertion::Activity] if activity
|
16
|
+
|
17
|
+
receiver.include(*modules.reverse)
|
18
|
+
end
|
19
|
+
|
20
|
+
SUCCESS_TERMINI = [:success, :pass_fast] # DISCUSS: where should this be defined?
|
21
|
+
|
22
|
+
# @private
|
23
|
+
# Invoker for Operation
|
24
|
+
def self.invoke_operation(operation, ctx)
|
25
|
+
result = operation.call(ctx)
|
26
|
+
|
27
|
+
return result.terminus, result # translate the holy {Operation::Result} object back to a normal "circuit interface" return value.
|
28
|
+
end
|
29
|
+
|
30
|
+
# @private
|
31
|
+
# Invoker with debugging for Operation
|
32
|
+
def self.invoke_operation_with_wtf(operation, ctx)
|
33
|
+
result = operation.wtf?(ctx)
|
34
|
+
|
35
|
+
return result.terminus, result
|
36
|
+
end
|
37
|
+
|
38
|
+
# Evaluate value if it's a lambda, and let the caller know whether we need an
|
39
|
+
# assert_equal or an assert.
|
40
|
+
def self.expected(asserted, value, actual)
|
41
|
+
value.is_a?(Proc) ? [value.(actual: actual, asserted: asserted), false] : [value, true]
|
42
|
+
end
|
43
|
+
|
44
|
+
# # Read the actual value from the asserted object (e.g. a model).
|
45
|
+
def self.actual(asserted, reader, name)
|
46
|
+
reader ? asserted.public_send(reader, name) : asserted.public_send(name)
|
47
|
+
end
|
48
|
+
|
49
|
+
# DISCUSS: move to Assertion::Minitest?
|
50
|
+
# Test case instance method. Specific to Minitest.
|
51
|
+
def assert_pass(activity, options, invoke: Assertion.method(:invoke_operation), model_at: :model, **kws, &block)
|
52
|
+
# DISCUSS: {:model_at} and {:invoke_method} block actual attributes.
|
53
|
+
AssertPass.(activity, options,
|
54
|
+
test: self,
|
55
|
+
user_block: block,
|
56
|
+
expected_model_attributes: kws,
|
57
|
+
model_at: model_at,
|
58
|
+
invoke: invoke,
|
59
|
+
) # Forward {#assert_pass} to {AssertPass.call} or wherever your implementation sits.
|
60
|
+
end
|
61
|
+
|
62
|
+
# DISCUSS: move to Assertion::Minitest?
|
63
|
+
# Test case instance method. Specific to Minitest.
|
64
|
+
def assert_fail(activity, options, *args, invoke: Assertion.method(:invoke_operation), **kws, &block)
|
65
|
+
AssertFail.(activity, options, *args, test: self, user_block: block, invoke: invoke, **kws) # Forward {#assert_fail} to {AssertFail.call} or wherever your implementation sits.
|
66
|
+
end
|
67
|
+
|
68
|
+
def assert_pass?(*args, **options, &block)
|
69
|
+
assert_pass(*args, **options, invoke: Assertion.method(:invoke_operation_with_wtf), &block)
|
70
|
+
end
|
71
|
+
|
72
|
+
def assert_fail?(*args, **options, &block)
|
73
|
+
assert_fail(*args, **options, invoke: Assertion.method(:invoke_operation_with_wtf), &block)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Assertions for Activity, not for Operation.
|
77
|
+
module Activity
|
78
|
+
def self.invoke_activity(activity, ctx)
|
79
|
+
signal, (ctx, _) = activity.call([ctx, {}]) # call with circuit interface. https://trailblazer.to/2.1/docs/operation/#operation-internals-circuit-interface
|
80
|
+
|
81
|
+
return signal, ctx
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.invoke_activity_with_task_wrap(activity, ctx)
|
85
|
+
signal, (ctx, _) = ::Trailblazer::Activity::TaskWrap.invoke(activity, [ctx, {}]) # call with circuit interface. https://trailblazer.to/2.1/docs/operation/#operation-internals-circuit-interface
|
86
|
+
|
87
|
+
return signal, ctx
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.invoke_activity_with_tracing(activity, ctx)
|
91
|
+
signal, (ctx, _) = Developer::Wtf.invoke(activity, [ctx, {}])
|
92
|
+
|
93
|
+
return signal, ctx
|
94
|
+
end
|
95
|
+
|
96
|
+
def assert_pass(*args, invoke: Activity.method(:invoke_activity_with_task_wrap), **options, &block)
|
97
|
+
super(*args, **options, invoke: invoke, &block)
|
98
|
+
end
|
99
|
+
|
100
|
+
def assert_fail(*args, invoke: Activity.method(:invoke_activity_with_task_wrap), **options, &block)
|
101
|
+
super(*args, **options, invoke: invoke, &block)
|
102
|
+
end
|
103
|
+
|
104
|
+
def assert_pass?(*args, **options, &block)
|
105
|
+
assert_pass(*args, **options, invoke: Activity.method(:invoke_activity_with_tracing), &block)
|
106
|
+
end
|
107
|
+
|
108
|
+
def assert_fail?(*args, **options, &block)
|
109
|
+
assert_fail(*args, **options, invoke: Activity.method(:invoke_activity_with_tracing), &block)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
module Test
|
3
|
+
# DISCUSS: this is not really endpoint related, it's more like setting a "global" invocation mechanism.
|
4
|
+
module Endpoint
|
5
|
+
# DISCUSS: not sure this is the final "technique" to use the global endpoint invoker.
|
6
|
+
def self.module!(receiver, invoke_method:, invoke_method_wtf:)
|
7
|
+
receiver.class_eval do
|
8
|
+
@@INVOKE_METHOD = invoke_method
|
9
|
+
@@INVOKE_METHOD_WTF = invoke_method_wtf
|
10
|
+
|
11
|
+
def trailblazer_test_invoke_method
|
12
|
+
@@INVOKE_METHOD
|
13
|
+
end
|
14
|
+
|
15
|
+
def trailblazer_test_invoke_method_wtf
|
16
|
+
@@INVOKE_METHOD_WTF
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Assertion
|
21
|
+
end
|
22
|
+
|
23
|
+
module Assertion
|
24
|
+
def assert_pass(*args, invoke: trailblazer_test_invoke_method, **options, &block)
|
25
|
+
super(*args, **options, invoke: invoke, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def assert_fail(*args, invoke: trailblazer_test_invoke_method, **options, &block)
|
29
|
+
super(*args, **options, invoke: invoke, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def assert_pass?(*args, **options, &block)
|
33
|
+
assert_pass(*args, **options, invoke: trailblazer_test_invoke_method_wtf, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def assert_fail?(*args, **options, &block)
|
37
|
+
assert_fail(*args, **options, invoke: trailblazer_test_invoke_method_wtf, &block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Trailblazer::Test
|
2
|
+
module Helper
|
3
|
+
module MockStep
|
4
|
+
def mock_step(activity, path:, &block)
|
5
|
+
raise ArgumentError, "Missing block: `#mock_step` requires a block." unless block_given?
|
6
|
+
|
7
|
+
mocked_step_id, path = path.last, path[0..-2]
|
8
|
+
|
9
|
+
mock_step = ->(ctx, **) { yield(ctx) }
|
10
|
+
mock_instruction = -> { step mock_step, replace: mocked_step_id }
|
11
|
+
|
12
|
+
|
13
|
+
Trailblazer::Activity::DSL::Linear::Patch.(activity, path, mock_instruction)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# [:delete_assets] => -> { step Destroy.method(:tidy_storage), before: :rm_uploads }
|