foreman_rh_cloud 13.2.9 → 13.2.10
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/insights_cloud/api/machine_telemetries_controller.rb +4 -8
- data/app/controllers/insights_cloud/ui_requests_controller.rb +3 -7
- data/lib/foreman_inventory_upload/generators/fact_helpers.rb +26 -4
- data/lib/foreman_inventory_upload.rb +8 -1
- data/lib/foreman_rh_cloud/version.rb +1 -1
- data/lib/foreman_rh_cloud.rb +36 -9
- data/lib/insights_cloud/async/insights_generate_notifications.rb +10 -1
- data/lib/inventory_sync/async/inventory_self_host_sync.rb +12 -2
- data/package.json +1 -1
- data/test/controllers/insights_cloud/api/machine_telemetries_controller_test.rb +16 -2
- data/test/controllers/insights_cloud/ui_requests_controller_test.rb +16 -2
- data/test/jobs/insights_generate_notifications_test.rb +26 -0
- data/test/jobs/inventory_self_host_sync_test.rb +9 -0
- data/test/unit/foreman_rh_cloud_self_host_test.rb +50 -2
- data/test/unit/metadata_generator_test.rb +24 -1
- data/webpack/CVEsHostDetailsTab/CVEsHostDetailsTab.js +24 -2
- data/webpack/CVEsHostDetailsTab/__tests__/CVEsHostDetailsTab.test.js +73 -10
- data/webpack/ForemanRhCloudHelpers.js +22 -0
- data/webpack/InsightsHostDetailsTab/NewHostDetailsTab.js +27 -1
- data/webpack/InsightsHostDetailsTab/__tests__/NewHostDetailsTab.test.js +134 -22
- metadata +10 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4301f698357e719f6ba5d08677bdeaf3ccefb995835dcf86f3e253cb0c01d599
|
|
4
|
+
data.tar.gz: 77f3bc1ef3e85cc1d8988a53f2cd5fdcd65b99a9fb6d6da1cc5f366bbfebc6d3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 000fad5efc5a8bd42e03045e33bc10795a5d658aa3ef1098e6f95fa2a0476630cc85fe6b7b40929e817a4ca0d166fd88c8073b4faa972b72a84d0a146eb7aa70
|
|
7
|
+
data.tar.gz: 06fe93496e585831f593b8d31223577eb236adf7d1e4d90342cd203ddc9fbf9a80e9b58636b033fa04adefc806885feba8326a4e34dc38844bbaacd083df9c91
|
|
@@ -32,14 +32,10 @@ module InsightsCloud::Api
|
|
|
32
32
|
rescue RestClient::ExceptionWithResponse => e
|
|
33
33
|
response_obj = e.response.presence || e.exception
|
|
34
34
|
code = response_obj.try(:code) || response_obj.try(:http_code) || 500
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
:error => response_obj.to_s,
|
|
40
|
-
:headers => {},
|
|
41
|
-
:response => response_obj,
|
|
42
|
-
}, status: code
|
|
35
|
+
upstream_content_type = response_obj.try(:headers)&.[](:content_type)
|
|
36
|
+
content_type = upstream_content_type&.match?(/json/) ? upstream_content_type : 'application/json'
|
|
37
|
+
|
|
38
|
+
return render body: response_obj.to_s, status: code, content_type: content_type
|
|
43
39
|
rescue StandardError => e
|
|
44
40
|
# Catch any other exceptions here, such as Errno::ECONNREFUSED
|
|
45
41
|
logger.warn("Cloud request failed with exception: #{e}")
|
|
@@ -36,14 +36,10 @@ module InsightsCloud
|
|
|
36
36
|
rescue RestClient::ExceptionWithResponse => e
|
|
37
37
|
response_obj = e.response.presence || e.exception
|
|
38
38
|
code = response_obj.try(:code) || response_obj.try(:http_code) || 500
|
|
39
|
-
|
|
39
|
+
upstream_content_type = response_obj.try(:headers)&.[](:content_type)
|
|
40
|
+
content_type = upstream_content_type&.match?(/json/) ? upstream_content_type : 'application/json'
|
|
40
41
|
|
|
41
|
-
return render
|
|
42
|
-
:message => message,
|
|
43
|
-
:error => response_obj.to_s,
|
|
44
|
-
:headers => {},
|
|
45
|
-
:response => response_obj,
|
|
46
|
-
}, status: code
|
|
42
|
+
return render body: response_obj.to_s, status: code, content_type: content_type
|
|
47
43
|
rescue StandardError => e
|
|
48
44
|
# Catch any other exceptions here, such as Errno::ECONNREFUSED
|
|
49
45
|
logger.warn("Cloud request failed with exception: #{e}")
|
|
@@ -114,6 +114,7 @@ module ForemanInventoryUpload
|
|
|
114
114
|
|
|
115
115
|
def host_ips(host)
|
|
116
116
|
# Determines and returns the IP addresses associated with a host, applying obfuscation if enabled.
|
|
117
|
+
return {} if host.nil?
|
|
117
118
|
|
|
118
119
|
# If IP obfuscation is enabled for the host return a representation of obfuscated IP addresses.
|
|
119
120
|
return obfuscated_ips(host) if obfuscate_ips?(host)
|
|
@@ -163,10 +164,29 @@ module ForemanInventoryUpload
|
|
|
163
164
|
|
|
164
165
|
def hostname_match
|
|
165
166
|
bash_hostname = `uname -n`.chomp
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
167
|
+
foreman_host = ForemanRhCloud.foreman_host
|
|
168
|
+
|
|
169
|
+
# If bash hostname matches foreman_host, use fqdn
|
|
170
|
+
if foreman_host && bash_hostname == foreman_host.name
|
|
171
|
+
return fqdn(foreman_host)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# If no foreman_host, try foreman_host_name from Setting[:foreman_url]
|
|
175
|
+
unless foreman_host
|
|
176
|
+
foreman_hostname_from_setting = ForemanRhCloud.foreman_host_name
|
|
177
|
+
if foreman_hostname_from_setting
|
|
178
|
+
# Apply obfuscation if enabled
|
|
179
|
+
return obfuscate_fqdn(foreman_hostname_from_setting) if Setting[:obfuscate_inventory_hostnames]
|
|
180
|
+
|
|
181
|
+
return foreman_hostname_from_setting
|
|
182
|
+
end
|
|
183
|
+
# Otherwise fall through to bash_hostname below
|
|
184
|
+
# NOTE: Containerized foremanctl setups must configure Setting[:foreman_url]
|
|
185
|
+
# as bash hostname may not be available or meaningful in containers
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Fallback to bash hostname (with obfuscation if enabled)
|
|
189
|
+
if Setting[:obfuscate_inventory_hostnames]
|
|
170
190
|
obfuscate_fqdn(bash_hostname)
|
|
171
191
|
else
|
|
172
192
|
bash_hostname
|
|
@@ -174,6 +194,8 @@ module ForemanInventoryUpload
|
|
|
174
194
|
end
|
|
175
195
|
|
|
176
196
|
def bios_uuid(host)
|
|
197
|
+
return nil if host.nil?
|
|
198
|
+
|
|
177
199
|
value = fact_value(host, 'dmi::system::uuid') || ''
|
|
178
200
|
uuid_value(value)
|
|
179
201
|
end
|
|
@@ -96,7 +96,14 @@ module ForemanInventoryUpload
|
|
|
96
96
|
end
|
|
97
97
|
|
|
98
98
|
def self.inventory_self_url
|
|
99
|
-
|
|
99
|
+
host = ForemanRhCloud.foreman_host
|
|
100
|
+
hostname = host ? host.fqdn : ForemanRhCloud.foreman_host_name
|
|
101
|
+
if hostname.nil?
|
|
102
|
+
Rails.logger.warn("Cannot determine Foreman hostname for inventory sync. " \
|
|
103
|
+
"Please configure Setting[:foreman_url]. " \
|
|
104
|
+
"Containerized setups must explicitly set this.")
|
|
105
|
+
end
|
|
106
|
+
inventory_base_url + "?hostname_or_id=#{hostname}"
|
|
100
107
|
end
|
|
101
108
|
|
|
102
109
|
def self.host_by_id_url(host_uuid)
|
data/lib/foreman_rh_cloud.rb
CHANGED
|
@@ -84,23 +84,50 @@ module ForemanRhCloud
|
|
|
84
84
|
|
|
85
85
|
# For testing purposes we can override the default hostname with an environment variable SATELLITE_RH_CLOUD_FOREMAN_HOST
|
|
86
86
|
def self.foreman_host
|
|
87
|
-
@foreman_host
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
return @foreman_host if defined?(@foreman_host)
|
|
88
|
+
|
|
89
|
+
fullname = foreman_host_name
|
|
90
|
+
return @foreman_host = nil unless fullname
|
|
91
|
+
|
|
92
|
+
# Try fullname first
|
|
93
|
+
host = ::Host.unscoped.friendly.where(name: fullname).first
|
|
94
|
+
|
|
95
|
+
# If not found, try shortname
|
|
96
|
+
if host.nil?
|
|
92
97
|
shortname = /(?<shortname>[^\.]*)\.?.*/.match(fullname)[:shortname]
|
|
93
|
-
::Host.unscoped.friendly.
|
|
98
|
+
host = ::Host.unscoped.friendly.where(name: shortname).first
|
|
94
99
|
end
|
|
100
|
+
|
|
101
|
+
@foreman_host = host
|
|
95
102
|
end
|
|
96
103
|
|
|
97
104
|
def self.foreman_host_name
|
|
98
|
-
ENV['SATELLITE_RH_CLOUD_FOREMAN_HOST'] || marked_foreman_host&.name ||
|
|
105
|
+
ENV['SATELLITE_RH_CLOUD_FOREMAN_HOST'] || marked_foreman_host&.name || foreman_url_hostname
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def self.foreman_url_hostname
|
|
109
|
+
return nil unless Setting[:foreman_url]
|
|
110
|
+
|
|
111
|
+
begin
|
|
112
|
+
# Ensure setting is a string to avoid TypeError from URI.parse
|
|
113
|
+
url = Setting[:foreman_url].to_s
|
|
114
|
+
URI.parse(url).host
|
|
115
|
+
rescue URI::InvalidURIError, ArgumentError, TypeError => e
|
|
116
|
+
Rails.logger.warn("Invalid foreman_url setting: #{e.message}")
|
|
117
|
+
nil
|
|
118
|
+
end
|
|
99
119
|
end
|
|
100
120
|
|
|
101
121
|
def self.marked_foreman_host
|
|
102
|
-
|
|
103
|
-
|
|
122
|
+
# Find host with infrastructure_facet.foreman_instance = true
|
|
123
|
+
# Facets use a special mechanism in Foreman, so we query the facet table directly
|
|
124
|
+
return nil unless defined?(HostFacets::InfrastructureFacet)
|
|
125
|
+
|
|
126
|
+
facet = HostFacets::InfrastructureFacet.find_by(foreman_instance: true)
|
|
127
|
+
facet&.host
|
|
128
|
+
rescue ActiveRecord::StatementInvalid => e
|
|
129
|
+
# Table might not exist yet during migrations
|
|
130
|
+
Rails.logger.debug("Could not query marked foreman host: #{e.message}")
|
|
104
131
|
nil
|
|
105
132
|
end
|
|
106
133
|
|
|
@@ -13,7 +13,16 @@ module InsightsCloud
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def add_satellite_notifications
|
|
16
|
-
|
|
16
|
+
host = foreman_host
|
|
17
|
+
|
|
18
|
+
# Skip if no Foreman host record exists
|
|
19
|
+
unless host
|
|
20
|
+
logger.debug("Skipping Insights notifications: no Foreman host record found")
|
|
21
|
+
blueprint&.notifications&.destroy_all
|
|
22
|
+
return
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
hits_count = InsightsHit.where(host_id: host.id).count
|
|
17
26
|
|
|
18
27
|
# Remove stale notifications
|
|
19
28
|
blueprint.notifications.destroy_all
|
|
@@ -4,7 +4,14 @@ module InventorySync
|
|
|
4
4
|
set_callback :step, :around, :create_facets
|
|
5
5
|
|
|
6
6
|
def plan
|
|
7
|
-
|
|
7
|
+
host = ForemanRhCloud.foreman_host
|
|
8
|
+
|
|
9
|
+
if host.nil?
|
|
10
|
+
logger.warn("Skipping self-host inventory sync: no Foreman host record found.")
|
|
11
|
+
return
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
super(host.organization)
|
|
8
15
|
end
|
|
9
16
|
|
|
10
17
|
def create_facets
|
|
@@ -22,7 +29,10 @@ module InventorySync
|
|
|
22
29
|
private
|
|
23
30
|
|
|
24
31
|
def add_missing_insights_facet(uuids_hash)
|
|
25
|
-
|
|
32
|
+
host = ForemanRhCloud.foreman_host
|
|
33
|
+
return unless host # Guard against nil
|
|
34
|
+
|
|
35
|
+
facet = InsightsFacet.find_or_create_by(host_id: host.id) do |facet|
|
|
26
36
|
facet.uuid = uuids_hash.values.first
|
|
27
37
|
end
|
|
28
38
|
|
data/package.json
CHANGED
|
@@ -154,8 +154,22 @@ module InsightsCloud::Api
|
|
|
154
154
|
|
|
155
155
|
get :forward_request, params: { "path" => "platform/module-update-router/v1/channel" }
|
|
156
156
|
assert_equal 500, @response.status
|
|
157
|
-
assert_equal
|
|
158
|
-
|
|
157
|
+
assert_equal @body, @response.body
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
test "should forward JSON error responses without double-escaping" do
|
|
161
|
+
json_error = { errors: [{ detail: 'inventory_id must exist', status: '404' }] }.to_json
|
|
162
|
+
net_http_resp = Net::HTTPResponse.new(1.0, 404, "Not Found")
|
|
163
|
+
net_http_resp['content-type'] = 'application/json'
|
|
164
|
+
res = RestClient::Response.create(json_error, net_http_resp, @http_req)
|
|
165
|
+
::ForemanRhCloud::CloudRequestForwarder.any_instance.stubs(:execute_cloud_request).raises(RestClient::NotFound.new(res))
|
|
166
|
+
|
|
167
|
+
get :forward_request, params: { "path" => "api/vulnerability/v1/systems/00000000-0000-0000-0000-000000000000" }
|
|
168
|
+
assert_equal 404, @response.status
|
|
169
|
+
assert_includes @response.content_type, 'application/json'
|
|
170
|
+
assert_equal json_error, @response.body
|
|
171
|
+
parsed = JSON.parse(@response.body)
|
|
172
|
+
assert_equal 'inventory_id must exist', parsed['errors'][0]['detail']
|
|
159
173
|
end
|
|
160
174
|
|
|
161
175
|
test "should create insights facet" do
|
|
@@ -177,8 +177,22 @@ module InsightsCloud
|
|
|
177
177
|
|
|
178
178
|
get :forward_request, params: { "controller" => "vulnerabilities", "path" => "api/vulnerability/v1/cves" }, session: set_session
|
|
179
179
|
assert_equal 500, @response.status
|
|
180
|
-
assert_equal
|
|
181
|
-
|
|
180
|
+
assert_equal @body, @response.body
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
test "should forward JSON error responses without double-escaping" do
|
|
184
|
+
json_error = { errors: [{ detail: 'inventory_id must exist', status: '404' }] }.to_json
|
|
185
|
+
net_http_resp = Net::HTTPResponse.new(1.0, 404, "Not Found")
|
|
186
|
+
net_http_resp['content-type'] = 'application/json'
|
|
187
|
+
res = RestClient::Response.create(json_error, net_http_resp, @http_req)
|
|
188
|
+
::ForemanRhCloud::InsightsApiForwarder.any_instance.stubs(:execute_cloud_request).raises(RestClient::NotFound.new(res))
|
|
189
|
+
|
|
190
|
+
get :forward_request, params: { "controller" => "vulnerabilities", "path" => "api/vulnerability/v1/systems/00000000-0000-0000-0000-000000000000" }, session: set_session
|
|
191
|
+
assert_equal 404, @response.status
|
|
192
|
+
assert_includes @response.content_type, 'application/json'
|
|
193
|
+
assert_equal json_error, @response.body
|
|
194
|
+
parsed = JSON.parse(@response.body)
|
|
195
|
+
assert_equal 'inventory_id must exist', parsed['errors'][0]['detail']
|
|
182
196
|
end
|
|
183
197
|
|
|
184
198
|
test "should allow forward_request with nil location (Any location)" do
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'test_plugin_helper'
|
|
2
|
+
require 'foreman_tasks/test_helpers'
|
|
3
|
+
|
|
4
|
+
class InsightsGenerateNotificationsTest < ActiveSupport::TestCase
|
|
5
|
+
include Dynflow::Testing::Factories
|
|
6
|
+
|
|
7
|
+
setup do
|
|
8
|
+
User.current = User.find_by(login: 'secret_admin')
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
test 'skips notifications when foreman_host is nil' do
|
|
12
|
+
ForemanRhCloud.stubs(:foreman_host).returns(nil)
|
|
13
|
+
|
|
14
|
+
# Ensure blueprint exists or create it
|
|
15
|
+
NotificationBlueprint.find_or_create_by(name: 'insights_satellite_hits') do |bp|
|
|
16
|
+
bp.message = 'Test message'
|
|
17
|
+
bp.level = 'info'
|
|
18
|
+
bp.expires_in = 7.days
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
plan = ForemanTasks.sync_task(InsightsCloud::Async::InsightsGenerateNotifications)
|
|
22
|
+
|
|
23
|
+
# Should not raise an error, task completes successfully
|
|
24
|
+
assert_includes ['success', 'stopped'], plan.state, "Task should complete without error"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -106,4 +106,13 @@ class InventorySelfHostSyncTest < ActiveSupport::TestCase
|
|
|
106
106
|
|
|
107
107
|
assert_equal @host1_inventory_id, @host1.insights.uuid
|
|
108
108
|
end
|
|
109
|
+
|
|
110
|
+
test 'skips sync when foreman_host is nil' do
|
|
111
|
+
ForemanRhCloud.stubs(:foreman_host).returns(nil)
|
|
112
|
+
|
|
113
|
+
plan = ForemanTasks.sync_task(InventorySync::Async::InventorySelfHostSync)
|
|
114
|
+
|
|
115
|
+
# Task should be stopped (not executed) when host is nil, not failed
|
|
116
|
+
assert_equal 'stopped', plan.state
|
|
117
|
+
end
|
|
109
118
|
end
|
|
@@ -2,8 +2,9 @@ require 'test_plugin_helper'
|
|
|
2
2
|
|
|
3
3
|
class ForemanRhCloudSelfHostTest < ActiveSupport::TestCase
|
|
4
4
|
setup do
|
|
5
|
-
# reset cached value
|
|
6
|
-
ForemanRhCloud.
|
|
5
|
+
# reset cached value - must remove the variable entirely, not just set to nil
|
|
6
|
+
ForemanRhCloud.remove_instance_variable(:@foreman_host) if ForemanRhCloud.instance_variable_defined?(:@foreman_host)
|
|
7
|
+
ENV.delete('SATELLITE_RH_CLOUD_FOREMAN_HOST')
|
|
7
8
|
end
|
|
8
9
|
|
|
9
10
|
test 'finds host by fullname' do
|
|
@@ -32,4 +33,51 @@ class ForemanRhCloudSelfHostTest < ActiveSupport::TestCase
|
|
|
32
33
|
|
|
33
34
|
assert_equal @host, actual
|
|
34
35
|
end
|
|
36
|
+
|
|
37
|
+
test 'returns nil when host does not exist' do
|
|
38
|
+
ForemanRhCloud.expects(:foreman_host_name).returns('nonexistent.example.com')
|
|
39
|
+
|
|
40
|
+
actual = ForemanRhCloud.foreman_host
|
|
41
|
+
|
|
42
|
+
assert_nil actual
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
test 'returns nil and does not query Host when foreman_host_name is nil' do
|
|
46
|
+
ForemanRhCloud.stubs(:foreman_host_name).returns(nil)
|
|
47
|
+
::Host.unscoped.friendly.expects(:where).never
|
|
48
|
+
|
|
49
|
+
assert_nil ForemanRhCloud.foreman_host
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
test 'caches nil value to avoid repeated lookups' do
|
|
53
|
+
ForemanRhCloud.expects(:foreman_host_name).once.returns('nonexistent.example.com')
|
|
54
|
+
|
|
55
|
+
2.times { ForemanRhCloud.foreman_host }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
test 'extracts hostname from foreman_url setting' do
|
|
59
|
+
Setting[:foreman_url] = 'https://satellite.example.com'
|
|
60
|
+
|
|
61
|
+
actual = ForemanRhCloud.foreman_url_hostname
|
|
62
|
+
|
|
63
|
+
assert_equal 'satellite.example.com', actual
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
test 'handles invalid foreman_url gracefully' do
|
|
67
|
+
# Stub Setting to return invalid URL without validation
|
|
68
|
+
Setting.stubs(:[]).with(:foreman_url).returns('not a valid url')
|
|
69
|
+
|
|
70
|
+
actual = ForemanRhCloud.foreman_url_hostname
|
|
71
|
+
|
|
72
|
+
assert_nil actual
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
test 'foreman_host_name uses foreman_url when marked_foreman_host is nil' do
|
|
76
|
+
ForemanRhCloud.expects(:marked_foreman_host).returns(nil)
|
|
77
|
+
ForemanRhCloud.expects(:foreman_url_hostname).returns('satellite.example.com')
|
|
78
|
+
|
|
79
|
+
actual = ForemanRhCloud.foreman_host_name
|
|
80
|
+
|
|
81
|
+
assert_equal 'satellite.example.com', actual
|
|
82
|
+
end
|
|
35
83
|
end
|
|
@@ -2,7 +2,8 @@ require 'test_plugin_helper'
|
|
|
2
2
|
|
|
3
3
|
class MetadataGeneratorTest < ActiveSupport::TestCase
|
|
4
4
|
setup do
|
|
5
|
-
|
|
5
|
+
# reset cached value - must remove the variable entirely, not just set to nil
|
|
6
|
+
ForemanRhCloud.remove_instance_variable(:@foreman_host) if ForemanRhCloud.instance_variable_defined?(:@foreman_host)
|
|
6
7
|
end
|
|
7
8
|
|
|
8
9
|
test 'generates an empty report' do
|
|
@@ -64,4 +65,26 @@ class MetadataGeneratorTest < ActiveSupport::TestCase
|
|
|
64
65
|
assert_not_nil(slice = slices['test_12345'])
|
|
65
66
|
assert_equal 3, slice['number_hosts']
|
|
66
67
|
end
|
|
68
|
+
|
|
69
|
+
test 'generates metadata when foreman_host is nil' do
|
|
70
|
+
ForemanRhCloud.stubs(:foreman_host).returns(nil)
|
|
71
|
+
ForemanRhCloud.stubs(:foreman_host_name).returns('satellite.example.com')
|
|
72
|
+
|
|
73
|
+
generator = ForemanInventoryUpload::Generators::Metadata.new
|
|
74
|
+
|
|
75
|
+
# Should not raise an error
|
|
76
|
+
json_str = nil
|
|
77
|
+
assert_nothing_raised do
|
|
78
|
+
json_str = generator.render do
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Verify hostname is from foreman_host_name
|
|
83
|
+
actual = JSON.parse(json_str.join("\n"))
|
|
84
|
+
assert_equal 'satellite.example.com', actual['reporting_host_name']
|
|
85
|
+
# Verify IP and BIOS UUID fields are nil when host is nil
|
|
86
|
+
# This is acceptable per SAT-25889 - cloud services don't rely on these fields
|
|
87
|
+
assert_nil actual['reporting_host_ips']
|
|
88
|
+
assert_nil actual['reporting_host_bios_uuid']
|
|
89
|
+
end
|
|
67
90
|
end
|
|
@@ -3,6 +3,10 @@ import PropTypes from 'prop-types';
|
|
|
3
3
|
import { ScalprumComponent, ScalprumProvider } from '@scalprum/react-core';
|
|
4
4
|
import { createProviderOptions } from '../common/ScalprumModule/ScalprumContext';
|
|
5
5
|
import { useInsightsPermissions } from '../common/Hooks/PermissionsHooks';
|
|
6
|
+
import {
|
|
7
|
+
vulnerabilityDisabled,
|
|
8
|
+
useTabRedirect,
|
|
9
|
+
} from '../ForemanRhCloudHelpers';
|
|
6
10
|
import './CVEsHostDetailsTab.scss';
|
|
7
11
|
|
|
8
12
|
const CVEsHostDetailsTab = ({ systemId }) => {
|
|
@@ -26,6 +30,15 @@ CVEsHostDetailsTab.propTypes = {
|
|
|
26
30
|
|
|
27
31
|
const CVEsHostDetailsTabWrapper = ({ response }) => {
|
|
28
32
|
const permissions = useInsightsPermissions();
|
|
33
|
+
const isHostDataLoaded = Boolean(response?.id);
|
|
34
|
+
const shouldHideTab = useTabRedirect(
|
|
35
|
+
isHostDataLoaded && vulnerabilityDisabled({ hostDetails: response })
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
if (shouldHideTab) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
29
42
|
return (
|
|
30
43
|
<ScalprumProvider {...createProviderOptions(permissions)}>
|
|
31
44
|
<CVEsHostDetailsTab
|
|
@@ -38,10 +51,19 @@ const CVEsHostDetailsTabWrapper = ({ response }) => {
|
|
|
38
51
|
|
|
39
52
|
CVEsHostDetailsTabWrapper.propTypes = {
|
|
40
53
|
response: PropTypes.shape({
|
|
54
|
+
id: PropTypes.number,
|
|
55
|
+
operatingsystem_name: PropTypes.string,
|
|
56
|
+
vulnerability: PropTypes.shape({
|
|
57
|
+
enabled: PropTypes.bool,
|
|
58
|
+
}),
|
|
41
59
|
subscription_facet_attributes: PropTypes.shape({
|
|
42
|
-
uuid: PropTypes.string
|
|
60
|
+
uuid: PropTypes.string,
|
|
43
61
|
}),
|
|
44
|
-
})
|
|
62
|
+
}),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
CVEsHostDetailsTabWrapper.defaultProps = {
|
|
66
|
+
response: {},
|
|
45
67
|
};
|
|
46
68
|
|
|
47
69
|
export default CVEsHostDetailsTabWrapper;
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { render } from '@testing-library/react';
|
|
3
|
+
import { MemoryRouter } from 'react-router-dom';
|
|
3
4
|
import CVEsHostDetailsTabWrapper from '../CVEsHostDetailsTab';
|
|
5
|
+
import { OVERVIEW_TAB_PATH } from '../../ForemanRhCloudHelpers';
|
|
6
|
+
|
|
7
|
+
const mockHistoryReplace = jest.fn();
|
|
8
|
+
|
|
9
|
+
jest.mock('react-router-dom', () => ({
|
|
10
|
+
...jest.requireActual('react-router-dom'),
|
|
11
|
+
useHistory: () => ({
|
|
12
|
+
replace: mockHistoryReplace,
|
|
13
|
+
}),
|
|
14
|
+
}));
|
|
4
15
|
|
|
5
16
|
jest.mock('foremanReact/Root/Context/ForemanContext', () => ({
|
|
6
17
|
useForemanContext: () => ({
|
|
@@ -25,31 +36,44 @@ jest.mock('@scalprum/react-core', () => {
|
|
|
25
36
|
};
|
|
26
37
|
});
|
|
27
38
|
|
|
39
|
+
const defaultResponse = {
|
|
40
|
+
id: 1,
|
|
41
|
+
operatingsystem_name: 'Red Hat Enterprise Linux 8',
|
|
42
|
+
vulnerability: { enabled: true },
|
|
43
|
+
subscription_facet_attributes: { uuid: '1-2-3' },
|
|
44
|
+
};
|
|
45
|
+
|
|
28
46
|
describe('CVEsHostDetailsTabWrapper', () => {
|
|
29
47
|
beforeEach(() => {
|
|
30
48
|
jest.clearAllMocks();
|
|
31
49
|
});
|
|
32
50
|
|
|
33
|
-
it('renders without crashing', () => {
|
|
51
|
+
it('renders without crashing and does not redirect for valid host', () => {
|
|
34
52
|
const { container } = render(
|
|
35
|
-
<
|
|
36
|
-
response={
|
|
37
|
-
|
|
53
|
+
<MemoryRouter>
|
|
54
|
+
<CVEsHostDetailsTabWrapper response={defaultResponse} />
|
|
55
|
+
</MemoryRouter>
|
|
38
56
|
);
|
|
39
57
|
expect(
|
|
40
58
|
container.querySelector(
|
|
41
59
|
'.rh-cloud-insights-vulnerability-host-details-component'
|
|
42
60
|
)
|
|
43
61
|
).toBeTruthy();
|
|
62
|
+
expect(mockHistoryReplace).not.toHaveBeenCalled();
|
|
44
63
|
});
|
|
45
64
|
|
|
46
65
|
it('remounts ScalprumComponent when systemId changes', () => {
|
|
47
66
|
const { ScalprumComponent } = require('@scalprum/react-core');
|
|
48
67
|
|
|
68
|
+
const responseHostA = {
|
|
69
|
+
...defaultResponse,
|
|
70
|
+
subscription_facet_attributes: { uuid: 'uuid-host-A' },
|
|
71
|
+
};
|
|
72
|
+
|
|
49
73
|
const { rerender } = render(
|
|
50
|
-
<
|
|
51
|
-
response={
|
|
52
|
-
|
|
74
|
+
<MemoryRouter>
|
|
75
|
+
<CVEsHostDetailsTabWrapper response={responseHostA} />
|
|
76
|
+
</MemoryRouter>
|
|
53
77
|
);
|
|
54
78
|
|
|
55
79
|
expect(mockUnmountTracker).not.toHaveBeenCalled();
|
|
@@ -58,10 +82,15 @@ describe('CVEsHostDetailsTabWrapper', () => {
|
|
|
58
82
|
expect.anything()
|
|
59
83
|
);
|
|
60
84
|
|
|
85
|
+
const responseHostB = {
|
|
86
|
+
...defaultResponse,
|
|
87
|
+
subscription_facet_attributes: { uuid: 'uuid-host-B' },
|
|
88
|
+
};
|
|
89
|
+
|
|
61
90
|
rerender(
|
|
62
|
-
<
|
|
63
|
-
response={
|
|
64
|
-
|
|
91
|
+
<MemoryRouter>
|
|
92
|
+
<CVEsHostDetailsTabWrapper response={responseHostB} />
|
|
93
|
+
</MemoryRouter>
|
|
65
94
|
);
|
|
66
95
|
|
|
67
96
|
expect(mockUnmountTracker).toHaveBeenCalledTimes(1);
|
|
@@ -70,4 +99,38 @@ describe('CVEsHostDetailsTabWrapper', () => {
|
|
|
70
99
|
expect.anything()
|
|
71
100
|
);
|
|
72
101
|
});
|
|
102
|
+
|
|
103
|
+
it('redirects to Overview when tab should be hidden', () => {
|
|
104
|
+
const nonRhelResponse = {
|
|
105
|
+
id: 2,
|
|
106
|
+
operatingsystem_name: 'Ubuntu 20.04',
|
|
107
|
+
vulnerability: { enabled: false },
|
|
108
|
+
subscription_facet_attributes: { uuid: '1-2-3' },
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const { container } = render(
|
|
112
|
+
<MemoryRouter>
|
|
113
|
+
<CVEsHostDetailsTabWrapper response={nonRhelResponse} />
|
|
114
|
+
</MemoryRouter>
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
expect(mockHistoryReplace).toHaveBeenCalledWith(OVERVIEW_TAB_PATH);
|
|
118
|
+
expect(
|
|
119
|
+
container.querySelector(
|
|
120
|
+
'.rh-cloud-insights-vulnerability-host-details-component'
|
|
121
|
+
)
|
|
122
|
+
).toBeNull();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('does not redirect when host data is not yet loaded', () => {
|
|
126
|
+
const emptyResponse = { subscription_facet_attributes: { uuid: '1-2-3' } };
|
|
127
|
+
|
|
128
|
+
render(
|
|
129
|
+
<MemoryRouter>
|
|
130
|
+
<CVEsHostDetailsTabWrapper response={emptyResponse} />
|
|
131
|
+
</MemoryRouter>
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
expect(mockHistoryReplace).not.toHaveBeenCalled();
|
|
135
|
+
});
|
|
73
136
|
});
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useHistory } from 'react-router-dom';
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* copied from core, since it's not in the ReactApp folder,
|
|
3
6
|
* it's complicated to import it and mock it in tests.
|
|
@@ -5,6 +8,25 @@
|
|
|
5
8
|
*/
|
|
6
9
|
export const foremanUrl = path => `${window.URL_PREFIX}${path}`;
|
|
7
10
|
|
|
11
|
+
export const OVERVIEW_TAB_PATH = '/Overview';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Redirects to Overview tab when the current tab should be hidden
|
|
15
|
+
* @param {boolean} shouldRedirect - Whether to redirect (e.g., host loaded AND tab should hide)
|
|
16
|
+
* @returns {boolean} - Returns shouldRedirect for convenience
|
|
17
|
+
*/
|
|
18
|
+
export const useTabRedirect = shouldRedirect => {
|
|
19
|
+
const history = useHistory();
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (shouldRedirect && history) {
|
|
23
|
+
history.replace(OVERVIEW_TAB_PATH);
|
|
24
|
+
}
|
|
25
|
+
}, [shouldRedirect, history]);
|
|
26
|
+
|
|
27
|
+
return shouldRedirect;
|
|
28
|
+
};
|
|
29
|
+
|
|
8
30
|
export const isNotRhelHost = ({ hostDetails }) =>
|
|
9
31
|
// This regex tries matches sane variations of "RedHat", "RHEL" and "RHCOS"
|
|
10
32
|
!new RegExp('red[\\s\\-]?hat|rh[\\s\\-]?el|rhc[\\s\\-]?os', 'i').test(
|
|
@@ -25,6 +25,11 @@ import { useIopConfig } from '../common/Hooks/ConfigHooks';
|
|
|
25
25
|
import { generateRuleUrl } from '../InsightsCloudSync/InsightsCloudSync';
|
|
26
26
|
import { createProviderOptions } from '../common/ScalprumModule/ScalprumContext';
|
|
27
27
|
import { useInsightsPermissions } from '../common/Hooks/PermissionsHooks';
|
|
28
|
+
import {
|
|
29
|
+
isNotRhelHost,
|
|
30
|
+
hasNoInsightsFacet,
|
|
31
|
+
useTabRedirect,
|
|
32
|
+
} from '../ForemanRhCloudHelpers';
|
|
28
33
|
|
|
29
34
|
// Hosted Insights advisor
|
|
30
35
|
const NewHostDetailsTab = ({ hostName, router }) => {
|
|
@@ -165,7 +170,18 @@ const IopInsightsTabWrapped = props => {
|
|
|
165
170
|
};
|
|
166
171
|
|
|
167
172
|
const InsightsTab = props => {
|
|
173
|
+
const { response } = props;
|
|
168
174
|
const isIop = useIopConfig();
|
|
175
|
+
const isHostDataLoaded = Boolean(response?.id);
|
|
176
|
+
const shouldHideTab = useTabRedirect(
|
|
177
|
+
isHostDataLoaded &&
|
|
178
|
+
(isNotRhelHost({ hostDetails: response }) ||
|
|
179
|
+
hasNoInsightsFacet({ response, hostDetails: response }))
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
if (shouldHideTab) {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
169
185
|
|
|
170
186
|
return isIop ? (
|
|
171
187
|
<IopInsightsTabWrapped {...props} />
|
|
@@ -174,6 +190,16 @@ const InsightsTab = props => {
|
|
|
174
190
|
);
|
|
175
191
|
};
|
|
176
192
|
|
|
177
|
-
InsightsTab.
|
|
193
|
+
InsightsTab.propTypes = {
|
|
194
|
+
response: PropTypes.shape({
|
|
195
|
+
id: PropTypes.number,
|
|
196
|
+
operatingsystem_name: PropTypes.string,
|
|
197
|
+
insights_attributes: PropTypes.object,
|
|
198
|
+
}),
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
InsightsTab.defaultProps = {
|
|
202
|
+
response: {},
|
|
203
|
+
};
|
|
178
204
|
|
|
179
205
|
export default InsightsTab;
|
|
@@ -2,9 +2,20 @@ import React from 'react';
|
|
|
2
2
|
import { render } from '@testing-library/react';
|
|
3
3
|
import '@testing-library/jest-dom';
|
|
4
4
|
import { Provider } from 'react-redux';
|
|
5
|
+
import { MemoryRouter } from 'react-router-dom';
|
|
5
6
|
import configureMockStore from 'redux-mock-store';
|
|
6
7
|
import thunk from 'redux-thunk';
|
|
7
8
|
import NewHostDetailsTab from '../NewHostDetailsTab';
|
|
9
|
+
import { OVERVIEW_TAB_PATH } from '../../ForemanRhCloudHelpers';
|
|
10
|
+
|
|
11
|
+
const mockHistoryReplace = jest.fn();
|
|
12
|
+
|
|
13
|
+
jest.mock('react-router-dom', () => ({
|
|
14
|
+
...jest.requireActual('react-router-dom'),
|
|
15
|
+
useHistory: () => ({
|
|
16
|
+
replace: mockHistoryReplace,
|
|
17
|
+
}),
|
|
18
|
+
}));
|
|
8
19
|
|
|
9
20
|
jest.mock('../../common/Hooks/ConfigHooks', () => ({
|
|
10
21
|
useIopConfig: jest.fn(() => false),
|
|
@@ -14,8 +25,33 @@ jest.mock('foremanReact/common/I18n', () => ({
|
|
|
14
25
|
translate: jest.fn(str => str),
|
|
15
26
|
}));
|
|
16
27
|
|
|
28
|
+
jest.mock(
|
|
29
|
+
'foremanReact/components/SearchBar',
|
|
30
|
+
() => () => <div>SearchBar</div>,
|
|
31
|
+
{ virtual: true }
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
jest.mock('../../InsightsCloudSync/Components/InsightsTable', () => () => (
|
|
35
|
+
<div>InsightsTable</div>
|
|
36
|
+
));
|
|
37
|
+
|
|
38
|
+
jest.mock('../../InsightsCloudSync/Components/RemediationModal', () => () => (
|
|
39
|
+
<div>RemediationModal</div>
|
|
40
|
+
));
|
|
41
|
+
|
|
42
|
+
jest.mock(
|
|
43
|
+
'../../InsightsCloudSync/Components/InsightsTable/Pagination',
|
|
44
|
+
() => () => <div>Pagination</div>
|
|
45
|
+
);
|
|
46
|
+
|
|
17
47
|
const mockStore = configureMockStore([thunk]);
|
|
18
48
|
|
|
49
|
+
const defaultResponse = {
|
|
50
|
+
id: 1,
|
|
51
|
+
operatingsystem_name: 'Red Hat Enterprise Linux 8',
|
|
52
|
+
insights_attributes: { uuid: 'test-uuid' },
|
|
53
|
+
};
|
|
54
|
+
|
|
19
55
|
describe('NewHostDetailsTab', () => {
|
|
20
56
|
let store;
|
|
21
57
|
let mockRouter;
|
|
@@ -68,17 +104,18 @@ describe('NewHostDetailsTab', () => {
|
|
|
68
104
|
it('should preserve hash when clearing search params on unmount', () => {
|
|
69
105
|
const { unmount } = render(
|
|
70
106
|
<Provider store={store}>
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
107
|
+
<MemoryRouter>
|
|
108
|
+
<NewHostDetailsTab
|
|
109
|
+
hostName="test-host.example.com"
|
|
110
|
+
router={mockRouter}
|
|
111
|
+
response={defaultResponse}
|
|
112
|
+
/>
|
|
113
|
+
</MemoryRouter>
|
|
75
114
|
</Provider>
|
|
76
115
|
);
|
|
77
116
|
|
|
78
|
-
// Unmount the component to trigger cleanup
|
|
79
117
|
unmount();
|
|
80
118
|
|
|
81
|
-
// Verify router.replace was called with both search: null AND the existing hash
|
|
82
119
|
expect(mockRouter.replace).toHaveBeenCalledWith({
|
|
83
120
|
search: null,
|
|
84
121
|
hash: '#/Insights',
|
|
@@ -90,16 +127,18 @@ describe('NewHostDetailsTab', () => {
|
|
|
90
127
|
|
|
91
128
|
const { unmount } = render(
|
|
92
129
|
<Provider store={store}>
|
|
93
|
-
<
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
130
|
+
<MemoryRouter>
|
|
131
|
+
<NewHostDetailsTab
|
|
132
|
+
hostName="test-host.example.com"
|
|
133
|
+
router={mockRouter}
|
|
134
|
+
response={defaultResponse}
|
|
135
|
+
/>
|
|
136
|
+
</MemoryRouter>
|
|
97
137
|
</Provider>
|
|
98
138
|
);
|
|
99
139
|
|
|
100
140
|
unmount();
|
|
101
141
|
|
|
102
|
-
// When there's no hash, should only pass search: null
|
|
103
142
|
expect(mockRouter.replace).toHaveBeenCalledWith({
|
|
104
143
|
search: null,
|
|
105
144
|
});
|
|
@@ -114,16 +153,18 @@ describe('NewHostDetailsTab', () => {
|
|
|
114
153
|
|
|
115
154
|
const { unmount } = render(
|
|
116
155
|
<Provider store={store}>
|
|
117
|
-
<
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
156
|
+
<MemoryRouter>
|
|
157
|
+
<NewHostDetailsTab
|
|
158
|
+
hostName="test-host.example.com"
|
|
159
|
+
router={routerWithoutLocation}
|
|
160
|
+
response={defaultResponse}
|
|
161
|
+
/>
|
|
162
|
+
</MemoryRouter>
|
|
121
163
|
</Provider>
|
|
122
164
|
);
|
|
123
165
|
|
|
124
166
|
unmount();
|
|
125
167
|
|
|
126
|
-
// Should still call replace with search: null even if location is undefined
|
|
127
168
|
expect(routerWithoutLocation.replace).toHaveBeenCalledWith({
|
|
128
169
|
search: null,
|
|
129
170
|
});
|
|
@@ -132,23 +173,94 @@ describe('NewHostDetailsTab', () => {
|
|
|
132
173
|
it('should use the latest hash value at unmount time, not a stale captured value', () => {
|
|
133
174
|
const { unmount } = render(
|
|
134
175
|
<Provider store={store}>
|
|
135
|
-
<
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
176
|
+
<MemoryRouter>
|
|
177
|
+
<NewHostDetailsTab
|
|
178
|
+
hostName="test-host.example.com"
|
|
179
|
+
router={mockRouter}
|
|
180
|
+
response={defaultResponse}
|
|
181
|
+
/>
|
|
182
|
+
</MemoryRouter>
|
|
139
183
|
</Provider>
|
|
140
184
|
);
|
|
141
185
|
|
|
142
|
-
// Change the hash after mount, before unmount
|
|
143
186
|
mockRouter.location.hash = '#/Overview';
|
|
144
187
|
|
|
145
188
|
unmount();
|
|
146
189
|
|
|
147
|
-
// Verify router.replace was called with the UPDATED hash, not the initial '#/Insights'
|
|
148
190
|
expect(mockRouter.replace).toHaveBeenCalledWith({
|
|
149
191
|
search: null,
|
|
150
192
|
hash: '#/Overview',
|
|
151
193
|
});
|
|
152
194
|
});
|
|
153
195
|
});
|
|
196
|
+
|
|
197
|
+
describe('tab visibility', () => {
|
|
198
|
+
it('should redirect to Overview when host is not RHEL', () => {
|
|
199
|
+
const nonRhelResponse = {
|
|
200
|
+
id: 2,
|
|
201
|
+
operatingsystem_name: 'Ubuntu 20.04',
|
|
202
|
+
insights_attributes: { uuid: 'test-uuid' },
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
render(
|
|
206
|
+
<Provider store={store}>
|
|
207
|
+
<MemoryRouter>
|
|
208
|
+
<NewHostDetailsTab
|
|
209
|
+
hostName="test-host.example.com"
|
|
210
|
+
response={nonRhelResponse}
|
|
211
|
+
/>
|
|
212
|
+
</MemoryRouter>
|
|
213
|
+
</Provider>
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
expect(mockHistoryReplace).toHaveBeenCalledWith(OVERVIEW_TAB_PATH);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('should redirect to Overview when insights facet is missing', () => {
|
|
220
|
+
const responseWithoutInsights = {
|
|
221
|
+
id: 3,
|
|
222
|
+
operatingsystem_name: 'Red Hat Enterprise Linux 8',
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
render(
|
|
226
|
+
<Provider store={store}>
|
|
227
|
+
<MemoryRouter>
|
|
228
|
+
<NewHostDetailsTab
|
|
229
|
+
hostName="test-host.example.com"
|
|
230
|
+
response={responseWithoutInsights}
|
|
231
|
+
/>
|
|
232
|
+
</MemoryRouter>
|
|
233
|
+
</Provider>
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
expect(mockHistoryReplace).toHaveBeenCalledWith(OVERVIEW_TAB_PATH);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('should not redirect when host is valid RHEL with insights facet', () => {
|
|
240
|
+
render(
|
|
241
|
+
<Provider store={store}>
|
|
242
|
+
<MemoryRouter>
|
|
243
|
+
<NewHostDetailsTab
|
|
244
|
+
hostName="test-host.example.com"
|
|
245
|
+
response={defaultResponse}
|
|
246
|
+
/>
|
|
247
|
+
</MemoryRouter>
|
|
248
|
+
</Provider>
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
expect(mockHistoryReplace).not.toHaveBeenCalled();
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('should not redirect when host data is not yet loaded', () => {
|
|
255
|
+
render(
|
|
256
|
+
<Provider store={store}>
|
|
257
|
+
<MemoryRouter>
|
|
258
|
+
<NewHostDetailsTab hostName="test-host.example.com" response={{}} />
|
|
259
|
+
</MemoryRouter>
|
|
260
|
+
</Provider>
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
expect(mockHistoryReplace).not.toHaveBeenCalled();
|
|
264
|
+
});
|
|
265
|
+
});
|
|
154
266
|
});
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: foreman_rh_cloud
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 13.2.
|
|
4
|
+
version: 13.2.10
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Foreman Red Hat Cloud team
|
|
@@ -30,6 +30,9 @@ dependencies:
|
|
|
30
30
|
- - ">="
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
32
|
version: 10.0.0
|
|
33
|
+
- - "<"
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: 13.0.0
|
|
33
36
|
type: :runtime
|
|
34
37
|
prerelease: false
|
|
35
38
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -37,6 +40,9 @@ dependencies:
|
|
|
37
40
|
- - ">="
|
|
38
41
|
- !ruby/object:Gem::Version
|
|
39
42
|
version: 10.0.0
|
|
43
|
+
- - "<"
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: 13.0.0
|
|
40
46
|
- !ruby/object:Gem::Dependency
|
|
41
47
|
name: katello
|
|
42
48
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -265,6 +271,7 @@ files:
|
|
|
265
271
|
- test/jobs/host_inventory_report_job_test.rb
|
|
266
272
|
- test/jobs/insights_client_status_aging_test.rb
|
|
267
273
|
- test/jobs/insights_full_sync_test.rb
|
|
274
|
+
- test/jobs/insights_generate_notifications_test.rb
|
|
268
275
|
- test/jobs/insights_resolutions_sync_test.rb
|
|
269
276
|
- test/jobs/insights_rules_sync_test.rb
|
|
270
277
|
- test/jobs/inventory_full_sync_test.rb
|
|
@@ -673,7 +680,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
673
680
|
- !ruby/object:Gem::Version
|
|
674
681
|
version: '0'
|
|
675
682
|
requirements: []
|
|
676
|
-
rubygems_version: 4.0.
|
|
683
|
+
rubygems_version: 4.0.16
|
|
677
684
|
specification_version: 4
|
|
678
685
|
summary: Summary of ForemanRhCloud.
|
|
679
686
|
test_files:
|
|
@@ -697,6 +704,7 @@ test_files:
|
|
|
697
704
|
- test/jobs/host_inventory_report_job_test.rb
|
|
698
705
|
- test/jobs/insights_client_status_aging_test.rb
|
|
699
706
|
- test/jobs/insights_full_sync_test.rb
|
|
707
|
+
- test/jobs/insights_generate_notifications_test.rb
|
|
700
708
|
- test/jobs/insights_resolutions_sync_test.rb
|
|
701
709
|
- test/jobs/insights_rules_sync_test.rb
|
|
702
710
|
- test/jobs/inventory_full_sync_test.rb
|