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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b770b657000088615d19ad34ac855072fbaeb473a21200abdd935857fabcab6
4
- data.tar.gz: 48b27cf6eb625f2743910dcb22d66ca4d786144d83cc38a5d2dcca48f2f1269d
3
+ metadata.gz: aba47c451526fd85b565ba8fdcb17d6b7d643cd3c6fbea186a40943a6a4887bc
4
+ data.tar.gz: 16357a50048143a026497016cd38533b34d211c6ea61fccf55bd2a298852766e
5
5
  SHA512:
6
- metadata.gz: 326aa6a5e33f546ccbad30236013cec601d73e3304cea0983e9e5e199167bba4e2a10a17a5de73e9df610d49c47f83c03e7c0b1ebfe8840a16a951d409eb4f47
7
- data.tar.gz: 02dacd87fe7709d8ca1ce4f3e1d3c7cf24f238ff8e103d69d78b3cf61c538ac739634e5485242639b8ba8755f35da4bd1e68bdb423180bee7b2234da1f151481
6
+ metadata.gz: c863fed43a17edb0cfdb465e4b26b529e9a09460ab9677cea6e94bc3fea6c560c8fdf01c691ea4ea2d495c7ab715db90cbee5c3bf87217e3aae135c9b29b5e2f
7
+ data.tar.gz: c1d61f3f092e87829a28fd1ac237f46c29772aeae926d2a3c65cba440d3233844aa8dd40eb38a18ff56a09c68f9578aaf83a142d37d1aff06679d0ba6b87c904
@@ -0,0 +1,39 @@
1
+ module ForemanRhCloud
2
+ module RegistrationManagerExtensions
3
+ include ForemanRhCloud::CertAuth
4
+
5
+ def logger
6
+ Rails.logger
7
+ end
8
+
9
+ def unregister_host(host, options = {})
10
+ organization_destroy = options.fetch(:organization_destroy, false)
11
+
12
+ # Reload to ensure we have fresh association data
13
+ host.reload
14
+
15
+ # Only delete from HBI in IoP mode (hosted mode uses async job for cleanup)
16
+ hbi_host_destroy(host) if ForemanRhCloud.with_iop_smart_proxy? && !organization_destroy && host.insights_facet&.uuid&.presence
17
+ host.insights&.destroy!
18
+ super(host, options)
19
+ end
20
+
21
+ def hbi_host_destroy(host)
22
+ uuid = host.insights_facet.uuid
23
+ logger.debug "Unregistering host #{uuid} from HBI"
24
+ execute_cloud_request(
25
+ organization: host.organization,
26
+ method: :delete,
27
+ url: ForemanInventoryUpload.host_by_id_url(uuid),
28
+ headers: {
29
+ content_type: :json,
30
+ }
31
+ )
32
+ rescue RestClient::NotFound
33
+ Rails.logger.warn(_("Attempted to destroy HBI host %s, but host does not exist in HBI") % uuid)
34
+ rescue StandardError => e
35
+ # TODO: Improve error handling - don't break registration if HBI delete fails
36
+ Rails.logger.error(format(_("Failed to destroy HBI host %s: %s"), uuid, e.message))
37
+ end
38
+ end
39
+ end
@@ -8,7 +8,7 @@ module ForemanInventoryUpload
8
8
  def run
9
9
  organization = ::Organization.find(input[:organization_id])
10
10
  hosts_without_facets = ::ForemanInventoryUpload::Generators::Queries.for_org(organization, hosts_query: 'null? insights_uuid')
11
- facet_count = hosts_without_facets.count
11
+ facet_count = 0
12
12
  hosts_without_facets.each do |batch|
13
13
  facets = batch.pluck(:id, 'katello_subscription_facets.uuid').map do |host_id, uuid|
14
14
  {
@@ -20,9 +20,10 @@ module ForemanInventoryUpload
20
20
  # rubocop:disable Rails/SkipsModelValidations
21
21
  InsightsFacet.upsert_all(facets, unique_by: :host_id) unless facets.empty?
22
22
  # rubocop:enable Rails/SkipsModelValidations
23
+ facet_count += facets.size
23
24
  end
24
25
  output[:result] = facet_count.zero? ? _("There were no missing Insights facets") : format(_("Missing Insights facets created: %s"), facet_count)
25
- Rails.logger.info output[:result]
26
+ Rails.logger.debug output[:result]
26
27
  end
27
28
  end
28
29
  end
@@ -30,7 +30,7 @@ fi
30
30
  ORG_HEADER=()
31
31
  if [ -n "$ORG_ID" ]
32
32
  then
33
- + ORG_HEADER=("-H" "X-Org-Id: $ORG_ID")
33
+ ORG_HEADER=("-H" "X-Org-Id: $ORG_ID")
34
34
  fi
35
35
 
36
36
  # /tmp/a b/x.pem
@@ -88,8 +88,12 @@ module ForemanInventoryUpload
88
88
  inventory_base_url + "?hostname_or_id=#{ForemanRhCloud.foreman_host.fqdn}"
89
89
  end
90
90
 
91
- def self.hosts_by_ids_url(host_ids)
92
- host_ids_string = host_ids.join(',')
91
+ def self.host_by_id_url(host_uuid)
92
+ "#{inventory_base_url}/#{host_uuid}"
93
+ end
94
+
95
+ def self.hosts_by_ids_url(host_uuids)
96
+ host_ids_string = host_uuids.join(',')
93
97
  "#{inventory_base_url}/#{host_ids_string}"
94
98
  end
95
99
  end
@@ -40,6 +40,7 @@ module ForemanRhCloud
40
40
  ::Host::Managed.include RhCloudHost
41
41
 
42
42
  ::Katello::Api::Rhsm::CandlepinDynflowProxyController.include InsightsCloud::PackageProfileUploadExtensions
43
+ ::Katello::RegistrationManager.singleton_class.prepend ::ForemanRhCloud::RegistrationManagerExtensions
43
44
  end
44
45
  end
45
46
 
@@ -146,6 +146,10 @@ module ForemanRhCloud
146
146
  end
147
147
  end
148
148
 
149
+ ::Foreman::Plugin.app_metadata_registry.register(:foreman_rh_cloud, {
150
+ iop: ForemanRhCloud.with_iop_smart_proxy?,
151
+ })
152
+
149
153
  extend_template_helpers ForemanRhCloud::TemplateRendererHelper
150
154
  allowed_template_helpers :remediations_playbook, :download_rh_playbook
151
155
  end
@@ -1,3 +1,3 @@
1
1
  module ForemanRhCloud
2
- VERSION = '13.0.6'.freeze
2
+ VERSION = '13.0.8'.freeze
3
3
  end
@@ -39,13 +39,23 @@ namespace :rh_cloud_inventory do
39
39
 
40
40
  User.as_anonymous_admin do
41
41
  organization_ids.each do |organization_id|
42
- ForemanTasks.sync_task(
42
+ task = ForemanTasks.sync_task(
43
43
  ForemanInventoryUpload::Async::HostInventoryReportJob,
44
44
  base_folder,
45
45
  organization_id,
46
46
  filter,
47
47
  false # don't upload; the user ran report:generate and not report:generate_upload
48
48
  )
49
+
50
+ if task.result == 'success'
51
+ unless Setting[:subscription_connection_enabled]
52
+ target_file = File.join(base_folder, ForemanInventoryUpload.facts_archive_name(organization_id, filter))
53
+ puts "Generated #{target_file} for organization id #{organization_id}"
54
+ end
55
+ else
56
+ puts "Failed to generate report for organization id #{organization_id}. Task result: #{task.result}"
57
+ puts "Check task #{task.id} in the Tasks dashboard for error details."
58
+ end
49
59
  end
50
60
  puts "Check the Uploading tab for report uploading status." if Setting[:subscription_connection_enabled]
51
61
  end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foreman_rh_cloud",
3
- "version": "13.0.6",
3
+ "version": "13.0.8",
4
4
  "description": "Inventory Upload =============",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -4,7 +4,7 @@ require 'foreman_tasks/test_helpers'
4
4
  require "#{ForemanTasks::Engine.root}/test/support/dummy_dynflow_action"
5
5
 
6
6
  class CloudConnectorAnnounceTaskTest < ActiveSupport::TestCase
7
- include ForemanTasks::TestHelpers::WithInThreadExecutor
7
+ include Dynflow::Testing::Factories
8
8
 
9
9
  setup do
10
10
  RemoteExecutionFeature.register(
@@ -23,7 +23,8 @@ class CloudConnectorAnnounceTaskTest < ActiveSupport::TestCase
23
23
  test 'It executes cloud presence announcer' do
24
24
  ForemanRhCloud::CloudPresence.any_instance.expects(:announce_to_sources).times(Organization.unscoped.count)
25
25
 
26
- ForemanTasks.sync_task(InsightsCloud::Async::CloudConnectorAnnounceTask, @job_invocation)
26
+ action = create_and_plan_action(InsightsCloud::Async::CloudConnectorAnnounceTask, @job_invocation)
27
+ finalize_action(action)
27
28
  end
28
29
 
29
30
  private
@@ -4,7 +4,7 @@ require 'foreman_tasks/test_helpers'
4
4
  require "#{ForemanTasks::Engine.root}/test/support/dummy_dynflow_action"
5
5
 
6
6
  class ConnectorPlaybookExecutionReporterTaskTest < ActiveSupport::TestCase
7
- include ForemanTasks::TestHelpers::WithInThreadExecutor
7
+ include Dynflow::Testing::Factories
8
8
 
9
9
  # override default send behavior for the test
10
10
  class TestConnectorPlaybookExecutionReporterTask < InsightsCloud::Async::ConnectorPlaybookExecutionReporterTask
@@ -35,17 +35,19 @@ class ConnectorPlaybookExecutionReporterTaskTest < ActiveSupport::TestCase
35
35
 
36
36
  TestConnectorPlaybookExecutionReporterTask.any_instance.stubs(:job_finished?).returns(true)
37
37
 
38
- actual = ForemanTasks.sync_task(TestConnectorPlaybookExecutionReporterTask, @job_invocation)
38
+ action = create_and_plan_action(TestConnectorPlaybookExecutionReporterTask, @job_invocation)
39
+ action = run_action(action)
39
40
 
40
- actual_report = actual.output[:saved_reports].first.to_s
41
+ saved_reports = action.output[:saved_reports]
42
+ actual_report = saved_reports.first.to_s
41
43
 
42
- assert_equal 1, actual.output[:saved_reports].size
44
+ assert_equal 1, saved_reports.size
43
45
  assert_not_nil actual_report
44
46
  actual_jsonl = read_jsonl(actual_report)
45
47
 
46
- assert_equal true, actual.output['task']['invocation_status']['task_state']['task_done_reported']
47
- assert_equal 0, actual.output['task']['invocation_status']['hosts_state']['TEST_UUID1']['exit_status']
48
- assert_equal 0, actual.output['task']['invocation_status']['hosts_state']['TEST_UUID2']['exit_status']
48
+ assert_equal true, action.output['task']['invocation_status']['task_state']['task_done_reported']
49
+ assert_equal 0, action.output['task']['invocation_status']['hosts_state']['TEST_UUID1']['exit_status']
50
+ assert_equal 0, action.output['task']['invocation_status']['hosts_state']['TEST_UUID2']['exit_status']
49
51
 
50
52
  assert_equal true, @job_invocation.finished?
51
53
  assert_equal 'stopped', @job_invocation.sub_task_for_host(Host.where(name: 'host1').first)['state']
@@ -77,20 +79,25 @@ class ConnectorPlaybookExecutionReporterTaskTest < ActiveSupport::TestCase
77
79
 
78
80
  ArrangeTestHost.any_instance.stubs(:job_finished?).returns(false, true)
79
81
 
80
- actual = ForemanTasks.sync_task(ArrangeTestHost, @job_invocation)
82
+ action = create_and_plan_action(ArrangeTestHost, @job_invocation)
83
+ action = run_action(action)
81
84
 
82
- actual_report1 = actual.output[:saved_reports].first.to_s
83
- actual_report2 = actual.output[:saved_reports].second.to_s
85
+ # Process polling cycles - manually trigger Poll events until done
86
+ action = run_action(action, Dynflow::Action::Polling::Poll) until action.done?
84
87
 
85
- assert_equal 2, actual.output[:saved_reports].size
88
+ saved_reports = action.output[:saved_reports]
89
+ actual_report1 = saved_reports.first.to_s
90
+ actual_report2 = saved_reports.second.to_s
91
+
92
+ assert_equal 2, saved_reports.size
86
93
  assert_not_nil actual_report1
87
94
  assert_not_nil actual_report2
88
95
 
89
96
  actual_json1 = read_jsonl(actual_report1)
90
97
  actual_json2 = read_jsonl(actual_report2)
91
98
 
92
- assert_equal true, actual.output['task']['invocation_status']['task_state']['task_done_reported']
93
- assert_equal 0, actual.output['task']['invocation_status']['hosts_state']['TEST_UUID1']['exit_status']
99
+ assert_equal true, action.output['task']['invocation_status']['task_state']['task_done_reported']
100
+ assert_equal 0, action.output['task']['invocation_status']['hosts_state']['TEST_UUID1']['exit_status']
94
101
 
95
102
  assert_equal 'stopped', @job_invocation.sub_task_for_host(Host.where(name: 'host1').first)['state']
96
103
 
@@ -141,13 +148,18 @@ class ConnectorPlaybookExecutionReporterTaskTest < ActiveSupport::TestCase
141
148
 
142
149
  ArrangeTestHostTwo.any_instance.stubs(:job_finished?).returns(false, false, true)
143
150
 
144
- actual = ForemanTasks.sync_task(ArrangeTestHostTwo, @job_invocation)
151
+ action = create_and_plan_action(ArrangeTestHostTwo, @job_invocation)
152
+ action = run_action(action)
153
+
154
+ # Process polling cycles - manually trigger Poll events until done
155
+ action = run_action(action, Dynflow::Action::Polling::Poll) until action.done?
145
156
 
146
- actual_report1 = actual.output[:saved_reports].first.to_s
147
- actual_report2 = actual.output[:saved_reports].second.to_s
148
- actual_report3 = actual.output[:saved_reports].third.to_s
157
+ saved_reports = action.output[:saved_reports]
158
+ actual_report1 = saved_reports.first.to_s
159
+ actual_report2 = saved_reports.second.to_s
160
+ actual_report3 = saved_reports.third.to_s
149
161
 
150
- assert_equal 3, actual.output[:saved_reports].size
162
+ assert_equal 3, saved_reports.size
151
163
  assert_not_nil actual_report1
152
164
  assert_not_nil actual_report2
153
165
  assert_not_nil actual_report3
@@ -156,8 +168,8 @@ class ConnectorPlaybookExecutionReporterTaskTest < ActiveSupport::TestCase
156
168
  actual_json2 = read_jsonl(actual_report2)
157
169
  actual_json3 = read_jsonl(actual_report3)
158
170
 
159
- assert_equal true, actual.output['task']['invocation_status']['task_state']['task_done_reported']
160
- assert_equal 0, actual.output['task']['invocation_status']['hosts_state']['TEST_UUID1']['exit_status']
171
+ assert_equal true, action.output['task']['invocation_status']['task_state']['task_done_reported']
172
+ assert_equal 0, action.output['task']['invocation_status']['hosts_state']['TEST_UUID1']['exit_status']
161
173
 
162
174
  assert_not_nil actual_report_updated = actual_json1.find { |l| l['type'] == 'playbook_run_update' && l['host'] == 'TEST_UUID1' }
163
175
  assert_equal 'TEST_CORRELATION', actual_report_updated['correlation_id']
@@ -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