foreman_rh_cloud 14.4.0 → 14.6.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/app/controllers/api/v2/rh_cloud/cloud_request_controller.rb +1 -0
- data/app/controllers/api/v2/rh_cloud/inventory_controller.rb +11 -5
- data/app/controllers/foreman_inventory_upload/accounts_controller.rb +0 -1
- data/app/controllers/foreman_inventory_upload/uploads_controller.rb +0 -11
- data/app/controllers/foreman_inventory_upload/uploads_settings_controller.rb +0 -8
- data/app/services/foreman_rh_cloud/cert_auth.rb +2 -1
- data/app/services/foreman_rh_cloud/cloud_presence.rb +23 -0
- data/app/services/foreman_rh_cloud/cloud_request.rb +2 -1
- data/app/services/foreman_rh_cloud/cloud_request_forwarder.rb +9 -5
- data/config/routes.rb +1 -3
- data/lib/foreman_rh_cloud/engine.rb +1 -6
- data/lib/foreman_rh_cloud/plugin.rb +4 -2
- data/lib/foreman_rh_cloud/version.rb +1 -1
- data/lib/foreman_rh_cloud.rb +16 -0
- data/lib/insights_cloud/async/cloud_connector_announce_task.rb +76 -19
- data/package.json +1 -1
- data/test/controllers/inventory_upload/api/inventory_controller_test.rb +10 -8
- data/test/jobs/cloud_connector_announce_task_test.rb +116 -106
- data/test/unit/foreman_rh_cloud_iop_metadata_test.rb +27 -0
- data/test/unit/services/foreman_rh_cloud/cloud_request_forwarder_test.rb +28 -0
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/integrations.test.js +4 -12
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/components/__tests__/Toast.test.js +62 -61
- data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTableActions.test.js +2 -2
- data/webpack/InsightsCloudSync/__tests__/InsightsCloudSyncActions.test.js +7 -5
- data/webpack/InsightsHostDetailsTab/__tests__/InsightsTabIntegration.test.js +69 -8
- data/webpack/InsightsVulnerabilityActionsBar/__tests__/InsightsVulnerabilityActionsBar.test.js +1 -5
- data/webpack/test_setup.js +1 -0
- metadata +2 -8
- data/app/services/foreman_rh_cloud/cloud_connector.rb +0 -74
- data/webpack/__mocks__/foremanReact/Root/Context/ForemanContext.js +0 -6
- data/webpack/__mocks__/foremanReact/common/I18n.js +0 -6
- data/webpack/__mocks__/foremanReact/common/helpers.js +0 -14
- data/webpack/__mocks__/foremanReact/constants.js +0 -24
- data/webpack/__mocks__/foremanReact/redux/API/APISelectors.js +0 -24
- data/webpack/__mocks__/foremanReact/redux/API/index.js +0 -12
|
@@ -1,125 +1,135 @@
|
|
|
1
|
-
require 'json'
|
|
2
1
|
require 'test_plugin_helper'
|
|
3
2
|
require 'foreman_tasks/test_helpers'
|
|
4
|
-
require "#{ForemanTasks::Engine.root}/test/support/dummy_dynflow_action"
|
|
5
3
|
|
|
6
4
|
class CloudConnectorAnnounceTaskTest < ActiveSupport::TestCase
|
|
7
5
|
include Dynflow::Testing::Factories
|
|
8
6
|
|
|
9
7
|
setup do
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
)
|
|
8
|
+
InsightsCloud::Async::CloudConnectorAnnounceTask.any_instance
|
|
9
|
+
.stubs(:recent_cloud_remediation?).returns(false)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
teardown do
|
|
13
|
+
ForemanRhCloud.unstub(:with_iop_smart_proxy?)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
test 'announces to sources for all organizations when rhc_instance_id is set' do
|
|
17
|
+
Setting[:rhc_instance_id] = 'test-rhc-id'
|
|
18
|
+
|
|
19
|
+
InsightsCloud::Async::CloudConnectorAnnounceTask.any_instance
|
|
20
|
+
.stubs(:cert_auth_available?).returns(true)
|
|
16
21
|
|
|
17
|
-
|
|
22
|
+
ForemanRhCloud::CloudPresence.any_instance
|
|
23
|
+
.expects(:announce_to_sources)
|
|
24
|
+
.times(Organization.unscoped.count)
|
|
18
25
|
|
|
19
|
-
|
|
20
|
-
|
|
26
|
+
action = create_and_plan_action(InsightsCloud::Async::CloudConnectorAnnounceTask)
|
|
27
|
+
action = run_action(action)
|
|
28
|
+
|
|
29
|
+
status = action.output[:status].to_s
|
|
30
|
+
assert_match(/Registered:|Already registered:/, status)
|
|
31
|
+
refute_match(/Skipped/, status)
|
|
32
|
+
refute_match(/Failed/, status)
|
|
21
33
|
end
|
|
22
34
|
|
|
23
|
-
test '
|
|
24
|
-
|
|
35
|
+
test 'skips when rhc_instance_id is not set' do
|
|
36
|
+
Setting[:rhc_instance_id] = nil
|
|
37
|
+
|
|
38
|
+
action = create_and_plan_action(InsightsCloud::Async::CloudConnectorAnnounceTask)
|
|
25
39
|
|
|
26
|
-
action
|
|
27
|
-
finalize_action(action)
|
|
40
|
+
assert_empty action.execution_plan.planned_run_steps
|
|
28
41
|
end
|
|
29
42
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
:required => true)
|
|
57
|
-
job_template.template_inputs << report_interval_input = FactoryBot.build(:template_input,
|
|
58
|
-
:name => 'report_interval',
|
|
59
|
-
:input_type => 'user',
|
|
60
|
-
:required => true)
|
|
61
|
-
|
|
62
|
-
template_invocation = FactoryBot.build(:template_invocation,
|
|
63
|
-
:template => job_template,
|
|
64
|
-
:job_invocation => job_invocation)
|
|
65
|
-
|
|
66
|
-
template_invocation.input_values << FactoryBot.create(
|
|
67
|
-
:template_invocation_input_value,
|
|
68
|
-
:template_invocation => template_invocation,
|
|
69
|
-
:template_input => playbook_url_input,
|
|
70
|
-
:value => 'http://example.com/TEST_PLAYBOOK'
|
|
71
|
-
)
|
|
72
|
-
template_invocation.input_values << FactoryBot.create(
|
|
73
|
-
:template_invocation_input_value,
|
|
74
|
-
:template_invocation => template_invocation,
|
|
75
|
-
:template_input => report_url_input,
|
|
76
|
-
:value => 'http://example.com/TEST_REPORT'
|
|
77
|
-
)
|
|
78
|
-
template_invocation.input_values << FactoryBot.create(
|
|
79
|
-
:template_invocation_input_value,
|
|
80
|
-
:template_invocation => template_invocation,
|
|
81
|
-
:template_input => correlation_id_input,
|
|
82
|
-
:value => 'TEST_CORRELATION'
|
|
83
|
-
)
|
|
84
|
-
template_invocation.input_values << FactoryBot.create(
|
|
85
|
-
:template_invocation_input_value,
|
|
86
|
-
:template_invocation => template_invocation,
|
|
87
|
-
:template_input => report_interval_input,
|
|
88
|
-
:value => '1'
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
@host1 = FactoryBot.create(:host, :with_insights_hits, name: 'host1')
|
|
92
|
-
@host1.insights.uuid = 'TEST_UUID1'
|
|
93
|
-
@host1.insights.save!
|
|
94
|
-
@host2 = FactoryBot.create(:host, :with_insights_hits, name: 'host2')
|
|
95
|
-
@host2.insights.uuid = 'TEST_UUID2'
|
|
96
|
-
@host2.insights.save!
|
|
97
|
-
|
|
98
|
-
targeting = FactoryBot.create(:targeting, hosts: [@host1, @host2])
|
|
99
|
-
job_invocation.targeting = targeting
|
|
100
|
-
job_invocation.save!
|
|
101
|
-
|
|
102
|
-
job_invocation.template_invocations << FactoryBot.create(
|
|
103
|
-
:template_invocation,
|
|
104
|
-
run_host_job_task: FactoryBot.create(:dynflow_task),
|
|
105
|
-
host_id: @host1.id
|
|
106
|
-
)
|
|
107
|
-
job_invocation.template_invocations << FactoryBot.create(
|
|
108
|
-
:template_invocation,
|
|
109
|
-
run_host_job_task: FactoryBot.create(:dynflow_task),
|
|
110
|
-
host_id: @host2.id
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
fake_output = (1..5).map do |i|
|
|
114
|
-
{ 'timestamp' => (Time.now - (5 - i)).to_f, 'output' => "#{i}\n" }
|
|
115
|
-
end
|
|
116
|
-
Support::DummyDynflowAction.any_instance.stubs(:live_output).returns(fake_output)
|
|
117
|
-
Support::DummyDynflowAction.any_instance.stubs(:exit_status).returns(0)
|
|
43
|
+
test 'skips when rhc_instance_id is empty string' do
|
|
44
|
+
Setting[:rhc_instance_id] = ''
|
|
45
|
+
|
|
46
|
+
action = create_and_plan_action(InsightsCloud::Async::CloudConnectorAnnounceTask)
|
|
47
|
+
|
|
48
|
+
assert_empty action.execution_plan.planned_run_steps
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
test 'skips when in IoP mode' do
|
|
52
|
+
Setting[:rhc_instance_id] = 'test-rhc-id'
|
|
53
|
+
ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
|
|
54
|
+
|
|
55
|
+
action = create_and_plan_action(InsightsCloud::Async::CloudConnectorAnnounceTask)
|
|
56
|
+
|
|
57
|
+
assert_empty action.execution_plan.planned_run_steps
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
test 'skips organizations without a manifest' do
|
|
61
|
+
Setting[:rhc_instance_id] = 'test-rhc-id'
|
|
62
|
+
|
|
63
|
+
InsightsCloud::Async::CloudConnectorAnnounceTask.any_instance
|
|
64
|
+
.stubs(:cert_auth_available?).returns(false)
|
|
65
|
+
|
|
66
|
+
ForemanRhCloud::CloudPresence.any_instance
|
|
67
|
+
.expects(:announce_to_sources)
|
|
68
|
+
.never
|
|
118
69
|
|
|
119
|
-
|
|
70
|
+
action = create_and_plan_action(InsightsCloud::Async::CloudConnectorAnnounceTask)
|
|
71
|
+
action = run_action(action)
|
|
72
|
+
|
|
73
|
+
status = action.output[:status].to_s
|
|
74
|
+
assert_match(/Skipped \(no manifest\):/, status)
|
|
75
|
+
refute_match(/Registered/, status)
|
|
76
|
+
refute_match(/Failed/, status)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
test 'still runs when allow_auto_inventory_upload is disabled' do
|
|
80
|
+
Setting[:rhc_instance_id] = 'test-rhc-id'
|
|
81
|
+
Setting[:allow_auto_inventory_upload] = false
|
|
82
|
+
|
|
83
|
+
InsightsCloud::Async::CloudConnectorAnnounceTask.any_instance
|
|
84
|
+
.stubs(:cert_auth_available?).returns(true)
|
|
85
|
+
|
|
86
|
+
ForemanRhCloud::CloudPresence.any_instance
|
|
87
|
+
.expects(:announce_to_sources)
|
|
88
|
+
.times(Organization.unscoped.count)
|
|
89
|
+
|
|
90
|
+
action = create_and_plan_action(InsightsCloud::Async::CloudConnectorAnnounceTask)
|
|
91
|
+
action = run_action(action)
|
|
92
|
+
|
|
93
|
+
assert_match(/Registered:|Already registered:/, action.output[:status].to_s)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
test 'skips orgs with recent cloud remediation jobs' do
|
|
97
|
+
Setting[:rhc_instance_id] = 'test-rhc-id'
|
|
98
|
+
|
|
99
|
+
InsightsCloud::Async::CloudConnectorAnnounceTask.any_instance
|
|
100
|
+
.stubs(:cert_auth_available?).returns(true)
|
|
101
|
+
InsightsCloud::Async::CloudConnectorAnnounceTask.any_instance
|
|
102
|
+
.stubs(:recent_cloud_remediation?).returns(true)
|
|
103
|
+
|
|
104
|
+
ForemanRhCloud::CloudPresence.any_instance
|
|
105
|
+
.expects(:announce_to_sources)
|
|
106
|
+
.never
|
|
107
|
+
|
|
108
|
+
action = create_and_plan_action(InsightsCloud::Async::CloudConnectorAnnounceTask)
|
|
109
|
+
action = run_action(action)
|
|
110
|
+
|
|
111
|
+
status = action.output[:status].to_s
|
|
112
|
+
assert_match(/Already registered \(recent cloud remediation\):/, status)
|
|
113
|
+
refute_match(/\bRegistered:/, status)
|
|
120
114
|
end
|
|
121
115
|
|
|
122
|
-
|
|
123
|
-
|
|
116
|
+
test 'continues processing other orgs when one fails and task errors' do
|
|
117
|
+
Setting[:rhc_instance_id] = 'test-rhc-id'
|
|
118
|
+
|
|
119
|
+
InsightsCloud::Async::CloudConnectorAnnounceTask.any_instance
|
|
120
|
+
.stubs(:cert_auth_available?).returns(true)
|
|
121
|
+
|
|
122
|
+
call_count = 0
|
|
123
|
+
ForemanRhCloud::CloudPresence.any_instance.stubs(:announce_to_sources).with do
|
|
124
|
+
call_count += 1
|
|
125
|
+
raise(StandardError.new('API error')) if call_count == 1
|
|
126
|
+
true
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
action = create_and_plan_action(InsightsCloud::Async::CloudConnectorAnnounceTask)
|
|
130
|
+
|
|
131
|
+
error = assert_raises(StandardError) { run_action(action) }
|
|
132
|
+
assert_match(/Sources announcement failed for:/, error.message)
|
|
133
|
+
assert_equal Organization.unscoped.count, call_count
|
|
124
134
|
end
|
|
125
135
|
end
|
|
@@ -101,6 +101,22 @@ class ForemanRhCloudIopMetadataTest < ActiveSupport::TestCase
|
|
|
101
101
|
assert_equal 'https://iop.example.com', ForemanRhCloud.cert_base_url
|
|
102
102
|
end
|
|
103
103
|
|
|
104
|
+
test 'cloud_cert_base_url returns IoP URL when IoP exists and force_cla_connection is off' do
|
|
105
|
+
create_iop_proxy
|
|
106
|
+
Setting[:force_cla_connection] = false
|
|
107
|
+
|
|
108
|
+
assert_equal 'https://iop.example.com', ForemanRhCloud.cloud_cert_base_url
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
test 'cloud_cert_base_url returns cloud URL when IoP exists and force_cla_connection is on' do
|
|
112
|
+
create_iop_proxy
|
|
113
|
+
Setting[:force_cla_connection] = true
|
|
114
|
+
|
|
115
|
+
assert_equal 'https://cert.cloud.redhat.com', ForemanRhCloud.cloud_cert_base_url
|
|
116
|
+
ensure
|
|
117
|
+
Setting[:force_cla_connection] = false
|
|
118
|
+
end
|
|
119
|
+
|
|
104
120
|
test 'legacy_insights_url returns IoP URL when IoP smart proxy exists' do
|
|
105
121
|
create_iop_proxy
|
|
106
122
|
|
|
@@ -153,6 +169,17 @@ class ForemanRhCloudIopMetadataTest < ActiveSupport::TestCase
|
|
|
153
169
|
ENV.delete('SATELLITE_CERT_RH_CLOUD_URL')
|
|
154
170
|
end
|
|
155
171
|
|
|
172
|
+
test 'cloud_cert_base_url uses ENV var when force_cla_connection is on' do
|
|
173
|
+
ENV['SATELLITE_CERT_RH_CLOUD_URL'] = 'https://env-cert.cloud.test'
|
|
174
|
+
Setting[:force_cla_connection] = true
|
|
175
|
+
create_iop_proxy
|
|
176
|
+
|
|
177
|
+
assert_equal 'https://env-cert.cloud.test', ForemanRhCloud.cloud_cert_base_url
|
|
178
|
+
ensure
|
|
179
|
+
ENV.delete('SATELLITE_CERT_RH_CLOUD_URL')
|
|
180
|
+
Setting[:force_cla_connection] = false
|
|
181
|
+
end
|
|
182
|
+
|
|
156
183
|
test 'legacy_insights_url prefers IoP URL over ENV var' do
|
|
157
184
|
ENV['SATELLITE_LEGACY_INSIGHTS_URL'] = 'https://env-legacy.insights.test'
|
|
158
185
|
|
|
@@ -10,7 +10,9 @@ class CloudRequestForwarderTest < ActiveSupport::TestCase
|
|
|
10
10
|
|
|
11
11
|
ForemanRhCloud.stubs(:base_url).returns('https://cloud.example.com')
|
|
12
12
|
ForemanRhCloud.stubs(:cert_base_url).returns('https://cert.cloud.example.com')
|
|
13
|
+
ForemanRhCloud.stubs(:cloud_cert_base_url).returns('https://cert.cloud.example.com')
|
|
13
14
|
ForemanRhCloud.stubs(:legacy_insights_url).returns('https://cert-api.access.example.com')
|
|
15
|
+
ForemanRhCloud.stubs(:transformed_cloud_http_proxy_string).returns(nil)
|
|
14
16
|
|
|
15
17
|
UpstreamOnlySettingsTestHelper.set_if_available('allow_multiple_content_views')
|
|
16
18
|
env = FactoryBot.create(:katello_k_t_environment)
|
|
@@ -34,6 +36,7 @@ class CloudRequestForwarderTest < ActiveSupport::TestCase
|
|
|
34
36
|
|
|
35
37
|
test 'should prepare correct cloud url' do
|
|
36
38
|
paths = {
|
|
39
|
+
"/api/lightspeed/v1/query" => "https://cert.cloud.example.com/api/lightspeed/v1/query",
|
|
37
40
|
"/redhat_access/r/insights/platform/module-update-router/v1/channel?module=insights-core" => "https://cert.cloud.example.com/api/module-update-router/v1/channel?module=insights-core",
|
|
38
41
|
"/redhat_access/r/insights/v1/static/release/insights-core.egg" => "https://cert-api.access.example.com/r/insights/v1/static/release/insights-core.egg",
|
|
39
42
|
"/redhat_access/r/insights/v1/static/uploader.v2.json" => "https://cert-api.access.example.com/r/insights/v1/static/uploader.v2.json",
|
|
@@ -50,6 +53,31 @@ class CloudRequestForwarderTest < ActiveSupport::TestCase
|
|
|
50
53
|
end
|
|
51
54
|
end
|
|
52
55
|
|
|
56
|
+
test 'does not force CLA to cloud when IoP is enabled and setting is off' do
|
|
57
|
+
ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
|
|
58
|
+
ForemanRhCloud.stubs(:cert_base_url).returns('https://iop.example.com')
|
|
59
|
+
ForemanRhCloud.stubs(:cloud_cert_base_url).returns('https://iop.example.com')
|
|
60
|
+
Setting.stubs(:[]).with(:force_cla_connection).returns(false)
|
|
61
|
+
|
|
62
|
+
actual = @forwarder.path_params('/api/lightspeed/v1/query')
|
|
63
|
+
|
|
64
|
+
assert_equal 'https://iop.example.com/api/lightspeed/v1/query', actual[:url]
|
|
65
|
+
refute actual[:force_cloud]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
test 'forwards CLA to cloud when IoP is enabled and force_cla_connection is on' do
|
|
69
|
+
ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
|
|
70
|
+
ForemanRhCloud.stubs(:cert_base_url).returns('https://iop.example.com')
|
|
71
|
+
ForemanRhCloud.stubs(:cloud_cert_base_url).returns('https://cert.cloud.example.com')
|
|
72
|
+
ForemanRhCloud.stubs(:transformed_cloud_http_proxy_string).returns(nil)
|
|
73
|
+
Setting.stubs(:[]).with(:force_cla_connection).returns(true)
|
|
74
|
+
|
|
75
|
+
actual = @forwarder.path_params('/api/lightspeed/v1/query')
|
|
76
|
+
|
|
77
|
+
assert_equal 'https://cert.cloud.example.com/api/lightspeed/v1/query', actual[:url]
|
|
78
|
+
assert actual[:force_cloud]
|
|
79
|
+
end
|
|
80
|
+
|
|
53
81
|
test 'should forward payload from request parameters' do
|
|
54
82
|
params = { 'pumpkin' => 'pie' }
|
|
55
83
|
req = ActionDispatch::Request.new(
|
|
@@ -3,24 +3,16 @@ import { render, screen, fireEvent } from '@testing-library/react';
|
|
|
3
3
|
import { Provider } from 'react-redux';
|
|
4
4
|
import configureMockStore from 'redux-mock-store';
|
|
5
5
|
import thunk from 'redux-thunk';
|
|
6
|
-
import * as API from 'foremanReact/redux/API';
|
|
7
6
|
import ConnectedSyncButton from '../index';
|
|
8
|
-
import { successResponse } from './SyncButtonFixtures';
|
|
9
|
-
import { INVENTORY_SYNC } from '../SyncButtonConstants';
|
|
10
|
-
|
|
11
|
-
jest.spyOn(API, 'post');
|
|
12
7
|
|
|
13
8
|
const mockStore = configureMockStore([thunk]);
|
|
14
9
|
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
jest.clearAllMocks();
|
|
12
|
+
});
|
|
13
|
+
|
|
15
14
|
describe('SyncButton integration test', () => {
|
|
16
15
|
it('dispatches sync action when button is clicked', () => {
|
|
17
|
-
API.post.mockImplementation(({ handleSuccess, key, ...action }) => {
|
|
18
|
-
if (key === INVENTORY_SYNC && handleSuccess) {
|
|
19
|
-
handleSuccess(successResponse);
|
|
20
|
-
}
|
|
21
|
-
return { type: 'API_POST', ...action };
|
|
22
|
-
});
|
|
23
|
-
|
|
24
16
|
const store = mockStore({
|
|
25
17
|
API: {},
|
|
26
18
|
});
|
|
@@ -1,82 +1,83 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import { MemoryRouter } from 'react-router-dom';
|
|
4
|
+
import '@testing-library/jest-dom';
|
|
3
5
|
import Toast from '../Toast';
|
|
4
6
|
|
|
7
|
+
jest.mock('foremanReact/common/I18n', () => ({
|
|
8
|
+
translate: jest.fn(str => str),
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
const renderToast = (props = {}) =>
|
|
12
|
+
render(
|
|
13
|
+
<MemoryRouter>
|
|
14
|
+
<Toast syncHosts={5} disconnectHosts={3} {...props} />
|
|
15
|
+
</MemoryRouter>
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const omittedText =
|
|
19
|
+
'Excluded from upload to console.redhat.com Inventory service because host_registration_insights_inventory parameter value is false:';
|
|
20
|
+
|
|
5
21
|
describe('Toast', () => {
|
|
6
22
|
it('renders with all three status counts including user_omitted', () => {
|
|
7
|
-
|
|
8
|
-
<Toast syncHosts={5} disconnectHosts={3} userOmittedHosts={2} />
|
|
9
|
-
);
|
|
10
|
-
|
|
11
|
-
const links = wrapper.find('HostsWithStatusLink');
|
|
12
|
-
expect(links).toHaveLength(3);
|
|
23
|
+
renderToast({ userOmittedHosts: 2 });
|
|
13
24
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
.text()
|
|
32
|
-
).toBe('2');
|
|
25
|
+
expect(screen.getByRole('link', { name: '10' })).toHaveAttribute(
|
|
26
|
+
'href',
|
|
27
|
+
'/new/hosts?search=set%3F+subscription_uuid&page=1'
|
|
28
|
+
);
|
|
29
|
+
expect(screen.getByRole('link', { name: '5' })).toHaveAttribute(
|
|
30
|
+
'href',
|
|
31
|
+
'/new/hosts?search=insights_inventory_sync_status+%3D+sync&page=1'
|
|
32
|
+
);
|
|
33
|
+
expect(screen.getByRole('link', { name: '3' })).toHaveAttribute(
|
|
34
|
+
'href',
|
|
35
|
+
'/new/hosts?search=insights_inventory_sync_status+%3D+disconnect&page=1'
|
|
36
|
+
);
|
|
37
|
+
expect(screen.getByRole('link', { name: '2' })).toHaveAttribute(
|
|
38
|
+
'href',
|
|
39
|
+
'/new/hosts?search=insights_inventory_sync_status+%3D+user_omitted&page=1'
|
|
40
|
+
);
|
|
41
|
+
expect(screen.getByText(omittedText, { exact: false })).toBeInTheDocument();
|
|
33
42
|
});
|
|
34
43
|
|
|
35
44
|
it('does not render user_omitted section when count is 0', () => {
|
|
36
|
-
|
|
37
|
-
<Toast syncHosts={5} disconnectHosts={3} userOmittedHosts={0} />
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
// Should have only 2 HostsWithStatusLink components (sync and disconnect)
|
|
41
|
-
const links = wrapper.find('HostsWithStatusLink');
|
|
42
|
-
expect(links).toHaveLength(2);
|
|
45
|
+
renderToast({ userOmittedHosts: 0 });
|
|
43
46
|
|
|
44
|
-
|
|
45
|
-
expect(
|
|
46
|
-
|
|
47
|
-
);
|
|
47
|
+
expect(screen.getByRole('link', { name: '8' })).toBeInTheDocument();
|
|
48
|
+
expect(screen.getByRole('link', { name: '5' })).toBeInTheDocument();
|
|
49
|
+
expect(screen.getByRole('link', { name: '3' })).toBeInTheDocument();
|
|
50
|
+
expect(screen.queryByRole('link', { name: '2' })).not.toBeInTheDocument();
|
|
51
|
+
expect(
|
|
52
|
+
screen.queryByText(omittedText, { exact: false })
|
|
53
|
+
).not.toBeInTheDocument();
|
|
48
54
|
});
|
|
49
55
|
|
|
50
56
|
it('renders without crashing when userOmittedHosts is not provided (default)', () => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
// Should use default value of 0, so only 2 links
|
|
54
|
-
const links = wrapper.find('HostsWithStatusLink');
|
|
55
|
-
expect(links).toHaveLength(2);
|
|
57
|
+
renderToast();
|
|
56
58
|
|
|
57
|
-
|
|
59
|
+
expect(screen.getByRole('link', { name: '8' })).toBeInTheDocument();
|
|
60
|
+
expect(screen.getByRole('link', { name: '5' })).toBeInTheDocument();
|
|
61
|
+
expect(screen.getByRole('link', { name: '3' })).toBeInTheDocument();
|
|
58
62
|
expect(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
.children()
|
|
62
|
-
.text()
|
|
63
|
-
).toBe('5');
|
|
64
|
-
expect(
|
|
65
|
-
links
|
|
66
|
-
.at(1)
|
|
67
|
-
.children()
|
|
68
|
-
.text()
|
|
69
|
-
).toBe('3');
|
|
63
|
+
screen.queryByText(omittedText, { exact: false })
|
|
64
|
+
).not.toBeInTheDocument();
|
|
70
65
|
});
|
|
71
66
|
|
|
72
67
|
it('renders correct status links for each category', () => {
|
|
73
|
-
|
|
74
|
-
<Toast syncHosts={5} disconnectHosts={3} userOmittedHosts={2} />
|
|
75
|
-
);
|
|
68
|
+
renderToast({ userOmittedHosts: 2 });
|
|
76
69
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
70
|
+
expect(screen.getByRole('link', { name: '5' })).toHaveAttribute(
|
|
71
|
+
'href',
|
|
72
|
+
expect.stringContaining('sync')
|
|
73
|
+
);
|
|
74
|
+
expect(screen.getByRole('link', { name: '3' })).toHaveAttribute(
|
|
75
|
+
'href',
|
|
76
|
+
expect.stringContaining('disconnect')
|
|
77
|
+
);
|
|
78
|
+
expect(screen.getByRole('link', { name: '2' })).toHaveAttribute(
|
|
79
|
+
'href',
|
|
80
|
+
expect.stringContaining('user_omitted')
|
|
81
|
+
);
|
|
81
82
|
});
|
|
82
83
|
});
|
data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTableActions.test.js
CHANGED
|
@@ -117,10 +117,10 @@ describe('InsightsTable actions', () => {
|
|
|
117
117
|
expect(push).toHaveBeenCalled();
|
|
118
118
|
|
|
119
119
|
const apiAction = dispatch.mock.calls.find(
|
|
120
|
-
call => call[0] && call[0].key === INSIGHTS_HITS_API_KEY
|
|
120
|
+
call => call[0] && call[0].payload?.key === INSIGHTS_HITS_API_KEY
|
|
121
121
|
);
|
|
122
122
|
expect(apiAction).toBeTruthy();
|
|
123
|
-
const getArg = apiAction[0];
|
|
123
|
+
const getArg = apiAction[0].payload;
|
|
124
124
|
expect(getArg.url).toBe(INSIGHTS_HITS_PATH);
|
|
125
125
|
expect(getArg.params.page).toBe(2);
|
|
126
126
|
expect(getArg.params.per_page).toBe(7);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { API_OPERATIONS } from 'foremanReact/redux/API';
|
|
1
2
|
import { syncInsights } from '../InsightsCloudSyncActions';
|
|
2
3
|
import { INSIGHTS_CLOUD_SYNC } from '../InsightsCloudSyncConstants';
|
|
3
4
|
|
|
@@ -17,17 +18,18 @@ describe('InsightsCloudSync actions', () => {
|
|
|
17
18
|
|
|
18
19
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
|
19
20
|
const dispatched = dispatch.mock.calls[0][0];
|
|
20
|
-
expect(dispatched.
|
|
21
|
-
expect(dispatched.
|
|
22
|
-
expect(
|
|
23
|
-
expect(typeof dispatched.
|
|
21
|
+
expect(dispatched.type).toBe(API_OPERATIONS.POST);
|
|
22
|
+
expect(dispatched.payload.key).toBe(INSIGHTS_CLOUD_SYNC);
|
|
23
|
+
expect(dispatched.payload.url).toBe('/insights_cloud/tasks');
|
|
24
|
+
expect(typeof dispatched.payload.handleSuccess).toBe('function');
|
|
25
|
+
expect(typeof dispatched.payload.errorToast).toBe('function');
|
|
24
26
|
});
|
|
25
27
|
|
|
26
28
|
it('errorToast returns failure message with error details', () => {
|
|
27
29
|
syncInsights(jest.fn(), '')(dispatch);
|
|
28
30
|
const dispatched = dispatch.mock.calls[0][0];
|
|
29
31
|
|
|
30
|
-
const result = dispatched.errorToast('some error');
|
|
32
|
+
const result = dispatched.payload.errorToast('some error');
|
|
31
33
|
expect(result).toContain('some error');
|
|
32
34
|
});
|
|
33
35
|
});
|
|
@@ -1,17 +1,78 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { screen, waitFor } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
4
|
+
import { Provider } from 'react-redux';
|
|
5
|
+
import { applyMiddleware, combineReducers, createStore } from 'redux';
|
|
6
|
+
import thunk from 'redux-thunk';
|
|
7
|
+
import { rtlHelpers } from 'foremanReact/common/rtlTestHelpers';
|
|
8
|
+
import { API } from 'foremanReact/redux/API';
|
|
3
9
|
|
|
4
10
|
import InsightsTab from '../index';
|
|
5
11
|
import reducers from '../../ForemanRhCloudReducers';
|
|
6
|
-
import {
|
|
12
|
+
import { INSIGHTS_HITS_SUCCESS } from '../InsightsTabConstants';
|
|
13
|
+
import { hostID, hits } from './InsightsTab.fixtures';
|
|
7
14
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
const { renderWithI18n } = rtlHelpers;
|
|
16
|
+
|
|
17
|
+
jest.mock('foremanReact/redux/API');
|
|
18
|
+
|
|
19
|
+
const recommendationTitle =
|
|
20
|
+
'New Ansible Engine packages are inaccessible when dedicated Ansible repo is not enabled';
|
|
21
|
+
|
|
22
|
+
const createTabStore = () =>
|
|
23
|
+
createStore(combineReducers({ ...reducers }), applyMiddleware(thunk));
|
|
24
|
+
|
|
25
|
+
const renderConnectedTab = (store = createTabStore()) =>
|
|
26
|
+
renderWithI18n(
|
|
27
|
+
<Provider store={store}>
|
|
12
28
|
<InsightsTab hostID={hostID} />
|
|
29
|
+
</Provider>
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
describe('InsightsTab integration test', () => {
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
jest.clearAllMocks();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('requests hits for the host and shows empty state when there are none', async () => {
|
|
38
|
+
let resolveHits;
|
|
39
|
+
API.get.mockReturnValue(
|
|
40
|
+
new Promise(resolve => {
|
|
41
|
+
resolveHits = resolve;
|
|
42
|
+
})
|
|
13
43
|
);
|
|
14
|
-
|
|
15
|
-
|
|
44
|
+
|
|
45
|
+
const store = createTabStore();
|
|
46
|
+
store.dispatch({
|
|
47
|
+
type: INSIGHTS_HITS_SUCCESS,
|
|
48
|
+
payload: { hits },
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
renderConnectedTab(store);
|
|
52
|
+
|
|
53
|
+
expect(await screen.findByText(recommendationTitle)).toBeInTheDocument();
|
|
54
|
+
|
|
55
|
+
await waitFor(() => {
|
|
56
|
+
expect(API.get).toHaveBeenCalledWith(`/insights_cloud/hits/${hostID}`);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
resolveHits({ data: { hits: [] } });
|
|
60
|
+
|
|
61
|
+
expect(
|
|
62
|
+
await screen.findByRole('heading', {
|
|
63
|
+
name: 'No recommendations were found for this host!',
|
|
64
|
+
})
|
|
65
|
+
).toBeInTheDocument();
|
|
66
|
+
expect(screen.queryByText(recommendationTitle)).not.toBeInTheDocument();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('shows recommendation titles from the hits API response', async () => {
|
|
70
|
+
API.get.mockResolvedValue({ data: { hits } });
|
|
71
|
+
|
|
72
|
+
renderConnectedTab();
|
|
73
|
+
|
|
74
|
+
expect(await screen.findByText(recommendationTitle)).toBeInTheDocument();
|
|
75
|
+
|
|
76
|
+
expect(API.get).toHaveBeenCalledWith(`/insights_cloud/hits/${hostID}`);
|
|
16
77
|
});
|
|
17
78
|
});
|
data/webpack/InsightsVulnerabilityActionsBar/__tests__/InsightsVulnerabilityActionsBar.test.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { render, screen } from '@testing-library/react';
|
|
3
3
|
import '@testing-library/jest-dom';
|
|
4
|
-
import * as ForemanContext from 'foremanReact/Root/Context/ForemanContext';
|
|
5
4
|
import { ForemanHostsIndexActionsBarContext } from 'foremanReact/components/HostsIndex';
|
|
6
5
|
import * as ConfigHooks from '../../common/Hooks/ConfigHooks';
|
|
7
6
|
import InsightsVulnerabilityActionsBar from '../index';
|
|
8
7
|
|
|
9
8
|
jest.mock('../../common/Hooks/ConfigHooks');
|
|
10
|
-
jest.mock('foremanReact/Root/Context/ForemanContext');
|
|
11
9
|
jest.mock('../InsightsVulnerabilityActionsBarActions');
|
|
12
10
|
|
|
13
11
|
const mockContextValue = {
|
|
@@ -16,11 +14,9 @@ const mockContextValue = {
|
|
|
16
14
|
setMenuOpen: jest.fn(),
|
|
17
15
|
};
|
|
18
16
|
|
|
19
|
-
const renderComponent = (contextOverrides = {}
|
|
17
|
+
const renderComponent = (contextOverrides = {}) => {
|
|
20
18
|
const contextValue = { ...mockContextValue, ...contextOverrides };
|
|
21
19
|
|
|
22
|
-
ForemanContext.useForemanOrganization.mockReturnValue({ id: orgId });
|
|
23
|
-
|
|
24
20
|
return render(
|
|
25
21
|
<ForemanHostsIndexActionsBarContext.Provider value={contextValue}>
|
|
26
22
|
<InsightsVulnerabilityActionsBar />
|