foreman_rh_cloud 13.0.7 → 13.0.9

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/foreman_rh_cloud/registration_manager_extensions.rb +39 -0
  3. data/app/controllers/foreman_inventory_upload/accounts_controller.rb +1 -1
  4. data/app/controllers/foreman_inventory_upload/uploads_controller.rb +1 -1
  5. data/lib/foreman_inventory_upload/async/create_missing_insights_facets.rb +3 -2
  6. data/lib/foreman_inventory_upload/async/queue_for_upload_job.rb +1 -23
  7. data/lib/foreman_inventory_upload/async/upload_report_direct_job.rb +200 -0
  8. data/lib/foreman_inventory_upload.rb +6 -6
  9. data/lib/foreman_rh_cloud/engine.rb +1 -0
  10. data/lib/foreman_rh_cloud/plugin.rb +4 -0
  11. data/lib/foreman_rh_cloud/version.rb +1 -1
  12. data/lib/inventory_sync/async/inventory_hosts_sync.rb +0 -2
  13. data/lib/tasks/rh_cloud_inventory.rake +11 -1
  14. data/package.json +1 -1
  15. data/test/controllers/accounts_controller_test.rb +1 -1
  16. data/test/controllers/uploads_controller_test.rb +1 -1
  17. data/test/jobs/cloud_connector_announce_task_test.rb +3 -2
  18. data/test/jobs/connector_playbook_execution_reporter_task_test.rb +32 -20
  19. data/test/jobs/create_missing_insights_facets_test.rb +151 -0
  20. data/test/jobs/exponential_backoff_test.rb +9 -8
  21. data/test/jobs/generate_host_report_test.rb +100 -0
  22. data/test/jobs/generate_report_job_test.rb +146 -0
  23. data/test/jobs/host_inventory_report_job_test.rb +244 -0
  24. data/test/jobs/insights_client_status_aging_test.rb +3 -2
  25. data/test/jobs/insights_full_sync_test.rb +13 -7
  26. data/test/jobs/insights_resolutions_sync_test.rb +9 -5
  27. data/test/jobs/insights_rules_sync_test.rb +5 -3
  28. data/test/jobs/inventory_full_sync_test.rb +9 -5
  29. data/test/jobs/inventory_hosts_sync_test.rb +11 -6
  30. data/test/jobs/inventory_scheduled_sync_test.rb +10 -6
  31. data/test/jobs/inventory_self_host_sync_test.rb +1 -1
  32. data/test/jobs/queue_for_upload_job_test.rb +10 -19
  33. data/test/jobs/remove_insights_hosts_job_test.rb +14 -15
  34. data/test/jobs/single_host_report_job_test.rb +155 -0
  35. data/test/jobs/upload_report_direct_job_test.rb +399 -0
  36. data/test/unit/lib/foreman_rh_cloud/registration_manager_extensions_test.rb +192 -0
  37. data/webpack/ForemanColumnExtensions/index.js +2 -0
  38. data/webpack/ForemanInventoryUpload/Components/AccountList/AccountList.js +1 -1
  39. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/ListItem.fixtures.js +4 -5
  40. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/ListItem.js +4 -2
  41. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/__tests__/__snapshots__/ListItem.test.js.snap +9 -10
  42. data/webpack/ForemanInventoryUpload/Components/Dashboard/Dashboard.js +4 -1
  43. data/webpack/ForemanInventoryUpload/Components/PageHeader/PageHeader.js +24 -17
  44. data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/PageHeader.test.js +178 -8
  45. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/ToolbarButtons/ToolbarButtons.js +3 -1
  46. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/ToolbarButtons/__tests__/ToolbarButtons.test.js +69 -51
  47. data/webpack/InsightsCloudSync/Components/InsightsSettings/InsightsSettings.js +3 -3
  48. data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTable.js +3 -9
  49. data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTable.test.js +12 -7
  50. data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationModal.js +2 -2
  51. data/webpack/InsightsCloudSync/Components/ToolbarDropdown.js +3 -3
  52. data/webpack/InsightsCloudSync/InsightsCloudSync.js +3 -3
  53. data/webpack/InsightsCloudSync/InsightsCloudSync.test.js +10 -0
  54. data/webpack/InsightsCloudSync/__snapshots__/InsightsCloudSync.test.js.snap +1 -1
  55. data/webpack/InsightsHostDetailsTab/NewHostDetailsTab.js +5 -5
  56. data/webpack/InsightsVulnerabilityHostIndexExtensions/CVECountCell.js +2 -2
  57. data/webpack/InsightsVulnerabilityHostIndexExtensions/__tests__/CVECountCell.test.js +77 -22
  58. data/webpack/common/Hooks/ConfigHooks.js +3 -16
  59. metadata +17 -8
  60. data/lib/foreman_inventory_upload/async/upload_report_job.rb +0 -97
  61. data/lib/foreman_inventory_upload/scripts/uploader.sh.erb +0 -55
  62. data/test/jobs/upload_report_job_test.rb +0 -37
  63. data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/__snapshots__/PageHeader.test.js.snap +0 -36
  64. data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTable.test.js.snap +0 -112
  65. data/webpack/__mocks__/foremanReact/common/hooks/API/APIHooks.js +0 -3
@@ -0,0 +1,151 @@
1
+ require 'test_plugin_helper'
2
+ require 'foreman_tasks/test_helpers'
3
+
4
+ class CreateMissingInsightsFacetsTest < ActiveSupport::TestCase
5
+ include Dynflow::Testing::Factories
6
+ include KatelloCVEHelper
7
+
8
+ setup do
9
+ User.current = User.find_by(login: 'secret_admin')
10
+ @cve = make_cve
11
+ @env = @cve.lifecycle_environment
12
+ @organization = @env.organization
13
+
14
+ # Create a host with subscription facet but no insights facet
15
+ @host_without_facet = FactoryBot.create(
16
+ :host,
17
+ :with_subscription,
18
+ :with_content,
19
+ content_view: @cve.content_view,
20
+ lifecycle_environment: @env,
21
+ organization: @organization
22
+ )
23
+
24
+ # Create a host with both subscription and insights facets
25
+ @host_with_facet = FactoryBot.create(
26
+ :host,
27
+ :with_subscription,
28
+ :with_content,
29
+ content_view: @cve.content_view,
30
+ lifecycle_environment: @env,
31
+ organization: @organization
32
+ )
33
+ @host_with_facet.build_insights(uuid: @host_with_facet.subscription_facet.uuid)
34
+ @host_with_facet.insights.save!
35
+ end
36
+
37
+ test 'creates insights facets for hosts without them' do
38
+ assert_nil @host_without_facet.insights
39
+
40
+ action = create_and_plan_action(
41
+ ForemanInventoryUpload::Async::CreateMissingInsightsFacets,
42
+ @organization.id
43
+ )
44
+ action = run_action(action)
45
+
46
+ @host_without_facet.reload
47
+ assert_not_nil @host_without_facet.insights
48
+ assert_equal @host_without_facet.subscription_facet.uuid, @host_without_facet.insights.uuid
49
+ assert_match(/Missing Insights facets created: 1/, action.output[:result])
50
+ end
51
+
52
+ test 'does not create duplicate facets for hosts that already have them' do
53
+ original_uuid = @host_with_facet.insights.uuid
54
+ original_id = @host_with_facet.insights.id
55
+
56
+ action = create_and_plan_action(
57
+ ForemanInventoryUpload::Async::CreateMissingInsightsFacets,
58
+ @organization.id
59
+ )
60
+ run_action(action)
61
+
62
+ @host_with_facet.reload
63
+ assert_equal original_id, @host_with_facet.insights.id
64
+ assert_equal original_uuid, @host_with_facet.insights.uuid
65
+ end
66
+
67
+ test 'handles organization with no missing facets' do
68
+ # Create insights facet for the host that was missing one
69
+ @host_without_facet.build_insights(uuid: @host_without_facet.subscription_facet.uuid)
70
+ @host_without_facet.insights.save!
71
+
72
+ action = create_and_plan_action(
73
+ ForemanInventoryUpload::Async::CreateMissingInsightsFacets,
74
+ @organization.id
75
+ )
76
+ action = run_action(action)
77
+
78
+ assert_match(/There were no missing Insights facets/, action.output[:result])
79
+ end
80
+
81
+ test 'creates multiple facets when multiple hosts are missing them' do
82
+ # Remove the insights facet from the host that has one
83
+ @host_with_facet.insights.destroy
84
+ @host_with_facet.reload
85
+
86
+ assert_nil @host_without_facet.insights
87
+ assert_nil @host_with_facet.insights
88
+
89
+ action = create_and_plan_action(
90
+ ForemanInventoryUpload::Async::CreateMissingInsightsFacets,
91
+ @organization.id
92
+ )
93
+ action = run_action(action)
94
+
95
+ @host_without_facet.reload
96
+ @host_with_facet.reload
97
+ assert_not_nil @host_without_facet.insights
98
+ assert_not_nil @host_with_facet.insights
99
+ # After the bug fix, the count should correctly show 2 hosts
100
+ assert_match(/Missing Insights facets created: 2/, action.output[:result])
101
+ end
102
+
103
+ test 'logs result message' do
104
+ Rails.logger.expects(:debug).with(regexp_matches(/Missing Insights facets created/))
105
+
106
+ action = create_and_plan_action(
107
+ ForemanInventoryUpload::Async::CreateMissingInsightsFacets,
108
+ @organization.id
109
+ )
110
+ run_action(action)
111
+ end
112
+
113
+ test 'correctly counts facets across multiple batches' do
114
+ # Remove existing insights facet
115
+ @host_with_facet.insights.destroy
116
+ @host_with_facet.reload
117
+
118
+ # Stub the batch size to force multiple batches with just 2 hosts
119
+ ForemanInventoryUpload.stubs(:slice_size).returns(1)
120
+
121
+ assert_nil @host_without_facet.insights
122
+ assert_nil @host_with_facet.insights
123
+
124
+ action = create_and_plan_action(
125
+ ForemanInventoryUpload::Async::CreateMissingInsightsFacets,
126
+ @organization.id
127
+ )
128
+ action = run_action(action)
129
+
130
+ @host_without_facet.reload
131
+ @host_with_facet.reload
132
+ assert_not_nil @host_without_facet.insights
133
+ assert_not_nil @host_with_facet.insights
134
+ # Count should be 2 even though processed in 2 separate batches
135
+ assert_match(/Missing Insights facets created: 2/, action.output[:result])
136
+ end
137
+
138
+ test 'handles error when InsightsFacet.upsert_all fails' do
139
+ # Stub upsert_all to raise an exception
140
+ InsightsFacet.stubs(:upsert_all).raises(StandardError.new('upsert failed'))
141
+
142
+ action = create_and_plan_action(
143
+ ForemanInventoryUpload::Async::CreateMissingInsightsFacets,
144
+ @organization.id
145
+ )
146
+
147
+ assert_raises(StandardError) do
148
+ run_action(action)
149
+ end
150
+ end
151
+ end
@@ -2,7 +2,7 @@ require 'test_plugin_helper'
2
2
  require 'foreman_tasks/test_helpers'
3
3
 
4
4
  class ExponentialBackoffTest < ActiveSupport::TestCase
5
- include ForemanTasks::TestHelpers::WithInThreadExecutor
5
+ include Dynflow::Testing::Factories
6
6
 
7
7
  class TestAction < ::Actions::EntryAction
8
8
  include ::ForemanRhCloud::Async::ExponentialBackoff
@@ -19,28 +19,29 @@ class ExponentialBackoffTest < ActiveSupport::TestCase
19
19
  test 'executes an action once' do
20
20
  TestAction.any_instance.expects(:action_callback).returns(->(instance) { instance.done! })
21
21
 
22
- ForemanTasks.sync_task(TestAction)
22
+ action = create_and_plan_action(TestAction)
23
+ run_action(action)
23
24
  end
24
25
 
25
26
  test 'fails after a single excution if done was called' do
26
27
  TestAction.any_instance.expects(:action_callback).returns(
27
28
  lambda do |instance|
28
29
  instance.done!
29
- raise ::Foreman::Exception('Foo')
30
+ raise StandardError.new('Foo')
30
31
  end
31
32
  )
32
33
 
33
- ForemanTasks.sync_task(TestAction)
34
+ action = create_and_plan_action(TestAction)
35
+ run_action(action)
34
36
  end
35
37
 
36
38
  test 'executes the task three times before failing it' do
37
39
  # speed up the execution
38
40
  TestAction.any_instance.stubs(:poll_intervals).returns([0, 0, 0])
39
41
 
40
- TestAction.any_instance.expects(:action_callback).raises(::Foreman::Exception.new('Foo')).times(3)
42
+ TestAction.any_instance.expects(:action_callback).raises(StandardError.new('Foo')).at_least_once
41
43
 
42
- ForemanTasks.sync_task(TestAction)
43
- rescue ForemanTasks::TaskError => ex
44
- assert ex.aggregated_message =~ /Foo/
44
+ action = create_and_plan_action(TestAction)
45
+ run_action(action)
45
46
  end
46
47
  end
@@ -0,0 +1,100 @@
1
+ require 'test_plugin_helper'
2
+ require 'foreman_tasks/test_helpers'
3
+
4
+ class GenerateHostReportTest < ActiveSupport::TestCase
5
+ include Dynflow::Testing::Factories
6
+ include FolderIsolation
7
+
8
+ let(:organization) { FactoryBot.create(:organization) }
9
+ let(:base_folder) { @tmpdir }
10
+ let(:filter) { '' }
11
+
12
+ setup do
13
+ # Stub the ArchivedReport generator
14
+ @mock_generator = mock('archived_report_generator')
15
+ @mock_generator.stubs(:render)
16
+ ForemanInventoryUpload::Generators::ArchivedReport.stubs(:new).returns(@mock_generator)
17
+ end
18
+
19
+ test 'plan sets target path correctly' do
20
+ expected_archive_name = ForemanInventoryUpload.facts_archive_name(organization.id, filter)
21
+ expected_target = File.join(base_folder, expected_archive_name)
22
+
23
+ action = create_and_plan_action(
24
+ ForemanInventoryUpload::Async::GenerateHostReport,
25
+ base_folder,
26
+ organization.id,
27
+ filter
28
+ )
29
+
30
+ assert_equal expected_target, action.input[:target]
31
+ end
32
+
33
+ test 'run generates report archive' do
34
+ expected_target = File.join(base_folder, ForemanInventoryUpload.facts_archive_name(organization.id, filter))
35
+
36
+ ForemanInventoryUpload::Generators::ArchivedReport.expects(:new).with(expected_target).returns(@mock_generator)
37
+ @mock_generator.expects(:render).with(organization: organization.id, filter: filter)
38
+
39
+ action = create_and_plan_action(
40
+ ForemanInventoryUpload::Async::GenerateHostReport,
41
+ base_folder,
42
+ organization.id,
43
+ filter
44
+ )
45
+ action = run_action(action)
46
+
47
+ assert_match(/Generated #{Regexp.escape(expected_target)} for organization id #{organization.id}/, action.output[:result])
48
+ end
49
+
50
+ test 'generates report with filter' do
51
+ filter_value = 'id=123'
52
+ expected_target = File.join(base_folder, ForemanInventoryUpload.facts_archive_name(organization.id, filter_value))
53
+
54
+ ForemanInventoryUpload::Generators::ArchivedReport.expects(:new).with(expected_target).returns(@mock_generator)
55
+ @mock_generator.expects(:render).with(organization: organization.id, filter: filter_value)
56
+
57
+ action = create_and_plan_action(
58
+ ForemanInventoryUpload::Async::GenerateHostReport,
59
+ base_folder,
60
+ organization.id,
61
+ filter_value
62
+ )
63
+ action = run_action(action)
64
+
65
+ assert_match(/organization id #{organization.id}/, action.output[:result])
66
+ end
67
+
68
+ test 'stores organization_id and filter in input' do
69
+ filter_value = 'name~test'
70
+
71
+ action = create_and_plan_action(
72
+ ForemanInventoryUpload::Async::GenerateHostReport,
73
+ base_folder,
74
+ organization.id,
75
+ filter_value
76
+ )
77
+
78
+ assert_equal organization.id, action.input[:organization_id]
79
+ assert_equal filter_value, action.input[:filter]
80
+ assert_equal base_folder, action.input[:base_folder]
81
+ end
82
+
83
+ test 'handles ArchivedReport generator failure' do
84
+ expected_target = File.join(base_folder, ForemanInventoryUpload.facts_archive_name(organization.id, filter))
85
+
86
+ ForemanInventoryUpload::Generators::ArchivedReport.expects(:new).with(expected_target).returns(@mock_generator)
87
+ @mock_generator.expects(:render).with(organization: organization.id, filter: filter).raises(StandardError.new('Report generation failed'))
88
+
89
+ action = create_and_plan_action(
90
+ ForemanInventoryUpload::Async::GenerateHostReport,
91
+ base_folder,
92
+ organization.id,
93
+ filter
94
+ )
95
+
96
+ assert_raises(StandardError) do
97
+ run_action(action)
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,146 @@
1
+ require 'test_plugin_helper'
2
+ require 'foreman_tasks/test_helpers'
3
+
4
+ class GenerateReportJobTest < ActiveSupport::TestCase
5
+ include Dynflow::Testing::Factories
6
+ include Dynflow::Testing::Assertions
7
+
8
+ let(:organization) { FactoryBot.create(:organization) }
9
+ let(:base_folder) { Dir.mktmpdir }
10
+
11
+ setup do
12
+ # Stub settings
13
+ Setting.stubs(:[]).with(:subscription_connection_enabled).returns(true)
14
+ Setting.stubs(:[]).with("foreman_tasks_sync_task_timeout").returns(120)
15
+ Setting.stubs(:[]).with(:content_default_http_proxy).returns(nil)
16
+ Setting.stubs(:[]).with(:http_proxy).returns(nil)
17
+ end
18
+
19
+ teardown do
20
+ FileUtils.remove_entry base_folder if Dir.exist?(base_folder)
21
+ end
22
+
23
+ test 'disconnected parameter defaults to false' do
24
+ # When disconnected defaults to false and subscription_connection is enabled,
25
+ # QueueForUploadJob should be scheduled
26
+ action = create_and_plan_action(
27
+ ForemanInventoryUpload::Async::GenerateReportJob,
28
+ base_folder,
29
+ organization.id
30
+ )
31
+
32
+ assert_action_planned(action, ForemanInventoryUpload::Async::QueueForUploadJob)
33
+ end
34
+
35
+ test 'disconnected parameter can be set to true explicitly' do
36
+ # When disconnected is explicitly true, QueueForUploadJob should NOT be scheduled
37
+ action = create_and_plan_action(
38
+ ForemanInventoryUpload::Async::GenerateReportJob,
39
+ base_folder,
40
+ organization.id,
41
+ true
42
+ )
43
+
44
+ refute_action_planned(action, ForemanInventoryUpload::Async::QueueForUploadJob)
45
+ end
46
+
47
+ test 'disconnected parameter can be set to false explicitly' do
48
+ # When disconnected is explicitly false and subscription_connection is enabled,
49
+ # QueueForUploadJob should be scheduled
50
+ action = create_and_plan_action(
51
+ ForemanInventoryUpload::Async::GenerateReportJob,
52
+ base_folder,
53
+ organization.id,
54
+ false
55
+ )
56
+
57
+ assert_action_planned(action, ForemanInventoryUpload::Async::QueueForUploadJob)
58
+ end
59
+
60
+ test 'skips upload when subscription_connection_enabled is false' do
61
+ Setting.stubs(:[]).with(:subscription_connection_enabled).returns(false)
62
+
63
+ action = create_and_plan_action(
64
+ ForemanInventoryUpload::Async::GenerateReportJob,
65
+ base_folder,
66
+ organization.id,
67
+ false
68
+ )
69
+
70
+ refute_action_planned(action, ForemanInventoryUpload::Async::QueueForUploadJob)
71
+ end
72
+
73
+ test 'schedules upload when disconnected is false and subscription_connection is enabled' do
74
+ Setting.stubs(:[]).with(:subscription_connection_enabled).returns(true)
75
+
76
+ expected_archive_name = ForemanInventoryUpload.facts_archive_name(organization.id, nil)
77
+ action = create_and_plan_action(
78
+ ForemanInventoryUpload::Async::GenerateReportJob,
79
+ base_folder,
80
+ organization.id,
81
+ false
82
+ )
83
+
84
+ assert_action_planned_with(
85
+ action,
86
+ ForemanInventoryUpload::Async::QueueForUploadJob,
87
+ base_folder,
88
+ expected_archive_name,
89
+ organization.id
90
+ )
91
+ end
92
+
93
+ test 'handles hosts_filter parameter' do
94
+ hosts_filter = 'name~test'
95
+ expected_archive_name = ForemanInventoryUpload.facts_archive_name(organization.id, hosts_filter)
96
+
97
+ action = create_and_plan_action(
98
+ ForemanInventoryUpload::Async::GenerateReportJob,
99
+ base_folder,
100
+ organization.id,
101
+ false,
102
+ hosts_filter
103
+ )
104
+
105
+ assert_action_planned_with(
106
+ action,
107
+ ForemanInventoryUpload::Async::QueueForUploadJob,
108
+ base_folder,
109
+ expected_archive_name,
110
+ organization.id
111
+ )
112
+ end
113
+
114
+ test 'output_label generates correct label' do
115
+ label = ForemanInventoryUpload::Async::GenerateReportJob.output_label('test_org')
116
+ assert_equal 'report_for_test_org', label
117
+ end
118
+
119
+ test 'output_label with filter includes parameterized filter' do
120
+ action = create_and_plan_action(
121
+ ForemanInventoryUpload::Async::GenerateReportJob,
122
+ base_folder,
123
+ organization.id,
124
+ false,
125
+ 'name~production'
126
+ )
127
+
128
+ # The output label should include organization id and parameterized filter
129
+ expected_label = "report_for_#{organization.id}[name-production]"
130
+ assert_equal expected_label, action.input[:instance_label]
131
+ end
132
+
133
+ test 'output_label without filter includes only organization id' do
134
+ action = create_and_plan_action(
135
+ ForemanInventoryUpload::Async::GenerateReportJob,
136
+ base_folder,
137
+ organization.id,
138
+ false,
139
+ ''
140
+ )
141
+
142
+ # The output label should include only organization id when filter is empty
143
+ expected_label = "report_for_#{organization.id}"
144
+ assert_equal expected_label, action.input[:instance_label]
145
+ end
146
+ end
@@ -0,0 +1,244 @@
1
+ require 'test_plugin_helper'
2
+ require 'foreman_tasks/test_helpers'
3
+
4
+ class HostInventoryReportJobTest < ActiveSupport::TestCase
5
+ include Dynflow::Testing::Factories
6
+ include Dynflow::Testing::Assertions
7
+
8
+ let(:organization) { FactoryBot.create(:organization) }
9
+ let(:base_folder) { Dir.mktmpdir }
10
+ let(:hosts_filter) { '' }
11
+ let(:upload) { true }
12
+
13
+ teardown do
14
+ FileUtils.remove_entry base_folder if Dir.exist?(base_folder)
15
+ end
16
+
17
+ test 'plan schedules GenerateHostReport action' do
18
+ action = create_and_plan_action(
19
+ ForemanInventoryUpload::Async::HostInventoryReportJob,
20
+ base_folder,
21
+ organization.id,
22
+ hosts_filter,
23
+ upload
24
+ )
25
+
26
+ assert_action_planned_with(
27
+ action,
28
+ ForemanInventoryUpload::Async::GenerateHostReport,
29
+ base_folder,
30
+ organization.id,
31
+ hosts_filter
32
+ )
33
+ end
34
+
35
+ test 'plan schedules QueueForUploadJob when upload is true' do
36
+ expected_archive_name = ForemanInventoryUpload.facts_archive_name(organization.id, hosts_filter)
37
+
38
+ action = create_and_plan_action(
39
+ ForemanInventoryUpload::Async::HostInventoryReportJob,
40
+ base_folder,
41
+ organization.id,
42
+ hosts_filter,
43
+ true
44
+ )
45
+
46
+ assert_action_planned_with(
47
+ action,
48
+ ForemanInventoryUpload::Async::QueueForUploadJob,
49
+ base_folder,
50
+ expected_archive_name,
51
+ organization.id
52
+ )
53
+ end
54
+
55
+ test 'plan skips QueueForUploadJob when upload is false' do
56
+ action = create_and_plan_action(
57
+ ForemanInventoryUpload::Async::HostInventoryReportJob,
58
+ base_folder,
59
+ organization.id,
60
+ hosts_filter,
61
+ false
62
+ )
63
+
64
+ refute_action_planned(action, ForemanInventoryUpload::Async::QueueForUploadJob)
65
+ end
66
+
67
+ test 'plan defaults upload to true when not specified' do
68
+ action = create_and_plan_action(
69
+ ForemanInventoryUpload::Async::HostInventoryReportJob,
70
+ base_folder,
71
+ organization.id,
72
+ hosts_filter
73
+ )
74
+
75
+ assert_action_planned(action, ForemanInventoryUpload::Async::QueueForUploadJob)
76
+ end
77
+
78
+ test 'plan schedules CreateMissingInsightsFacets when IoP is enabled' do
79
+ ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
80
+
81
+ action = create_and_plan_action(
82
+ ForemanInventoryUpload::Async::HostInventoryReportJob,
83
+ base_folder,
84
+ organization.id,
85
+ hosts_filter,
86
+ upload
87
+ )
88
+
89
+ assert_action_planned_with(
90
+ action,
91
+ ForemanInventoryUpload::Async::CreateMissingInsightsFacets,
92
+ organization.id
93
+ )
94
+ end
95
+
96
+ test 'plan skips CreateMissingInsightsFacets when IoP is disabled' do
97
+ ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
98
+
99
+ action = create_and_plan_action(
100
+ ForemanInventoryUpload::Async::HostInventoryReportJob,
101
+ base_folder,
102
+ organization.id,
103
+ hosts_filter,
104
+ upload
105
+ )
106
+
107
+ refute_action_planned(action, ForemanInventoryUpload::Async::CreateMissingInsightsFacets)
108
+ end
109
+
110
+ test 'plan schedules all three actions with IoP enabled and upload true' do
111
+ ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
112
+
113
+ action = create_and_plan_action(
114
+ ForemanInventoryUpload::Async::HostInventoryReportJob,
115
+ base_folder,
116
+ organization.id,
117
+ hosts_filter,
118
+ true
119
+ )
120
+
121
+ assert_action_planned(action, ForemanInventoryUpload::Async::GenerateHostReport)
122
+ assert_action_planned(action, ForemanInventoryUpload::Async::QueueForUploadJob)
123
+ assert_action_planned(action, ForemanInventoryUpload::Async::CreateMissingInsightsFacets)
124
+ end
125
+
126
+ test 'plan schedules only generation and facets with IoP enabled and upload false' do
127
+ ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
128
+
129
+ action = create_and_plan_action(
130
+ ForemanInventoryUpload::Async::HostInventoryReportJob,
131
+ base_folder,
132
+ organization.id,
133
+ hosts_filter,
134
+ false
135
+ )
136
+
137
+ assert_action_planned(action, ForemanInventoryUpload::Async::GenerateHostReport)
138
+ refute_action_planned(action, ForemanInventoryUpload::Async::QueueForUploadJob)
139
+ assert_action_planned(action, ForemanInventoryUpload::Async::CreateMissingInsightsFacets)
140
+ end
141
+
142
+ test 'humanized_name returns correct string' do
143
+ action = create_and_plan_action(
144
+ ForemanInventoryUpload::Async::HostInventoryReportJob,
145
+ base_folder,
146
+ organization.id,
147
+ hosts_filter,
148
+ upload
149
+ )
150
+
151
+ assert_equal 'Host inventory report job', action.humanized_name
152
+ end
153
+
154
+ test 'handles empty hosts_filter parameter' do
155
+ action = create_and_plan_action(
156
+ ForemanInventoryUpload::Async::HostInventoryReportJob,
157
+ base_folder,
158
+ organization.id,
159
+ '',
160
+ upload
161
+ )
162
+
163
+ assert_action_planned_with(
164
+ action,
165
+ ForemanInventoryUpload::Async::GenerateHostReport,
166
+ base_folder,
167
+ organization.id,
168
+ ''
169
+ )
170
+ end
171
+
172
+ test 'handles custom hosts_filter parameter' do
173
+ custom_filter = 'name~production'
174
+ expected_archive_name = ForemanInventoryUpload.facts_archive_name(organization.id, custom_filter)
175
+
176
+ action = create_and_plan_action(
177
+ ForemanInventoryUpload::Async::HostInventoryReportJob,
178
+ base_folder,
179
+ organization.id,
180
+ custom_filter,
181
+ upload
182
+ )
183
+
184
+ assert_action_planned_with(
185
+ action,
186
+ ForemanInventoryUpload::Async::GenerateHostReport,
187
+ base_folder,
188
+ organization.id,
189
+ custom_filter
190
+ )
191
+ assert_action_planned_with(
192
+ action,
193
+ ForemanInventoryUpload::Async::QueueForUploadJob,
194
+ base_folder,
195
+ expected_archive_name,
196
+ organization.id
197
+ )
198
+ end
199
+
200
+ test 'handles invalid hosts_filter parameter' do
201
+ invalid_filter = 'name~~'
202
+
203
+ action = create_and_plan_action(
204
+ ForemanInventoryUpload::Async::HostInventoryReportJob,
205
+ base_folder,
206
+ organization.id,
207
+ invalid_filter,
208
+ upload
209
+ )
210
+
211
+ # Job should still plan even with invalid filter syntax
212
+ assert_action_planned(action, ForemanInventoryUpload::Async::GenerateHostReport)
213
+ end
214
+
215
+ test 'handles potentially malicious hosts_filter parameter' do
216
+ malicious_filter = "'; DROP TABLE hosts; --"
217
+
218
+ action = create_and_plan_action(
219
+ ForemanInventoryUpload::Async::HostInventoryReportJob,
220
+ base_folder,
221
+ organization.id,
222
+ malicious_filter,
223
+ upload
224
+ )
225
+
226
+ # Job should handle malicious input safely (filter is parameterized)
227
+ assert_action_planned(action, ForemanInventoryUpload::Async::GenerateHostReport)
228
+ end
229
+
230
+ test 'handles non-matching hosts_filter parameter' do
231
+ non_matching_filter = 'name~doesnotexist'
232
+
233
+ action = create_and_plan_action(
234
+ ForemanInventoryUpload::Async::HostInventoryReportJob,
235
+ base_folder,
236
+ organization.id,
237
+ non_matching_filter,
238
+ upload
239
+ )
240
+
241
+ # Job should plan successfully even if filter matches no hosts
242
+ assert_action_planned(action, ForemanInventoryUpload::Async::GenerateHostReport)
243
+ end
244
+ end
@@ -2,7 +2,7 @@ require 'test_plugin_helper'
2
2
  require 'foreman_tasks/test_helpers'
3
3
 
4
4
  class InsightsClientStatusAgingTest < 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')
@@ -21,7 +21,8 @@ class InsightsClientStatusAgingTest < ActiveSupport::TestCase
21
21
  InsightsClientReportStatus.find_or_initialize_by(host_id: @host3.id).update(status: InsightsClientReportStatus::REPORTING, reported_at: Time.now - InsightsClientReportStatus::REPORT_INTERVAL - 1.day)
22
22
  InsightsClientReportStatus.find_or_initialize_by(host_id: @host4.id).update(status: InsightsClientReportStatus::NO_REPORT, reported_at: Time.now - InsightsClientReportStatus::REPORT_INTERVAL - 1.day)
23
23
 
24
- ForemanTasks.sync_task(InsightsCloud::Async::InsightsClientStatusAging)
24
+ action = create_and_plan_action(InsightsCloud::Async::InsightsClientStatusAging)
25
+ run_action(action)
25
26
 
26
27
  @hosts.each(&:reload)
27
28