foreman_openbolt 1.1.0 → 1.1.1
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/README.md +9 -15
- data/Rakefile +0 -0
- data/lib/foreman_openbolt/version.rb +1 -1
- data/package.json +1 -1
- data/test/acceptance/acceptance_helper.rb +146 -0
- data/test/acceptance/docker/docker-compose.yml +69 -0
- data/test/acceptance/docker/foreman/Dockerfile +45 -0
- data/test/acceptance/docker/foreman/entrypoint.sh +26 -0
- data/test/acceptance/docker/target/Dockerfile +29 -0
- data/test/acceptance/docker/target/entrypoint.sh +11 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/complex_params.json +30 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/complex_params.sh +16 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/echo.json +13 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/echo.sh +3 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/failing_task.json +8 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/failing_task.sh +3 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/noop_task.json +8 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/noop_task.sh +2 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/slow_task.json +14 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/slow_task.sh +3 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/target_conditional.json +13 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/target_conditional.sh +9 -0
- data/test/acceptance/fixtures/openbolt.yml +7 -0
- data/test/acceptance/tests/error_handling_test.rb +40 -0
- data/test/acceptance/tests/host_selector_test.rb +31 -0
- data/test/acceptance/tests/launch_task_test.rb +96 -0
- data/test/acceptance/tests/parameter_table_test.rb +61 -0
- data/test/acceptance/tests/settings_test.rb +95 -0
- data/test/acceptance/tests/ssh_options_test.rb +77 -0
- data/test/acceptance/tests/task_execution_test.rb +40 -0
- data/test/acceptance/tests/task_history_test.rb +84 -0
- data/test/acceptance/tests/transport_options_test.rb +121 -0
- data/test/test_plugin_helper.rb +17 -0
- data/test/unit/controllers/task_controller_test.rb +351 -0
- data/test/unit/docker/Dockerfile +47 -0
- data/test/unit/docker/docker-compose.yml +33 -0
- data/test/unit/docker/entrypoint.sh +4 -0
- data/test/unit/factories/foreman_openbolt_factories.rb +39 -0
- data/test/unit/lib/actions/cleanup_proxy_artifacts_test.rb +51 -0
- data/test/unit/lib/actions/poll_task_status_test.rb +141 -0
- data/test/unit/lib/proxy_api/openbolt_test.rb +174 -0
- data/test/unit/models/task_job_test.rb +278 -0
- metadata +39 -1
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../acceptance_helper'
|
|
4
|
+
|
|
5
|
+
# Tests launching various task types and verifying their execution results.
|
|
6
|
+
class LaunchTaskTest < AcceptanceTestCase
|
|
7
|
+
def setup
|
|
8
|
+
super
|
|
9
|
+
foreman_login
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def test_echo_task_succeeds_on_all_targets
|
|
13
|
+
launch_task_via_ui('acceptance::echo',
|
|
14
|
+
params: { 'message' => 'hello from acceptance test' })
|
|
15
|
+
|
|
16
|
+
assert_task_completed
|
|
17
|
+
assert_result_has_content
|
|
18
|
+
assert_result_contains 'hello from acceptance test'
|
|
19
|
+
assert_result_contains 'hostname'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_noop_task_succeeds
|
|
23
|
+
launch_task_via_ui('acceptance::noop_task')
|
|
24
|
+
assert_task_completed
|
|
25
|
+
assert_result_has_content
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_complex_params_task_succeeds
|
|
29
|
+
launch_task_via_ui('acceptance::complex_params',
|
|
30
|
+
params: {
|
|
31
|
+
'required_string' => 'test_value',
|
|
32
|
+
'array_param' => '["a","b","c"]',
|
|
33
|
+
'with_default' => 'overridden',
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
assert_task_completed
|
|
37
|
+
assert_result_has_content
|
|
38
|
+
assert_result_contains 'test_value'
|
|
39
|
+
assert_result_contains 'overridden'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def test_slow_task_transitions_through_running
|
|
43
|
+
launch_task_via_ui('acceptance::slow_task', params: { 'seconds' => '8' })
|
|
44
|
+
assert_selector '.pf-v5-c-label', text: /Running/i, wait: 30
|
|
45
|
+
assert_task_completed
|
|
46
|
+
assert_result_has_content
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def test_failing_task_shows_failure_with_error_detail
|
|
50
|
+
launch_task_via_ui('acceptance::failing_task')
|
|
51
|
+
assert_task_failed
|
|
52
|
+
assert_result_contains 'This task always fails'
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def test_run_another_task_navigates_back
|
|
56
|
+
launch_task_via_ui('acceptance::noop_task')
|
|
57
|
+
assert_task_completed
|
|
58
|
+
click_button 'Run Another Task'
|
|
59
|
+
assert_selector 'h1', text: 'Launch OpenBolt Task', wait: 15
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_launch_button_disabled_until_all_selections_made
|
|
63
|
+
visit '/foreman_openbolt/page_launch_task'
|
|
64
|
+
assert_selector '#smart-proxy-input', wait: 15
|
|
65
|
+
|
|
66
|
+
# No selections at all
|
|
67
|
+
assert find('button', text: /Launch Task/).disabled?,
|
|
68
|
+
'Expected Launch Task disabled with no selections'
|
|
69
|
+
|
|
70
|
+
# Proxy only
|
|
71
|
+
select_first_proxy
|
|
72
|
+
assert_selector '#task-name-input option', minimum: 2, wait: 15
|
|
73
|
+
assert find('button', text: /Launch Task/).disabled?,
|
|
74
|
+
'Expected Launch Task disabled with only proxy selected'
|
|
75
|
+
|
|
76
|
+
# Proxy + task, no targets
|
|
77
|
+
select 'acceptance::noop_task', from: 'task-name-input'
|
|
78
|
+
assert find('button', text: /Launch Task/).disabled?,
|
|
79
|
+
'Expected Launch Task disabled with no targets'
|
|
80
|
+
|
|
81
|
+
# Proxy + task + targets — button enables
|
|
82
|
+
select_hosts_via_search('target1')
|
|
83
|
+
assert_selector 'button:not([disabled])', text: /Launch Task/, wait: 10
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def test_running_task_shows_loading_indicator
|
|
87
|
+
launch_task_via_ui('acceptance::slow_task', params: { 'seconds' => '8' })
|
|
88
|
+
# While the job is still polling, LoadingIndicator renders an EmptyState
|
|
89
|
+
# with role=status and a title of "Task is <status>..." (running or pending).
|
|
90
|
+
assert_selector '[role="status"]',
|
|
91
|
+
text: /Task is (running|pending)/i, wait: 30
|
|
92
|
+
assert_selector '.pf-v5-c-empty-state__body',
|
|
93
|
+
text: /update automatically when the task completes/, wait: 5
|
|
94
|
+
assert_task_completed
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../acceptance_helper'
|
|
4
|
+
|
|
5
|
+
# Tests the parameter table rendering logic: required indicator
|
|
6
|
+
# based on the Optional[...] type prefix, and the expandable row
|
|
7
|
+
# that reveals type and description for each parameter.
|
|
8
|
+
class ParameterTableTest < AcceptanceTestCase
|
|
9
|
+
def setup
|
|
10
|
+
super
|
|
11
|
+
foreman_login
|
|
12
|
+
visit '/foreman_openbolt/page_launch_task'
|
|
13
|
+
assert_selector '#smart-proxy-input', wait: 15
|
|
14
|
+
select_first_proxy
|
|
15
|
+
assert_selector '#task-name-input option', minimum: 2, wait: 15
|
|
16
|
+
select 'acceptance::complex_params', from: 'task-name-input'
|
|
17
|
+
assert_selector '#param_required_string', wait: 10
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def test_required_param_shows_required_indicator
|
|
21
|
+
# ParametersSection flags params whose type does not start with
|
|
22
|
+
# "optional" as required, and FieldTable renders a span with
|
|
23
|
+
# role=img and aria-label="Required" in those rows.
|
|
24
|
+
within(:xpath, "//tr[.//input[@id='param_required_string']]") do
|
|
25
|
+
assert_selector 'span[role="img"][aria-label="Required"]'
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def test_optional_param_has_no_required_indicator
|
|
30
|
+
within(:xpath, "//tr[.//input[@id='param_optional_string']]") do
|
|
31
|
+
assert_no_selector 'span[role="img"][aria-label="Required"]'
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_expanding_required_param_row_shows_type_description_and_required_warning
|
|
36
|
+
within(:xpath, "//tr[.//input[@id='param_required_string']]") do
|
|
37
|
+
first('button').click
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# The expanded row is a sibling in the same Tbody. Assertions on
|
|
41
|
+
# the page level since the ExpandableRowContent is outside the
|
|
42
|
+
# trigger row's scope.
|
|
43
|
+
assert_selector '.pf-v5-c-helper-text__item',
|
|
44
|
+
text: 'This field is required', wait: 5
|
|
45
|
+
assert_selector '.pf-v5-c-helper-text__item code', text: 'String'
|
|
46
|
+
assert_selector '.pf-v5-c-helper-text__item',
|
|
47
|
+
text: 'A required string parameter'
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def test_expanding_optional_param_row_shows_type_and_description_without_required_warning
|
|
51
|
+
within(:xpath, "//tr[.//input[@id='param_optional_string']]") do
|
|
52
|
+
first('button').click
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
assert_selector '.pf-v5-c-helper-text__item code', text: 'Optional[String]'
|
|
56
|
+
assert_selector '.pf-v5-c-helper-text__item',
|
|
57
|
+
text: 'An optional string parameter'
|
|
58
|
+
assert_no_selector '.pf-v5-c-helper-text__item',
|
|
59
|
+
text: 'This field is required'
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../acceptance_helper'
|
|
4
|
+
|
|
5
|
+
# Tests that Foreman settings correctly populate the OpenBolt options UI
|
|
6
|
+
# and that changes to settings are reflected in the plugin's behavior.
|
|
7
|
+
# This exercises the settings lookup bug fix (Foreman.settings vs Setting.where).
|
|
8
|
+
class SettingsTest < AcceptanceTestCase
|
|
9
|
+
def setup
|
|
10
|
+
super
|
|
11
|
+
foreman_login
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_openbolt_user_setting_populates_default
|
|
15
|
+
visit '/foreman_openbolt/page_launch_task'
|
|
16
|
+
assert_selector '#smart-proxy-input', wait: 15
|
|
17
|
+
select_first_proxy
|
|
18
|
+
|
|
19
|
+
# The user option should show the value configured during acceptance:up
|
|
20
|
+
assert_selector '#param_user', wait: 10
|
|
21
|
+
assert_equal 'openbolt', find_by_id('param_user').value
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# The string the UI shows in an encrypted-default field so the user can tell
|
|
25
|
+
# a saved value exists without exposing it to the browser. Kept in sync with
|
|
26
|
+
# ENCRYPTED_PLACEHOLDER in task_controller.rb and ENCRYPTED_DEFAULT_PLACEHOLDER
|
|
27
|
+
# in webpack/src/Components/common/constants.js.
|
|
28
|
+
ENCRYPTED_DEFAULT_PLACEHOLDER = '[Use saved encrypted default]'
|
|
29
|
+
|
|
30
|
+
def test_encrypted_setting_shows_placeholder_and_is_overrideable
|
|
31
|
+
# Scope the saved password to this test so it does not leak into other
|
|
32
|
+
# task launches (merge_encrypted_defaults would inject it into bolt).
|
|
33
|
+
update_foreman_setting('openbolt_password', 'acceptance-test-password')
|
|
34
|
+
begin
|
|
35
|
+
visit '/foreman_openbolt/page_launch_task'
|
|
36
|
+
assert_selector '#smart-proxy-input', wait: 15
|
|
37
|
+
select_first_proxy
|
|
38
|
+
|
|
39
|
+
# With a saved encrypted setting, the field prefills with the placeholder
|
|
40
|
+
# string so the user knows the saved value will be used. The real value
|
|
41
|
+
# is never sent to the browser.
|
|
42
|
+
field = find_by_id('param_password', wait: 10)
|
|
43
|
+
assert_equal 'password', field['type']
|
|
44
|
+
assert_equal ENCRYPTED_DEFAULT_PLACEHOLDER, field.value.to_s
|
|
45
|
+
|
|
46
|
+
# Expanding the row reveals a warning that an encrypted default is saved.
|
|
47
|
+
# The FieldTable renders an expand toggle in the first cell of each row;
|
|
48
|
+
# click the first button within the row containing our field.
|
|
49
|
+
within(:xpath, "//tr[.//input[@id='param_password']]") do
|
|
50
|
+
first('button').click
|
|
51
|
+
end
|
|
52
|
+
assert_selector '.pf-v5-c-helper-text__item',
|
|
53
|
+
text: /saved, encrypted default/i, wait: 10
|
|
54
|
+
|
|
55
|
+
# Typing a new value must replace the placeholder so the task uses
|
|
56
|
+
# what the user entered instead of the saved default.
|
|
57
|
+
field.fill_in with: 'override-value'
|
|
58
|
+
assert_equal 'override-value', field.value.to_s
|
|
59
|
+
ensure
|
|
60
|
+
update_foreman_setting('openbolt_password', '')
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def test_changing_user_setting_updates_launch_page
|
|
65
|
+
# Change the setting through the Foreman settings UI
|
|
66
|
+
update_foreman_setting('openbolt_user', 'newuser')
|
|
67
|
+
|
|
68
|
+
# Verify the launch page reflects the change
|
|
69
|
+
visit '/foreman_openbolt/page_launch_task'
|
|
70
|
+
assert_selector '#smart-proxy-input', wait: 15
|
|
71
|
+
select_first_proxy
|
|
72
|
+
assert_selector '#param_user', wait: 10
|
|
73
|
+
assert_equal 'newuser', find_by_id('param_user').value
|
|
74
|
+
|
|
75
|
+
# Restore the original value
|
|
76
|
+
update_foreman_setting('openbolt_user', 'openbolt')
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def test_host_key_check_setting_flows_through_to_checkbox_state
|
|
80
|
+
visit '/foreman_openbolt/page_launch_task'
|
|
81
|
+
assert_selector '#smart-proxy-input', wait: 15
|
|
82
|
+
select_first_proxy
|
|
83
|
+
|
|
84
|
+
# openbolt_host-key-check is a boolean Foreman setting; the
|
|
85
|
+
# acceptance fixture (rakelib/acceptance.rake) sets it to false so
|
|
86
|
+
# SSH does not reject the ephemeral target containers. The registered
|
|
87
|
+
# default in engine.rb is true, so if the Foreman setting lookup
|
|
88
|
+
# were broken, the checkbox would render checked. Asserting
|
|
89
|
+
# unchecked here proves the configured value (false) flowed through.
|
|
90
|
+
field = find_by_id('param_host-key-check', wait: 10)
|
|
91
|
+
assert_equal 'checkbox', field['type']
|
|
92
|
+
refute field.checked?,
|
|
93
|
+
'Expected host-key-check to be unchecked, matching the acceptance fixture value'
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../acceptance_helper'
|
|
4
|
+
|
|
5
|
+
# Tests that SSH transport options are correctly passed through to the
|
|
6
|
+
# OpenBolt CLI and affect task execution.
|
|
7
|
+
class SshOptionsTest < AcceptanceTestCase
|
|
8
|
+
def setup
|
|
9
|
+
super
|
|
10
|
+
foreman_login
|
|
11
|
+
visit '/foreman_openbolt/page_launch_task'
|
|
12
|
+
assert_selector '#smart-proxy-input', wait: 15
|
|
13
|
+
select_first_proxy
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def test_default_user_passed_to_bolt
|
|
17
|
+
select_hosts_via_search('target1')
|
|
18
|
+
assert_selector '#task-name-input option', minimum: 2, wait: 15
|
|
19
|
+
select 'acceptance::echo', from: 'task-name-input'
|
|
20
|
+
fill_in 'param_message', with: 'user test'
|
|
21
|
+
click_button 'Launch Task'
|
|
22
|
+
assert_selector 'h1', text: 'Task Execution', wait: 15
|
|
23
|
+
|
|
24
|
+
assert_task_completed
|
|
25
|
+
# The default user (from settings) should appear in the bolt command on the Log Output tab
|
|
26
|
+
assert_log_contains '--user=openbolt'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def test_user_override_passed_to_bolt
|
|
30
|
+
select_hosts_via_search('target1')
|
|
31
|
+
assert_selector '#task-name-input option', minimum: 2, wait: 15
|
|
32
|
+
select 'acceptance::echo', from: 'task-name-input'
|
|
33
|
+
fill_in 'param_message', with: 'override test'
|
|
34
|
+
|
|
35
|
+
set_openbolt_option('user', 'root')
|
|
36
|
+
click_button 'Launch Task'
|
|
37
|
+
assert_selector 'h1', text: 'Task Execution', wait: 15
|
|
38
|
+
|
|
39
|
+
# Task may fail because root doesn't have the SSH key, but the
|
|
40
|
+
# command should show the overridden user
|
|
41
|
+
assert_selector '.pf-v5-c-label', text: /Success|Failed/i, wait: 120
|
|
42
|
+
assert_log_contains '--user=root'
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_host_key_check_false_passed_to_bolt
|
|
46
|
+
select_hosts_via_search('target1')
|
|
47
|
+
assert_selector '#task-name-input option', minimum: 2, wait: 15
|
|
48
|
+
select 'acceptance::noop_task', from: 'task-name-input'
|
|
49
|
+
|
|
50
|
+
# host-key-check is a boolean OpenBolt option and must render as a
|
|
51
|
+
# checkbox (ParameterField's boolean branch), not a text input.
|
|
52
|
+
assert_equal 'checkbox', find_by_id('param_host-key-check')['type']
|
|
53
|
+
|
|
54
|
+
set_openbolt_option('host-key-check', false)
|
|
55
|
+
click_button 'Launch Task'
|
|
56
|
+
assert_selector 'h1', text: 'Task Execution', wait: 15
|
|
57
|
+
|
|
58
|
+
assert_task_completed
|
|
59
|
+
assert_log_contains '--no-host-key-check'
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_verbose_flag_passed_to_bolt
|
|
63
|
+
select_hosts_via_search('target1')
|
|
64
|
+
assert_selector '#task-name-input option', minimum: 2, wait: 15
|
|
65
|
+
select 'acceptance::noop_task', from: 'task-name-input'
|
|
66
|
+
|
|
67
|
+
# verbose is boolean and must render as a checkbox.
|
|
68
|
+
assert_equal 'checkbox', find_by_id('param_verbose')['type']
|
|
69
|
+
|
|
70
|
+
set_openbolt_option('verbose', true)
|
|
71
|
+
click_button 'Launch Task'
|
|
72
|
+
assert_selector 'h1', text: 'Task Execution', wait: 15
|
|
73
|
+
|
|
74
|
+
assert_task_completed
|
|
75
|
+
assert_log_contains '--verbose'
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../acceptance_helper'
|
|
4
|
+
|
|
5
|
+
# Tests for the Task Execution page: URL validation, tab switching, and
|
|
6
|
+
# task metadata display on the Task Details tab.
|
|
7
|
+
class TaskExecutionTest < AcceptanceTestCase
|
|
8
|
+
def setup
|
|
9
|
+
super
|
|
10
|
+
foreman_login
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_execution_page_without_job_id_redirects_to_launch
|
|
14
|
+
# Visiting the execution URL without a job_id query parameter should
|
|
15
|
+
# client-side redirect back to the Launch page (TaskExecution/index.js
|
|
16
|
+
# useEffect with !jobId calls history.push(LAUNCH_TASK)).
|
|
17
|
+
visit '/foreman_openbolt/page_task_execution'
|
|
18
|
+
assert_selector 'h1', text: 'Launch OpenBolt Task', wait: 15
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_task_details_tab_shows_task_name_and_submitted_parameters
|
|
22
|
+
launch_task_via_ui('acceptance::echo',
|
|
23
|
+
params: { 'message' => 'details tab test' })
|
|
24
|
+
assert_task_completed
|
|
25
|
+
|
|
26
|
+
# Click the "Task Details" tab (second tab in ExecutionDisplay).
|
|
27
|
+
find('.pf-v5-c-tabs__link', text: 'Task Details').click
|
|
28
|
+
|
|
29
|
+
# TaskDetails renders task name in a DescriptionList and the
|
|
30
|
+
# submitted parameters in a table with aria-label="Task parameters".
|
|
31
|
+
assert_selector 'dt', text: 'Task Name', wait: 10
|
|
32
|
+
assert_selector 'dd', text: 'acceptance::echo'
|
|
33
|
+
|
|
34
|
+
assert_selector 'table[aria-label="Task parameters"]', wait: 10
|
|
35
|
+
within 'table[aria-label="Task parameters"]' do
|
|
36
|
+
assert_selector 'td', text: 'message'
|
|
37
|
+
assert_selector 'td', text: 'details tab test'
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../acceptance_helper'
|
|
4
|
+
|
|
5
|
+
# Tests the task history page: listing, navigation, and new entry creation.
|
|
6
|
+
class TaskHistoryTest < AcceptanceTestCase
|
|
7
|
+
def setup
|
|
8
|
+
super
|
|
9
|
+
foreman_login
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def test_history_shows_completed_tasks_and_navigation
|
|
13
|
+
# Launch a task to seed history
|
|
14
|
+
launch_task_via_ui('acceptance::echo', params: { 'message' => 'history test' })
|
|
15
|
+
assert_task_completed
|
|
16
|
+
|
|
17
|
+
# Visit history and verify the entry appears
|
|
18
|
+
visit '/foreman_openbolt/page_task_history'
|
|
19
|
+
assert_selector 'h1', text: 'Task History', wait: 15
|
|
20
|
+
assert_no_selector '[aria-label="Loading task history"]', wait: 15
|
|
21
|
+
assert_selector 'table[aria-label="Task history table"] tbody tr', minimum: 1
|
|
22
|
+
# Capture the first row so we can confirm a newer entry lands above it.
|
|
23
|
+
# History paginates (default 20 rows), so counting rows is unreliable
|
|
24
|
+
# once history fills the first page.
|
|
25
|
+
first_row_before = first('table[aria-label="Task history table"] tbody tr').text
|
|
26
|
+
|
|
27
|
+
# Navigate to execution details from history
|
|
28
|
+
first('a[aria-label="View Details"]').click
|
|
29
|
+
assert_selector 'h1', text: 'Task Execution', wait: 15
|
|
30
|
+
|
|
31
|
+
# Launch another task and verify it appears at the top of history.
|
|
32
|
+
launch_task_via_ui('acceptance::noop_task')
|
|
33
|
+
assert_task_completed
|
|
34
|
+
|
|
35
|
+
visit '/foreman_openbolt/page_task_history'
|
|
36
|
+
assert_selector 'table[aria-label="Task history table"] tbody tr', minimum: 1, wait: 15
|
|
37
|
+
first_row_after = first('table[aria-label="Task history table"] tbody tr').text
|
|
38
|
+
assert_not_equal first_row_before, first_row_after,
|
|
39
|
+
'Expected newly launched task to appear as the first history row'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def test_hosts_popover_lists_targets_from_history_row
|
|
43
|
+
# 'target' matches both target1 and target2, so the popover count
|
|
44
|
+
# will be 2.
|
|
45
|
+
launch_task_via_ui('acceptance::noop_task')
|
|
46
|
+
assert_task_completed
|
|
47
|
+
|
|
48
|
+
visit '/foreman_openbolt/page_task_history'
|
|
49
|
+
assert_no_selector '[aria-label="Loading task history"]', wait: 15
|
|
50
|
+
assert_selector 'table[aria-label="Task history table"] tbody tr', minimum: 1, wait: 15
|
|
51
|
+
|
|
52
|
+
# The hosts count button has aria-label '<count> target hosts'
|
|
53
|
+
within first('table[aria-label="Task history table"] tbody tr') do
|
|
54
|
+
find('button[aria-label$=" target hosts"]').click
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Popover content is portaled to document body
|
|
58
|
+
assert_selector 'table[aria-label="Target hosts"]', wait: 10
|
|
59
|
+
within 'table[aria-label="Target hosts"]' do
|
|
60
|
+
assert_selector 'td', text: 'target1'
|
|
61
|
+
assert_selector 'td', text: 'target2'
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def test_task_popover_shows_submitted_parameters_on_history_row
|
|
66
|
+
launch_task_via_ui('acceptance::echo',
|
|
67
|
+
params: { 'message' => 'popover history param' })
|
|
68
|
+
assert_task_completed
|
|
69
|
+
|
|
70
|
+
visit '/foreman_openbolt/page_task_history'
|
|
71
|
+
assert_no_selector '[aria-label="Loading task history"]', wait: 15
|
|
72
|
+
assert_selector 'table[aria-label="Task history table"] tbody tr', minimum: 1, wait: 15
|
|
73
|
+
|
|
74
|
+
# The task name is rendered as a popover trigger whose aria-label is
|
|
75
|
+
# "View details for task <name>"
|
|
76
|
+
first('button[aria-label="View details for task acceptance::echo"]').click
|
|
77
|
+
|
|
78
|
+
assert_selector 'table[aria-label="Task parameters"]', wait: 10
|
|
79
|
+
within 'table[aria-label="Task parameters"]' do
|
|
80
|
+
assert_selector 'td', text: 'message'
|
|
81
|
+
assert_selector 'td', text: 'popover history param'
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../acceptance_helper'
|
|
4
|
+
|
|
5
|
+
# Tests that the proxy correctly loads and exposes tasks, and that
|
|
6
|
+
# task metadata (parameters) is displayed in the UI.
|
|
7
|
+
class TransportOptionsTest < AcceptanceTestCase
|
|
8
|
+
def setup
|
|
9
|
+
super
|
|
10
|
+
foreman_login
|
|
11
|
+
visit '/foreman_openbolt/page_launch_task'
|
|
12
|
+
assert_selector '#smart-proxy-input', wait: 15
|
|
13
|
+
select_first_proxy
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def test_proxy_selection_populates_task_list
|
|
17
|
+
assert_selector '#task-name-input option', minimum: 2, wait: 15
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def test_all_fixture_tasks_discoverable
|
|
21
|
+
assert_selector '#task-name-input option', minimum: 2, wait: 15
|
|
22
|
+
options = all('#task-name-input option').map(&:text)
|
|
23
|
+
|
|
24
|
+
%w[echo complex_params failing_task noop_task slow_task target_conditional].each do |task|
|
|
25
|
+
assert options.any?("acceptance::#{task}"),
|
|
26
|
+
"Expected acceptance::#{task} in task list, got: #{options}"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_echo_task_shows_message_param_as_empty_text_input
|
|
31
|
+
assert_selector '#task-name-input option', minimum: 2, wait: 15
|
|
32
|
+
select 'acceptance::echo', from: 'task-name-input'
|
|
33
|
+
message = find_by_id('param_message', wait: 10)
|
|
34
|
+
assert_equal 'text', message['type']
|
|
35
|
+
assert_equal '', message.value
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def test_complex_params_task_populates_fields_with_correct_defaults
|
|
39
|
+
assert_selector '#task-name-input option', minimum: 2, wait: 15
|
|
40
|
+
select 'acceptance::complex_params', from: 'task-name-input'
|
|
41
|
+
|
|
42
|
+
# No-default fields render empty.
|
|
43
|
+
assert_equal '', find_by_id('param_required_string', wait: 10).value
|
|
44
|
+
assert_equal '', find_by_id('param_array_param').value
|
|
45
|
+
# with_default has a 'default_value' default in its task metadata;
|
|
46
|
+
# the launch page must populate it when the task is selected
|
|
47
|
+
# (handleTaskChange in LaunchTask/index.js).
|
|
48
|
+
assert_equal 'default_value', find_by_id('param_with_default').value
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def test_slow_task_shows_seconds_param_with_default_value
|
|
52
|
+
assert_selector '#task-name-input option', minimum: 2, wait: 15
|
|
53
|
+
select 'acceptance::slow_task', from: 'task-name-input'
|
|
54
|
+
# slow_task.json declares "default": 5
|
|
55
|
+
assert_equal '5', find_by_id('param_seconds', wait: 10).value
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_target_conditional_shows_succeed_on_param_as_empty_text_input
|
|
59
|
+
assert_selector '#task-name-input option', minimum: 2, wait: 15
|
|
60
|
+
select 'acceptance::target_conditional', from: 'task-name-input'
|
|
61
|
+
succeed_on = find_by_id('param_succeed_on', wait: 10)
|
|
62
|
+
assert_equal 'text', succeed_on['type']
|
|
63
|
+
assert_equal '', succeed_on.value
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def test_switching_tasks_swaps_parameter_fields_and_defaults
|
|
67
|
+
assert_selector '#task-name-input option', minimum: 2, wait: 15
|
|
68
|
+
|
|
69
|
+
select 'acceptance::echo', from: 'task-name-input'
|
|
70
|
+
assert_equal '', find_by_id('param_message', wait: 10).value
|
|
71
|
+
|
|
72
|
+
select 'acceptance::slow_task', from: 'task-name-input'
|
|
73
|
+
assert_no_selector '#param_message', wait: 5
|
|
74
|
+
# Asserting the seconds default of 5 (not just field presence)
|
|
75
|
+
# proves the new task's metadata loaded rather than stale state.
|
|
76
|
+
assert_equal '5', find_by_id('param_seconds', wait: 10).value
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def test_deselecting_proxy_clears_task_selection_and_parameters
|
|
80
|
+
assert_selector '#task-name-input option', minimum: 2, wait: 15
|
|
81
|
+
select 'acceptance::echo', from: 'task-name-input'
|
|
82
|
+
assert_selector '#param_message', wait: 10
|
|
83
|
+
|
|
84
|
+
# Deselect by choosing the placeholder option (value="")
|
|
85
|
+
select 'Select Smart Proxy', from: 'smart-proxy-input'
|
|
86
|
+
|
|
87
|
+
# Task selection and its parameters should be cleared, showing the
|
|
88
|
+
# "Select a task to see parameters" empty state.
|
|
89
|
+
assert_no_selector '#param_message', wait: 10
|
|
90
|
+
assert_selector '.pf-v5-c-empty-state',
|
|
91
|
+
text: 'Select a task to see parameters', wait: 10
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def test_transport_option_renders_as_select_with_ssh_and_winrm
|
|
95
|
+
# The transport OpenBolt option has type ["ssh", "winrm"] and
|
|
96
|
+
# ParameterField renders array-typed values as a <select>.
|
|
97
|
+
field = find_by_id('param_transport', wait: 10)
|
|
98
|
+
assert_equal 'select', field.tag_name
|
|
99
|
+
option_values = all('#param_transport option').map(&:value)
|
|
100
|
+
assert_equal %w[ssh winrm], option_values
|
|
101
|
+
assert_equal 'ssh', field.value
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def test_switching_transport_to_winrm_hides_ssh_only_options
|
|
105
|
+
# SSH-only options (private-key, host-key-check) are present on load
|
|
106
|
+
# because the default transport is ssh.
|
|
107
|
+
assert_selector '#param_host-key-check', wait: 10
|
|
108
|
+
assert_selector '#param_private-key', wait: 10
|
|
109
|
+
|
|
110
|
+
# Switching transport to winrm should hide options whose metadata
|
|
111
|
+
# tags them as ssh-only (OpenBoltOptionsSection filters by transport).
|
|
112
|
+
select 'winrm', from: 'param_transport'
|
|
113
|
+
assert_no_selector '#param_host-key-check', wait: 10
|
|
114
|
+
assert_no_selector '#param_private-key', wait: 10
|
|
115
|
+
|
|
116
|
+
# Switching back restores them
|
|
117
|
+
select 'ssh', from: 'param_transport'
|
|
118
|
+
assert_selector '#param_host-key-check', wait: 10
|
|
119
|
+
assert_selector '#param_private-key', wait: 10
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'dynflow/testing'
|
|
5
|
+
|
|
6
|
+
# Load plugin factories and foreman-tasks factories
|
|
7
|
+
FactoryBot.definition_file_paths << File.join(File.dirname(__FILE__), 'unit', 'factories')
|
|
8
|
+
FactoryBot.definition_file_paths << File.join(ForemanTasks::Engine.root, 'test', 'factories')
|
|
9
|
+
FactoryBot.reload
|
|
10
|
+
|
|
11
|
+
module ForemanOpenbolt
|
|
12
|
+
class PluginTestCase < ActiveSupport::TestCase
|
|
13
|
+
teardown do
|
|
14
|
+
WebMock.reset!
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|