foreman_rh_cloud 12.2.17 → 13.0.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.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/foreman_rh_cloud/locale/fr/foreman_rh_cloud.js +24 -78
  3. data/app/assets/javascripts/foreman_rh_cloud/locale/ja/foreman_rh_cloud.js +24 -78
  4. data/app/assets/javascripts/foreman_rh_cloud/locale/ka/foreman_rh_cloud.js +23 -77
  5. data/app/assets/javascripts/foreman_rh_cloud/locale/ko/foreman_rh_cloud.js +23 -77
  6. data/app/assets/javascripts/foreman_rh_cloud/locale/zh_CN/foreman_rh_cloud.js +23 -77
  7. data/app/controllers/concerns/insights_cloud/package_profile_upload_extensions.rb +3 -5
  8. data/app/controllers/foreman_inventory_upload/accounts_controller.rb +1 -1
  9. data/app/controllers/foreman_inventory_upload/uploads_controller.rb +1 -1
  10. data/app/controllers/insights_cloud/ui_requests_controller.rb +3 -2
  11. data/app/models/concerns/rh_cloud_host.rb +1 -45
  12. data/app/models/foreman_rh_cloud/ping.rb +1 -2
  13. data/app/models/insights_hit.rb +1 -1
  14. data/app/services/foreman_rh_cloud/cert_auth.rb +3 -13
  15. data/app/services/foreman_rh_cloud/gateway_request.rb +26 -0
  16. data/app/services/foreman_rh_cloud/insights_api_forwarder.rb +1 -3
  17. data/app/services/foreman_rh_cloud/tags_auth.rb +4 -15
  18. data/app/views/api/v2/advisor_engine/host_details.json.rabl +3 -1
  19. data/app/views/api/v2/hosts/insights/base.rabl +2 -3
  20. data/lib/foreman_inventory_upload/async/generate_report_job.rb +8 -13
  21. data/lib/foreman_inventory_upload/async/queue_for_upload_job.rb +26 -4
  22. data/lib/foreman_inventory_upload/async/upload_report_job.rb +96 -0
  23. data/lib/foreman_inventory_upload/generators/fact_helpers.rb +2 -2
  24. data/lib/foreman_inventory_upload/generators/slice.rb +3 -3
  25. data/lib/foreman_inventory_upload/scripts/uploader.sh.erb +49 -0
  26. data/lib/foreman_inventory_upload.rb +6 -6
  27. data/lib/foreman_rh_cloud/engine.rb +15 -34
  28. data/lib/foreman_rh_cloud/plugin.rb +13 -20
  29. data/lib/foreman_rh_cloud/version.rb +1 -1
  30. data/lib/foreman_rh_cloud.rb +3 -3
  31. data/lib/insights_cloud/async/connector_playbook_execution_reporter_task.rb +3 -3
  32. data/lib/inventory_sync/async/inventory_hosts_sync.rb +2 -0
  33. data/lib/tasks/rh_cloud_inventory.rake +31 -14
  34. data/locale/foreman_rh_cloud.pot +157 -261
  35. data/locale/fr/LC_MESSAGES/foreman_rh_cloud.mo +0 -0
  36. data/locale/fr/foreman_rh_cloud.po +26 -79
  37. data/locale/ja/LC_MESSAGES/foreman_rh_cloud.mo +0 -0
  38. data/locale/ja/foreman_rh_cloud.po +26 -79
  39. data/locale/ka/LC_MESSAGES/foreman_rh_cloud.mo +0 -0
  40. data/locale/ka/foreman_rh_cloud.po +24 -77
  41. data/locale/ko/LC_MESSAGES/foreman_rh_cloud.mo +0 -0
  42. data/locale/ko/foreman_rh_cloud.po +25 -78
  43. data/locale/zh_CN/LC_MESSAGES/foreman_rh_cloud.mo +0 -0
  44. data/locale/zh_CN/foreman_rh_cloud.po +25 -78
  45. data/package.json +1 -1
  46. data/test/controllers/accounts_controller_test.rb +1 -1
  47. data/test/controllers/insights_cloud/api/advisor_engine_controller_test.rb +1 -28
  48. data/test/controllers/insights_cloud/ui_requests_controller_test.rb +0 -26
  49. data/test/controllers/uploads_controller_test.rb +1 -1
  50. data/test/factories/insights_factories.rb +0 -29
  51. data/test/jobs/cloud_connector_announce_task_test.rb +2 -3
  52. data/test/jobs/connector_playbook_execution_reporter_task_test.rb +20 -32
  53. data/test/jobs/exponential_backoff_test.rb +8 -9
  54. data/test/jobs/insights_client_status_aging_test.rb +2 -3
  55. data/test/jobs/insights_full_sync_test.rb +7 -13
  56. data/test/jobs/insights_resolutions_sync_test.rb +5 -9
  57. data/test/jobs/insights_rules_sync_test.rb +3 -5
  58. data/test/jobs/inventory_full_sync_test.rb +5 -9
  59. data/test/jobs/inventory_hosts_sync_test.rb +6 -11
  60. data/test/jobs/inventory_scheduled_sync_test.rb +6 -10
  61. data/test/jobs/inventory_self_host_sync_test.rb +1 -1
  62. data/test/jobs/remove_insights_hosts_job_test.rb +15 -14
  63. data/test/jobs/upload_report_job_test.rb +36 -0
  64. data/test/unit/fact_helpers_test.rb +0 -47
  65. data/test/unit/services/foreman_rh_cloud/tags_auth_test.rb +0 -14
  66. data/test/unit/slice_generator_test.rb +0 -57
  67. data/webpack/ForemanColumnExtensions/index.js +0 -2
  68. data/webpack/ForemanInventoryUpload/Components/PageHeader/PageHeader.js +17 -24
  69. data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/PageHeader.test.js +8 -178
  70. data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/__snapshots__/PageHeader.test.js.snap +36 -0
  71. data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/__snapshots__/PageTitle.test.js.snap +1 -1
  72. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/ToolbarButtons/ToolbarButtons.js +1 -3
  73. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/ToolbarButtons/__tests__/ToolbarButtons.test.js +51 -69
  74. data/webpack/ForemanInventoryUpload/ForemanInventoryHelpers.js +1 -1
  75. data/webpack/ForemanInventoryUpload/__tests__/__snapshots__/ForemanInventoryHelpers.test.js.snap +1 -1
  76. data/webpack/ForemanRhCloudFills.js +2 -6
  77. data/webpack/ForemanRhCloudHelpers.js +0 -4
  78. data/webpack/InsightsCloudSync/Components/InsightsSettings/InsightsSettings.js +3 -3
  79. data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTable.js +9 -3
  80. data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTable.test.js +4 -24
  81. data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTable.test.js.snap +112 -0
  82. data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationModal.js +2 -2
  83. data/webpack/InsightsCloudSync/Components/ToolbarDropdown.js +3 -3
  84. data/webpack/InsightsCloudSync/InsightsCloudSync.js +3 -3
  85. data/webpack/InsightsCloudSync/InsightsCloudSync.test.js +0 -10
  86. data/webpack/InsightsCloudSync/__snapshots__/InsightsCloudSync.test.js.snap +1 -1
  87. data/webpack/InsightsHostDetailsTab/InsightsTab.scss +0 -4
  88. data/webpack/InsightsHostDetailsTab/InsightsTotalRiskChart.js +23 -59
  89. data/webpack/InsightsHostDetailsTab/NewHostDetailsTab.js +23 -25
  90. data/webpack/InsightsVulnerabilityHostIndexExtensions/CVECountCell.js +2 -8
  91. data/webpack/InsightsVulnerabilityHostIndexExtensions/__tests__/CVECountCell.test.js +4 -105
  92. data/webpack/__mocks__/foremanReact/common/hooks/API/APIHooks.js +3 -0
  93. data/webpack/__tests__/ForemanRhCloudHelpers.test.js +1 -16
  94. data/webpack/__tests__/__snapshots__/ForemanRhCloudHelpers.test.js.snap +0 -6
  95. data/webpack/common/Hooks/ConfigHooks.js +16 -3
  96. metadata +12 -32
  97. data/app/controllers/concerns/foreman_rh_cloud/registration_manager_extensions.rb +0 -39
  98. data/lib/foreman_inventory_upload/async/create_missing_insights_facets.rb +0 -36
  99. data/lib/foreman_inventory_upload/async/generate_host_report.rb +0 -20
  100. data/lib/foreman_inventory_upload/async/host_inventory_report_job.rb +0 -39
  101. data/lib/foreman_inventory_upload/async/single_host_report_job.rb +0 -20
  102. data/lib/foreman_inventory_upload/async/upload_report_direct_job.rb +0 -200
  103. data/test/jobs/create_missing_insights_facets_test.rb +0 -151
  104. data/test/jobs/generate_host_report_test.rb +0 -100
  105. data/test/jobs/generate_report_job_test.rb +0 -146
  106. data/test/jobs/host_inventory_report_job_test.rb +0 -244
  107. data/test/jobs/queue_for_upload_job_test.rb +0 -54
  108. data/test/jobs/single_host_report_job_test.rb +0 -155
  109. data/test/jobs/upload_report_direct_job_test.rb +0 -399
  110. data/test/unit/foreman_rh_cloud_iop_metadata_test.rb +0 -200
  111. data/test/unit/lib/foreman_rh_cloud/registration_manager_extensions_test.rb +0 -154
  112. data/test/unit/rh_cloud_host_test.rb +0 -345
  113. data/webpack/InsightsHostDetailsTab/__tests__/InsightsTotalRiskChart.test.js +0 -194
  114. data/webpack/InsightsHostDetailsTab/__tests__/NewHostDetailsTab.test.js +0 -154
@@ -1,154 +0,0 @@
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, :with_subscription, organization: @org)
8
- @insights_facet = ::InsightsFacet.create!(host: @host, uuid: @host.subscription_facet.uuid)
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 UUID' do
19
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
20
- expected_url = ForemanInventoryUpload.host_by_id_url(@host.subscription_facet.uuid)
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 always destroy insights_facet regardless of IoP mode' do
49
- facet_id = @insights_facet.id
50
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
51
-
52
- assert_not_nil InsightsFacet.find_by(id: facet_id)
53
-
54
- Katello::RegistrationManager.unregister_host(@host, unregistering: true)
55
-
56
- assert_nil InsightsFacet.find_by(id: facet_id)
57
- end
58
- end
59
-
60
- context 'hbi_host_destroy error handling' do
61
- setup do
62
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
63
- @expected_url = ForemanInventoryUpload.host_by_id_url('test-uuid-123')
64
- @facet_id = @insights_facet.id
65
- # Unstub execute_cloud_request for error tests
66
- Katello::RegistrationManager.unstub(:execute_cloud_request)
67
- end
68
-
69
- test 'should handle RestClient::NotFound gracefully' do
70
- Katello::RegistrationManager.stubs(:execute_cloud_request).raises(RestClient::NotFound)
71
-
72
- # Should log warning but not raise
73
- Rails.logger.expects(:warn).with(regexp_matches(/host does not exist in HBI/))
74
-
75
- assert_nothing_raised do
76
- Katello::RegistrationManager.unregister_host(@host, unregistering: true)
77
- end
78
-
79
- # Facet should still be destroyed
80
- assert_nil InsightsFacet.find_by(id: @facet_id)
81
- end
82
-
83
- test 'should handle server errors gracefully' do
84
- error = RestClient::InternalServerError.new
85
- Katello::RegistrationManager.stubs(:execute_cloud_request).raises(error)
86
-
87
- # Should log error but not raise
88
- Rails.logger.expects(:error).with(regexp_matches(/Failed to destroy HBI host/))
89
-
90
- assert_nothing_raised do
91
- Katello::RegistrationManager.unregister_host(@host, unregistering: true)
92
- end
93
-
94
- # Facet should still be destroyed
95
- assert_nil InsightsFacet.find_by(id: @facet_id)
96
- end
97
-
98
- test 'should handle timeout errors gracefully' do
99
- error = RestClient::Exceptions::ReadTimeout.new
100
- Katello::RegistrationManager.stubs(:execute_cloud_request).raises(error)
101
-
102
- # Should log error but not raise
103
- Rails.logger.expects(:error).with(regexp_matches(/Failed to destroy HBI host/))
104
-
105
- assert_nothing_raised do
106
- Katello::RegistrationManager.unregister_host(@host, unregistering: true)
107
- end
108
-
109
- # Facet should still be destroyed
110
- assert_nil InsightsFacet.find_by(id: @facet_id)
111
- end
112
-
113
- test 'should handle connection errors gracefully' do
114
- error = Errno::ECONNREFUSED.new
115
- Katello::RegistrationManager.stubs(:execute_cloud_request).raises(error)
116
-
117
- # Should log error but not raise
118
- Rails.logger.expects(:error).with(regexp_matches(/Failed to destroy HBI host/))
119
-
120
- assert_nothing_raised do
121
- Katello::RegistrationManager.unregister_host(@host, unregistering: true)
122
- end
123
-
124
- # Facet should still be destroyed
125
- assert_nil InsightsFacet.find_by(id: @facet_id)
126
- end
127
- end
128
-
129
- context 'integration' do
130
- test 'should be properly prepended to RegistrationManager' do
131
- assert_includes Katello::RegistrationManager.singleton_class.ancestors,
132
- ForemanRhCloud::RegistrationManagerExtensions
133
- end
134
-
135
- test 'should preserve original unregister_host behavior' do
136
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
137
-
138
- # Just verify the extension is properly integrated
139
- # Detailed Katello behavior is tested in Katello's own tests
140
- assert_nothing_raised do
141
- Katello::RegistrationManager.unregister_host(@host, unregistering: true)
142
- end
143
- end
144
- end
145
-
146
- context 'URL generation' do
147
- test 'host_by_id_url should return correct format' do
148
- url = ForemanInventoryUpload.host_by_id_url('test-uuid-123')
149
-
150
- assert_match %r{/hosts/test-uuid-123$}, url
151
- end
152
- end
153
- end
154
- end
@@ -1,345 +0,0 @@
1
- require 'test_plugin_helper'
2
-
3
- class RhCloudHostTest < ActiveSupport::TestCase
4
- setup do
5
- @org = FactoryBot.create(:organization)
6
- end
7
-
8
- teardown do
9
- ForemanRhCloud.unstub(:with_iop_smart_proxy?)
10
- end
11
-
12
- context 'insights_uuid method' do
13
- test 'returns insights_facet uuid in non-IoP mode' do
14
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
15
- @host = FactoryBot.create(:host, :managed, :with_subscription, organization: @org)
16
- @host.insights = FactoryBot.create(:insights_facet, host_id: @host.id, uuid: 'insights-123')
17
-
18
- assert_equal 'insights-123', @host.insights_uuid
19
- assert_not_equal @host.subscription_facet.uuid, @host.insights_uuid
20
- end
21
-
22
- test 'returns subscription_facet uuid in IoP mode' do
23
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
24
- @host = FactoryBot.create(:host, :managed, :with_subscription, organization: @org)
25
- @host.insights = FactoryBot.create(:insights_facet, host_id: @host.id, uuid: 'insights-456')
26
-
27
- assert_equal @host.subscription_facet.uuid, @host.insights_uuid
28
- assert_not_equal 'insights-456', @host.insights_uuid
29
- end
30
-
31
- test 'returns nil when insights_facet missing in non-IoP mode' do
32
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
33
- @host = FactoryBot.create(:host, :managed, :with_subscription, organization: @org)
34
-
35
- assert_nil @host.insights_uuid
36
- end
37
-
38
- test 'returns nil when subscription_facet missing in IoP mode' do
39
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
40
- @host = FactoryBot.create(:host, :managed, organization: @org)
41
- @host.insights = FactoryBot.create(:insights_facet, host_id: @host.id, uuid: 'insights-789')
42
-
43
- assert_nil @host.insights_uuid
44
- end
45
-
46
- test 'returns nil when both facets missing' do
47
- @host = FactoryBot.create(:host, :managed, organization: @org)
48
-
49
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
50
- assert_nil @host.insights_uuid
51
-
52
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
53
- assert_nil @host.insights_uuid
54
- end
55
-
56
- test 'never returns stale insights_facet uuid in IoP mode' do
57
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
58
- @host = FactoryBot.create(:host, :managed, :with_subscription, organization: @org)
59
- current_uuid = @host.subscription_facet.uuid
60
- @host.insights = FactoryBot.create(:insights_facet, host_id: @host.id, uuid: 'stale-456')
61
-
62
- # Should return subscription_facet UUID, never the stale insights_facet UUID
63
- assert_equal current_uuid, @host.insights_uuid
64
- assert_not_equal 'stale-456', @host.insights_uuid
65
-
66
- # Verify multiple calls always return subscription_facet UUID
67
- 3.times do
68
- assert_equal current_uuid, @host.insights_uuid
69
- end
70
- end
71
-
72
- test 'dynamically responds to IoP mode changes' do
73
- @host = FactoryBot.create(:host, :managed, :with_subscription, organization: @org)
74
- @host.insights = FactoryBot.create(:insights_facet, host_id: @host.id, uuid: 'insights-123')
75
- subscription_uuid = @host.subscription_facet.uuid
76
-
77
- # Non-IoP mode: should return insights_facet UUID
78
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
79
- assert_equal 'insights-123', @host.insights_uuid
80
-
81
- # IoP mode: should return subscription_facet UUID
82
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
83
- assert_equal subscription_uuid, @host.insights_uuid
84
-
85
- # Toggle back to non-IoP mode
86
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
87
- assert_equal 'insights-123', @host.insights_uuid
88
- end
89
- end
90
-
91
- context 'ensure_iop_insights_uuid method' do
92
- test 'updates insights_facet uuid when different from subscription_facet' do
93
- @host = FactoryBot.create(:host, :managed, :with_subscription, organization: @org)
94
- correct_uuid = @host.subscription_facet.uuid
95
- @host.insights = FactoryBot.create(:insights_facet, host_id: @host.id, uuid: 'wrong-456')
96
-
97
- assert_equal 'wrong-456', @host.insights_facet.uuid
98
-
99
- @host.ensure_iop_insights_uuid
100
-
101
- assert_equal correct_uuid, @host.insights_facet.reload.uuid
102
- end
103
-
104
- test 'does nothing when uuids already match' do
105
- @host = FactoryBot.create(:host, :managed, :with_subscription, organization: @org)
106
- matching_uuid = @host.subscription_facet.uuid
107
- @host.insights = FactoryBot.create(:insights_facet, host_id: @host.id, uuid: matching_uuid)
108
-
109
- # Expect no update call since UUIDs already match
110
- @host.insights_facet.expects(:update!).never
111
-
112
- @host.ensure_iop_insights_uuid
113
-
114
- assert_equal matching_uuid, @host.insights_facet.uuid
115
- end
116
-
117
- test 'does nothing when insights_facet missing' do
118
- @host = FactoryBot.create(:host, :managed, :with_subscription, organization: @org)
119
-
120
- assert_nothing_raised do
121
- @host.ensure_iop_insights_uuid
122
- end
123
- end
124
-
125
- test 'does nothing when subscription_facet missing' do
126
- @host = FactoryBot.create(:host, :managed, organization: @org)
127
- @host.insights = FactoryBot.create(:insights_facet, host_id: @host.id, uuid: 'insights-999')
128
- original_uuid = @host.insights_facet.uuid
129
-
130
- assert_nothing_raised do
131
- @host.ensure_iop_insights_uuid
132
- end
133
-
134
- assert_equal original_uuid, @host.insights_facet.uuid
135
- end
136
-
137
- test 'does nothing when both facets missing' do
138
- @host = FactoryBot.create(:host, :managed, organization: @org)
139
-
140
- assert_nothing_raised do
141
- @host.ensure_iop_insights_uuid
142
- end
143
- end
144
-
145
- test 'handles nil uuids gracefully' do
146
- @host = FactoryBot.create(:host, :managed, :with_subscription, organization: @org)
147
-
148
- # Scenario 1: subscription_facet uuid is nil (shouldn't happen but test gracefully)
149
- @host.subscription_facet.update(uuid: nil)
150
- @host.insights = FactoryBot.create(:insights_facet, host_id: @host.id, uuid: 'some-uuid')
151
-
152
- assert_nothing_raised do
153
- @host.ensure_iop_insights_uuid
154
- end
155
-
156
- # Scenario 2: insights_facet uuid is nil
157
- @host.subscription_facet.update(uuid: 'valid-uuid')
158
- @host.insights_facet.update(uuid: nil)
159
-
160
- assert_nothing_raised do
161
- @host.ensure_iop_insights_uuid
162
- end
163
-
164
- # Should update to match subscription_facet
165
- assert_equal 'valid-uuid', @host.insights_facet.reload.uuid
166
- end
167
-
168
- test 'corrects stale uuid after cloud registration' do
169
- # Simulate a host that was previously registered to cloud with cloud-assigned UUID
170
- @host = FactoryBot.create(:host, :managed, :with_subscription, organization: @org)
171
- local_uuid = @host.subscription_facet.uuid
172
- @host.insights = FactoryBot.create(:insights_facet, host_id: @host.id, uuid: 'cloud-456')
173
-
174
- # Before sync: insights_facet has stale cloud UUID
175
- assert_equal 'cloud-456', @host.insights_facet.uuid
176
-
177
- # In IoP mode, insights_uuid should return subscription_facet UUID (not stale)
178
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
179
- assert_equal local_uuid, @host.insights_uuid
180
-
181
- # Call sync to correct the stale UUID
182
- @host.ensure_iop_insights_uuid
183
-
184
- # After sync: insights_facet UUID should match subscription_facet UUID
185
- assert_equal local_uuid, @host.insights_facet.reload.uuid
186
-
187
- # Verify insights_uuid still returns correct value
188
- assert_equal local_uuid, @host.insights_uuid
189
- end
190
- end
191
-
192
- context 'scoped search on insights_uuid' do
193
- setup do
194
- @org = FactoryBot.create(:organization)
195
- end
196
-
197
- teardown do
198
- ForemanRhCloud.unstub(:with_iop_smart_proxy?)
199
- end
200
-
201
- test 'searches insights_facet.uuid in non-IoP mode with = operator' do
202
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
203
- host1 = FactoryBot.create(:host, :managed, organization: @org)
204
- host1.insights = FactoryBot.create(:insights_facet, host_id: host1.id, uuid: 'insights-uuid-123')
205
- host2 = FactoryBot.create(:host, :managed, organization: @org)
206
- host2.insights = FactoryBot.create(:insights_facet, host_id: host2.id, uuid: 'insights-uuid-456')
207
-
208
- results = Host::Managed.search_for('insights_uuid = insights-uuid-123')
209
-
210
- assert_includes results, host1
211
- assert_not_includes results, host2
212
- end
213
-
214
- test 'searches subscription_facet.uuid in IoP mode with = operator' do
215
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
216
- host1 = FactoryBot.create(:host, :managed, :with_subscription, organization: @org)
217
- host2 = FactoryBot.create(:host, :managed, :with_subscription, organization: @org)
218
-
219
- # Even if insights_facet has different UUID, should use subscription_facet UUID
220
- host1.insights = FactoryBot.create(:insights_facet, host_id: host1.id, uuid: 'stale-123')
221
-
222
- results = Host::Managed.search_for("insights_uuid = #{host1.subscription_facet.uuid}")
223
-
224
- assert_includes results, host1
225
- assert_not_includes results, host2
226
- end
227
-
228
- test 'searches with ^ operator (IN) in non-IoP mode' do
229
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
230
- host1 = FactoryBot.create(:host, :managed, organization: @org)
231
- host1.insights = FactoryBot.create(:insights_facet, host_id: host1.id, uuid: 'uuid-1')
232
- host2 = FactoryBot.create(:host, :managed, organization: @org)
233
- host2.insights = FactoryBot.create(:insights_facet, host_id: host2.id, uuid: 'uuid-2')
234
- host3 = FactoryBot.create(:host, :managed, organization: @org)
235
- host3.insights = FactoryBot.create(:insights_facet, host_id: host3.id, uuid: 'uuid-3')
236
-
237
- results = Host::Managed.search_for('insights_uuid ^ (uuid-1,uuid-2)')
238
-
239
- assert_includes results, host1
240
- assert_includes results, host2
241
- assert_not_includes results, host3
242
- end
243
-
244
- test 'searches with ^ operator (IN) in IoP mode - THE BUG FIX' do
245
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
246
- host1 = FactoryBot.create(:host, :managed, :with_subscription, organization: @org)
247
- host2 = FactoryBot.create(:host, :managed, :with_subscription, organization: @org)
248
- host3 = FactoryBot.create(:host, :managed, :with_subscription, organization: @org)
249
-
250
- # Create insights facets with stale UUIDs to verify we're using subscription_facet
251
- host1.insights = FactoryBot.create(:insights_facet, host_id: host1.id, uuid: 'stale-1')
252
- host2.insights = FactoryBot.create(:insights_facet, host_id: host2.id, uuid: 'stale-2')
253
- host3.insights = FactoryBot.create(:insights_facet, host_id: host3.id, uuid: 'stale-3')
254
-
255
- uuid1 = host1.subscription_facet.uuid
256
- uuid2 = host2.subscription_facet.uuid
257
-
258
- # This is the search query that remediation modal creates
259
- results = Host::Managed.search_for("insights_uuid ^ (#{uuid1},#{uuid2})")
260
-
261
- # Should find hosts by subscription_facet UUID, not insights_facet UUID
262
- assert_includes results, host1
263
- assert_includes results, host2
264
- assert_not_includes results, host3
265
- end
266
-
267
- test 'searches with !^ operator (NOT IN) in non-IoP mode' do
268
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
269
- host1 = FactoryBot.create(:host, :managed, organization: @org)
270
- host1.insights = FactoryBot.create(:insights_facet, host_id: host1.id, uuid: 'uuid-1')
271
- host2 = FactoryBot.create(:host, :managed, organization: @org)
272
- host2.insights = FactoryBot.create(:insights_facet, host_id: host2.id, uuid: 'uuid-2')
273
- host3 = FactoryBot.create(:host, :managed, organization: @org)
274
- host3.insights = FactoryBot.create(:insights_facet, host_id: host3.id, uuid: 'uuid-3')
275
-
276
- results = Host::Managed.search_for('insights_uuid !^ (uuid-1,uuid-2)')
277
-
278
- assert_not_includes results, host1
279
- assert_not_includes results, host2
280
- assert_includes results, host3
281
- end
282
-
283
- test 'searches with !^ operator (NOT IN) in IoP mode' do
284
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
285
- host1 = FactoryBot.create(:host, :managed, :with_subscription, organization: @org)
286
- host2 = FactoryBot.create(:host, :managed, :with_subscription, organization: @org)
287
- host3 = FactoryBot.create(:host, :managed, :with_subscription, organization: @org)
288
-
289
- uuid1 = host1.subscription_facet.uuid
290
- uuid2 = host2.subscription_facet.uuid
291
-
292
- results = Host::Managed.search_for("insights_uuid !^ (#{uuid1},#{uuid2})")
293
-
294
- assert_not_includes results, host1
295
- assert_not_includes results, host2
296
- assert_includes results, host3
297
- end
298
-
299
- test 'handles hosts without facets in non-IoP mode' do
300
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
301
- host_without_facet = FactoryBot.create(:host, :managed, organization: @org)
302
- host_with_facet = FactoryBot.create(:host, :managed, organization: @org)
303
- host_with_facet.insights = FactoryBot.create(:insights_facet, host_id: host_with_facet.id, uuid: 'uuid-1')
304
-
305
- results = Host::Managed.search_for('insights_uuid = uuid-1')
306
-
307
- assert_includes results, host_with_facet
308
- assert_not_includes results, host_without_facet
309
- end
310
-
311
- test 'handles hosts without subscription_facet in IoP mode' do
312
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
313
- host_without_sub = FactoryBot.create(:host, :managed, organization: @org)
314
- host_with_sub = FactoryBot.create(:host, :managed, :with_subscription, organization: @org)
315
-
316
- uuid = host_with_sub.subscription_facet.uuid
317
-
318
- results = Host::Managed.search_for("insights_uuid = #{uuid}")
319
-
320
- assert_includes results, host_with_sub
321
- assert_not_includes results, host_without_sub
322
- end
323
-
324
- test 'mode changes are reflected in searches' do
325
- host1 = FactoryBot.create(:host, :managed, :with_subscription, organization: @org)
326
- host1.insights = FactoryBot.create(:insights_facet, host_id: host1.id, uuid: 'insights-uuid-abc')
327
- insights_uuid = 'insights-uuid-abc'
328
- subscription_uuid = host1.subscription_facet.uuid
329
-
330
- # Non-IoP mode: should find by insights_facet UUID
331
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
332
- results = Host::Managed.search_for("insights_uuid = #{insights_uuid}")
333
- assert_includes results, host1
334
-
335
- # IoP mode: should find by subscription_facet UUID
336
- ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
337
- results = Host::Managed.search_for("insights_uuid = #{subscription_uuid}")
338
- assert_includes results, host1
339
-
340
- # Should NOT find by old insights_facet UUID in IoP mode
341
- results = Host::Managed.search_for("insights_uuid = #{insights_uuid}")
342
- assert_not_includes results, host1
343
- end
344
- end
345
- end
@@ -1,194 +0,0 @@
1
- import React from 'react';
2
- import { render, screen, waitFor } from '@testing-library/react';
3
- import '@testing-library/jest-dom';
4
- import { Provider } from 'react-redux';
5
- import { ConnectedRouter } from 'connected-react-router';
6
- import { createMemoryHistory } from 'history';
7
- import configureMockStore from 'redux-mock-store';
8
- import { STATUS } from 'foremanReact/constants';
9
- import * as APIHooks from 'foremanReact/common/hooks/API/APIHooks';
10
- import InsightsTotalRiskCard from '../InsightsTotalRiskChart';
11
-
12
- jest.mock('foremanReact/common/hooks/API/APIHooks');
13
- jest.mock('foremanReact/common/I18n', () => ({
14
- translate: jest.fn(str => str),
15
- }));
16
-
17
- const mockStore = configureMockStore();
18
- const history = createMemoryHistory();
19
- const store = mockStore({
20
- router: {
21
- location: {
22
- pathname: '/',
23
- search: '',
24
- hash: '',
25
- state: null,
26
- },
27
- action: 'POP',
28
- },
29
- });
30
-
31
- const defaultHostDetails = {
32
- id: 1,
33
- insights_attributes: {
34
- uuid: 'test-uuid',
35
- use_iop_mode: false,
36
- },
37
- };
38
-
39
- const renderComponent = (props = {}) => {
40
- const allProps = {
41
- hostDetails: defaultHostDetails,
42
- ...props,
43
- };
44
-
45
- return render(
46
- <Provider store={store}>
47
- <ConnectedRouter history={history}>
48
- <InsightsTotalRiskCard {...allProps} />
49
- </ConnectedRouter>
50
- </Provider>
51
- );
52
- };
53
-
54
- describe('InsightsTotalRiskChart', () => {
55
- beforeEach(() => {
56
- store.clearActions();
57
- jest.clearAllMocks();
58
- });
59
-
60
- it('should show loading state initially', () => {
61
- APIHooks.useAPI.mockReturnValue({
62
- status: STATUS.PENDING,
63
- response: null,
64
- });
65
-
66
- renderComponent();
67
- // SkeletonLoader shows loading state when status is PENDING
68
- expect(screen.queryByText('No results found')).not.toBeInTheDocument();
69
- expect(
70
- screen.queryByTestId('rh-cloud-total-risk-card')
71
- ).not.toBeInTheDocument();
72
- });
73
-
74
- it('should display error state when API fails', async () => {
75
- APIHooks.useAPI.mockReturnValue({
76
- status: STATUS.ERROR,
77
- response: null,
78
- });
79
-
80
- renderComponent();
81
- expect(screen.getByText('No results found')).toBeInTheDocument();
82
- expect(
83
- screen.queryByTestId('rh-cloud-total-risk-card')
84
- ).not.toBeInTheDocument();
85
- });
86
-
87
- it('should handle non-IoP mode API response correctly', async () => {
88
- const mockResponse = {
89
- hits: [
90
- { total_risk: 1 },
91
- { total_risk: 2 },
92
- { total_risk: 2 },
93
- { total_risk: 3 },
94
- { total_risk: 4 },
95
- ],
96
- };
97
-
98
- APIHooks.useAPI.mockReturnValue({
99
- status: STATUS.RESOLVED,
100
- response: mockResponse,
101
- });
102
-
103
- renderComponent();
104
-
105
- await waitFor(() => {
106
- // Check if total number of recommendations is displayed
107
- expect(screen.getByText('5')).toBeInTheDocument();
108
- // Check if risk levels are displayed correctly
109
- expect(screen.getByText(/Low: 1/)).toBeInTheDocument();
110
- expect(screen.getByText(/Moderate: 2/)).toBeInTheDocument();
111
- expect(screen.getByText(/Important: 1/)).toBeInTheDocument();
112
- expect(screen.getByText(/Critical: 1/)).toBeInTheDocument();
113
- });
114
- });
115
-
116
- it('should handle IOP mode API response correctly', async () => {
117
- const mockResponse = {
118
- low_hits: 2,
119
- moderate_hits: 3,
120
- important_hits: 1,
121
- critical_hits: 2,
122
- hits: 8,
123
- };
124
-
125
- APIHooks.useAPI.mockReturnValue({
126
- status: STATUS.RESOLVED,
127
- response: mockResponse,
128
- });
129
-
130
- renderComponent({
131
- hostDetails: {
132
- ...defaultHostDetails,
133
- insights_attributes: {
134
- ...defaultHostDetails.insights_attributes,
135
- use_iop_mode: true,
136
- },
137
- },
138
- });
139
-
140
- await waitFor(() => {
141
- // Check if total number of recommendations is displayed
142
- expect(screen.getByText('8')).toBeInTheDocument();
143
- // Check if risk levels are displayed correctly
144
- expect(screen.getByText(/Low: 2/)).toBeInTheDocument();
145
- expect(screen.getByText(/Moderate: 3/)).toBeInTheDocument();
146
- expect(screen.getByText(/Important: 1/)).toBeInTheDocument();
147
- expect(screen.getByText(/Critical: 2/)).toBeInTheDocument();
148
- });
149
- });
150
-
151
- it('should show empty state when no recommendations exist', async () => {
152
- APIHooks.useAPI.mockReturnValue({
153
- status: STATUS.RESOLVED,
154
- response: { hits: [] },
155
- });
156
-
157
- renderComponent();
158
-
159
- await waitFor(() => {
160
- expect(screen.getByText(/Low: 0/)).toBeInTheDocument();
161
- expect(screen.getByText(/Moderate: 0/)).toBeInTheDocument();
162
- expect(screen.getByText(/Important: 0/)).toBeInTheDocument();
163
- expect(screen.getByText(/Critical: 0/)).toBeInTheDocument();
164
- });
165
- });
166
-
167
- it('should use correct API endpoint based on IOP mode', () => {
168
- renderComponent({
169
- hostDetails: {
170
- ...defaultHostDetails,
171
- insights_attributes: {
172
- ...defaultHostDetails.insights_attributes,
173
- use_iop_mode: true,
174
- },
175
- },
176
- });
177
-
178
- expect(APIHooks.useAPI).toHaveBeenCalledWith(
179
- 'get',
180
- expect.stringContaining('/api/insights/v1/system/test-uuid'),
181
- expect.any(Object)
182
- );
183
-
184
- jest.clearAllMocks();
185
-
186
- renderComponent();
187
-
188
- expect(APIHooks.useAPI).toHaveBeenCalledWith(
189
- 'get',
190
- expect.stringContaining('/hits/1'),
191
- expect.any(Object)
192
- );
193
- });
194
- });