foreman_rh_cloud 13.0.6 → 13.0.8

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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/foreman_rh_cloud/registration_manager_extensions.rb +39 -0
  3. data/lib/foreman_inventory_upload/async/create_missing_insights_facets.rb +3 -2
  4. data/lib/foreman_inventory_upload/scripts/uploader.sh.erb +1 -1
  5. data/lib/foreman_inventory_upload.rb +6 -2
  6. data/lib/foreman_rh_cloud/engine.rb +1 -0
  7. data/lib/foreman_rh_cloud/plugin.rb +4 -0
  8. data/lib/foreman_rh_cloud/version.rb +1 -1
  9. data/lib/tasks/rh_cloud_inventory.rake +11 -1
  10. data/package.json +1 -1
  11. data/test/jobs/cloud_connector_announce_task_test.rb +3 -2
  12. data/test/jobs/connector_playbook_execution_reporter_task_test.rb +32 -20
  13. data/test/jobs/create_missing_insights_facets_test.rb +151 -0
  14. data/test/jobs/exponential_backoff_test.rb +9 -8
  15. data/test/jobs/generate_host_report_test.rb +100 -0
  16. data/test/jobs/generate_report_job_test.rb +146 -0
  17. data/test/jobs/host_inventory_report_job_test.rb +244 -0
  18. data/test/jobs/insights_client_status_aging_test.rb +3 -2
  19. data/test/jobs/insights_full_sync_test.rb +13 -7
  20. data/test/jobs/insights_resolutions_sync_test.rb +9 -5
  21. data/test/jobs/insights_rules_sync_test.rb +5 -3
  22. data/test/jobs/inventory_full_sync_test.rb +9 -5
  23. data/test/jobs/inventory_hosts_sync_test.rb +11 -6
  24. data/test/jobs/inventory_scheduled_sync_test.rb +10 -6
  25. data/test/jobs/inventory_self_host_sync_test.rb +1 -1
  26. data/test/jobs/queue_for_upload_job_test.rb +9 -7
  27. data/test/jobs/remove_insights_hosts_job_test.rb +14 -15
  28. data/test/jobs/single_host_report_job_test.rb +155 -0
  29. data/test/jobs/upload_report_job_test.rb +5 -4
  30. data/test/unit/lib/foreman_rh_cloud/registration_manager_extensions_test.rb +192 -0
  31. data/webpack/ForemanColumnExtensions/index.js +2 -0
  32. data/webpack/InsightsCloudSync/Components/InsightsSettings/InsightsSettings.js +3 -3
  33. data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTable.js +3 -9
  34. data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTable.test.js +13 -0
  35. data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationModal.js +2 -2
  36. data/webpack/InsightsCloudSync/Components/ToolbarDropdown.js +3 -3
  37. data/webpack/InsightsCloudSync/InsightsCloudSync.js +3 -3
  38. data/webpack/InsightsCloudSync/InsightsCloudSync.test.js +10 -0
  39. data/webpack/InsightsCloudSync/__snapshots__/InsightsCloudSync.test.js.snap +1 -1
  40. data/webpack/InsightsHostDetailsTab/NewHostDetailsTab.js +5 -5
  41. data/webpack/InsightsVulnerabilityHostIndexExtensions/CVECountCell.js +2 -2
  42. data/webpack/InsightsVulnerabilityHostIndexExtensions/__tests__/CVECountCell.test.js +7 -7
  43. data/webpack/common/Hooks/ConfigHooks.js +3 -16
  44. metadata +14 -1
@@ -2,7 +2,7 @@ require 'test_plugin_helper'
2
2
  require 'foreman_tasks/test_helpers'
3
3
 
4
4
  class InventoryScheduledSyncTest < ActiveSupport::TestCase
5
- include ForemanTasks::TestHelpers::WithInThreadExecutor
5
+ include Dynflow::Testing::Factories
6
6
 
7
7
  test 'Schedules an execution if auto upload is enabled' do
8
8
  Setting[:allow_auto_inventory_upload] = true
@@ -11,7 +11,8 @@ class InventoryScheduledSyncTest < ActiveSupport::TestCase
11
11
  InventorySync::Async::InventoryScheduledSync.any_instance.expects(:plan_org_sync).times(Organization.unscoped.count)
12
12
  InventorySync::Async::InventoryScheduledSync.any_instance.expects(:plan_remove_insights_hosts).times(Organization.unscoped.count)
13
13
 
14
- ForemanTasks.sync_task(InventorySync::Async::InventoryScheduledSync)
14
+ action = create_and_plan_action(InventorySync::Async::InventoryScheduledSync)
15
+ run_action(action)
15
16
  end
16
17
 
17
18
  test 'Skips execution if with_iop_smart_proxy? is true' do
@@ -19,8 +20,9 @@ class InventoryScheduledSyncTest < ActiveSupport::TestCase
19
20
 
20
21
  InventorySync::Async::InventoryScheduledSync.any_instance.expects(:plan_org_sync).never
21
22
 
22
- task = ForemanTasks.sync_task(InventorySync::Async::InventoryScheduledSync)
23
- status = task.output[:status].to_s
23
+ action = create_and_plan_action(InventorySync::Async::InventoryScheduledSync)
24
+ action = run_action(action)
25
+ status = action.output[:status].to_s
24
26
  assert_match(/Foreman is configured with a local IoP Smart Proxy/, status)
25
27
  end
26
28
 
@@ -29,7 +31,8 @@ class InventoryScheduledSyncTest < ActiveSupport::TestCase
29
31
 
30
32
  InventorySync::Async::InventoryScheduledSync.any_instance.expects(:plan_org_sync).never
31
33
 
32
- ForemanTasks.sync_task(InventorySync::Async::InventoryScheduledSync)
34
+ action = create_and_plan_action(InventorySync::Async::InventoryScheduledSync)
35
+ run_action(action)
33
36
  end
34
37
 
35
38
  test 'Skips mismatch deletion if the setting is disabled' do
@@ -39,6 +42,7 @@ class InventoryScheduledSyncTest < ActiveSupport::TestCase
39
42
  InventorySync::Async::InventoryScheduledSync.any_instance.expects(:plan_org_sync).times(Organization.unscoped.count)
40
43
  InventorySync::Async::InventoryScheduledSync.any_instance.expects(:plan_remove_insights_hosts).never
41
44
 
42
- ForemanTasks.sync_task(InventorySync::Async::InventoryScheduledSync)
45
+ action = create_and_plan_action(InventorySync::Async::InventoryScheduledSync)
46
+ run_action(action)
43
47
  end
44
48
  end
@@ -2,7 +2,7 @@ require 'test_plugin_helper'
2
2
  require 'foreman_tasks/test_helpers'
3
3
 
4
4
  class InventorySelfHostSyncTest < ActiveSupport::TestCase
5
- include ForemanTasks::TestHelpers::WithInThreadExecutor
5
+ include Dynflow::Testing::Factories
6
6
  include MockCerts
7
7
 
8
8
  setup do
@@ -2,15 +2,13 @@ require 'test_plugin_helper'
2
2
  require 'foreman_tasks/test_helpers'
3
3
 
4
4
  class QueueForUploadJobTest < ActiveSupport::TestCase
5
- include ForemanTasks::TestHelpers::WithInThreadExecutor
6
- include FolderIsolation
5
+ include Dynflow::Testing::Factories
7
6
 
8
7
  let(:organization) { FactoryBot.create(:organization) }
9
- let(:base_folder) { @tmpdir }
8
+ let(:base_folder) { Dir.mktmpdir }
10
9
  let(:report_file) { 'test_report.tar.xz' }
11
10
  let(:report_path) { File.join(base_folder, report_file) }
12
11
  let(:uploads_folder) { ForemanInventoryUpload.uploads_folder }
13
- subject { ForemanTasks.sync_task(ForemanInventoryUpload::Async::QueueForUploadJob, base_folder, report_file, organization.id) }
14
12
 
15
13
  setup do
16
14
  # Stub the script template source
@@ -29,19 +27,22 @@ class QueueForUploadJobTest < ActiveSupport::TestCase
29
27
 
30
28
  teardown do
31
29
  FileUtils.rm_rf(uploads_folder) if Dir.exist?(uploads_folder)
30
+ FileUtils.remove_entry base_folder if Dir.exist?(base_folder)
32
31
  end
33
32
 
34
33
  test 'plan method sets up the job correctly and calls plan_upload_report' do
35
34
  # Mock plan_upload_report to verify it's called
36
35
  ForemanInventoryUpload::Async::QueueForUploadJob.any_instance.expects(:plan_upload_report).once
37
36
 
38
- assert_equal 'success', subject.result
37
+ action = create_and_plan_action(ForemanInventoryUpload::Async::QueueForUploadJob, base_folder, report_file, organization.id)
38
+ run_action(action)
39
39
  end
40
40
 
41
41
  test 'run method processes file and moves it to uploads folder' do
42
42
  ForemanInventoryUpload::Async::QueueForUploadJob.any_instance.stubs(:plan_upload_report)
43
43
 
44
- assert_equal 'success', subject.result
44
+ action = create_and_plan_action(ForemanInventoryUpload::Async::QueueForUploadJob, base_folder, report_file, organization.id)
45
+ run_action(action)
45
46
 
46
47
  # Verify the file was moved
47
48
  refute File.exist?(report_path), "Original file should be moved"
@@ -51,7 +52,8 @@ class QueueForUploadJobTest < ActiveSupport::TestCase
51
52
  test 'creates necessary folders and scripts' do
52
53
  ForemanInventoryUpload::Async::QueueForUploadJob.any_instance.stubs(:plan_upload_report)
53
54
 
54
- assert_equal 'success', subject.result
55
+ action = create_and_plan_action(ForemanInventoryUpload::Async::QueueForUploadJob, base_folder, report_file, organization.id)
56
+ run_action(action)
55
57
 
56
58
  # Verify the uploads folder was created
57
59
  assert Dir.exist?(uploads_folder), "Uploads folder should be created"
@@ -2,7 +2,7 @@ require 'test_plugin_helper'
2
2
  require 'foreman_tasks/test_helpers'
3
3
 
4
4
  class RemoveInsightsHostJobTest < ActiveSupport::TestCase
5
- include ForemanTasks::TestHelpers::WithInThreadExecutor
5
+ include Dynflow::Testing::Factories
6
6
 
7
7
  setup do
8
8
  User.current = User.find_by(login: 'secret_admin')
@@ -18,10 +18,10 @@ class RemoveInsightsHostJobTest < ActiveSupport::TestCase
18
18
 
19
19
  FactoryBot.create(:insights_missing_host, organization: @org)
20
20
 
21
- task = ForemanTasks.sync_task(ForemanInventoryUpload::Async::RemoveInsightsHostsJob, '', @org.id)
21
+ action = create_and_plan_action(ForemanInventoryUpload::Async::RemoveInsightsHostsJob, '', @org.id)
22
+ run_action(action)
22
23
 
23
24
  assert_equal 0, InsightsMissingHost.count
24
- assert_equal 'success', task.result
25
25
  end
26
26
 
27
27
  test 'Does not delete hosts on cloud failure' do
@@ -31,14 +31,13 @@ class RemoveInsightsHostJobTest < ActiveSupport::TestCase
31
31
 
32
32
  FactoryBot.create(:insights_missing_host, organization: @org)
33
33
 
34
- begin
35
- ForemanTasks.sync_task(ForemanInventoryUpload::Async::RemoveInsightsHostsJob, '', @org.id)
36
- rescue ForemanTasks::TaskError => ex
37
- task = ex.task
34
+ action = create_and_plan_action(ForemanInventoryUpload::Async::RemoveInsightsHostsJob, '', @org.id)
35
+
36
+ assert_raises(StandardError) do
37
+ run_action(action)
38
38
  end
39
39
 
40
40
  assert_equal 1, InsightsMissingHost.count
41
- assert_equal 'error', task.result
42
41
  end
43
42
 
44
43
  test 'Paginates the hosts list' do
@@ -54,12 +53,12 @@ class RemoveInsightsHostJobTest < ActiveSupport::TestCase
54
53
  FactoryBot.create(:insights_missing_host, organization: @org)
55
54
  FactoryBot.create(:insights_missing_host, organization: @org)
56
55
 
57
- task = ForemanTasks.sync_task(ForemanInventoryUpload::Async::RemoveInsightsHostsJob, '', @org.id)
56
+ action = create_and_plan_action(ForemanInventoryUpload::Async::RemoveInsightsHostsJob, '', @org.id)
57
+ action = run_action(action)
58
58
 
59
59
  assert_equal 0, InsightsMissingHost.count
60
- assert_equal 'success', task.result
61
- assert_equal 'response1', task.output[:response_page1]
62
- assert_equal 'response2', task.output[:response_page2]
60
+ assert_equal 'response1', action.output[:response_page1]
61
+ assert_equal 'response2', action.output[:response_page2]
63
62
  end
64
63
 
65
64
  test 'Uses scoped_search to select hosts' do
@@ -73,12 +72,12 @@ class RemoveInsightsHostJobTest < ActiveSupport::TestCase
73
72
  FactoryBot.create(:insights_missing_host, name: 'test a', organization: @org)
74
73
  FactoryBot.create(:insights_missing_host, name: 'test b', organization: @org)
75
74
 
76
- task = ForemanTasks.sync_task(ForemanInventoryUpload::Async::RemoveInsightsHostsJob, 'name ~ b', @org.id)
75
+ action = create_and_plan_action(ForemanInventoryUpload::Async::RemoveInsightsHostsJob, 'name ~ b', @org.id)
76
+ action = run_action(action)
77
77
 
78
78
  assert_equal 1, InsightsMissingHost.count
79
79
  assert_equal 'test a', InsightsMissingHost.first.name
80
- assert_equal 'success', task.result
81
- assert_equal 'response1', task.output[:response_page1]
80
+ assert_equal 'response1', action.output[:response_page1]
82
81
  end
83
82
 
84
83
  def mock_response(code: 200, body: '')
@@ -0,0 +1,155 @@
1
+ require 'test_plugin_helper'
2
+ require 'foreman_tasks/test_helpers'
3
+
4
+ class SingleHostReportJobTest < ActiveSupport::TestCase
5
+ include Dynflow::Testing::Factories
6
+ include Dynflow::Testing::Assertions
7
+ include KatelloCVEHelper
8
+
9
+ let(:base_folder) { Dir.mktmpdir }
10
+
11
+ setup do
12
+ User.current = User.find_by(login: 'secret_admin')
13
+ cve = make_cve
14
+ env = cve.lifecycle_environment
15
+
16
+ @host = FactoryBot.create(
17
+ :host,
18
+ :with_subscription,
19
+ :with_content,
20
+ content_view: cve.content_view,
21
+ lifecycle_environment: env,
22
+ organization: env.organization
23
+ )
24
+ end
25
+
26
+ teardown do
27
+ FileUtils.remove_entry base_folder if Dir.exist?(base_folder)
28
+ end
29
+
30
+ test 'plan sets host_id in input' do
31
+ action = create_and_plan_action(
32
+ ForemanInventoryUpload::Async::SingleHostReportJob,
33
+ base_folder,
34
+ @host.organization_id,
35
+ @host.id
36
+ )
37
+
38
+ assert_equal @host.id, action.input[:host_id]
39
+ end
40
+
41
+ test 'plan calls super with id filter' do
42
+ expected_filter = "id=#{@host.id}"
43
+
44
+ action = create_and_plan_action(
45
+ ForemanInventoryUpload::Async::SingleHostReportJob,
46
+ base_folder,
47
+ @host.organization_id,
48
+ @host.id
49
+ )
50
+
51
+ assert_action_planned_with(
52
+ action,
53
+ ForemanInventoryUpload::Async::GenerateHostReport,
54
+ base_folder,
55
+ @host.organization_id,
56
+ expected_filter
57
+ )
58
+ end
59
+
60
+ test 'inherits behavior from HostInventoryReportJob' do
61
+ action = create_and_plan_action(
62
+ ForemanInventoryUpload::Async::SingleHostReportJob,
63
+ base_folder,
64
+ @host.organization_id,
65
+ @host.id
66
+ )
67
+
68
+ # Should schedule all parent actions
69
+ assert_action_planned(action, ForemanInventoryUpload::Async::GenerateHostReport)
70
+ assert_action_planned(action, ForemanInventoryUpload::Async::QueueForUploadJob)
71
+ end
72
+
73
+ test 'humanized_name includes hostname' do
74
+ action = create_and_plan_action(
75
+ ForemanInventoryUpload::Async::SingleHostReportJob,
76
+ base_folder,
77
+ @host.organization_id,
78
+ @host.id
79
+ )
80
+
81
+ assert_equal "Single-host report job for host #{@host.name}", action.humanized_name
82
+ end
83
+
84
+ test 'humanized_name handles missing host' do
85
+ non_existent_host_id = 999_999
86
+
87
+ action = create_and_plan_action(
88
+ ForemanInventoryUpload::Async::SingleHostReportJob,
89
+ base_folder,
90
+ @host.organization_id,
91
+ non_existent_host_id
92
+ )
93
+
94
+ assert_equal 'Single-host report job', action.humanized_name
95
+ end
96
+
97
+ test 'hostname method returns correct name' do
98
+ action = create_and_plan_action(
99
+ ForemanInventoryUpload::Async::SingleHostReportJob,
100
+ base_folder,
101
+ @host.organization_id,
102
+ @host.id
103
+ )
104
+
105
+ assert_equal @host.name, action.hostname(@host.id)
106
+ end
107
+
108
+ test 'hostname method handles nil gracefully' do
109
+ action = create_and_plan_action(
110
+ ForemanInventoryUpload::Async::SingleHostReportJob,
111
+ base_folder,
112
+ @host.organization_id,
113
+ @host.id
114
+ )
115
+
116
+ assert_nil action.hostname(999_999)
117
+ end
118
+
119
+ test 'generates report with correct archive name for single host' do
120
+ expected_filter = "id=#{@host.id}"
121
+ expected_archive_name = ForemanInventoryUpload.facts_archive_name(@host.organization_id, expected_filter)
122
+
123
+ action = create_and_plan_action(
124
+ ForemanInventoryUpload::Async::SingleHostReportJob,
125
+ base_folder,
126
+ @host.organization_id,
127
+ @host.id
128
+ )
129
+
130
+ assert_action_planned_with(
131
+ action,
132
+ ForemanInventoryUpload::Async::QueueForUploadJob,
133
+ base_folder,
134
+ expected_archive_name,
135
+ @host.organization_id
136
+ )
137
+ end
138
+
139
+ test 'respects IoP mode for facet creation' do
140
+ ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
141
+
142
+ action = create_and_plan_action(
143
+ ForemanInventoryUpload::Async::SingleHostReportJob,
144
+ base_folder,
145
+ @host.organization_id,
146
+ @host.id
147
+ )
148
+
149
+ assert_action_planned_with(
150
+ action,
151
+ ForemanInventoryUpload::Async::CreateMissingInsightsFacets,
152
+ @host.organization_id
153
+ )
154
+ end
155
+ end
@@ -2,8 +2,7 @@ require 'test_plugin_helper'
2
2
  require 'foreman_tasks/test_helpers'
3
3
 
4
4
  class UploadReportJobTest < ActiveSupport::TestCase
5
- include ForemanTasks::TestHelpers::WithInThreadExecutor
6
- include FolderIsolation
5
+ include Dynflow::Testing::Factories
7
6
 
8
7
  test 'returns aborted state when disconnected' do
9
8
  organization = FactoryBot.create(:organization)
@@ -14,7 +13,8 @@ class UploadReportJobTest < ActiveSupport::TestCase
14
13
  )
15
14
  ForemanInventoryUpload::Async::UploadReportJob.any_instance.expects(:content_disconnected?).returns(true)
16
15
 
17
- ForemanTasks.sync_task(ForemanInventoryUpload::Async::UploadReportJob, '', organization.id)
16
+ action = create_and_plan_action(ForemanInventoryUpload::Async::UploadReportJob, '', organization.id)
17
+ run_action(action)
18
18
 
19
19
  label = ForemanInventoryUpload::Async::UploadReportJob.output_label(organization.id)
20
20
  progress_output = ForemanInventoryUpload::Async::ProgressOutput.get(label)
@@ -27,7 +27,8 @@ class UploadReportJobTest < ActiveSupport::TestCase
27
27
  organization = FactoryBot.create(:organization)
28
28
  Organization.any_instance.expects(:owner_details).returns(nil)
29
29
 
30
- ForemanTasks.sync_task(ForemanInventoryUpload::Async::UploadReportJob, '', organization.id)
30
+ action = create_and_plan_action(ForemanInventoryUpload::Async::UploadReportJob, '', organization.id)
31
+ run_action(action)
31
32
 
32
33
  label = ForemanInventoryUpload::Async::UploadReportJob.output_label(organization.id)
33
34
  progress_output = ForemanInventoryUpload::Async::ProgressOutput.get(label)
@@ -0,0 +1,192 @@
1
+ require 'test_plugin_helper'
2
+
3
+ module ForemanRhCloud
4
+ class RegistrationManagerExtensionsTest < ActiveSupport::TestCase
5
+ setup do
6
+ @org = FactoryBot.create(:organization)
7
+ @host = FactoryBot.create(:host, :managed, organization: @org)
8
+ @insights_facet = ::InsightsFacet.create!(host: @host, uuid: 'test-uuid-123')
9
+
10
+ # Stub Candlepin interaction (from Katello)
11
+ ::Katello::Resources::Candlepin::Consumer.stubs(:destroy)
12
+
13
+ # Stub the cloud request to avoid actual HTTP calls
14
+ Katello::RegistrationManager.stubs(:execute_cloud_request).returns(true)
15
+ end
16
+
17
+ context 'unregister_host' do
18
+ test 'should call HBI delete in IoP mode when host has insights facet with UUID' do
19
+ ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
20
+ expected_url = ForemanInventoryUpload.host_by_id_url('test-uuid-123')
21
+
22
+ # Expect the cloud request to be made
23
+ Katello::RegistrationManager.expects(:execute_cloud_request).with do |params|
24
+ params[:organization] == @org &&
25
+ params[:method] == :delete &&
26
+ params[:url] == expected_url &&
27
+ params[:headers][:content_type] == :json
28
+ end.returns(true)
29
+
30
+ Katello::RegistrationManager.unregister_host(@host, unregistering: true)
31
+
32
+ # Verify insights_facet was destroyed
33
+ assert_nil InsightsFacet.find_by(id: @insights_facet.id)
34
+ end
35
+
36
+ test 'should NOT call HBI delete in non-IoP mode' do
37
+ ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
38
+
39
+ # Should NOT attempt to delete from HBI
40
+ Katello::RegistrationManager.expects(:execute_cloud_request).never
41
+
42
+ Katello::RegistrationManager.unregister_host(@host, unregistering: true)
43
+
44
+ # Verify insights_facet was still destroyed
45
+ assert_nil InsightsFacet.find_by(id: @insights_facet.id)
46
+ end
47
+
48
+ test 'should NOT call HBI delete when host has no insights_facet' do
49
+ host_without_facet = FactoryBot.create(:host, :managed, organization: @org)
50
+ ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
51
+
52
+ Katello::RegistrationManager.expects(:execute_cloud_request).never
53
+
54
+ assert_nothing_raised do
55
+ Katello::RegistrationManager.unregister_host(host_without_facet, unregistering: true)
56
+ end
57
+ end
58
+
59
+ test 'should NOT call HBI delete when host has insights_facet with empty UUID' do
60
+ host_with_empty_uuid_facet = FactoryBot.create(:host, :managed, organization: @org)
61
+ empty_uuid_facet = InsightsFacet.create!(host: host_with_empty_uuid_facet, uuid: '')
62
+ ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
63
+
64
+ Katello::RegistrationManager.expects(:execute_cloud_request).never
65
+
66
+ assert_nothing_raised do
67
+ Katello::RegistrationManager.unregister_host(host_with_empty_uuid_facet, unregistering: true)
68
+ end
69
+
70
+ assert_nil InsightsFacet.find_by(id: empty_uuid_facet.id)
71
+ end
72
+
73
+ test 'should NOT call HBI delete when insights_facet has no UUID' do
74
+ facet_id = @insights_facet.id
75
+ @insights_facet.update(uuid: nil)
76
+ ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
77
+
78
+ Katello::RegistrationManager.expects(:execute_cloud_request).never
79
+
80
+ Katello::RegistrationManager.unregister_host(@host, unregistering: true)
81
+
82
+ # Verify facet was still destroyed
83
+ assert_nil InsightsFacet.find_by(id: facet_id)
84
+ end
85
+
86
+ test 'should always destroy insights_facet regardless of IoP mode' do
87
+ facet_id = @insights_facet.id
88
+ ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
89
+
90
+ assert_not_nil InsightsFacet.find_by(id: facet_id)
91
+
92
+ Katello::RegistrationManager.unregister_host(@host, unregistering: true)
93
+
94
+ assert_nil InsightsFacet.find_by(id: facet_id)
95
+ end
96
+ end
97
+
98
+ context 'hbi_host_destroy error handling' do
99
+ setup do
100
+ ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
101
+ @expected_url = ForemanInventoryUpload.host_by_id_url('test-uuid-123')
102
+ @facet_id = @insights_facet.id
103
+ # Unstub execute_cloud_request for error tests
104
+ Katello::RegistrationManager.unstub(:execute_cloud_request)
105
+ end
106
+
107
+ test 'should handle RestClient::NotFound gracefully' do
108
+ Katello::RegistrationManager.stubs(:execute_cloud_request).raises(RestClient::NotFound)
109
+
110
+ # Should log warning but not raise
111
+ Rails.logger.expects(:warn).with(regexp_matches(/host does not exist in HBI/))
112
+
113
+ assert_nothing_raised do
114
+ Katello::RegistrationManager.unregister_host(@host, unregistering: true)
115
+ end
116
+
117
+ # Facet should still be destroyed
118
+ assert_nil InsightsFacet.find_by(id: @facet_id)
119
+ end
120
+
121
+ test 'should handle server errors gracefully' do
122
+ error = RestClient::InternalServerError.new
123
+ Katello::RegistrationManager.stubs(:execute_cloud_request).raises(error)
124
+
125
+ # Should log error but not raise
126
+ Rails.logger.expects(:error).with(regexp_matches(/Failed to destroy HBI host/))
127
+
128
+ assert_nothing_raised do
129
+ Katello::RegistrationManager.unregister_host(@host, unregistering: true)
130
+ end
131
+
132
+ # Facet should still be destroyed
133
+ assert_nil InsightsFacet.find_by(id: @facet_id)
134
+ end
135
+
136
+ test 'should handle timeout errors gracefully' do
137
+ error = RestClient::Exceptions::ReadTimeout.new
138
+ Katello::RegistrationManager.stubs(:execute_cloud_request).raises(error)
139
+
140
+ # Should log error but not raise
141
+ Rails.logger.expects(:error).with(regexp_matches(/Failed to destroy HBI host/))
142
+
143
+ assert_nothing_raised do
144
+ Katello::RegistrationManager.unregister_host(@host, unregistering: true)
145
+ end
146
+
147
+ # Facet should still be destroyed
148
+ assert_nil InsightsFacet.find_by(id: @facet_id)
149
+ end
150
+
151
+ test 'should handle connection errors gracefully' do
152
+ error = Errno::ECONNREFUSED.new
153
+ Katello::RegistrationManager.stubs(:execute_cloud_request).raises(error)
154
+
155
+ # Should log error but not raise
156
+ Rails.logger.expects(:error).with(regexp_matches(/Failed to destroy HBI host/))
157
+
158
+ assert_nothing_raised do
159
+ Katello::RegistrationManager.unregister_host(@host, unregistering: true)
160
+ end
161
+
162
+ # Facet should still be destroyed
163
+ assert_nil InsightsFacet.find_by(id: @facet_id)
164
+ end
165
+ end
166
+
167
+ context 'integration' do
168
+ test 'should be properly prepended to RegistrationManager' do
169
+ assert_includes Katello::RegistrationManager.singleton_class.ancestors,
170
+ ForemanRhCloud::RegistrationManagerExtensions
171
+ end
172
+
173
+ test 'should preserve original unregister_host behavior' do
174
+ ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
175
+
176
+ # Just verify the extension is properly integrated
177
+ # Detailed Katello behavior is tested in Katello's own tests
178
+ assert_nothing_raised do
179
+ Katello::RegistrationManager.unregister_host(@host, unregistering: true)
180
+ end
181
+ end
182
+ end
183
+
184
+ context 'URL generation' do
185
+ test 'host_by_id_url should return correct format' do
186
+ url = ForemanInventoryUpload.host_by_id_url('test-uuid-123')
187
+
188
+ assert_match %r{/hosts/test-uuid-123$}, url
189
+ end
190
+ end
191
+ end
192
+ end
@@ -79,6 +79,8 @@ const hostsIndexColumnExtensions = [
79
79
  categoryName: insightsCategoryName,
80
80
  categoryKey: 'insights',
81
81
  isSorted: false,
82
+ // eslint-disable-next-line camelcase
83
+ isRelevant: contextData => contextData?.metadata?.foreman_rh_cloud?.iop,
82
84
  },
83
85
  ];
84
86
 
@@ -3,19 +3,19 @@ import PropTypes from 'prop-types';
3
3
  import { translate as __ } from 'foremanReact/common/I18n';
4
4
  import SwitcherPF4 from '../../../common/Switcher/SwitcherPF4';
5
5
  import './insightsSettings.scss';
6
- import { useAdvisorEngineConfig } from '../../../common/Hooks/ConfigHooks';
6
+ import { useIopConfig } from '../../../common/Hooks/ConfigHooks';
7
7
 
8
8
  const InsightsSettings = ({
9
9
  insightsSyncEnabled,
10
10
  getInsightsSyncSettings,
11
11
  setInsightsSyncEnabled,
12
12
  }) => {
13
- const isLocalAdvisorEngine = useAdvisorEngineConfig();
13
+ const isIop = useIopConfig();
14
14
  useEffect(() => {
15
15
  getInsightsSyncSettings();
16
16
  }, [getInsightsSyncSettings]);
17
17
 
18
- if (isLocalAdvisorEngine) return null;
18
+ if (isIop) return null;
19
19
 
20
20
  return (
21
21
  <div className="insights_settings">
@@ -16,7 +16,7 @@ import TableEmptyState from '../../../common/table/EmptyState';
16
16
  import { modifySelectedRows, getSortColumnIndex } from './InsightsTableHelpers';
17
17
  import Pagination from './Pagination';
18
18
  import './table.scss';
19
- import { useAdvisorEngineConfig } from '../../../common/Hooks/ConfigHooks';
19
+ import { useIopConfig } from '../../../common/Hooks/ConfigHooks';
20
20
 
21
21
  const InsightsTable = ({
22
22
  page,
@@ -48,17 +48,11 @@ const InsightsTable = ({
48
48
  fetchInsights({ page, perPage, query, sortBy, sortOrder });
49
49
  }, [hostname]);
50
50
 
51
- const isLocalAdvisorEngine = useAdvisorEngineConfig();
51
+ const isIop = useIopConfig();
52
52
 
53
53
  useEffect(() => {
54
54
  setRows(
55
- modifySelectedRows(
56
- hits,
57
- selectedIds,
58
- showSelectAllAlert,
59
- hideHost,
60
- isLocalAdvisorEngine
61
- )
55
+ modifySelectedRows(hits, selectedIds, showSelectAllAlert, hideHost, isIop)
62
56
  );
63
57
 
64
58
  if (hideHost) setColumns(getColumnsWithoutHostname());
@@ -3,6 +3,19 @@ import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
3
3
  import InsightsTable from '../InsightsTable';
4
4
  import { tableProps } from './fixtures';
5
5
 
6
+ jest.mock('foremanReact/Root/Context/ForemanContext', () => ({
7
+ useForemanContext: () => ({
8
+ metadata: {
9
+ foreman_rh_cloud: {
10
+ iop: true,
11
+ },
12
+ },
13
+ }),
14
+ useForemanSettings: () => ({
15
+ perPage: 20,
16
+ }),
17
+ }));
18
+
6
19
  const fixtures = {
7
20
  'render with Props': tableProps,
8
21
  };
@@ -16,7 +16,7 @@ import { modifyRows } from './RemediationHelpers';
16
16
  import ModalFooter from './RemediationModalFooter';
17
17
  import TableEmptyState from '../../../common/table/EmptyState';
18
18
  import './RemediationModal.scss';
19
- import { useAdvisorEngineConfig } from '../../../common/Hooks/ConfigHooks';
19
+ import { useIopConfig } from '../../../common/Hooks/ConfigHooks';
20
20
 
21
21
  /* eslint-disable spellcheck/spell-checker */
22
22
 
@@ -82,7 +82,7 @@ const RemediationModal = ({
82
82
  const [rows, setRows] = React.useState([]);
83
83
  const toggleModal = () => setOpen(prevValue => !prevValue);
84
84
 
85
- const isIop = useAdvisorEngineConfig();
85
+ const isIop = useIopConfig();
86
86
  useEffect(() => {
87
87
  // only fetch for Hosted. IoP provides via props.
88
88
  if (!isIop && open)
@@ -8,12 +8,12 @@ import {
8
8
  } from '@patternfly/react-core/deprecated';
9
9
  import { ExternalLinkAltIcon } from '@patternfly/react-icons';
10
10
  import { redHatAdvisorSystems } from '../InsightsCloudSyncHelpers';
11
- import { useAdvisorEngineConfig } from '../../common/Hooks/ConfigHooks';
11
+ import { useIopConfig } from '../../common/Hooks/ConfigHooks';
12
12
 
13
13
  const ToolbarDropdown = ({ onRecommendationSync }) => {
14
14
  const [isDropdownOpen, setIsDropdownOpen] = useState(false);
15
- const isLocalAdvisorEngine = useAdvisorEngineConfig();
16
- if (isLocalAdvisorEngine) {
15
+ const isIop = useIopConfig();
16
+ if (isIop) {
17
17
  return null;
18
18
  }
19
19
  const dropdownItems = [