foreman_rh_cloud 13.0.9 → 13.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/inventory_upload/report_actions.rb +8 -1
  3. data/app/controllers/foreman_inventory_upload/accounts_controller.rb +82 -7
  4. data/app/controllers/foreman_inventory_upload/api/tasks_controller.rb +110 -0
  5. data/app/controllers/foreman_inventory_upload/reports_controller.rb +41 -17
  6. data/app/controllers/foreman_inventory_upload/uploads_controller.rb +0 -9
  7. data/config/routes.rb +4 -2
  8. data/db/migrate/20251209163012_drop_task_output_tables.foreman_rh_cloud.rb +24 -0
  9. data/lib/foreman_inventory_upload/async/generate_all_reports_job.rb +1 -1
  10. data/lib/foreman_inventory_upload/async/host_inventory_report_job.rb +39 -0
  11. data/lib/foreman_inventory_upload/async/upload_report_direct_job.rb +28 -57
  12. data/lib/foreman_rh_cloud/plugin.rb +1 -0
  13. data/lib/foreman_rh_cloud/version.rb +1 -1
  14. data/lib/tasks/rh_cloud_inventory.rake +4 -2
  15. data/package.json +1 -1
  16. data/test/controllers/accounts_controller_test.rb +10 -11
  17. data/test/controllers/insights_cloud/api/cloud_request_controller_test.rb +1 -2
  18. data/test/jobs/host_inventory_report_job_test.rb +161 -97
  19. data/test/jobs/single_host_report_job_test.rb +36 -54
  20. data/test/jobs/upload_report_direct_job_test.rb +132 -132
  21. data/test/unit/rh_cloud_permissions_test.rb +2 -0
  22. data/webpack/ForemanInventoryUpload/Components/AccountList/AccountList.fixtures.js +6 -6
  23. data/webpack/ForemanInventoryUpload/Components/AccountList/AccountList.js +49 -34
  24. data/webpack/ForemanInventoryUpload/Components/AccountList/AccountListActions.js +2 -2
  25. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/ListItem.fixtures.js +2 -2
  26. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/ListItem.js +15 -9
  27. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/__tests__/__snapshots__/ListItem.test.js.snap +6 -5
  28. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/ListItemStatus.fixtures.js +2 -2
  29. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/ListItemStatus.js +10 -14
  30. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/ListItemStatusHelper.js +9 -4
  31. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/__tests__/__snapshots__/ListItemStatus.test.js.snap +4 -4
  32. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountList.test.js.snap +15 -9
  33. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListActions.test.js.snap +7 -7
  34. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListReducer.test.js.snap +6 -6
  35. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListSelectors.test.js.snap +12 -12
  36. data/webpack/ForemanInventoryUpload/Components/Dashboard/Dashboard.js +36 -132
  37. data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/Dashboard.test.js +60 -17
  38. data/webpack/ForemanInventoryUpload/Components/Dashboard/index.js +1 -34
  39. data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/__snapshots__/integration.test.js.snap +0 -1
  40. data/webpack/ForemanInventoryUpload/Components/NavContainer/NavContainer.js +1 -26
  41. data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/__snapshots__/PageTitle.test.js.snap +2 -2
  42. data/webpack/ForemanInventoryUpload/Components/TabHeader/TabHeader.js +22 -9
  43. data/webpack/ForemanInventoryUpload/Components/TabHeader/__tests__/TabHeader.test.js +67 -4
  44. data/webpack/ForemanInventoryUpload/Components/TaskHistory/TaskHistory.js +140 -0
  45. data/webpack/ForemanInventoryUpload/Components/TaskHistory/index.js +1 -0
  46. data/webpack/ForemanInventoryUpload/Components/TaskHistory/taskHistory.scss +40 -0
  47. data/webpack/ForemanInventoryUpload/Components/TaskProgress/TaskProgress.js +340 -0
  48. data/webpack/ForemanInventoryUpload/Components/TaskProgress/index.js +1 -0
  49. data/webpack/ForemanInventoryUpload/Components/TaskProgress/taskProgress.scss +8 -0
  50. data/webpack/ForemanInventoryUpload/ForemanInventoryHelpers.js +2 -2
  51. data/webpack/ForemanInventoryUpload/ForemanInventoryUploadReducers.js +0 -2
  52. data/webpack/ForemanInventoryUpload/__tests__/__snapshots__/ForemanInventoryHelpers.test.js.snap +1 -1
  53. data/webpack/ForemanRhCloudPages.js +0 -1
  54. data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationHelpers.js +1 -2
  55. data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationModal.js +2 -4
  56. data/webpack/__mocks__/foremanReact/components/common/dates/RelativeDateTime.js +14 -0
  57. data/webpack/__tests__/ForemanRhCloudHelpers.test.js +5 -1
  58. metadata +10 -61
  59. data/app/models/task_output_line.rb +0 -2
  60. data/app/models/task_output_status.rb +0 -2
  61. data/lib/foreman_inventory_upload/async/generate_report_job.rb +0 -61
  62. data/lib/foreman_inventory_upload/async/progress_output.rb +0 -38
  63. data/lib/foreman_inventory_upload/async/shell_process.rb +0 -77
  64. data/test/controllers/reports_controller_test.rb +0 -21
  65. data/test/controllers/uploads_controller_test.rb +0 -21
  66. data/test/jobs/generate_report_job_test.rb +0 -146
  67. data/test/unit/shell_process_job_test.rb +0 -29
  68. data/webpack/ForemanInventoryUpload/Components/Dashboard/DashboardActions.js +0 -88
  69. data/webpack/ForemanInventoryUpload/Components/Dashboard/DashboardConstants.js +0 -9
  70. data/webpack/ForemanInventoryUpload/Components/Dashboard/DashboardReducer.js +0 -68
  71. data/webpack/ForemanInventoryUpload/Components/Dashboard/DashboardSelectors.js +0 -17
  72. data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/DashboardActions.test.js +0 -51
  73. data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/DashboardIntegration.test.js +0 -17
  74. data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/DashboardReducer.test.js +0 -64
  75. data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/DashboardSelectors.test.js +0 -46
  76. data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/__snapshots__/Dashboard.test.js.snap +0 -36
  77. data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/__snapshots__/DashboardActions.test.js.snap +0 -76
  78. data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/__snapshots__/DashboardReducer.test.js.snap +0 -44
  79. data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/__snapshots__/DashboardSelectors.test.js.snap +0 -42
  80. data/webpack/ForemanInventoryUpload/Components/FullScreenModal/FullScreenModal.fixtures.js +0 -0
  81. data/webpack/ForemanInventoryUpload/Components/FullScreenModal/FullScreenModal.js +0 -55
  82. data/webpack/ForemanInventoryUpload/Components/FullScreenModal/FullScreenModalHelper.js +0 -0
  83. data/webpack/ForemanInventoryUpload/Components/FullScreenModal/__tests__/FullScreenModal.test.js +0 -13
  84. data/webpack/ForemanInventoryUpload/Components/FullScreenModal/__tests__/__snapshots__/FullScreenModal.test.js.snap +0 -65
  85. data/webpack/ForemanInventoryUpload/Components/FullScreenModal/fullScreenModal.scss +0 -20
  86. data/webpack/ForemanInventoryUpload/Components/FullScreenModal/index.js +0 -1
  87. data/webpack/ForemanInventoryUpload/Components/ReportGenerate/ReportGenerate.fixtures.js +0 -18
  88. data/webpack/ForemanInventoryUpload/Components/ReportGenerate/ReportGenerate.js +0 -65
  89. data/webpack/ForemanInventoryUpload/Components/ReportGenerate/ReportGenerateHelper.js +0 -0
  90. data/webpack/ForemanInventoryUpload/Components/ReportGenerate/__tests__/ReportGenerate.test.js +0 -14
  91. data/webpack/ForemanInventoryUpload/Components/ReportGenerate/__tests__/__snapshots__/ReportGenerate.test.js.snap +0 -47
  92. data/webpack/ForemanInventoryUpload/Components/ReportGenerate/index.js +0 -1
  93. data/webpack/ForemanInventoryUpload/Components/ReportGenerate/reportGenerate.scss +0 -0
  94. data/webpack/ForemanInventoryUpload/Components/ReportUpload/ReportUpload.fixtures.js +0 -18
  95. data/webpack/ForemanInventoryUpload/Components/ReportUpload/ReportUpload.js +0 -46
  96. data/webpack/ForemanInventoryUpload/Components/ReportUpload/ReportUploadHelper.js +0 -0
  97. data/webpack/ForemanInventoryUpload/Components/ReportUpload/__tests__/ReportUpload.test.js +0 -14
  98. data/webpack/ForemanInventoryUpload/Components/ReportUpload/__tests__/__snapshots__/ReportUpload.test.js.snap +0 -47
  99. data/webpack/ForemanInventoryUpload/Components/ReportUpload/index.js +0 -1
  100. data/webpack/ForemanInventoryUpload/Components/ReportUpload/reportUpload.scss +0 -0
  101. data/webpack/ForemanInventoryUpload/Components/TabBody/TabBody.fixtures.js +0 -0
  102. data/webpack/ForemanInventoryUpload/Components/TabBody/TabBody.js +0 -31
  103. data/webpack/ForemanInventoryUpload/Components/TabBody/TabBodyHelper.js +0 -0
  104. data/webpack/ForemanInventoryUpload/Components/TabBody/__tests__/TabBody.test.js +0 -13
  105. data/webpack/ForemanInventoryUpload/Components/TabBody/__tests__/__snapshots__/TabBody.test.js.snap +0 -19
  106. data/webpack/ForemanInventoryUpload/Components/TabBody/index.js +0 -1
  107. data/webpack/ForemanInventoryUpload/Components/TabBody/tabBody.scss +0 -5
  108. data/webpack/ForemanInventoryUpload/Components/Terminal/Terminal.fixtures.js +0 -10
  109. data/webpack/ForemanInventoryUpload/Components/Terminal/Terminal.js +0 -110
  110. data/webpack/ForemanInventoryUpload/Components/Terminal/TerminalHelper.js +0 -6
  111. data/webpack/ForemanInventoryUpload/Components/Terminal/__tests__/Terminal.test.js +0 -34
  112. data/webpack/ForemanInventoryUpload/Components/Terminal/__tests__/__snapshots__/Terminal.test.js.snap +0 -98
  113. data/webpack/ForemanInventoryUpload/Components/Terminal/index.js +0 -1
  114. data/webpack/ForemanInventoryUpload/Components/Terminal/terminal.scss +0 -32
@@ -28,22 +28,15 @@ class UploadReportDirectJobTest < ActiveSupport::TestCase
28
28
  },
29
29
  }
30
30
  Organization.any_instance.stubs(:owner_details).returns(@cert_data)
31
-
32
- # Clear task output
33
- TaskOutputLine.delete_all
34
- TaskOutputStatus.delete_all
35
31
  end
36
32
 
37
33
  test 'plan sets input correctly' do
38
- action = create_and_plan_action(
39
- ForemanInventoryUpload::Async::UploadReportDirectJob,
40
- @filename,
41
- @organization.id
42
- )
34
+ action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
35
+ action.expects(:action_subject).with(@organization)
36
+ plan_action(action, @filename, @organization.id)
43
37
 
44
38
  assert_equal @filename, action.input[:filename]
45
39
  assert_equal @organization.id, action.input[:organization_id]
46
- assert_equal "upload_for_#{@organization.id}", action.input[:instance_label]
47
40
  end
48
41
 
49
42
  test 'output_label generates correct label' do
@@ -54,11 +47,9 @@ class UploadReportDirectJobTest < ActiveSupport::TestCase
54
47
  test 'uses manifest certificate in regular mode' do
55
48
  ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
56
49
 
57
- action = create_and_plan_action(
58
- ForemanInventoryUpload::Async::UploadReportDirectJob,
59
- @filename,
60
- @organization.id
61
- )
50
+ action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
51
+ action.expects(:action_subject).with(@organization)
52
+ plan_action(action, @filename, @organization.id)
62
53
 
63
54
  cert = action.send(:certificate)
64
55
  assert_equal 'FAKE CERTIFICATE', cert[:cert]
@@ -66,11 +57,9 @@ class UploadReportDirectJobTest < ActiveSupport::TestCase
66
57
  end
67
58
 
68
59
  test 'manifest_certificate extracts from organization owner_details' do
69
- action = create_and_plan_action(
70
- ForemanInventoryUpload::Async::UploadReportDirectJob,
71
- @filename,
72
- @organization.id
73
- )
60
+ action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
61
+ action.expects(:action_subject).with(@organization)
62
+ plan_action(action, @filename, @organization.id)
74
63
 
75
64
  cert = action.send(:manifest_certificate)
76
65
  assert_equal 'FAKE CERTIFICATE', cert[:cert]
@@ -79,14 +68,14 @@ class UploadReportDirectJobTest < ActiveSupport::TestCase
79
68
 
80
69
  test 'uses foreman certificate in IoP mode' do
81
70
  ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
71
+ File.stubs(:readable?).with('/fake/cert.pem').returns(true)
72
+ File.stubs(:readable?).with('/fake/key.pem').returns(true)
82
73
  File.stubs(:read).with('/fake/cert.pem').returns('FOREMAN CERTIFICATE')
83
74
  File.stubs(:read).with('/fake/key.pem').returns('FOREMAN KEY')
84
75
 
85
- action = create_and_plan_action(
86
- ForemanInventoryUpload::Async::UploadReportDirectJob,
87
- @filename,
88
- @organization.id
89
- )
76
+ action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
77
+ action.expects(:action_subject).with(@organization)
78
+ plan_action(action, @filename, @organization.id)
90
79
 
91
80
  cert = action.send(:foreman_certificate)
92
81
  assert_equal 'FOREMAN CERTIFICATE', cert[:cert]
@@ -96,11 +85,9 @@ class UploadReportDirectJobTest < ActiveSupport::TestCase
96
85
  test 'certificate method returns manifest cert in regular mode' do
97
86
  ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
98
87
 
99
- action = create_and_plan_action(
100
- ForemanInventoryUpload::Async::UploadReportDirectJob,
101
- @filename,
102
- @organization.id
103
- )
88
+ action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
89
+ action.expects(:action_subject).with(@organization)
90
+ plan_action(action, @filename, @organization.id)
104
91
 
105
92
  cert = action.send(:certificate)
106
93
  assert_equal 'FAKE CERTIFICATE', cert[:cert]
@@ -109,36 +96,67 @@ class UploadReportDirectJobTest < ActiveSupport::TestCase
109
96
 
110
97
  test 'certificate method returns foreman cert in IoP mode' do
111
98
  ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
99
+ Setting.stubs(:[]).with(:ssl_certificate).returns('/fake/cert.pem')
100
+ Setting.stubs(:[]).with(:ssl_priv_key).returns('/fake/key.pem')
101
+ File.stubs(:readable?).with('/fake/cert.pem').returns(true)
102
+ File.stubs(:readable?).with('/fake/key.pem').returns(true)
112
103
  File.stubs(:read).with('/fake/cert.pem').returns('FOREMAN CERTIFICATE')
113
104
  File.stubs(:read).with('/fake/key.pem').returns('FOREMAN KEY')
114
105
 
115
- action = create_and_plan_action(
116
- ForemanInventoryUpload::Async::UploadReportDirectJob,
117
- @filename,
118
- @organization.id
119
- )
106
+ action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
107
+ action.expects(:action_subject).with(@organization)
108
+ plan_action(action, @filename, @organization.id)
120
109
 
121
110
  cert = action.send(:certificate)
122
111
  assert_equal 'FOREMAN CERTIFICATE', cert[:cert]
123
112
  assert_equal 'FOREMAN KEY', cert[:key]
124
113
  end
125
114
 
115
+ test 'foreman_certificate raises error when certificate file is missing' do
116
+ ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
117
+ Setting.stubs(:[]).with(:ssl_certificate).returns('/nonexistent/cert.pem')
118
+ Setting.stubs(:[]).with(:ssl_priv_key).returns('/fake/key.pem')
119
+ File.stubs(:readable?).with('/nonexistent/cert.pem').returns(false)
120
+
121
+ action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
122
+ action.expects(:action_subject).with(@organization)
123
+ plan_action(action, @filename, @organization.id)
124
+
125
+ error = assert_raises(RuntimeError) do
126
+ action.send(:foreman_certificate)
127
+ end
128
+ assert_match(/SSL certificate file not found or not readable/, error.message)
129
+ end
130
+
131
+ test 'foreman_certificate raises error when private key file is missing' do
132
+ ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
133
+ Setting.stubs(:[]).with(:ssl_certificate).returns('/fake/cert.pem')
134
+ Setting.stubs(:[]).with(:ssl_priv_key).returns('/nonexistent/key.pem')
135
+ File.stubs(:readable?).with('/fake/cert.pem').returns(true)
136
+ File.stubs(:readable?).with('/nonexistent/key.pem').returns(false)
137
+
138
+ action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
139
+ action.expects(:action_subject).with(@organization)
140
+ plan_action(action, @filename, @organization.id)
141
+
142
+ error = assert_raises(RuntimeError) do
143
+ action.send(:foreman_certificate)
144
+ end
145
+ assert_match(/SSL private key file not found or not readable/, error.message)
146
+ end
147
+
126
148
  test 'filename returns input filename' do
127
- action = create_and_plan_action(
128
- ForemanInventoryUpload::Async::UploadReportDirectJob,
129
- @filename,
130
- @organization.id
131
- )
149
+ action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
150
+ action.expects(:action_subject).with(@organization)
151
+ plan_action(action, @filename, @organization.id)
132
152
 
133
153
  assert_equal @filename, action.send(:filename)
134
154
  end
135
155
 
136
156
  test 'organization returns Organization from input organization_id' do
137
- action = create_and_plan_action(
138
- ForemanInventoryUpload::Async::UploadReportDirectJob,
139
- @filename,
140
- @organization.id
141
- )
157
+ action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
158
+ action.expects(:action_subject).with(@organization)
159
+ plan_action(action, @filename, @organization.id)
142
160
 
143
161
  org = action.send(:organization)
144
162
  assert_equal @organization.id, org.id
@@ -147,11 +165,9 @@ class UploadReportDirectJobTest < ActiveSupport::TestCase
147
165
  test 'content_disconnected? returns true when subscription_connection_enabled is false' do
148
166
  Setting.stubs(:[]).with(:subscription_connection_enabled).returns(false)
149
167
 
150
- action = create_and_plan_action(
151
- ForemanInventoryUpload::Async::UploadReportDirectJob,
152
- @filename,
153
- @organization.id
154
- )
168
+ action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
169
+ action.expects(:action_subject).with(@organization)
170
+ plan_action(action, @filename, @organization.id)
155
171
 
156
172
  assert action.send(:content_disconnected?)
157
173
  end
@@ -159,48 +175,28 @@ class UploadReportDirectJobTest < ActiveSupport::TestCase
159
175
  test 'content_disconnected? returns false when subscription_connection_enabled is true' do
160
176
  Setting.stubs(:[]).with(:subscription_connection_enabled).returns(true)
161
177
 
162
- action = create_and_plan_action(
163
- ForemanInventoryUpload::Async::UploadReportDirectJob,
164
- @filename,
165
- @organization.id
166
- )
178
+ action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
179
+ action.expects(:action_subject).with(@organization)
180
+ plan_action(action, @filename, @organization.id)
167
181
 
168
182
  refute action.send(:content_disconnected?)
169
183
  end
170
184
 
171
- test 'instance_label returns label from input' do
172
- action = create_and_plan_action(
173
- ForemanInventoryUpload::Async::UploadReportDirectJob,
174
- @filename,
175
- @organization.id
176
- )
185
+ test 'content_disconnected? returns false when IoP mode is enabled even if subscription_connection_enabled is false' do
186
+ Setting.stubs(:[]).with(:subscription_connection_enabled).returns(false)
187
+ ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
177
188
 
178
- assert_equal "upload_for_#{@organization.id}", action.send(:instance_label)
179
- end
189
+ action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
190
+ action.expects(:action_subject).with(@organization)
191
+ plan_action(action, @filename, @organization.id)
180
192
 
181
- test 'clears previous task output on plan' do
182
- # Create some old output
183
- old_label = ForemanInventoryUpload::Async::UploadReportDirectJob.output_label(@organization.id)
184
- TaskOutputLine.create!(label: old_label, line: 'old line')
185
- TaskOutputStatus.create!(label: old_label, status: 'old status')
186
-
187
- create_and_plan_action(
188
- ForemanInventoryUpload::Async::UploadReportDirectJob,
189
- @filename,
190
- @organization.id
191
- )
192
-
193
- # Verify old output was cleared
194
- assert_equal 0, TaskOutputLine.where(label: old_label).count
195
- assert_equal 0, TaskOutputStatus.where(label: old_label).count
193
+ refute action.send(:content_disconnected?)
196
194
  end
197
195
 
198
196
  test 'rescue_strategy_for_self returns Fail strategy' do
199
- action = create_and_plan_action(
200
- ForemanInventoryUpload::Async::UploadReportDirectJob,
201
- @filename,
202
- @organization.id
203
- )
197
+ action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
198
+ action.expects(:action_subject).with(@organization)
199
+ plan_action(action, @filename, @organization.id)
204
200
 
205
201
  assert_equal Dynflow::Action::Rescue::Fail, action.send(:rescue_strategy_for_self)
206
202
  end
@@ -215,21 +211,14 @@ class UploadReportDirectJobTest < ActiveSupport::TestCase
215
211
  ForemanInventoryUpload::Async::UploadReportDirectJob.any_instance.stubs(:upload_file)
216
212
  .raises(RestClient::InternalServerError.new(response))
217
213
 
218
- action = create_and_plan_action(
219
- ForemanInventoryUpload::Async::UploadReportDirectJob,
220
- @filename,
221
- @organization.id
222
- )
214
+ action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
215
+ action.expects(:action_subject).with(@organization)
216
+ plan_action(action, @filename, @organization.id)
223
217
 
224
218
  # Should raise the error (handled by Dynflow retry mechanism)
225
219
  assert_raises(RestClient::InternalServerError) do
226
220
  action.send(:try_execute)
227
221
  end
228
-
229
- # Verify progress output shows error
230
- label = ForemanInventoryUpload::Async::UploadReportDirectJob.output_label(@organization.id)
231
- output = ForemanInventoryUpload::Async::ProgressOutput.get(label).full_output
232
- assert_match(/Upload failed/, output)
233
222
  end
234
223
 
235
224
  test 'handles RestClient timeout gracefully' do
@@ -237,21 +226,14 @@ class UploadReportDirectJobTest < ActiveSupport::TestCase
237
226
  ForemanInventoryUpload::Async::UploadReportDirectJob.any_instance.stubs(:upload_file)
238
227
  .raises(RestClient::Exceptions::Timeout.new)
239
228
 
240
- action = create_and_plan_action(
241
- ForemanInventoryUpload::Async::UploadReportDirectJob,
242
- @filename,
243
- @organization.id
244
- )
229
+ action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
230
+ action.expects(:action_subject).with(@organization)
231
+ plan_action(action, @filename, @organization.id)
245
232
 
246
233
  # Should raise the error (handled by Dynflow retry mechanism via ExponentialBackoff)
247
234
  assert_raises(RestClient::Exceptions::Timeout) do
248
235
  action.send(:try_execute)
249
236
  end
250
-
251
- # Verify progress output shows error
252
- label = ForemanInventoryUpload::Async::UploadReportDirectJob.output_label(@organization.id)
253
- output = ForemanInventoryUpload::Async::ProgressOutput.get(label).full_output
254
- assert_match(/Upload failed/, output)
255
237
  end
256
238
 
257
239
  test 'uses proxy configuration from ForemanRhCloud' do
@@ -262,11 +244,9 @@ class UploadReportDirectJobTest < ActiveSupport::TestCase
262
244
  FileUtils.mkdir_p(File.dirname(@filename))
263
245
  FileUtils.touch(@filename)
264
246
 
265
- action = create_and_plan_action(
266
- ForemanInventoryUpload::Async::UploadReportDirectJob,
267
- @filename,
268
- @organization.id
269
- )
247
+ action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
248
+ action.expects(:action_subject).with(@organization)
249
+ plan_action(action, @filename, @organization.id)
270
250
 
271
251
  # Mock response
272
252
  response = mock('response')
@@ -296,29 +276,19 @@ class UploadReportDirectJobTest < ActiveSupport::TestCase
296
276
  # Create a real test file to verify it's not moved
297
277
  FileUtils.mkdir_p(@uploads_folder)
298
278
  test_file = File.join(@uploads_folder, 'test_file_for_cleanup.tar.xz')
279
+ FileUtils.mkdir_p(@uploads_folder)
299
280
  FileUtils.touch(test_file)
300
281
 
301
282
  begin
302
- action = create_and_plan_action(
303
- ForemanInventoryUpload::Async::UploadReportDirectJob,
304
- test_file,
305
- @organization.id
306
- )
283
+ action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
284
+ action.expects(:action_subject).with(@organization)
285
+ plan_action(action, test_file, @organization.id)
307
286
 
308
287
  # Execute the action
309
288
  action.send(:try_execute)
310
289
 
311
290
  # Verify file still exists (not moved or deleted)
312
291
  assert File.exist?(test_file), "File should remain when upload is aborted"
313
-
314
- # Verify progress output mentions missing certificate
315
- label = ForemanInventoryUpload::Async::UploadReportDirectJob.output_label(@organization.id)
316
- output = ForemanInventoryUpload::Async::ProgressOutput.get(label).full_output
317
- assert_match(/Skipping organization.*no candlepin certificate/, output)
318
-
319
- # Verify status indicates abortion
320
- status = ForemanInventoryUpload::Async::ProgressOutput.get(label).status
321
- assert_match(/exit 1/, status)
322
292
  ensure
323
293
  FileUtils.rm_f(test_file) if File.exist?(test_file)
324
294
  end
@@ -330,30 +300,60 @@ class UploadReportDirectJobTest < ActiveSupport::TestCase
330
300
  # Create a real test file
331
301
  FileUtils.mkdir_p(@uploads_folder)
332
302
  test_file = File.join(@uploads_folder, 'test_file_disconnected.tar.xz')
303
+ FileUtils.mkdir_p(@uploads_folder)
333
304
  FileUtils.touch(test_file)
334
305
 
335
306
  begin
336
- action = create_and_plan_action(
337
- ForemanInventoryUpload::Async::UploadReportDirectJob,
338
- test_file,
339
- @organization.id
340
- )
307
+ action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
308
+ action.expects(:action_subject).with(@organization)
309
+ plan_action(action, test_file, @organization.id)
341
310
 
342
311
  # Execute the action
343
312
  action.send(:try_execute)
344
313
 
345
314
  # Verify file still exists
346
315
  assert File.exist?(test_file), "File should remain when connection is disabled"
347
-
348
- # Verify progress output
349
- label = ForemanInventoryUpload::Async::UploadReportDirectJob.output_label(@organization.id)
350
- output = ForemanInventoryUpload::Async::ProgressOutput.get(label).full_output
351
- assert_match(/connection to Insights is not enabled/, output)
352
316
  ensure
353
317
  FileUtils.rm_f(test_file) if File.exist?(test_file)
354
318
  end
355
319
  end
356
320
 
321
+ test 'overwrites existing done file and logs warning' do
322
+ # Prepare new report file in uploads folder
323
+ FileUtils.mkdir_p(@uploads_folder)
324
+ source_file = File.join(@uploads_folder, 'overwrite_done_file.tar.xz')
325
+ new_report_content = 'new report content'
326
+ File.write(source_file, new_report_content)
327
+
328
+ # Pre-create done file with old content
329
+ done_file_path = ForemanInventoryUpload.done_file_path(File.basename(source_file))
330
+ FileUtils.mkdir_p(File.dirname(done_file_path))
331
+ old_report_content = 'old report content'
332
+ File.write(done_file_path, old_report_content)
333
+
334
+ action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
335
+ action.expects(:action_subject).with(@organization)
336
+
337
+ # Stub upload_file to avoid actual HTTP upload
338
+ action.stubs(:upload_file).returns(nil)
339
+
340
+ # Expect a warning when overwriting the existing done file
341
+ action.expects(:logger).at_least_once.returns(mock_logger = mock('logger'))
342
+ mock_logger.expects(:warn).with(regexp_matches(/already exists.*overwriting/i))
343
+ mock_logger.stubs(:debug)
344
+
345
+ plan_action(action, source_file, @organization.id)
346
+
347
+ # Execute the action (which will call move_to_done_folder)
348
+ action.send(:try_execute)
349
+
350
+ # Done file should now contain the new report content
351
+ assert_equal new_report_content, File.read(done_file_path)
352
+ ensure
353
+ FileUtils.rm_f(source_file) if defined?(source_file) && source_file
354
+ FileUtils.rm_f(done_file_path) if defined?(done_file_path) && done_file_path
355
+ end
356
+
357
357
  test 'FileUpload wrapper delegates to file object' do
358
358
  # Create test file
359
359
  FileUtils.mkdir_p(File.dirname(@filename))
@@ -9,6 +9,8 @@ class RhCloudPermissionsTest < ActiveSupport::TestCase
9
9
  [
10
10
  'insights_cloud/api/machine_telemetries/forward_request',
11
11
  'insights_cloud/api/machine_telemetries/branch_info',
12
+ 'api/v2/rh_cloud/foreman_inventory_upload/api/tasks/current',
13
+ 'api/v2/rh_cloud/foreman_inventory_upload/api/tasks/history',
12
14
  ],
13
15
  skip_patterns: [/^(?!foreman_inventory_upload|insights_cloud|.*rh_cloud).*/]
14
16
  ) # include only plugin paths
@@ -3,18 +3,18 @@ import { noop } from 'foremanReact/common/helpers';
3
3
  export const accounts = {
4
4
  Account1: {
5
5
  id: 1,
6
- upload_report_status: 'running',
7
- generate_report_status: 'running',
6
+ uploaded_status: 'running',
7
+ generated_status: 'running',
8
8
  },
9
9
  Account2: {
10
10
  id: 2,
11
- upload_report_status: 'unknown',
12
- generate_report_status: 'failure',
11
+ uploaded_status: 'unknown',
12
+ generated_status: 'failure',
13
13
  },
14
14
  Account3: {
15
15
  id: 3,
16
- upload_report_status: 'success',
17
- generate_report_status: 'running',
16
+ uploaded_status: 'success',
17
+ generated_status: 'running',
18
18
  },
19
19
  };
20
20
 
@@ -1,4 +1,4 @@
1
- import React, { Component } from 'react';
1
+ import React, { useEffect } from 'react';
2
2
  import { isEmpty } from 'lodash';
3
3
  import { noop } from 'foremanReact/common/helpers';
4
4
  import { Accordion } from '@patternfly/react-core';
@@ -10,42 +10,57 @@ import EmptyResults from './Components/EmptyResults';
10
10
  import { filterAccounts } from './AccountListHelper';
11
11
  import './accountList.scss';
12
12
 
13
- class AccountList extends Component {
14
- componentDidMount() {
15
- const { fetchAccountsStatus, startAccountStatusPolling } = this.props;
13
+ const AccountList = ({
14
+ accounts,
15
+ error,
16
+ filterTerm,
17
+ fetchAccountsStatus,
18
+ startAccountStatusPolling,
19
+ stopAccountStatusPolling,
20
+ pollingProcessID,
21
+ }) => {
22
+ useEffect(() => {
16
23
  fetchAccountsStatus();
17
- const pollingProcessID = setInterval(fetchAccountsStatus, 5000);
18
- startAccountStatusPolling(pollingProcessID);
19
- }
20
-
21
- componentWillUnmount() {
22
- const { stopAccountStatusPolling, pollingProcessID } = this.props;
23
- stopAccountStatusPolling(pollingProcessID);
24
- }
24
+ const pollingID = setInterval(fetchAccountsStatus, 2000);
25
+ startAccountStatusPolling(pollingID);
25
26
 
26
- render() {
27
- const { accounts, error, filterTerm } = this.props;
28
- const filteredAccount = filterAccounts(accounts, filterTerm);
27
+ return () => {
28
+ stopAccountStatusPolling(pollingID);
29
+ };
30
+ }, [
31
+ fetchAccountsStatus,
32
+ startAccountStatusPolling,
33
+ stopAccountStatusPolling,
34
+ ]);
29
35
 
30
- if (error) {
31
- return <ErrorState error={error} />;
32
- }
36
+ const filteredAccount = filterAccounts(accounts, filterTerm);
33
37
 
34
- if (isEmpty(accounts)) {
35
- return <EmptyState />;
36
- }
38
+ if (error) {
39
+ return <ErrorState error={error} />;
40
+ }
37
41
 
38
- if (isEmpty(filteredAccount)) {
39
- return <EmptyResults />;
40
- }
42
+ if (isEmpty(accounts)) {
43
+ return <EmptyState />;
44
+ }
41
45
 
42
- const items = Object.keys(filteredAccount).map((label, index) => {
43
- const account = filteredAccount[label];
44
- return <ListItem key={index} label={label} account={account} />;
45
- });
46
- return <Accordion className="account-list">{items}</Accordion>;
46
+ if (isEmpty(filteredAccount)) {
47
+ return <EmptyResults />;
47
48
  }
48
- }
49
+
50
+ const items = Object.keys(filteredAccount).map((label, index) => {
51
+ const account = accounts[label];
52
+ return (
53
+ <ListItem
54
+ key={label}
55
+ label={label}
56
+ account={account}
57
+ defaultExpanded={index === 0}
58
+ onTaskStart={fetchAccountsStatus}
59
+ />
60
+ );
61
+ });
62
+ return <Accordion className="account-list">{items}</Accordion>;
63
+ };
49
64
 
50
65
  AccountList.propTypes = {
51
66
  fetchAccountsStatus: PropTypes.func,
@@ -53,8 +68,8 @@ AccountList.propTypes = {
53
68
  stopAccountStatusPolling: PropTypes.func,
54
69
  pollingProcessID: PropTypes.number,
55
70
  account: PropTypes.shape({
56
- generate_report_status: PropTypes.string,
57
- upload_report_status: PropTypes.string,
71
+ generated_status: PropTypes.string,
72
+ uploaded_status: PropTypes.string,
58
73
  }),
59
74
  accounts: PropTypes.object,
60
75
  error: PropTypes.string,
@@ -67,8 +82,8 @@ AccountList.defaultProps = {
67
82
  stopAccountStatusPolling: noop,
68
83
  pollingProcessID: 0,
69
84
  account: {
70
- generate_report_status: 'unknown',
71
- upload_report_status: 'unknown',
85
+ generated_status: 'unknown',
86
+ uploaded_status: 'unknown',
72
87
  },
73
88
  accounts: {},
74
89
  error: '',
@@ -46,10 +46,10 @@ export const restartProcess = (accountID, activeTab) => async dispatch => {
46
46
 
47
47
  if (activeTab === 'uploading') {
48
48
  processController = 'uploads';
49
- processStatusName = 'upload_report_status';
49
+ processStatusName = 'uploaded_status';
50
50
  } else {
51
51
  processController = 'reports';
52
- processStatusName = 'generate_report_status';
52
+ processStatusName = 'generated_status';
53
53
  }
54
54
 
55
55
  try {
@@ -1,8 +1,8 @@
1
1
  export const props = {
2
2
  label: 'test',
3
3
  account: {
4
- generate_report_status: 'unknown',
5
- upload_report_status: 'unknown',
4
+ generated_status: 'unknown',
5
+ uploaded_status: 'unknown',
6
6
  id: 1,
7
7
  report_file_paths: [],
8
8
  },
@@ -10,10 +10,8 @@ import PropTypes from 'prop-types';
10
10
  import ListItemStatus from '../ListItemStatus';
11
11
  import Dashboard from '../../../Dashboard';
12
12
 
13
- const ListItem = ({ label, account = {} }) => {
14
- const [isExpanded, setIsExpanded] = useState(false);
15
- const accountId = account?.id ?? 0;
16
-
13
+ const ListItem = ({ label, account, defaultExpanded, onTaskStart }) => {
14
+ const [isExpanded, setIsExpanded] = useState(defaultExpanded);
17
15
  return (
18
16
  <AccordionItem>
19
17
  <AccordionToggle
@@ -32,7 +30,11 @@ const ListItem = ({ label, account = {} }) => {
32
30
  <ListItemStatus key={`${label}_status`} account={account} />
33
31
  </AccordionToggle>
34
32
  <AccordionContent isHidden={!isExpanded}>
35
- <Dashboard accountID={accountId} account={account} />
33
+ <Dashboard
34
+ accountID={account.id}
35
+ account={account}
36
+ onTaskStart={onTaskStart}
37
+ />
36
38
  </AccordionContent>
37
39
  </AccordionItem>
38
40
  );
@@ -41,18 +43,22 @@ const ListItem = ({ label, account = {} }) => {
41
43
  ListItem.propTypes = {
42
44
  label: PropTypes.string.isRequired,
43
45
  account: PropTypes.shape({
44
- generate_report_status: PropTypes.string,
45
- upload_report_status: PropTypes.string,
46
+ generated_status: PropTypes.string,
47
+ uploaded_status: PropTypes.string,
46
48
  id: PropTypes.number,
47
49
  }),
50
+ defaultExpanded: PropTypes.bool,
51
+ onTaskStart: PropTypes.func,
48
52
  };
49
53
 
50
54
  ListItem.defaultProps = {
51
55
  account: {
52
- generate_report_status: 'unknown',
53
- upload_report_status: 'unknown',
56
+ generated_status: 'unknown',
57
+ uploaded_status: 'unknown',
54
58
  id: 0,
55
59
  },
60
+ defaultExpanded: false,
61
+ onTaskStart: null,
56
62
  };
57
63
 
58
64
  export default ListItem;