foreman_rh_cloud 12.2.16 → 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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/foreman_rh_cloud/locale/fr/foreman_rh_cloud.js +24 -78
- data/app/assets/javascripts/foreman_rh_cloud/locale/ja/foreman_rh_cloud.js +24 -78
- data/app/assets/javascripts/foreman_rh_cloud/locale/ka/foreman_rh_cloud.js +23 -77
- data/app/assets/javascripts/foreman_rh_cloud/locale/ko/foreman_rh_cloud.js +23 -77
- data/app/assets/javascripts/foreman_rh_cloud/locale/zh_CN/foreman_rh_cloud.js +23 -77
- data/app/controllers/concerns/insights_cloud/package_profile_upload_extensions.rb +3 -5
- data/app/controllers/foreman_inventory_upload/accounts_controller.rb +1 -1
- data/app/controllers/foreman_inventory_upload/uploads_controller.rb +1 -1
- data/app/controllers/insights_cloud/ui_requests_controller.rb +3 -2
- data/app/models/concerns/rh_cloud_host.rb +0 -14
- data/app/models/foreman_rh_cloud/ping.rb +1 -2
- data/app/models/insights_hit.rb +1 -1
- data/app/services/foreman_rh_cloud/cert_auth.rb +3 -13
- data/app/services/foreman_rh_cloud/gateway_request.rb +26 -0
- data/app/services/foreman_rh_cloud/insights_api_forwarder.rb +1 -3
- data/app/services/foreman_rh_cloud/tags_auth.rb +4 -15
- data/app/views/api/v2/advisor_engine/host_details.json.rabl +3 -1
- data/app/views/api/v2/hosts/insights/base.rabl +2 -3
- data/lib/foreman_inventory_upload/async/generate_report_job.rb +8 -13
- data/lib/foreman_inventory_upload/async/queue_for_upload_job.rb +26 -4
- data/lib/foreman_inventory_upload/async/upload_report_job.rb +96 -0
- data/lib/foreman_inventory_upload/generators/fact_helpers.rb +2 -2
- data/lib/foreman_inventory_upload/generators/slice.rb +3 -3
- data/lib/foreman_inventory_upload/scripts/uploader.sh.erb +49 -0
- data/lib/foreman_inventory_upload.rb +6 -6
- data/lib/foreman_rh_cloud/engine.rb +15 -34
- data/lib/foreman_rh_cloud/plugin.rb +13 -20
- data/lib/foreman_rh_cloud/version.rb +1 -1
- data/lib/foreman_rh_cloud.rb +3 -3
- data/lib/insights_cloud/async/connector_playbook_execution_reporter_task.rb +3 -3
- data/lib/inventory_sync/async/inventory_hosts_sync.rb +2 -0
- data/lib/tasks/rh_cloud_inventory.rake +31 -14
- data/locale/foreman_rh_cloud.pot +157 -261
- data/locale/fr/LC_MESSAGES/foreman_rh_cloud.mo +0 -0
- data/locale/fr/foreman_rh_cloud.po +26 -79
- data/locale/ja/LC_MESSAGES/foreman_rh_cloud.mo +0 -0
- data/locale/ja/foreman_rh_cloud.po +26 -79
- data/locale/ka/LC_MESSAGES/foreman_rh_cloud.mo +0 -0
- data/locale/ka/foreman_rh_cloud.po +24 -77
- data/locale/ko/LC_MESSAGES/foreman_rh_cloud.mo +0 -0
- data/locale/ko/foreman_rh_cloud.po +25 -78
- data/locale/zh_CN/LC_MESSAGES/foreman_rh_cloud.mo +0 -0
- data/locale/zh_CN/foreman_rh_cloud.po +25 -78
- data/package.json +1 -1
- data/test/controllers/accounts_controller_test.rb +1 -1
- data/test/controllers/insights_cloud/api/advisor_engine_controller_test.rb +1 -28
- data/test/controllers/insights_cloud/ui_requests_controller_test.rb +0 -26
- data/test/controllers/uploads_controller_test.rb +1 -1
- data/test/factories/insights_factories.rb +0 -29
- data/test/jobs/cloud_connector_announce_task_test.rb +2 -3
- data/test/jobs/connector_playbook_execution_reporter_task_test.rb +20 -32
- data/test/jobs/exponential_backoff_test.rb +8 -9
- data/test/jobs/insights_client_status_aging_test.rb +2 -3
- data/test/jobs/insights_full_sync_test.rb +7 -13
- data/test/jobs/insights_resolutions_sync_test.rb +5 -9
- data/test/jobs/insights_rules_sync_test.rb +3 -5
- data/test/jobs/inventory_full_sync_test.rb +5 -9
- data/test/jobs/inventory_hosts_sync_test.rb +6 -11
- data/test/jobs/inventory_scheduled_sync_test.rb +6 -10
- data/test/jobs/inventory_self_host_sync_test.rb +1 -1
- data/test/jobs/remove_insights_hosts_job_test.rb +15 -14
- data/test/jobs/upload_report_job_test.rb +36 -0
- data/test/unit/fact_helpers_test.rb +0 -47
- data/test/unit/services/foreman_rh_cloud/tags_auth_test.rb +0 -14
- data/test/unit/slice_generator_test.rb +0 -57
- data/webpack/ForemanColumnExtensions/index.js +0 -2
- data/webpack/ForemanInventoryUpload/Components/PageHeader/PageHeader.js +17 -24
- data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/PageHeader.test.js +8 -178
- data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/__snapshots__/PageHeader.test.js.snap +36 -0
- data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/__snapshots__/PageTitle.test.js.snap +1 -1
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/ToolbarButtons/ToolbarButtons.js +1 -3
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/ToolbarButtons/__tests__/ToolbarButtons.test.js +51 -69
- data/webpack/ForemanInventoryUpload/ForemanInventoryHelpers.js +1 -1
- data/webpack/ForemanInventoryUpload/__tests__/__snapshots__/ForemanInventoryHelpers.test.js.snap +1 -1
- data/webpack/ForemanRhCloudFills.js +2 -6
- data/webpack/ForemanRhCloudHelpers.js +0 -4
- data/webpack/InsightsCloudSync/Components/InsightsSettings/InsightsSettings.js +3 -3
- data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTable.js +9 -3
- data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTable.test.js +4 -24
- data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTable.test.js.snap +112 -0
- data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationModal.js +2 -2
- data/webpack/InsightsCloudSync/Components/ToolbarDropdown.js +3 -3
- data/webpack/InsightsCloudSync/InsightsCloudSync.js +3 -3
- data/webpack/InsightsCloudSync/InsightsCloudSync.test.js +0 -10
- data/webpack/InsightsCloudSync/__snapshots__/InsightsCloudSync.test.js.snap +1 -1
- data/webpack/InsightsHostDetailsTab/InsightsTab.scss +0 -4
- data/webpack/InsightsHostDetailsTab/InsightsTotalRiskChart.js +23 -59
- data/webpack/InsightsHostDetailsTab/NewHostDetailsTab.js +19 -6
- data/webpack/InsightsVulnerabilityHostIndexExtensions/CVECountCell.js +2 -8
- data/webpack/InsightsVulnerabilityHostIndexExtensions/__tests__/CVECountCell.test.js +4 -105
- data/webpack/__mocks__/foremanReact/common/hooks/API/APIHooks.js +3 -0
- data/webpack/__tests__/ForemanRhCloudHelpers.test.js +1 -16
- data/webpack/__tests__/__snapshots__/ForemanRhCloudHelpers.test.js.snap +0 -6
- data/webpack/common/Hooks/ConfigHooks.js +16 -3
- metadata +12 -31
- data/app/controllers/concerns/foreman_rh_cloud/registration_manager_extensions.rb +0 -39
- data/lib/foreman_inventory_upload/async/create_missing_insights_facets.rb +0 -30
- data/lib/foreman_inventory_upload/async/generate_host_report.rb +0 -20
- data/lib/foreman_inventory_upload/async/host_inventory_report_job.rb +0 -39
- data/lib/foreman_inventory_upload/async/single_host_report_job.rb +0 -20
- data/lib/foreman_inventory_upload/async/upload_report_direct_job.rb +0 -200
- data/test/jobs/create_missing_insights_facets_test.rb +0 -151
- data/test/jobs/generate_host_report_test.rb +0 -100
- data/test/jobs/generate_report_job_test.rb +0 -146
- data/test/jobs/host_inventory_report_job_test.rb +0 -244
- data/test/jobs/queue_for_upload_job_test.rb +0 -54
- data/test/jobs/single_host_report_job_test.rb +0 -155
- data/test/jobs/upload_report_direct_job_test.rb +0 -399
- data/test/unit/foreman_rh_cloud_iop_metadata_test.rb +0 -200
- data/test/unit/lib/foreman_rh_cloud/registration_manager_extensions_test.rb +0 -154
- data/test/unit/rh_cloud_host_test.rb +0 -191
- data/webpack/InsightsHostDetailsTab/__tests__/InsightsTotalRiskChart.test.js +0 -194
|
@@ -8,12 +8,12 @@ import {
|
|
|
8
8
|
} from '@patternfly/react-core/deprecated';
|
|
9
9
|
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
|
|
10
10
|
import { redHatAdvisorSystems } from '../InsightsCloudSyncHelpers';
|
|
11
|
-
import {
|
|
11
|
+
import { useAdvisorEngineConfig } from '../../common/Hooks/ConfigHooks';
|
|
12
12
|
|
|
13
13
|
const ToolbarDropdown = ({ onRecommendationSync }) => {
|
|
14
14
|
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
|
15
|
-
const
|
|
16
|
-
if (
|
|
15
|
+
const isLocalAdvisorEngine = useAdvisorEngineConfig();
|
|
16
|
+
if (isLocalAdvisorEngine) {
|
|
17
17
|
return null;
|
|
18
18
|
}
|
|
19
19
|
const dropdownItems = [
|
|
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
|
|
3
3
|
import PageLayout from 'foremanReact/routes/common/PageLayout/PageLayout';
|
|
4
4
|
import { ScalprumComponent, ScalprumProvider } from '@scalprum/react-core';
|
|
5
5
|
import InsightsTable from './Components/InsightsTable';
|
|
6
|
-
import {
|
|
6
|
+
import { useAdvisorEngineConfig } from '../common/Hooks/ConfigHooks';
|
|
7
7
|
import { foremanUrl } from '../ForemanRhCloudHelpers';
|
|
8
8
|
import RemediationModal from './Components/RemediationModal';
|
|
9
9
|
import {
|
|
@@ -85,9 +85,9 @@ const IopRecommendationsPageWrapped = props => (
|
|
|
85
85
|
);
|
|
86
86
|
|
|
87
87
|
const RecommendationsPage = props => {
|
|
88
|
-
const
|
|
88
|
+
const isLocalAdvisorEngine = useAdvisorEngineConfig();
|
|
89
89
|
|
|
90
|
-
return
|
|
90
|
+
return isLocalAdvisorEngine ? (
|
|
91
91
|
<IopRecommendationsPageWrapped {...props} />
|
|
92
92
|
) : (
|
|
93
93
|
<InsightsCloudSync {...props} />
|
|
@@ -2,16 +2,6 @@ import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
|
|
|
2
2
|
import { noop } from 'foremanReact/common/helpers';
|
|
3
3
|
import InsightsCloudSync from './InsightsCloudSync';
|
|
4
4
|
|
|
5
|
-
jest.mock('foremanReact/Root/Context/ForemanContext', () => ({
|
|
6
|
-
useForemanContext: () => ({
|
|
7
|
-
metadata: {
|
|
8
|
-
foreman_rh_cloud: {
|
|
9
|
-
iop: true,
|
|
10
|
-
},
|
|
11
|
-
},
|
|
12
|
-
}),
|
|
13
|
-
}));
|
|
14
|
-
|
|
15
5
|
const fixtures = {
|
|
16
6
|
render: {
|
|
17
7
|
status: 'RESOLVED',
|
|
@@ -3,7 +3,8 @@ import PropTypes from 'prop-types';
|
|
|
3
3
|
import { useDispatch } from 'react-redux';
|
|
4
4
|
import { push } from 'connected-react-router';
|
|
5
5
|
import { useHistory } from 'react-router-dom';
|
|
6
|
-
import { Bullseye,
|
|
6
|
+
import { Bullseye, Title } from '@patternfly/react-core';
|
|
7
|
+
import { DropdownItem } from '@patternfly/react-core/deprecated';
|
|
7
8
|
import {
|
|
8
9
|
ChartDonut,
|
|
9
10
|
ChartLegend,
|
|
@@ -18,67 +19,36 @@ import SkeletonLoader from 'foremanReact/components/common/SkeletonLoader';
|
|
|
18
19
|
import { insightsCloudUrl } from '../InsightsCloudSync/InsightsCloudSyncHelpers';
|
|
19
20
|
import { getInitialRisks, theme } from './InsightsTabConstants';
|
|
20
21
|
|
|
21
|
-
const InsightsTotalRiskCard = ({ hostDetails }) => {
|
|
22
|
-
const { id, insights_attributes: insightsFacet } = hostDetails;
|
|
23
|
-
const uuid = insightsFacet?.uuid;
|
|
24
|
-
// eslint-disable-next-line camelcase
|
|
25
|
-
const isIop = insightsFacet?.use_iop_mode;
|
|
22
|
+
const InsightsTotalRiskCard = ({ hostDetails: { id } }) => {
|
|
26
23
|
const [totalRisks, setTotalRisks] = useState(getInitialRisks());
|
|
27
24
|
const hashHistory = useHistory();
|
|
28
25
|
const dispatch = useDispatch();
|
|
29
26
|
const API_KEY = `HOST_${id}_RECOMMENDATIONS`;
|
|
30
27
|
const API_OPTIONS = useMemo(() => ({ key: API_KEY }), [API_KEY]);
|
|
28
|
+
const url = id && insightsCloudUrl(`hits/${id}`); // This will keep the API call from being triggered if there's no host id.
|
|
29
|
+
const {
|
|
30
|
+
status = STATUS.PENDING,
|
|
31
|
+
response: { hits = [] },
|
|
32
|
+
} = useAPI('get', url, API_OPTIONS);
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
: id && insightsCloudUrl(`hits/${id}`);
|
|
36
|
-
const { status = STATUS.PENDING, response } = useAPI('get', url, API_OPTIONS);
|
|
37
|
-
|
|
38
|
-
const checkRisks = useMemo(() => {
|
|
39
|
-
if (!response || status !== STATUS.RESOLVED) {
|
|
40
|
-
return getInitialRisks();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const risks = getInitialRisks();
|
|
44
|
-
if (isIop) {
|
|
45
|
-
const {
|
|
46
|
-
low_hits: lowHits = 0,
|
|
47
|
-
moderate_hits: moderateHits = 0,
|
|
48
|
-
important_hits: importantHits = 0,
|
|
49
|
-
critical_hits: criticalHits = 0,
|
|
50
|
-
hits = 0,
|
|
51
|
-
} = response;
|
|
52
|
-
|
|
53
|
-
risks[1].value += lowHits;
|
|
54
|
-
risks[2].value += moderateHits;
|
|
55
|
-
risks[3].value += importantHits;
|
|
56
|
-
risks[4].value += criticalHits;
|
|
57
|
-
risks.total = hits;
|
|
58
|
-
} else {
|
|
59
|
-
const { hits = [] } = response;
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (status === STATUS.RESOLVED) {
|
|
36
|
+
const risks = getInitialRisks();
|
|
60
37
|
hits.forEach(({ total_risk: risk }) => {
|
|
61
38
|
risks[risk].value += 1;
|
|
62
39
|
});
|
|
63
40
|
risks.total = hits.length;
|
|
41
|
+
setTotalRisks(risks);
|
|
64
42
|
}
|
|
65
|
-
|
|
66
|
-
}, [response, status, isIop]);
|
|
67
|
-
|
|
68
|
-
useEffect(() => {
|
|
69
|
-
setTotalRisks(checkRisks);
|
|
70
|
-
}, [checkRisks]);
|
|
71
|
-
|
|
72
|
-
if (!insightsFacet) return null;
|
|
43
|
+
}, [hits, status]);
|
|
73
44
|
|
|
74
45
|
const onChartClick = (evt, { index }) => {
|
|
75
46
|
hashHistory.push(`/Insights`);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
);
|
|
47
|
+
dispatch(
|
|
48
|
+
push({
|
|
49
|
+
search: `search=total_risk+%3D+${index + 1}`,
|
|
50
|
+
})
|
|
51
|
+
);
|
|
82
52
|
};
|
|
83
53
|
|
|
84
54
|
const onChartHover = (evt, { index }) => [
|
|
@@ -92,16 +62,11 @@ const InsightsTotalRiskCard = ({ hostDetails }) => {
|
|
|
92
62
|
const { 1: low, 2: moderate, 3: important, 4: critical, total } = totalRisks;
|
|
93
63
|
|
|
94
64
|
// eslint-disable-next-line react/prop-types
|
|
95
|
-
const LegendLabel = ({ index, ...rest }) =>
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
<a key={index} onClick={() => onChartClick(null, { index })}>
|
|
101
|
-
<ChartLabel {...rest} />
|
|
102
|
-
</a>
|
|
103
|
-
);
|
|
104
|
-
};
|
|
65
|
+
const LegendLabel = ({ index, ...rest }) => (
|
|
66
|
+
<a key={index} onClick={() => onChartClick(null, { index })}>
|
|
67
|
+
<ChartLabel {...rest} />
|
|
68
|
+
</a>
|
|
69
|
+
);
|
|
105
70
|
|
|
106
71
|
const legend = (
|
|
107
72
|
<ChartLegend
|
|
@@ -162,7 +127,6 @@ const InsightsTotalRiskCard = ({ hostDetails }) => {
|
|
|
162
127
|
return (
|
|
163
128
|
<CardTemplate
|
|
164
129
|
header={__('Total risks')}
|
|
165
|
-
overrideDropdownProps={{ id: 'total-risks-dropdown-container' }}
|
|
166
130
|
dropdownItems={[
|
|
167
131
|
<DropdownItem
|
|
168
132
|
key="insights-tab"
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
selectHits,
|
|
22
22
|
} from '../InsightsCloudSync/Components/InsightsTable/InsightsTableSelectors';
|
|
23
23
|
import { redHatAdvisorSystems } from '../InsightsCloudSync/InsightsCloudSyncHelpers';
|
|
24
|
-
import {
|
|
24
|
+
import { useAdvisorEngineConfig } from '../common/Hooks/ConfigHooks';
|
|
25
25
|
import { generateRuleUrl } from '../InsightsCloudSync/InsightsCloudSync';
|
|
26
26
|
import { providerOptions } from '../common/ScalprumModule/ScalprumContext';
|
|
27
27
|
|
|
@@ -30,7 +30,7 @@ const NewHostDetailsTab = ({ hostName, router }) => {
|
|
|
30
30
|
const dispatch = useDispatch();
|
|
31
31
|
const query = useSelector(selectSearch);
|
|
32
32
|
const hits = useSelector(selectHits);
|
|
33
|
-
const
|
|
33
|
+
const isLocalAdvisorEngine = useAdvisorEngineConfig();
|
|
34
34
|
|
|
35
35
|
useEffect(() => () => router.replace({ search: null }), [router]);
|
|
36
36
|
|
|
@@ -46,7 +46,7 @@ const NewHostDetailsTab = ({ hostName, router }) => {
|
|
|
46
46
|
</DropdownItem>,
|
|
47
47
|
];
|
|
48
48
|
|
|
49
|
-
if (hits.length && !
|
|
49
|
+
if (hits.length && !isLocalAdvisorEngine) {
|
|
50
50
|
const { host_uuid: uuid } = hits[0];
|
|
51
51
|
dropdownItems.push(
|
|
52
52
|
<DropdownItem key="insights-advisor-link" ouiaId="insights-advisor-link">
|
|
@@ -131,15 +131,28 @@ const IopInsightsTabWrapped = props => (
|
|
|
131
131
|
);
|
|
132
132
|
|
|
133
133
|
const InsightsTab = props => {
|
|
134
|
-
const
|
|
134
|
+
const { response } = props;
|
|
135
|
+
const isLocalAdvisorEngine =
|
|
136
|
+
// eslint-disable-next-line camelcase
|
|
137
|
+
response?.insights_attributes?.use_iop_mode;
|
|
135
138
|
|
|
136
|
-
return
|
|
139
|
+
return isLocalAdvisorEngine ? (
|
|
137
140
|
<IopInsightsTabWrapped {...props} />
|
|
138
141
|
) : (
|
|
139
142
|
<NewHostDetailsTab {...props} />
|
|
140
143
|
);
|
|
141
144
|
};
|
|
142
145
|
|
|
143
|
-
InsightsTab.
|
|
146
|
+
InsightsTab.propTypes = {
|
|
147
|
+
response: PropTypes.shape({
|
|
148
|
+
insights_attributes: {
|
|
149
|
+
use_iop_mode: PropTypes.bool,
|
|
150
|
+
},
|
|
151
|
+
}),
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
InsightsTab.defaultProps = {
|
|
155
|
+
response: {},
|
|
156
|
+
};
|
|
144
157
|
|
|
145
158
|
export default InsightsTab;
|
|
@@ -3,31 +3,25 @@ import PropTypes from 'prop-types';
|
|
|
3
3
|
import { UnknownIcon } from '@patternfly/react-icons';
|
|
4
4
|
import { Link } from 'react-router-dom';
|
|
5
5
|
import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
|
|
6
|
+
|
|
6
7
|
import { insightsCloudUrl } from '../InsightsCloudSync/InsightsCloudSyncHelpers';
|
|
7
|
-
import { useIopConfig } from '../common/Hooks/ConfigHooks';
|
|
8
8
|
|
|
9
9
|
const vulnerabilityApiPath = path =>
|
|
10
10
|
insightsCloudUrl(`api/vulnerability/v1/${path}`);
|
|
11
11
|
|
|
12
12
|
export const CVECountCell = ({ hostDetails }) => {
|
|
13
|
-
const isIopEnabled = useIopConfig();
|
|
14
|
-
|
|
15
13
|
// eslint-disable-next-line camelcase
|
|
16
14
|
const uuid = hostDetails?.subscription_facet_attributes?.uuid;
|
|
17
15
|
|
|
18
16
|
const key = `HOST_CVE_COUNT_${uuid}`;
|
|
19
17
|
const response = useAPI(
|
|
20
|
-
|
|
18
|
+
uuid ? 'get' : null,
|
|
21
19
|
vulnerabilityApiPath(`systems?uuid=${uuid}`),
|
|
22
20
|
{
|
|
23
21
|
key,
|
|
24
22
|
}
|
|
25
23
|
);
|
|
26
24
|
|
|
27
|
-
if (!isIopEnabled) {
|
|
28
|
-
return <UnknownIcon />;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
25
|
if (uuid === undefined) {
|
|
32
26
|
return <UnknownIcon />;
|
|
33
27
|
}
|
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { screen } from '@testing-library/react';
|
|
3
|
-
import { rtlHelpers } from 'foremanReact/common/rtlTestHelpers';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
4
3
|
import { API } from 'foremanReact/redux/API';
|
|
5
4
|
import { CVECountCell } from '../CVECountCell';
|
|
6
5
|
|
|
7
6
|
jest.mock('foremanReact/redux/API');
|
|
8
|
-
jest.mock('../../common/Hooks/ConfigHooks');
|
|
9
|
-
|
|
10
|
-
const { renderWithStore } = rtlHelpers;
|
|
11
|
-
|
|
12
7
|
API.get.mockImplementation(async () => ({
|
|
13
8
|
data: [
|
|
14
9
|
{
|
|
@@ -19,111 +14,15 @@ API.get.mockImplementation(async () => ({
|
|
|
19
14
|
],
|
|
20
15
|
}));
|
|
21
16
|
|
|
22
|
-
const hostDetailsMock = {
|
|
23
|
-
name: 'test-host.example.com',
|
|
24
|
-
subscription_facet_attributes: {
|
|
25
|
-
uuid: 'test-uuid-123',
|
|
26
|
-
},
|
|
27
|
-
};
|
|
28
|
-
|
|
29
17
|
describe('CVECountCell', () => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('renders an empty cves count column when no subscription UUID', () => {
|
|
35
|
-
const hostDetailsMockIoP = {
|
|
18
|
+
it('renders an empty cves count column', () => {
|
|
19
|
+
const hostDetailsMock = {
|
|
36
20
|
name: 'test-host.example.com',
|
|
37
21
|
subscription_facet_attributes: {
|
|
38
22
|
uuid: null, // no subscription
|
|
39
23
|
},
|
|
40
24
|
};
|
|
41
|
-
|
|
42
|
-
router: {
|
|
43
|
-
location: {
|
|
44
|
-
pathname: '/',
|
|
45
|
-
search: '',
|
|
46
|
-
hash: '',
|
|
47
|
-
query: {},
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
API: {
|
|
51
|
-
ADVISOR_ENGINE_CONFIG: {
|
|
52
|
-
response: { use_iop_mode: true },
|
|
53
|
-
status: 'RESOLVED',
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
expect(screen.getByRole('img', { hidden: true })).toBeTruthy();
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('renders UnknownIcon when IoP is not enabled', () => {
|
|
61
|
-
renderWithStore(<CVECountCell hostDetails={hostDetailsMock} />, {
|
|
62
|
-
router: {
|
|
63
|
-
location: {
|
|
64
|
-
pathname: '/',
|
|
65
|
-
search: '',
|
|
66
|
-
hash: '',
|
|
67
|
-
query: {},
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
API: {
|
|
71
|
-
ADVISOR_ENGINE_CONFIG: {
|
|
72
|
-
response: { use_iop_mode: false },
|
|
73
|
-
status: 'RESOLVED',
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
});
|
|
77
|
-
expect(screen.getByRole('img', { hidden: true })).toBeTruthy();
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('renders UnknownIcon when IoP is enabled but CVE API call fails', () => {
|
|
81
|
-
// Mock CVE API failure - override the global mock for this test
|
|
82
|
-
API.get.mockImplementationOnce(async () => {
|
|
83
|
-
throw new Error('CVE API call failed');
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
renderWithStore(<CVECountCell hostDetails={hostDetailsMock} />, {
|
|
87
|
-
router: {
|
|
88
|
-
location: {
|
|
89
|
-
pathname: '/',
|
|
90
|
-
search: '',
|
|
91
|
-
hash: '',
|
|
92
|
-
query: {},
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
API: {
|
|
96
|
-
ADVISOR_ENGINE_CONFIG: {
|
|
97
|
-
response: { use_iop_mode: true },
|
|
98
|
-
status: 'RESOLVED',
|
|
99
|
-
},
|
|
100
|
-
// Mock the API failure state for the CVE endpoint
|
|
101
|
-
[`HOST_CVE_COUNT_${hostDetailsMock.subscription_facet_attributes.uuid}`]: {
|
|
102
|
-
status: 'ERROR',
|
|
103
|
-
error: 'CVE API call failed',
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
});
|
|
107
|
-
// Should render UnknownIcon when CVE API fails
|
|
108
|
-
expect(screen.getByRole('img', { hidden: true })).toBeTruthy();
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('renders UnknownIcon when IoP is undefined (API call pending)', () => {
|
|
112
|
-
renderWithStore(<CVECountCell hostDetails={hostDetailsMock} />, {
|
|
113
|
-
router: {
|
|
114
|
-
location: {
|
|
115
|
-
pathname: '/',
|
|
116
|
-
search: '',
|
|
117
|
-
hash: '',
|
|
118
|
-
query: {},
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
|
-
API: {
|
|
122
|
-
ADVISOR_ENGINE_CONFIG: {
|
|
123
|
-
status: 'PENDING',
|
|
124
|
-
},
|
|
125
|
-
},
|
|
126
|
-
});
|
|
25
|
+
render(<CVECountCell hostDetails={hostDetailsMock} />);
|
|
127
26
|
expect(screen.getByRole('img', { hidden: true })).toBeTruthy();
|
|
128
27
|
});
|
|
129
28
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { testSelectorsSnapshotWithFixtures } from '@theforeman/test';
|
|
2
|
-
import { foremanUrl, vulnerabilityDisabled
|
|
2
|
+
import { foremanUrl, vulnerabilityDisabled } from '../ForemanRhCloudHelpers';
|
|
3
3
|
|
|
4
4
|
global.URL_PREFIX = 'MY_TEST_URL_PREFIX.example.com';
|
|
5
5
|
|
|
@@ -34,21 +34,6 @@ const fixtures = {
|
|
|
34
34
|
}),
|
|
35
35
|
'vulnerabilityDisabled returns true for missing hostDetails': () =>
|
|
36
36
|
vulnerabilityDisabled({}),
|
|
37
|
-
'hasNoInsightsFacet returns false when insights_attributes is present': () =>
|
|
38
|
-
hasNoInsightsFacet({
|
|
39
|
-
response: {
|
|
40
|
-
insights_attributes: {
|
|
41
|
-
uuid: 'test-uuid',
|
|
42
|
-
insights_hits_count: 5,
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
}),
|
|
46
|
-
'hasNoInsightsFacet returns true when insights_attributes is missing': () =>
|
|
47
|
-
hasNoInsightsFacet({
|
|
48
|
-
response: {},
|
|
49
|
-
}),
|
|
50
|
-
'hasNoInsightsFacet returns true when response is missing': () =>
|
|
51
|
-
hasNoInsightsFacet({}),
|
|
52
37
|
};
|
|
53
38
|
|
|
54
39
|
describe('ForemanRhCloud helpers', () =>
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
|
-
exports[`ForemanRhCloud helpers hasNoInsightsFacet returns false when insights_attributes is present 1`] = `false`;
|
|
4
|
-
|
|
5
|
-
exports[`ForemanRhCloud helpers hasNoInsightsFacet returns true when insights_attributes is missing 1`] = `true`;
|
|
6
|
-
|
|
7
|
-
exports[`ForemanRhCloud helpers hasNoInsightsFacet returns true when response is missing 1`] = `true`;
|
|
8
|
-
|
|
9
3
|
exports[`ForemanRhCloud helpers should return foreman Url 1`] = `"MY_TEST_URL_PREFIX.example.com/test_path"`;
|
|
10
4
|
|
|
11
5
|
exports[`ForemanRhCloud helpers vulnerabilityDisabled returns false for RHEL host with vulnerability enabled 1`] = `false`;
|
|
@@ -1,5 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
|
|
2
|
+
import {
|
|
3
|
+
ADVISOR_ENGINE_CONFIG_KEY,
|
|
4
|
+
ADVISOR_ENGINE_CONFIG_PATH,
|
|
5
|
+
} from '../../InsightsCloudSync/Components/InsightsTable/InsightsTableConstants';
|
|
6
|
+
|
|
7
|
+
export const useAdvisorEngineConfig = () => {
|
|
8
|
+
const { response: advisorEngineConfig } = useAPI(
|
|
9
|
+
'get',
|
|
10
|
+
ADVISOR_ENGINE_CONFIG_PATH,
|
|
11
|
+
{
|
|
12
|
+
key: ADVISOR_ENGINE_CONFIG_KEY,
|
|
13
|
+
}
|
|
14
|
+
);
|
|
2
15
|
|
|
3
|
-
export const useIopConfig = () =>
|
|
4
16
|
// eslint-disable-next-line camelcase
|
|
5
|
-
|
|
17
|
+
return advisorEngineConfig?.use_iop_mode;
|
|
18
|
+
};
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: foreman_rh_cloud
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 13.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Foreman Red Hat Cloud team
|
|
@@ -43,14 +43,14 @@ dependencies:
|
|
|
43
43
|
requirements:
|
|
44
44
|
- - ">="
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version:
|
|
46
|
+
version: 4.14.0.rc1.1
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - ">="
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version:
|
|
53
|
+
version: 4.14.0.rc1.1
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
55
|
name: rdoc
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -99,7 +99,6 @@ files:
|
|
|
99
99
|
- app/controllers/api/v2/rh_cloud/cloud_request_controller.rb
|
|
100
100
|
- app/controllers/api/v2/rh_cloud/inventory_controller.rb
|
|
101
101
|
- app/controllers/concerns/foreman_rh_cloud/iop_smart_proxy_access.rb
|
|
102
|
-
- app/controllers/concerns/foreman_rh_cloud/registration_manager_extensions.rb
|
|
103
102
|
- app/controllers/concerns/insights_cloud/candlepin_cache.rb
|
|
104
103
|
- app/controllers/concerns/insights_cloud/client_authentication.rb
|
|
105
104
|
- app/controllers/concerns/insights_cloud/package_profile_upload_extensions.rb
|
|
@@ -140,6 +139,7 @@ files:
|
|
|
140
139
|
- app/services/foreman_rh_cloud/cloud_presence.rb
|
|
141
140
|
- app/services/foreman_rh_cloud/cloud_request.rb
|
|
142
141
|
- app/services/foreman_rh_cloud/cloud_request_forwarder.rb
|
|
142
|
+
- app/services/foreman_rh_cloud/gateway_request.rb
|
|
143
143
|
- app/services/foreman_rh_cloud/hit_remediations_retriever.rb
|
|
144
144
|
- app/services/foreman_rh_cloud/hits_uploader.rb
|
|
145
145
|
- app/services/foreman_rh_cloud/insights_api_forwarder.rb
|
|
@@ -185,18 +185,14 @@ files:
|
|
|
185
185
|
- db/seeds.d/50_job_templates.rb
|
|
186
186
|
- lib/foreman_inventory_upload.rb
|
|
187
187
|
- lib/foreman_inventory_upload/async/async_helpers.rb
|
|
188
|
-
- lib/foreman_inventory_upload/async/create_missing_insights_facets.rb
|
|
189
188
|
- lib/foreman_inventory_upload/async/delayed_start.rb
|
|
190
189
|
- lib/foreman_inventory_upload/async/generate_all_reports_job.rb
|
|
191
|
-
- lib/foreman_inventory_upload/async/generate_host_report.rb
|
|
192
190
|
- lib/foreman_inventory_upload/async/generate_report_job.rb
|
|
193
|
-
- lib/foreman_inventory_upload/async/host_inventory_report_job.rb
|
|
194
191
|
- lib/foreman_inventory_upload/async/progress_output.rb
|
|
195
192
|
- lib/foreman_inventory_upload/async/queue_for_upload_job.rb
|
|
196
193
|
- lib/foreman_inventory_upload/async/remove_insights_hosts_job.rb
|
|
197
194
|
- lib/foreman_inventory_upload/async/shell_process.rb
|
|
198
|
-
- lib/foreman_inventory_upload/async/
|
|
199
|
-
- lib/foreman_inventory_upload/async/upload_report_direct_job.rb
|
|
195
|
+
- lib/foreman_inventory_upload/async/upload_report_job.rb
|
|
200
196
|
- lib/foreman_inventory_upload/generators/archived_report.rb
|
|
201
197
|
- lib/foreman_inventory_upload/generators/fact_helpers.rb
|
|
202
198
|
- lib/foreman_inventory_upload/generators/json_stream.rb
|
|
@@ -205,6 +201,7 @@ files:
|
|
|
205
201
|
- lib/foreman_inventory_upload/generators/slice.rb
|
|
206
202
|
- lib/foreman_inventory_upload/generators/tags.rb
|
|
207
203
|
- lib/foreman_inventory_upload/notifications/manifest_import_success_notification_override.rb
|
|
204
|
+
- lib/foreman_inventory_upload/scripts/uploader.sh.erb
|
|
208
205
|
- lib/foreman_rh_cloud.rb
|
|
209
206
|
- lib/foreman_rh_cloud/async/exponential_backoff.rb
|
|
210
207
|
- lib/foreman_rh_cloud/engine.rb
|
|
@@ -261,11 +258,7 @@ files:
|
|
|
261
258
|
- test/factories/inventory_upload_factories.rb
|
|
262
259
|
- test/jobs/cloud_connector_announce_task_test.rb
|
|
263
260
|
- test/jobs/connector_playbook_execution_reporter_task_test.rb
|
|
264
|
-
- test/jobs/create_missing_insights_facets_test.rb
|
|
265
261
|
- test/jobs/exponential_backoff_test.rb
|
|
266
|
-
- test/jobs/generate_host_report_test.rb
|
|
267
|
-
- test/jobs/generate_report_job_test.rb
|
|
268
|
-
- test/jobs/host_inventory_report_job_test.rb
|
|
269
262
|
- test/jobs/insights_client_status_aging_test.rb
|
|
270
263
|
- test/jobs/insights_full_sync_test.rb
|
|
271
264
|
- test/jobs/insights_resolutions_sync_test.rb
|
|
@@ -274,21 +267,16 @@ files:
|
|
|
274
267
|
- test/jobs/inventory_hosts_sync_test.rb
|
|
275
268
|
- test/jobs/inventory_scheduled_sync_test.rb
|
|
276
269
|
- test/jobs/inventory_self_host_sync_test.rb
|
|
277
|
-
- test/jobs/queue_for_upload_job_test.rb
|
|
278
270
|
- test/jobs/remove_insights_hosts_job_test.rb
|
|
279
|
-
- test/jobs/
|
|
280
|
-
- test/jobs/upload_report_direct_job_test.rb
|
|
271
|
+
- test/jobs/upload_report_job_test.rb
|
|
281
272
|
- test/models/insights_client_report_status_test.rb
|
|
282
273
|
- test/test_plugin_helper.rb
|
|
283
274
|
- test/unit/archived_report_generator_test.rb
|
|
284
275
|
- test/unit/fact_helpers_test.rb
|
|
285
|
-
- test/unit/foreman_rh_cloud_iop_metadata_test.rb
|
|
286
276
|
- test/unit/foreman_rh_cloud_self_host_test.rb
|
|
287
277
|
- test/unit/insights_facet_test.rb
|
|
288
|
-
- test/unit/lib/foreman_rh_cloud/registration_manager_extensions_test.rb
|
|
289
278
|
- test/unit/metadata_generator_test.rb
|
|
290
279
|
- test/unit/playbook_progress_generator_test.rb
|
|
291
|
-
- test/unit/rh_cloud_host_test.rb
|
|
292
280
|
- test/unit/rh_cloud_http_proxy_test.rb
|
|
293
281
|
- test/unit/rh_cloud_permissions_test.rb
|
|
294
282
|
- test/unit/services/foreman_rh_cloud/branch_info_test.rb
|
|
@@ -442,6 +430,7 @@ files:
|
|
|
442
430
|
- webpack/ForemanInventoryUpload/Components/PageHeader/PageTitle.js
|
|
443
431
|
- webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/PageHeader.test.js
|
|
444
432
|
- webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/PageTitle.test.js
|
|
433
|
+
- webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/__snapshots__/PageHeader.test.js.snap
|
|
445
434
|
- webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/__snapshots__/PageTitle.test.js.snap
|
|
446
435
|
- webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/CloudConnectorActions.js
|
|
447
436
|
- webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/CloudConnectorButton.js
|
|
@@ -586,6 +575,7 @@ files:
|
|
|
586
575
|
- webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTable.test.js
|
|
587
576
|
- webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTableActions.test.js
|
|
588
577
|
- webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTableSelectors.test.js
|
|
578
|
+
- webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTable.test.js.snap
|
|
589
579
|
- webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTableActions.test.js.snap
|
|
590
580
|
- webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTableSelectors.test.js.snap
|
|
591
581
|
- webpack/InsightsCloudSync/Components/InsightsTable/__tests__/fixtures.js
|
|
@@ -628,7 +618,6 @@ files:
|
|
|
628
618
|
- webpack/InsightsHostDetailsTab/__tests__/InsightsTabIntegration.test.js
|
|
629
619
|
- webpack/InsightsHostDetailsTab/__tests__/InsightsTabReducer.test.js
|
|
630
620
|
- webpack/InsightsHostDetailsTab/__tests__/InsightsTabSelectors.test.js
|
|
631
|
-
- webpack/InsightsHostDetailsTab/__tests__/InsightsTotalRiskChart.test.js
|
|
632
621
|
- webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTab.test.js.snap
|
|
633
622
|
- webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTabActions.test.js.snap
|
|
634
623
|
- webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTabReducer.test.js.snap
|
|
@@ -645,6 +634,7 @@ files:
|
|
|
645
634
|
- webpack/__mocks__/foremanReact/common/I18n.js
|
|
646
635
|
- webpack/__mocks__/foremanReact/common/MountingService.js
|
|
647
636
|
- webpack/__mocks__/foremanReact/common/helpers.js
|
|
637
|
+
- webpack/__mocks__/foremanReact/common/hooks/API/APIHooks.js
|
|
648
638
|
- webpack/__mocks__/foremanReact/components/ConfirmModal/index.js
|
|
649
639
|
- webpack/__mocks__/foremanReact/components/Head.js
|
|
650
640
|
- webpack/__mocks__/foremanReact/components/Layout/LayoutConstants.js
|
|
@@ -701,7 +691,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
701
691
|
- !ruby/object:Gem::Version
|
|
702
692
|
version: '0'
|
|
703
693
|
requirements: []
|
|
704
|
-
rubygems_version:
|
|
694
|
+
rubygems_version: 3.6.9
|
|
705
695
|
specification_version: 4
|
|
706
696
|
summary: Summary of ForemanRhCloud.
|
|
707
697
|
test_files:
|
|
@@ -720,11 +710,7 @@ test_files:
|
|
|
720
710
|
- test/factories/inventory_upload_factories.rb
|
|
721
711
|
- test/jobs/cloud_connector_announce_task_test.rb
|
|
722
712
|
- test/jobs/connector_playbook_execution_reporter_task_test.rb
|
|
723
|
-
- test/jobs/create_missing_insights_facets_test.rb
|
|
724
713
|
- test/jobs/exponential_backoff_test.rb
|
|
725
|
-
- test/jobs/generate_host_report_test.rb
|
|
726
|
-
- test/jobs/generate_report_job_test.rb
|
|
727
|
-
- test/jobs/host_inventory_report_job_test.rb
|
|
728
714
|
- test/jobs/insights_client_status_aging_test.rb
|
|
729
715
|
- test/jobs/insights_full_sync_test.rb
|
|
730
716
|
- test/jobs/insights_resolutions_sync_test.rb
|
|
@@ -733,21 +719,16 @@ test_files:
|
|
|
733
719
|
- test/jobs/inventory_hosts_sync_test.rb
|
|
734
720
|
- test/jobs/inventory_scheduled_sync_test.rb
|
|
735
721
|
- test/jobs/inventory_self_host_sync_test.rb
|
|
736
|
-
- test/jobs/queue_for_upload_job_test.rb
|
|
737
722
|
- test/jobs/remove_insights_hosts_job_test.rb
|
|
738
|
-
- test/jobs/
|
|
739
|
-
- test/jobs/upload_report_direct_job_test.rb
|
|
723
|
+
- test/jobs/upload_report_job_test.rb
|
|
740
724
|
- test/models/insights_client_report_status_test.rb
|
|
741
725
|
- test/test_plugin_helper.rb
|
|
742
726
|
- test/unit/archived_report_generator_test.rb
|
|
743
727
|
- test/unit/fact_helpers_test.rb
|
|
744
|
-
- test/unit/foreman_rh_cloud_iop_metadata_test.rb
|
|
745
728
|
- test/unit/foreman_rh_cloud_self_host_test.rb
|
|
746
729
|
- test/unit/insights_facet_test.rb
|
|
747
|
-
- test/unit/lib/foreman_rh_cloud/registration_manager_extensions_test.rb
|
|
748
730
|
- test/unit/metadata_generator_test.rb
|
|
749
731
|
- test/unit/playbook_progress_generator_test.rb
|
|
750
|
-
- test/unit/rh_cloud_host_test.rb
|
|
751
732
|
- test/unit/rh_cloud_http_proxy_test.rb
|
|
752
733
|
- test/unit/rh_cloud_permissions_test.rb
|
|
753
734
|
- test/unit/services/foreman_rh_cloud/branch_info_test.rb
|