foreman_remote_execution 16.6.4 → 16.6.5
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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 803af8d6f1617172c150d68158d14d6fa9df2e5cc431226c1c0522f679420c7b
|
|
4
|
+
data.tar.gz: 43b21d05688e374f58999c890d0ed3fedcf8e98d978be053bc52f83701e89292
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9fed722946268e04461455cffe63c27add5a23a5a0e2bbdd698a2d7bd7c94017deb0342650ecaadf92426d64db8e40682f8871ab4a43a7e60e24c1096c230bb0
|
|
7
|
+
data.tar.gz: c11bfa8d9b1e72d2bb35d11829509923b6fb7babffd98a7782d5c8718adc9628def8739d388be1f5de07c239ba8b852c9de907c8e1b77383c573f460813ccff7
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require File.expand_path('../test_plugin_helper', __dir__)
|
|
4
|
+
require 'integration_test_helper'
|
|
5
|
+
require 'securerandom'
|
|
6
|
+
|
|
7
|
+
class JobWizardCategoryJsTest < IntegrationTestWithJavascript
|
|
8
|
+
WIZARD_INPUT_NAME_BASE = 'wizard_integration_input'
|
|
9
|
+
WIZARD_FEATURE_INPUT_NAME_BASE = 'wizard_integration_feature_input'
|
|
10
|
+
|
|
11
|
+
setup do
|
|
12
|
+
unique_suffix = SecureRandom.hex(6)
|
|
13
|
+
@wizard_input_name = "#{WIZARD_INPUT_NAME_BASE}_#{unique_suffix}"
|
|
14
|
+
@wizard_feature_input_name = "#{WIZARD_FEATURE_INPUT_NAME_BASE}_#{unique_suffix}"
|
|
15
|
+
default_template_name = "Integration Default REX Form Template #{unique_suffix}"
|
|
16
|
+
feature_template_name = "Integration Feature REX Template #{unique_suffix}"
|
|
17
|
+
|
|
18
|
+
# Prevent real Dynflow proxy calls in CI while planning jobs.
|
|
19
|
+
ProxyAPI::ForemanDynflow::DynflowProxy.any_instance.stubs(:tasks_count).returns(0)
|
|
20
|
+
|
|
21
|
+
@original_rex_form_template = Setting[:remote_execution_form_job_template]
|
|
22
|
+
as_admin do
|
|
23
|
+
@default_job_template = FactoryBot.create(
|
|
24
|
+
:job_template,
|
|
25
|
+
:name => default_template_name,
|
|
26
|
+
:job_category => 'Default Form Category'
|
|
27
|
+
)
|
|
28
|
+
@feature_job_template = FactoryBot.create(
|
|
29
|
+
:job_template,
|
|
30
|
+
:name => feature_template_name,
|
|
31
|
+
:job_category => 'Feature Linked Category'
|
|
32
|
+
)
|
|
33
|
+
add_plain_template_input!(@default_job_template, :name => @wizard_input_name)
|
|
34
|
+
add_plain_template_input!(@feature_job_template, :name => @wizard_feature_input_name)
|
|
35
|
+
Setting[:remote_execution_form_job_template] = @default_job_template.name
|
|
36
|
+
@remote_execution_feature = FactoryBot.create(
|
|
37
|
+
:remote_execution_feature,
|
|
38
|
+
:label => "integration_rex_category_feature_#{unique_suffix}",
|
|
39
|
+
:name => "Integration REX Category Feature #{unique_suffix}",
|
|
40
|
+
:job_template => @feature_job_template
|
|
41
|
+
)
|
|
42
|
+
@rex_host = FactoryBot.create(:host, :with_execution)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
teardown do
|
|
47
|
+
Setting[:remote_execution_form_job_template] = @original_rex_form_template
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
test 'job wizard shows feature template job category when feature query param is present' do
|
|
51
|
+
visit new_job_invocation_path(:feature => @remote_execution_feature.label)
|
|
52
|
+
assert_text 'Category and template', :wait => 30
|
|
53
|
+
wait_for_ajax
|
|
54
|
+
assert_selector(:ouia_component_id, 'job_category', :text => 'Feature Linked Category', :wait => 30)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
test 'job wizard shows default form category when no feature query param' do
|
|
58
|
+
visit new_job_invocation_path
|
|
59
|
+
assert_text 'Category and template', :wait => 30
|
|
60
|
+
wait_for_ajax
|
|
61
|
+
assert_selector(:ouia_component_id, 'job_category', :text => 'Default Form Category', :wait => 30)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
test 'job wizard prefills template input from inputs query param' do
|
|
65
|
+
visit new_job_invocation_path(
|
|
66
|
+
:inputs => { @wizard_input_name => 'from_query_string' },
|
|
67
|
+
:search => ''
|
|
68
|
+
)
|
|
69
|
+
go_to_target_hosts_and_inputs_step!
|
|
70
|
+
assert_selector "textarea##{@wizard_input_name}", :wait => 30
|
|
71
|
+
assert_equal 'from_query_string', find("textarea##{@wizard_input_name}").value
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
test 'job wizard does not prefill template input when inputs query param is omitted' do
|
|
75
|
+
visit new_job_invocation_path(:search => '')
|
|
76
|
+
go_to_target_hosts_and_inputs_step!
|
|
77
|
+
assert_selector "textarea##{@wizard_input_name}", :wait => 30
|
|
78
|
+
assert_equal '', find("textarea##{@wizard_input_name}").value
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
test 'job wizard prefills template input when feature and inputs query params are present' do
|
|
82
|
+
visit new_job_invocation_path(
|
|
83
|
+
:feature => @remote_execution_feature.label,
|
|
84
|
+
:inputs => { @wizard_feature_input_name => 'feature_and_inputs' },
|
|
85
|
+
:search => ''
|
|
86
|
+
)
|
|
87
|
+
go_to_target_hosts_and_inputs_step!
|
|
88
|
+
assert_selector "textarea##{@wizard_feature_input_name}", :wait => 30
|
|
89
|
+
assert_equal 'feature_and_inputs', find("textarea##{@wizard_feature_input_name}").value
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
test 'job wizard run on selected hosts is enabled on review and posts to job invocations API' do
|
|
93
|
+
posted_to_job_invocations_create = false
|
|
94
|
+
subscriber = ActiveSupport::Notifications.subscribe('start_processing.action_controller') do |_n, _s, _f, _i, payload|
|
|
95
|
+
next unless payload[:controller] == 'Api::V2::JobInvocationsController' && payload[:action].to_s == 'create'
|
|
96
|
+
root = payload[:params]
|
|
97
|
+
root = root.to_unsafe_h if root.respond_to?(:to_unsafe_h)
|
|
98
|
+
job_inv = (root[:job_invocation] || root['job_invocation'])
|
|
99
|
+
next unless job_inv
|
|
100
|
+
job_inv = job_inv.to_unsafe_h if job_inv.respond_to?(:to_unsafe_h)
|
|
101
|
+
next unless job_inv['job_template_id'].to_i == @default_job_template.id
|
|
102
|
+
posted_to_job_invocations_create = true
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
begin
|
|
106
|
+
visit new_job_invocation_path(:search => "id = #{@rex_host.id}")
|
|
107
|
+
navigate_job_wizard_to_review_step!
|
|
108
|
+
|
|
109
|
+
assert_selector(:ouia_component_id, 'run-on-selected-hosts-footer', :wait => 30)
|
|
110
|
+
run_on_hosts = find(:ouia_component_id, 'run-on-selected-hosts-footer')
|
|
111
|
+
assert_not_equal 'true', run_on_hosts[:'aria-disabled'], 'expected run-on-selected-hosts button to be enabled'
|
|
112
|
+
assert_text 'Run on selected hosts'
|
|
113
|
+
find(:ouia_component_id, 'run-on-selected-hosts-footer').click
|
|
114
|
+
wait_for_ajax
|
|
115
|
+
assert posted_to_job_invocations_create, 'expected Api::V2::JobInvocationsController#create to run'
|
|
116
|
+
assert_match(%r{\A/job_invocations/\d+\z}, page.current_path)
|
|
117
|
+
ensure
|
|
118
|
+
ActiveSupport::Notifications.unsubscribe(subscriber)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
test 'job wizard create API request sends job_template_id and inputs for default template' do
|
|
123
|
+
input_value = 'api_payload_default_template'
|
|
124
|
+
job_inv_params = capture_api_job_invocation_params_during(expected_template_id: @default_job_template.id) do
|
|
125
|
+
visit new_job_invocation_path(
|
|
126
|
+
:search => "id = #{@rex_host.id}",
|
|
127
|
+
:inputs => { @wizard_input_name => input_value }
|
|
128
|
+
)
|
|
129
|
+
navigate_job_wizard_to_review_step!
|
|
130
|
+
find(:ouia_component_id, 'run-on-selected-hosts-footer').click
|
|
131
|
+
wait_for_ajax
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
assert job_inv_params, 'expected job_invocation key in POST /api/job_invocations'
|
|
135
|
+
assert job_inv_params[:job_template_id].present?, 'expected job_template_id to be present'
|
|
136
|
+
assert_equal @default_job_template.id, job_inv_params[:job_template_id].to_i
|
|
137
|
+
assert job_inv_params[:feature].blank?
|
|
138
|
+
assert_equal input_value, hash_from_params_object(job_inv_params[:inputs])[@wizard_input_name]
|
|
139
|
+
job_invocation_id = page.current_path.split('/').last.to_i
|
|
140
|
+
assert_equal @default_job_template.job_category, JobInvocation.find(job_invocation_id).job_category, 'expected job category to be the default template category'
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
test 'job wizard create API request sends feature label and inputs without job_template_id' do
|
|
144
|
+
input_value = 'api_payload_feature_template'
|
|
145
|
+
job_inv_params = capture_api_job_invocation_params_during(expected_feature: @remote_execution_feature.label) do
|
|
146
|
+
visit new_job_invocation_path(
|
|
147
|
+
:feature => @remote_execution_feature.label,
|
|
148
|
+
:search => "id = #{@rex_host.id}",
|
|
149
|
+
:inputs => { @wizard_feature_input_name => input_value }
|
|
150
|
+
)
|
|
151
|
+
navigate_job_wizard_to_review_step!
|
|
152
|
+
find(:ouia_component_id, 'run-on-selected-hosts-footer').click
|
|
153
|
+
wait_for_ajax
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
assert job_inv_params, 'expected job_invocation key in POST /api/job_invocations'
|
|
157
|
+
assert_not job_inv_params[:job_template_id].present?
|
|
158
|
+
assert_equal @remote_execution_feature.label, job_inv_params[:feature]
|
|
159
|
+
assert_equal input_value, hash_from_params_object(job_inv_params[:inputs])[@wizard_feature_input_name]
|
|
160
|
+
job_invocation_id = page.current_path.split('/').last.to_i
|
|
161
|
+
assert_equal @feature_job_template.job_category, JobInvocation.find(job_invocation_id).job_category, 'expected job category to be the feature template category'
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
private
|
|
165
|
+
|
|
166
|
+
def capture_api_job_invocation_params_during(expected_template_id: nil, expected_feature: nil)
|
|
167
|
+
captured = nil
|
|
168
|
+
subscriber = ActiveSupport::Notifications.subscribe('start_processing.action_controller') do |_n, _s, _f, _i, payload|
|
|
169
|
+
next unless payload[:controller] == 'Api::V2::JobInvocationsController' && payload[:action].to_s == 'create'
|
|
170
|
+
root = payload[:params]
|
|
171
|
+
root = root.to_unsafe_h if root.respond_to?(:to_unsafe_h)
|
|
172
|
+
job_inv_params = root[:job_invocation] || root['job_invocation']
|
|
173
|
+
next unless job_inv_params
|
|
174
|
+
job_inv_params = job_inv_params.to_unsafe_h if job_inv_params.respond_to?(:to_unsafe_h)
|
|
175
|
+
job_inv_params = job_inv_params.with_indifferent_access
|
|
176
|
+
next if expected_template_id && job_inv_params[:job_template_id].to_i != expected_template_id
|
|
177
|
+
next if expected_feature && job_inv_params[:feature] != expected_feature
|
|
178
|
+
captured = job_inv_params
|
|
179
|
+
end
|
|
180
|
+
begin
|
|
181
|
+
yield
|
|
182
|
+
ensure
|
|
183
|
+
ActiveSupport::Notifications.unsubscribe(subscriber)
|
|
184
|
+
end
|
|
185
|
+
captured
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def hash_from_params_object(obj)
|
|
189
|
+
return {}.with_indifferent_access if obj.blank?
|
|
190
|
+
h = obj.respond_to?(:to_unsafe_h) ? obj.to_unsafe_h : obj.to_h
|
|
191
|
+
h.with_indifferent_access
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def add_plain_template_input!(job_template, name:)
|
|
195
|
+
job_template.template_inputs << FactoryBot.build(
|
|
196
|
+
:template_input,
|
|
197
|
+
:name => name,
|
|
198
|
+
:input_type => 'user',
|
|
199
|
+
:value_type => 'plain',
|
|
200
|
+
:required => false
|
|
201
|
+
)
|
|
202
|
+
job_template.save!
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def job_wizard_click_next!
|
|
206
|
+
wait_for_ajax
|
|
207
|
+
button = find(:ouia_component_id, 'next-footer')
|
|
208
|
+
page.execute_script('arguments[0].scrollIntoView({block: "center"})', button.native)
|
|
209
|
+
button.click
|
|
210
|
+
wait_for_ajax
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def go_to_target_hosts_and_inputs_step!
|
|
214
|
+
assert_text 'Category and template', :wait => 30
|
|
215
|
+
wait_for_ajax
|
|
216
|
+
job_wizard_click_next!
|
|
217
|
+
assert_text 'Target hosts and inputs', :wait => 30
|
|
218
|
+
wait_for_ajax
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def navigate_job_wizard_to_review_step!
|
|
222
|
+
assert_text 'Category and template', :wait => 30
|
|
223
|
+
wait_for_ajax
|
|
224
|
+
job_wizard_click_next!
|
|
225
|
+
assert_text 'Target hosts and inputs', :wait => 30
|
|
226
|
+
job_wizard_click_next!
|
|
227
|
+
assert_text 'Advanced fields', :wait => 30
|
|
228
|
+
job_wizard_click_next!
|
|
229
|
+
assert_text 'Immediate execution', :wait => 30
|
|
230
|
+
job_wizard_click_next!
|
|
231
|
+
assert_text 'Review details', :wait => 30
|
|
232
|
+
wait_for_ajax
|
|
233
|
+
end
|
|
234
|
+
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: foreman_remote_execution
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 16.6.
|
|
4
|
+
version: 16.6.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Foreman Remote Execution team
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-06-
|
|
10
|
+
date: 2026-06-23 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: deface
|
|
@@ -354,6 +354,7 @@ files:
|
|
|
354
354
|
- test/graphql/queries/job_invocation_query_test.rb
|
|
355
355
|
- test/graphql/queries/job_invocations_query_test.rb
|
|
356
356
|
- test/helpers/remote_execution_helper_test.rb
|
|
357
|
+
- test/integration/job_wizard_category_js_test.rb
|
|
357
358
|
- test/support/remote_execution_helper.rb
|
|
358
359
|
- test/test_plugin_helper.rb
|
|
359
360
|
- test/unit/actions/run_host_job_test.rb
|