cmdx 1.0.1 → 1.1.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/.cursor/prompts/rspec.md +20 -0
- data/.cursor/prompts/yardoc.md +8 -0
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +17 -2
- data/README.md +1 -1
- data/docs/basics/call.md +2 -2
- data/docs/basics/chain.md +1 -1
- data/docs/callbacks.md +3 -36
- data/docs/configuration.md +58 -12
- data/docs/interruptions/exceptions.md +1 -1
- data/docs/interruptions/faults.md +2 -2
- data/docs/logging.md +4 -4
- data/docs/middlewares.md +43 -43
- data/docs/parameters/coercions.md +49 -38
- data/docs/parameters/defaults.md +1 -1
- data/docs/parameters/validations.md +0 -39
- data/docs/testing.md +11 -12
- data/docs/workflows.md +4 -4
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/callback.rb +36 -56
- data/lib/cmdx/callback_registry.rb +82 -73
- data/lib/cmdx/chain.rb +65 -122
- data/lib/cmdx/chain_inspector.rb +22 -115
- data/lib/cmdx/chain_serializer.rb +17 -148
- data/lib/cmdx/coercion.rb +49 -0
- data/lib/cmdx/coercion_registry.rb +94 -0
- data/lib/cmdx/coercions/array.rb +18 -36
- data/lib/cmdx/coercions/big_decimal.rb +21 -33
- data/lib/cmdx/coercions/boolean.rb +21 -40
- data/lib/cmdx/coercions/complex.rb +18 -31
- data/lib/cmdx/coercions/date.rb +20 -39
- data/lib/cmdx/coercions/date_time.rb +22 -39
- data/lib/cmdx/coercions/float.rb +19 -32
- data/lib/cmdx/coercions/hash.rb +22 -41
- data/lib/cmdx/coercions/integer.rb +20 -33
- data/lib/cmdx/coercions/rational.rb +20 -32
- data/lib/cmdx/coercions/string.rb +23 -31
- data/lib/cmdx/coercions/time.rb +24 -40
- data/lib/cmdx/coercions/virtual.rb +14 -31
- data/lib/cmdx/configuration.rb +57 -171
- data/lib/cmdx/context.rb +22 -165
- data/lib/cmdx/core_ext/hash.rb +42 -67
- data/lib/cmdx/core_ext/module.rb +35 -79
- data/lib/cmdx/core_ext/object.rb +63 -98
- data/lib/cmdx/correlator.rb +40 -156
- data/lib/cmdx/error.rb +37 -202
- data/lib/cmdx/errors.rb +165 -202
- data/lib/cmdx/fault.rb +55 -158
- data/lib/cmdx/faults.rb +26 -137
- data/lib/cmdx/immutator.rb +22 -109
- data/lib/cmdx/lazy_struct.rb +103 -187
- data/lib/cmdx/log_formatters/json.rb +14 -40
- data/lib/cmdx/log_formatters/key_value.rb +14 -40
- data/lib/cmdx/log_formatters/line.rb +14 -48
- data/lib/cmdx/log_formatters/logstash.rb +14 -57
- data/lib/cmdx/log_formatters/pretty_json.rb +14 -50
- data/lib/cmdx/log_formatters/pretty_key_value.rb +13 -46
- data/lib/cmdx/log_formatters/pretty_line.rb +16 -54
- data/lib/cmdx/log_formatters/raw.rb +19 -49
- data/lib/cmdx/logger.rb +20 -82
- data/lib/cmdx/logger_ansi.rb +18 -75
- data/lib/cmdx/logger_serializer.rb +24 -114
- data/lib/cmdx/middleware.rb +38 -60
- data/lib/cmdx/middleware_registry.rb +81 -77
- data/lib/cmdx/middlewares/correlate.rb +41 -226
- data/lib/cmdx/middlewares/timeout.rb +46 -185
- data/lib/cmdx/parameter.rb +120 -198
- data/lib/cmdx/parameter_evaluator.rb +231 -0
- data/lib/cmdx/parameter_inspector.rb +25 -56
- data/lib/cmdx/parameter_registry.rb +59 -84
- data/lib/cmdx/parameter_serializer.rb +23 -74
- data/lib/cmdx/railtie.rb +24 -107
- data/lib/cmdx/result.rb +254 -260
- data/lib/cmdx/result_ansi.rb +19 -85
- data/lib/cmdx/result_inspector.rb +27 -68
- data/lib/cmdx/result_logger.rb +18 -81
- data/lib/cmdx/result_serializer.rb +28 -132
- data/lib/cmdx/rspec/matchers.rb +28 -0
- data/lib/cmdx/rspec/result_matchers/be_executed.rb +42 -0
- data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +94 -0
- data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +94 -0
- data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +59 -0
- data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +57 -0
- data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +87 -0
- data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +51 -0
- data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +58 -0
- data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +59 -0
- data/lib/cmdx/rspec/result_matchers/have_context.rb +86 -0
- data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +54 -0
- data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +52 -0
- data/lib/cmdx/rspec/result_matchers/have_metadata.rb +114 -0
- data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +66 -0
- data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +64 -0
- data/lib/cmdx/rspec/result_matchers/have_runtime.rb +78 -0
- data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +76 -0
- data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +62 -0
- data/lib/cmdx/rspec/task_matchers/have_callback.rb +85 -0
- data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +68 -0
- data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +92 -0
- data/lib/cmdx/rspec/task_matchers/have_middleware.rb +46 -0
- data/lib/cmdx/rspec/task_matchers/have_parameter.rb +181 -0
- data/lib/cmdx/task.rb +213 -425
- data/lib/cmdx/task_deprecator.rb +55 -0
- data/lib/cmdx/task_processor.rb +245 -0
- data/lib/cmdx/task_serializer.rb +22 -70
- data/lib/cmdx/utils/ansi_color.rb +13 -89
- data/lib/cmdx/utils/log_timestamp.rb +13 -42
- data/lib/cmdx/utils/monotonic_runtime.rb +13 -63
- data/lib/cmdx/utils/name_affix.rb +21 -71
- data/lib/cmdx/validator.rb +48 -0
- data/lib/cmdx/validator_registry.rb +86 -0
- data/lib/cmdx/validators/exclusion.rb +55 -94
- data/lib/cmdx/validators/format.rb +31 -85
- data/lib/cmdx/validators/inclusion.rb +65 -110
- data/lib/cmdx/validators/length.rb +117 -133
- data/lib/cmdx/validators/numeric.rb +123 -130
- data/lib/cmdx/validators/presence.rb +38 -79
- data/lib/cmdx/version.rb +1 -7
- data/lib/cmdx/workflow.rb +46 -339
- data/lib/cmdx.rb +1 -1
- data/lib/generators/cmdx/install_generator.rb +14 -31
- data/lib/generators/cmdx/task_generator.rb +39 -55
- data/lib/generators/cmdx/templates/install.rb +24 -6
- data/lib/generators/cmdx/workflow_generator.rb +41 -66
- data/lib/locales/ar.yml +0 -1
- data/lib/locales/cs.yml +0 -1
- data/lib/locales/da.yml +0 -1
- data/lib/locales/de.yml +0 -1
- data/lib/locales/el.yml +0 -1
- data/lib/locales/en.yml +0 -1
- data/lib/locales/es.yml +0 -1
- data/lib/locales/fi.yml +0 -1
- data/lib/locales/fr.yml +0 -1
- data/lib/locales/he.yml +0 -1
- data/lib/locales/hi.yml +0 -1
- data/lib/locales/it.yml +0 -1
- data/lib/locales/ja.yml +0 -1
- data/lib/locales/ko.yml +0 -1
- data/lib/locales/nl.yml +0 -1
- data/lib/locales/no.yml +0 -1
- data/lib/locales/pl.yml +0 -1
- data/lib/locales/pt.yml +0 -1
- data/lib/locales/ru.yml +0 -1
- data/lib/locales/sv.yml +0 -1
- data/lib/locales/th.yml +0 -1
- data/lib/locales/tr.yml +0 -1
- data/lib/locales/vi.yml +0 -1
- data/lib/locales/zh.yml +0 -1
- metadata +34 -8
- data/lib/cmdx/parameter_validator.rb +0 -81
- data/lib/cmdx/parameter_value.rb +0 -244
- data/lib/cmdx/parameters_inspector.rb +0 -72
- data/lib/cmdx/parameters_serializer.rb +0 -115
- data/lib/cmdx/rspec/result_matchers.rb +0 -917
- data/lib/cmdx/rspec/task_matchers.rb +0 -570
- data/lib/cmdx/validators/custom.rb +0 -102
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# RSpec matcher for asserting that a task class has a specific task setting.
|
4
|
+
#
|
5
|
+
# This matcher checks if a CMDx::Task class has registered a task setting with the
|
6
|
+
# specified name. Task settings are configuration options that control task behavior
|
7
|
+
# such as execution timeouts, retry policies, or custom flags. The matcher can
|
8
|
+
# optionally verify that the setting has a specific value for more precise validation.
|
9
|
+
#
|
10
|
+
# @param setting_name [Symbol, String] the name of the task setting to check for
|
11
|
+
# @param expected_value [Object, nil] the expected value of the setting (optional)
|
12
|
+
#
|
13
|
+
# @return [Boolean] true if the task has the specified setting and optionally the expected value
|
14
|
+
#
|
15
|
+
# @example Testing basic task setting presence
|
16
|
+
# class MyTask < CMDx::Task
|
17
|
+
# cmd_setting tags: ["admin"]
|
18
|
+
# def call; end
|
19
|
+
# end
|
20
|
+
# expect(MyTask).to have_cmd_setting(:tags)
|
21
|
+
#
|
22
|
+
# @example Testing task setting with specific value
|
23
|
+
# class ProcessTask < CMDx::Task
|
24
|
+
# cmd_setting tags: ["admin"]
|
25
|
+
# def call; end
|
26
|
+
# end
|
27
|
+
# expect(ProcessTask).to have_cmd_setting(:tags, ["admin"])
|
28
|
+
#
|
29
|
+
# @example Negative assertion
|
30
|
+
# class SimpleTask < CMDx::Task
|
31
|
+
# def call; end
|
32
|
+
# end
|
33
|
+
# expect(SimpleTask).not_to have_cmd_setting(:tags)
|
34
|
+
RSpec::Matchers.define :have_cmd_setting do |setting_name, expected_value = nil|
|
35
|
+
match do |task_class|
|
36
|
+
return false unless task_class.cmd_setting?(setting_name)
|
37
|
+
|
38
|
+
if expected_value
|
39
|
+
task_class.cmd_setting(setting_name) == expected_value
|
40
|
+
else
|
41
|
+
true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
failure_message do |task_class|
|
46
|
+
if expected_value
|
47
|
+
actual_value = task_class.cmd_setting(setting_name)
|
48
|
+
"expected task to have setting #{setting_name} with value #{expected_value}, but was #{actual_value}"
|
49
|
+
else
|
50
|
+
available_settings = task_class.cmd_settings.keys
|
51
|
+
"expected task to have setting #{setting_name}, but had #{available_settings}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
failure_message_when_negated do |_task_class|
|
56
|
+
if expected_value
|
57
|
+
"expected task not to have setting #{setting_name} with value #{expected_value}, but it did"
|
58
|
+
else
|
59
|
+
"expected task not to have setting #{setting_name}, but it did"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
description do
|
64
|
+
desc = "have task setting #{setting_name}"
|
65
|
+
desc += " with value #{expected_value}" if expected_value
|
66
|
+
desc
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# RSpec matcher for asserting that a task has executed specific callbacks.
|
4
|
+
#
|
5
|
+
# This matcher verifies that callbacks were actually invoked during task execution,
|
6
|
+
# not just registered. It works by mocking the callback execution to track which
|
7
|
+
# callbacks are called, then executing the task and checking that the expected
|
8
|
+
# callbacks were invoked. This is useful for testing that callback logic is properly
|
9
|
+
# triggered during task execution rather than just checking callback registration.
|
10
|
+
#
|
11
|
+
# @param callback_names [Array<Symbol, String>] the names of callbacks expected to execute
|
12
|
+
#
|
13
|
+
# @return [Boolean] true if all specified callbacks were executed during task execution
|
14
|
+
#
|
15
|
+
# @example Testing basic callback execution
|
16
|
+
# class MyTask < CMDx::Task
|
17
|
+
# before_execution :setup
|
18
|
+
# def call; end
|
19
|
+
# end
|
20
|
+
# expect(MyTask.new).to have_executed_callbacks(:before_execution)
|
21
|
+
#
|
22
|
+
# @example Testing callback execution with specific callable
|
23
|
+
# class ProcessTask < CMDx::Task
|
24
|
+
# after_execution :log_completion
|
25
|
+
# def call; end
|
26
|
+
# end
|
27
|
+
# expect(ProcessTask).to have_callback(:after_execution).with_callable(:log_completion)
|
28
|
+
#
|
29
|
+
# @example Testing callback execution with result
|
30
|
+
# result = MyTask.call(data: "test")
|
31
|
+
# expect(result).to have_executed_callbacks(:before_execution, :after_execution)
|
32
|
+
#
|
33
|
+
# @example Negative assertion
|
34
|
+
# class SimpleTask < CMDx::Task
|
35
|
+
# def call; end
|
36
|
+
# end
|
37
|
+
# expect(SimpleTask.new).not_to have_executed_callbacks(:before_execution)
|
38
|
+
RSpec::Matchers.define :have_executed_callbacks do |*callback_names|
|
39
|
+
match do |task_or_result|
|
40
|
+
@executed_callbacks = []
|
41
|
+
|
42
|
+
# Mock the callback execution to track what gets called
|
43
|
+
if task_or_result.is_a?(CMDx::Task)
|
44
|
+
task = task_or_result
|
45
|
+
original_callback_call = task.cmd_callbacks.method(:call)
|
46
|
+
|
47
|
+
allow(task.cmd_callbacks).to receive(:call) do |task_instance, callback_name|
|
48
|
+
@executed_callbacks << callback_name
|
49
|
+
original_callback_call.call(task_instance, callback_name)
|
50
|
+
end
|
51
|
+
|
52
|
+
task.process
|
53
|
+
else
|
54
|
+
# If it's a result, check if callbacks were executed during task execution
|
55
|
+
result = task_or_result
|
56
|
+
# This would require the callbacks to be tracked during execution
|
57
|
+
# For now, assume callbacks were executed based on result state
|
58
|
+
@executed_callbacks = infer_executed_callbacks(result)
|
59
|
+
end
|
60
|
+
|
61
|
+
callback_names.all? { |callback_name| @executed_callbacks.include?(callback_name) }
|
62
|
+
end
|
63
|
+
|
64
|
+
failure_message do |_task_or_result|
|
65
|
+
missing_callbacks = callback_names - @executed_callbacks
|
66
|
+
"expected to execute callbacks #{callback_names}, but missing #{missing_callbacks}. Executed: #{@executed_callbacks}"
|
67
|
+
end
|
68
|
+
|
69
|
+
failure_message_when_negated do |_task_or_result|
|
70
|
+
"expected not to execute callbacks #{callback_names}, but executed #{@executed_callbacks & callback_names}"
|
71
|
+
end
|
72
|
+
|
73
|
+
description do
|
74
|
+
"execute callbacks #{callback_names}"
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def infer_executed_callbacks(result)
|
80
|
+
callbacks = []
|
81
|
+
callbacks << :before_validation if result.executed?
|
82
|
+
callbacks << :after_validation if result.executed?
|
83
|
+
callbacks << :before_execution if result.executed?
|
84
|
+
callbacks << :after_execution if result.executed?
|
85
|
+
callbacks << :on_executed if result.executed?
|
86
|
+
callbacks << :"on_#{result.status}" if result.executed?
|
87
|
+
callbacks << :on_good if result.good?
|
88
|
+
callbacks << :on_bad if result.bad?
|
89
|
+
callbacks << :"on_#{result.state}" if result.executed?
|
90
|
+
callbacks
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# RSpec matcher for asserting that a task class has a specific middleware.
|
4
|
+
#
|
5
|
+
# This matcher checks if a CMDx::Task class has registered a middleware of the
|
6
|
+
# specified class. Middlewares are components that wrap around task execution to
|
7
|
+
# provide cross-cutting concerns like logging, timing, error handling, or other
|
8
|
+
# aspects. The matcher verifies that the middleware is properly registered and
|
9
|
+
# available for execution during task performance.
|
10
|
+
#
|
11
|
+
# @param middleware_class [Class] the middleware class to check for
|
12
|
+
#
|
13
|
+
# @return [Boolean] true if the task has the specified middleware class registered
|
14
|
+
#
|
15
|
+
# @example Testing middleware registration
|
16
|
+
# class MyTask < CMDx::Task
|
17
|
+
# use :middleware, CMDx::Middlewares::Timeout, timeout: 10
|
18
|
+
# def call; end
|
19
|
+
# end
|
20
|
+
# expect(MyTask).to have_middleware(TimeoutMiddleware)
|
21
|
+
#
|
22
|
+
# @example Negative assertion
|
23
|
+
# class SimpleTask < CMDx::Task
|
24
|
+
# def call; end
|
25
|
+
# end
|
26
|
+
# expect(SimpleTask).not_to have_middleware(TimeoutMiddleware)
|
27
|
+
RSpec::Matchers.define :have_middleware do |middleware_class|
|
28
|
+
match do |task_class|
|
29
|
+
task_class.cmd_middlewares.any? do |middleware|
|
30
|
+
middleware.is_a?(middleware_class) || middleware.instance_of?(middleware_class)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
failure_message do |task_class|
|
35
|
+
middleware_classes = task_class.cmd_middlewares.map(&:class)
|
36
|
+
"expected task to have middleware #{middleware_class}, but had #{middleware_classes}"
|
37
|
+
end
|
38
|
+
|
39
|
+
failure_message_when_negated do |_task_class|
|
40
|
+
"expected task not to have middleware #{middleware_class}, but it did"
|
41
|
+
end
|
42
|
+
|
43
|
+
description do
|
44
|
+
"have middleware #{middleware_class}"
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# RSpec matcher for asserting that a task class has a specific parameter.
|
4
|
+
#
|
5
|
+
# This matcher checks if a CMDx::Task class has registered a parameter with the
|
6
|
+
# specified name. Parameters are inputs to task execution that can be required
|
7
|
+
# or optional, typed with coercions, validated, and have default values. The
|
8
|
+
# matcher supports various chain methods for precise parameter validation.
|
9
|
+
#
|
10
|
+
# @param parameter_name [Symbol, String] the name of the parameter to check for
|
11
|
+
#
|
12
|
+
# @return [Boolean] true if the task has the specified parameter and optionally matches all criteria
|
13
|
+
#
|
14
|
+
# @example Testing basic parameter presence
|
15
|
+
# class MyTask < CMDx::Task
|
16
|
+
# optional :input_file, type: :string
|
17
|
+
# def call; end
|
18
|
+
# end
|
19
|
+
# expect(MyTask).to have_parameter(:input_file)
|
20
|
+
#
|
21
|
+
# @example Testing required parameter
|
22
|
+
# class ProcessTask < CMDx::Task
|
23
|
+
# required data, type: :string
|
24
|
+
# def call; end
|
25
|
+
# end
|
26
|
+
# expect(ProcessTask).to have_parameter(:data).that_is_required
|
27
|
+
#
|
28
|
+
# @example Testing optional parameter with default
|
29
|
+
# class ConfigTask < CMDx::Task
|
30
|
+
# optional timeout, type: :integer, default: 30
|
31
|
+
# def call; end
|
32
|
+
# end
|
33
|
+
# expect(ConfigTask).to have_parameter(:timeout).that_is_optional.with_default(30)
|
34
|
+
#
|
35
|
+
# @example Testing parameter with type coercion
|
36
|
+
# class ImportTask < CMDx::Task
|
37
|
+
# optional csv_file, type: :string
|
38
|
+
# optional batch_size, type: :integer
|
39
|
+
# def call; end
|
40
|
+
# end
|
41
|
+
# expect(ImportTask).to have_parameter(:csv_file).with_type(:string)
|
42
|
+
# expect(ImportTask).to have_parameter(:batch_size).with_coercion(:integer)
|
43
|
+
#
|
44
|
+
# @example Testing parameter with validations
|
45
|
+
# class UserTask < CMDx::Task
|
46
|
+
# optional email, type: :string, format: /@/, presence: true
|
47
|
+
# def call; end
|
48
|
+
# end
|
49
|
+
# expect(UserTask).to have_parameter(:email).with_validations(:format, :presence)
|
50
|
+
#
|
51
|
+
# @example Negative assertion
|
52
|
+
# class SimpleTask < CMDx::Task
|
53
|
+
# def call; end
|
54
|
+
# end
|
55
|
+
# expect(SimpleTask).not_to have_parameter(:nonexistent)
|
56
|
+
RSpec::Matchers.define :have_parameter do |parameter_name|
|
57
|
+
match do |task_class|
|
58
|
+
@parameter = task_class.cmd_parameters.registry.find { |p| p.method_name == parameter_name }
|
59
|
+
return false unless @parameter
|
60
|
+
|
61
|
+
# Check if parameter exists
|
62
|
+
parameter_exists = !@parameter.nil?
|
63
|
+
return false unless parameter_exists
|
64
|
+
|
65
|
+
# Check required/optional if specified
|
66
|
+
unless @expected_required.nil?
|
67
|
+
required_matches = @parameter.required? == @expected_required
|
68
|
+
return false unless required_matches
|
69
|
+
end
|
70
|
+
|
71
|
+
# Check type/coercion if specified
|
72
|
+
if @expected_type
|
73
|
+
type_matches = @parameter.type == @expected_type
|
74
|
+
return false unless type_matches
|
75
|
+
end
|
76
|
+
|
77
|
+
# Check validations if specified
|
78
|
+
if @expected_validations&.any?
|
79
|
+
validations_match = @expected_validations.all? do |validation_type|
|
80
|
+
@parameter.options.key?(validation_type)
|
81
|
+
end
|
82
|
+
return false unless validations_match
|
83
|
+
end
|
84
|
+
|
85
|
+
# Check default value if specified
|
86
|
+
if @expected_default_value != :__not_specified__
|
87
|
+
default_matches = @parameter.options[:default] == @expected_default_value
|
88
|
+
return false unless default_matches
|
89
|
+
end
|
90
|
+
|
91
|
+
true
|
92
|
+
end
|
93
|
+
|
94
|
+
chain :that_is_required do
|
95
|
+
@expected_required = true
|
96
|
+
end
|
97
|
+
|
98
|
+
chain :that_is_optional do
|
99
|
+
@expected_required = false
|
100
|
+
end
|
101
|
+
|
102
|
+
chain :with_type do |type|
|
103
|
+
@expected_type = type
|
104
|
+
end
|
105
|
+
|
106
|
+
chain :with_coercion do |type|
|
107
|
+
@expected_type = type
|
108
|
+
end
|
109
|
+
|
110
|
+
chain :with_validations do |*validations|
|
111
|
+
@expected_validations = validations
|
112
|
+
end
|
113
|
+
|
114
|
+
chain :with_validation do |validation|
|
115
|
+
@expected_validations = [@expected_validations, validation].flatten.compact
|
116
|
+
end
|
117
|
+
|
118
|
+
chain :with_default do |default_value|
|
119
|
+
@expected_default_value = default_value
|
120
|
+
end
|
121
|
+
|
122
|
+
define_method :initialize do |parameter_name|
|
123
|
+
@parameter_name = parameter_name
|
124
|
+
@expected_required = nil
|
125
|
+
@expected_type = nil
|
126
|
+
@expected_validations = []
|
127
|
+
@expected_default_value = :__not_specified__
|
128
|
+
end
|
129
|
+
|
130
|
+
failure_message do |task_class|
|
131
|
+
if @parameter.nil?
|
132
|
+
available_parameters = task_class.cmd_parameters.registry.map(&:method_name)
|
133
|
+
"expected task to have parameter #{@parameter_name}, but had parameters: #{available_parameters}"
|
134
|
+
else
|
135
|
+
issues = []
|
136
|
+
|
137
|
+
if !@expected_required.nil? && @parameter.required? != @expected_required
|
138
|
+
expected_req_text = @expected_required ? "required" : "optional"
|
139
|
+
actual_req_text = @parameter.required? ? "required" : "optional"
|
140
|
+
issues << "expected parameter to be #{expected_req_text}, but was #{actual_req_text}"
|
141
|
+
end
|
142
|
+
|
143
|
+
if @expected_type
|
144
|
+
actual_type = @parameter.type
|
145
|
+
issues << "expected parameter type to be #{@expected_type}, but was #{actual_type}" unless actual_type == @expected_type
|
146
|
+
end
|
147
|
+
|
148
|
+
if @expected_validations&.any?
|
149
|
+
missing_validations = @expected_validations.reject do |validation_type|
|
150
|
+
@parameter.options.key?(validation_type)
|
151
|
+
end
|
152
|
+
|
153
|
+
if missing_validations.any?
|
154
|
+
actual_validations = @parameter.options.keys
|
155
|
+
issues << "expected parameter to have validations #{missing_validations}, but had #{actual_validations}"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
issues << "expected parameter default to be #{@expected_default_value}, but was #{@parameter.options[:default]}" if (@expected_default_value != :__not_specified__) && @parameter.options[:default] != @expected_default_value
|
160
|
+
|
161
|
+
if issues.any?
|
162
|
+
"expected parameter #{@parameter_name} to match criteria, but #{issues.join(', ')}"
|
163
|
+
else
|
164
|
+
"expected parameter #{@parameter_name} to match all criteria, but something didn't match"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
failure_message_when_negated do |_task_class|
|
170
|
+
"expected task not to have parameter #{@parameter_name}, but it did"
|
171
|
+
end
|
172
|
+
|
173
|
+
description do
|
174
|
+
desc = "have parameter #{@parameter_name}"
|
175
|
+
desc += " that is #{@expected_required ? 'required' : 'optional'}" unless @expected_required.nil?
|
176
|
+
desc += " with type #{@expected_type}" if @expected_type
|
177
|
+
desc += " with validations #{@expected_validations}" if @expected_validations&.any?
|
178
|
+
desc += " with default #{@expected_default_value}" if @expected_default_value != :__not_specified__
|
179
|
+
desc
|
180
|
+
end
|
181
|
+
end
|