diff --git a/poem/Poem/api/internal_views/metrics.py b/poem/Poem/api/internal_views/metrics.py index 2074b8148..2aadce9c6 100644 --- a/poem/Poem/api/internal_views/metrics.py +++ b/poem/Poem/api/internal_views/metrics.py @@ -46,11 +46,12 @@ def get(self, request, name=None): if name: metrics = poem_models.Metric.objects.filter(name=name) if metrics.count() == 0: - raise NotFound(status=404, - detail='Metric not found') + raise NotFound(status=404, detail='Metric not found') else: metrics = poem_models.Metric.objects.all() + profiles4metrics = get_metrics_in_profiles(request.tenant) + results = [] for metric in metrics: if metric.probeversion: @@ -88,6 +89,8 @@ def get(self, request, name=None): name=metric.name, mtype=mt.mtype.name, tags=[tag.name for tag in mt.tags.all()], + profiles=profiles4metrics[metric.name] + if metric.name in profiles4metrics else [], probeversion=metric.probeversion if metric.probeversion else "", group=group, description=mt.description, diff --git a/poem/Poem/api/internal_views/metrictemplates.py b/poem/Poem/api/internal_views/metrictemplates.py index 41034fcb7..799f32924 100644 --- a/poem/Poem/api/internal_views/metrictemplates.py +++ b/poem/Poem/api/internal_views/metrictemplates.py @@ -758,7 +758,10 @@ def get(self, request, name=None): return Response({ "id": tag.id, "name": tag.name, - "metrics": sorted([metric.name for metric in metrics]) + "metrics": sorted( + [{"name": metric.name} for metric in metrics], + key=lambda m: m["name"] + ) }) except admin_models.MetricTags.DoesNotExist: @@ -777,7 +780,10 @@ def get(self, request, name=None): data.append({ "id": tag.id, "name": tag.name, - "metrics": sorted([metric.name for metric in metrics]) + "metrics": sorted( + [{"name": metric.name} for metric in metrics], + key=lambda m: m["name"] + ) }) return Response(data) @@ -831,14 +837,16 @@ def post(self, request): if len(missing_metrics) > 0: if len(missing_metrics) == 1: - warn_msg = \ - f"{warn_msg}Metric {list(missing_metrics)[0]} " \ + warn_msg = ( + f"{warn_msg}Metric {list(missing_metrics)[0]} " f"does not exist." + ) else: - warn_msg = "{}Metrics {} do not exist.".format( - warn_msg, - ", ".join(sorted(list(missing_metrics))) + warn_msg = ( + f"{warn_msg}Metrics " + f"{', '.join(sorted(list(missing_metrics)))} " + f"do not exist." ) if warn_msg: @@ -1103,3 +1111,14 @@ def post(self, request): status_code=status.HTTP_401_UNAUTHORIZED, detail="You do not have permission to add ports" ) + + +class ListPublicDefaultPorts(ListDefaultPorts): + authentication_classes = () + permission_classes = () + + def _denied(self): + return Response(status=status.HTTP_403_FORBIDDEN) + + def post(self, request): + return self._denied() diff --git a/poem/Poem/api/tests/test_helpers.py b/poem/Poem/api/tests/test_helpers.py index 87b89332f..9ba84e53b 100644 --- a/poem/Poem/api/tests/test_helpers.py +++ b/poem/Poem/api/tests/test_helpers.py @@ -3587,10 +3587,13 @@ def test_sync_active_metrics(self, mock_get_metrics): self.assertEqual(err, []) self.assertEqual(unavailable, []) self.assertEqual( - sorted(deleted), - ["eu.egi.cloud.OpenStack-Swift", "test.Metric-Template"] + sorted(deleted), [ + "argo.poem-tools.check", + "eu.egi.cloud.OpenStack-Swift", + "test.Metric-Template" + ] ) - self.assertEqual(len(poem_models.Metric.objects.all()), 5) + self.assertEqual(len(poem_models.Metric.objects.all()), 4) self.assertRaises( poem_models.Metric.DoesNotExist, poem_models.Metric.objects.get, @@ -3635,10 +3638,13 @@ def test_sync_passive_metrics(self, mock_get_metrics): self.assertEqual(err, []) self.assertEqual(unavailable, []) self.assertEqual( - sorted(deleted), - ["eu.egi.cloud.OpenStack-Swift", "test.Metric-Template"] + sorted(deleted), [ + "argo.poem-tools.check", + "eu.egi.cloud.OpenStack-Swift", + "test.Metric-Template" + ] ) - self.assertEqual(len(poem_models.Metric.objects.all()), 5) + self.assertEqual(len(poem_models.Metric.objects.all()), 4) self.assertRaises( poem_models.Metric.DoesNotExist, poem_models.Metric.objects.get, @@ -3683,10 +3689,13 @@ def test_sync_metrics_with_warning(self, mock_get_metrics): self.assertEqual(err, []) self.assertEqual(unavailable, []) self.assertEqual( - sorted(deleted), - ["eu.egi.cloud.OpenStack-Swift", "test.Metric-Template"] + sorted(deleted), [ + "argo.poem-tools.check", + "eu.egi.cloud.OpenStack-Swift", + "test.Metric-Template" + ] ) - self.assertEqual(len(poem_models.Metric.objects.all()), 5) + self.assertEqual(len(poem_models.Metric.objects.all()), 4) self.assertRaises( poem_models.Metric.DoesNotExist, poem_models.Metric.objects.get, @@ -3731,10 +3740,13 @@ def test_sync_metrics_if_version_unavailable(self, mock_get_metrics): self.assertEqual(err, []) self.assertEqual(unavailable, ["test.MetricTemplate"]) self.assertEqual( - sorted(deleted), - ["eu.egi.cloud.OpenStack-Swift", "test.Metric-Template"] + sorted(deleted), [ + "argo.poem-tools.check", + "eu.egi.cloud.OpenStack-Swift", + "test.Metric-Template" + ] ) - self.assertEqual(len(poem_models.Metric.objects.all()), 4) + self.assertEqual(len(poem_models.Metric.objects.all()), 3) self.assertRaises( poem_models.Metric.DoesNotExist, poem_models.Metric.objects.get, diff --git a/poem/Poem/api/tests/test_metrics.py b/poem/Poem/api/tests/test_metrics.py index 9fef91a86..bd9843534 100644 --- a/poem/Poem/api/tests/test_metrics.py +++ b/poem/Poem/api/tests/test_metrics.py @@ -313,6 +313,11 @@ def mock_db(): content_type=ct ) +profiles4metrics = { + "argo.AMS-Check": ["ARGO_MON", "ARGO_MON_CRITICAL"], + "argo.AMSPublisher-Check": ["ARGO_MON_INTERNAL"] +} + class ListAllMetricsAPIViewTests(TenantTestCase): @factory.django.mute_signals(pre_save) @@ -424,8 +429,11 @@ def setUp(self): name="org.apel.APEL-Pub" ) - def test_get_metric_list(self): + @patch("Poem.api.internal_views.metrics.get_metrics_in_profiles") + def test_get_metric_list(self, mock_profiles4metrics): + mock_profiles4metrics.return_value = profiles4metrics request = self.factory.get(self.url) + request.tenant = self.tenant force_authenticate(request, user=self.user) response = self.view(request) self.assertEqual( @@ -436,6 +444,7 @@ def test_get_metric_list(self): 'name': 'argo.AMS-Check', 'mtype': 'Active', 'tags': ['test_tag1', 'test_tag2'], + "profiles": ["ARGO_MON", "ARGO_MON_CRITICAL"], 'probeversion': 'ams-probe (0.1.7)', 'group': 'EGI', 'description': 'Description of argo.AMS-Check', @@ -490,6 +499,7 @@ def test_get_metric_list(self): 'name': 'argo.AMSPublisher-Check', 'mtype': 'Active', 'tags': ['test_tag1'], + "profiles": ["ARGO_MON_INTERNAL"], 'probeversion': 'ams-publisher-probe (0.1.7)', 'group': 'EUDAT', 'description': '', @@ -538,6 +548,7 @@ def test_get_metric_list(self): 'name': 'org.apel.APEL-Pub', 'mtype': 'Passive', 'tags': [], + "profiles": [], 'probeversion': '', 'group': 'EGI', 'description': '', @@ -563,8 +574,11 @@ def test_get_metric_list(self): ] ) - def test_get_metric_by_name(self): + @patch("Poem.api.internal_views.metrics.get_metrics_in_profiles") + def test_get_metric_by_name(self, mock_profiles4metrics): + mock_profiles4metrics.return_value = profiles4metrics request = self.factory.get(self.url + 'argo.AMS-Check') + request.tenant = self.tenant force_authenticate(request, user=self.user) response = self.view(request, 'argo.AMS-Check') self.assertEqual( @@ -574,6 +588,7 @@ def test_get_metric_by_name(self): 'name': 'argo.AMS-Check', 'mtype': 'Active', 'tags': ['test_tag1', 'test_tag2'], + "profiles": ["ARGO_MON", "ARGO_MON_CRITICAL"], 'probeversion': 'ams-probe (0.1.7)', 'group': 'EGI', 'description': 'Description of argo.AMS-Check', diff --git a/poem/Poem/api/tests/test_metrictemplates.py b/poem/Poem/api/tests/test_metrictemplates.py index d6a046773..74ae8e4d4 100644 --- a/poem/Poem/api/tests/test_metrictemplates.py +++ b/poem/Poem/api/tests/test_metrictemplates.py @@ -10022,17 +10022,25 @@ def test_get_metric_tags_admin_superuser(self): { "id": self.tag1.id, "name": "internal", - "metrics": ["argo.AMS-Check"] + "metrics": [{ + "name": "argo.AMS-Check" + }] }, { "id": self.tag3.id, "name": "test_tag1", - "metrics": ["argo.AMS-Check", "argo.EGI-Connectors-Check"] + "metrics": [ + {"name": "argo.AMS-Check"}, + {"name": "argo.EGI-Connectors-Check"} + ] }, { "id": self.tag4.id, "name": "test_tag2", - "metrics": ["argo.AMS-Check", "org.apel.APEL-Pub"] + "metrics": [ + {"name": "argo.AMS-Check"}, + {"name": "org.apel.APEL-Pub"} + ] } ] ) @@ -10052,17 +10060,25 @@ def test_get_metric_tags_admin_regular_user(self): { "id": self.tag1.id, "name": "internal", - "metrics": ["argo.AMS-Check"] + "metrics": [ + {"name": "argo.AMS-Check"} + ] }, { "id": self.tag3.id, "name": "test_tag1", - "metrics": ["argo.AMS-Check", "argo.EGI-Connectors-Check"] + "metrics": [ + {"name": "argo.AMS-Check"}, + {"name": "argo.EGI-Connectors-Check"} + ] }, { "id": self.tag4.id, "name": "test_tag2", - "metrics": ["argo.AMS-Check", "org.apel.APEL-Pub"] + "metrics": [ + {"name": "argo.AMS-Check"}, + {"name": "org.apel.APEL-Pub"} + ] } ] ) @@ -10082,17 +10098,25 @@ def test_get_metric_tags_tenant_superuser(self): { "id": self.tag1.id, "name": "internal", - "metrics": ["argo.AMS-Check"] + "metrics": [ + {"name": "argo.AMS-Check"} + ] }, { "id": self.tag3.id, "name": "test_tag1", - "metrics": ["argo.AMS-Check", "argo.EGI-Connectors-Check"] + "metrics": [ + {"name": "argo.AMS-Check"}, + {"name": "argo.EGI-Connectors-Check"} + ] }, { "id": self.tag4.id, "name": "test_tag2", - "metrics": ["argo.AMS-Check", "org.apel.APEL-Pub"] + "metrics": [ + {"name": "argo.AMS-Check"}, + {"name": "org.apel.APEL-Pub"} + ] } ] ) @@ -10112,17 +10136,25 @@ def test_get_metric_tags_tenant_regular_user(self): { "id": self.tag1.id, "name": "internal", - "metrics": ["argo.AMS-Check"] + "metrics": [ + {"name": "argo.AMS-Check"} + ] }, { "id": self.tag3.id, "name": "test_tag1", - "metrics": ["argo.AMS-Check", "argo.EGI-Connectors-Check"] + "metrics": [ + {"name": "argo.AMS-Check"}, + {"name": "argo.EGI-Connectors-Check"} + ] }, { "id": self.tag4.id, "name": "test_tag2", - "metrics": ["argo.AMS-Check", "org.apel.APEL-Pub"] + "metrics": [ + {"name": "argo.AMS-Check"}, + {"name": "org.apel.APEL-Pub"} + ] } ] ) @@ -10141,7 +10173,9 @@ def test_get_metric_tag_by_name_admin_superuser(self): { "id": self.tag1.id, "name": "internal", - "metrics": ["argo.AMS-Check"] + "metrics": [ + {"name": "argo.AMS-Check"} + ] } ) @@ -10154,7 +10188,9 @@ def test_get_metric_tag_by_name_admin_regular_user(self): { "id": self.tag1.id, "name": "internal", - "metrics": ["argo.AMS-Check"] + "metrics": [ + {"name": "argo.AMS-Check"} + ] } ) @@ -10167,7 +10203,9 @@ def test_get_metric_tag_by_name_tenant_superuser(self): { "id": self.tag1.id, "name": "internal", - "metrics": ["argo.AMS-Check"] + "metrics": [ + {"name": "argo.AMS-Check"} + ] } ) @@ -10180,7 +10218,9 @@ def test_get_metric_tag_by_name_tenant_regular_user(self): { "id": self.tag1.id, "name": "internal", - "metrics": ["argo.AMS-Check"] + "metrics": [ + {"name": "argo.AMS-Check"} + ] } ) diff --git a/poem/Poem/api/urls_internal.py b/poem/Poem/api/urls_internal.py index bfa9c86ec..35e22f5a9 100644 --- a/poem/Poem/api/urls_internal.py +++ b/poem/Poem/api/urls_internal.py @@ -93,6 +93,7 @@ path("metricconfiguration/", views_internal.ListMetricConfiguration.as_view(), name="metricconfiguration"), path("metricconfiguration/", views_internal.ListMetricConfiguration.as_view(), name="metricconfiguration"), path("default_ports/", views_internal.ListDefaultPorts.as_view(), name="default_ports"), + path("public_default_ports/", views_internal.ListPublicDefaultPorts.as_view(), name="default_ports"), path("probecandidates/", views_internal.ListProbeCandidates.as_view(), name="probecandidates"), path("probecandidates/", views_internal.ListProbeCandidates.as_view(), name="probecandidates"), path("probecandidatestatuses/", views_internal.ListProbeCandidateStatuses.as_view(), name="probecandidatestatus") diff --git a/poem/Poem/frontend/react/App.js b/poem/Poem/frontend/react/App.js index 61bf98331..65eff1f51 100644 --- a/poem/Poem/frontend/react/App.js +++ b/poem/Poem/frontend/react/App.js @@ -1274,6 +1274,15 @@ const App = () => { } /> + + + + } + /> + { } /> + + + + } + /> + } /> diff --git a/poem/Poem/frontend/react/DefaultPorts.js b/poem/Poem/frontend/react/DefaultPorts.js index f23eeb296..a57c0ef99 100644 --- a/poem/Poem/frontend/react/DefaultPorts.js +++ b/poem/Poem/frontend/react/DefaultPorts.js @@ -58,7 +58,7 @@ const validationSchema = Yup.object().shape({ }) -const PortsList = ({ data }) => { +const PortsList = ({ data, publicView }) => { const backend = new Backend() const queryClient = useQueryClient() @@ -154,14 +154,17 @@ const PortsList = ({ data }) => {

Default ports

- + { + !publicView && + + }
@@ -181,9 +184,19 @@ const PortsList = ({ data }) => { # - Port name - Port value - Action + { + publicView ? + <> + Port name + Port value + + : + <> + Port name + Port value + Action + + } @@ -217,7 +230,9 @@ const PortsList = ({ data }) => { } /> - + { + !publicView && + } { fieldsView.map((entry, index) => @@ -283,32 +298,37 @@ const PortsList = ({ data }) => { { entry.value } } - - - - + { + !publicView && + <> + + + + + + } ) } @@ -323,12 +343,14 @@ const PortsList = ({ data }) => { } -export const DefaultPortsList = () => { +export const DefaultPortsList = (props) => { + const publicView = props.publicView + const backend = new Backend(); const { data: defaultPorts, error, status } = useQuery( - "defaultports", async () => { - return await backend.fetchData("/api/v2/internal/default_ports") + `${publicView ? "public_" : ""}defaultports`, async () => { + return await backend.fetchData(`/api/v2/internal/${publicView ? "public_" : ""}default_ports`) } ) @@ -337,7 +359,7 @@ export const DefaultPortsList = () => { Save + !publicView && } /> ) @@ -347,7 +369,10 @@ export const DefaultPortsList = () => { else if (defaultPorts) { return ( - + ) } else diff --git a/poem/Poem/frontend/react/Home.js b/poem/Poem/frontend/react/Home.js index 8818df9d2..50ce658c2 100644 --- a/poem/Poem/frontend/react/Home.js +++ b/poem/Poem/frontend/react/Home.js @@ -51,6 +51,9 @@ export const PublicHome = (props) => {
Metric templates
+
+ Default ports +
@@ -145,6 +148,9 @@ export const PublicHome = (props) => {
Metric templates
+
+ Default ports +
@@ -168,6 +174,9 @@ export const PublicHome = (props) => {
Metric templates
+
+ Default ports +
diff --git a/poem/Poem/frontend/react/MetricOverrides.js b/poem/Poem/frontend/react/MetricOverrides.js index 119ebe29b..624004613 100644 --- a/poem/Poem/frontend/react/MetricOverrides.js +++ b/poem/Poem/frontend/react/MetricOverrides.js @@ -12,7 +12,11 @@ import { } from "./UIElements" import { Button, + ButtonDropdown, Col, + DropdownItem, + DropdownMenu, + DropdownToggle, Form, FormGroup, FormFeedback, @@ -39,6 +43,7 @@ import { InputPlaceholder, ListViewPlaceholder } from "./Placeholders" +import { downloadJSON } from './FileDownload'; const hostnameRegex = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])([_][A-Za-z0-9.\-_]*)*$/ @@ -206,7 +211,7 @@ const MetricOverrideForm = ({ }) => { const backend = new Backend() const queryClient = useQueryClient() - const navigate = useNavigate() ///// + const navigate = useNavigate() const addMutation = useMutation(async (values) => await backend.addObject("/api/v2/internal/metricconfiguration/", values)) const changeMutation = useMutation(async (values) => await backend.changeObject("/api/v2/internal/metricconfiguration/", values)) @@ -216,6 +221,8 @@ const MetricOverrideForm = ({ const [modalMsg, setModalMsg] = useState(undefined); const [modalTitle, setModalTitle] = useState(undefined); const [modalFlag, setModalFlag] = useState(undefined); + const [dropdownOpen, setDropdownOpen] = useState(false); + const hiddenFileInput = React.useRef(null); const { control, handleSubmit, setValue, getValues, trigger, formState: { errors } } = useForm({ defaultValues: { @@ -352,6 +359,19 @@ const MetricOverrideForm = ({ }) } + const handleFileRead = (e) => { + let jsonData = JSON.parse(e.target.result); + setValue('globalAttributes', jsonData.global_attributes); + setValue('hostAttributes', jsonData.host_attributes); + setValue('metricParameters', jsonData.metric_parameters); + } + + const handleFileChosen = (file) => { + var reader = new FileReader(); + reader.onload = handleFileRead; + reader.readAsText(file); + } + return ( setDropdownOpen(!dropdownOpen) }> + JSON + + { + let valueSave = JSON.parse(JSON.stringify(getValues())); + const jsonContent = { + global_attributes: valueSave.globalAttributes, + host_attributes: valueSave.hostAttributes, + metric_parameters: valueSave.metricParameters + } + let filename = `${name}.json` + downloadJSON(jsonContent, filename) + }} + disabled={ addview } + > + Export + + { hiddenFileInput.current.click() } } + > + Import + + + { handleFileChosen(e.target.files[0]) }} + style={{ display: 'none' }} + /> + + } >
diff --git a/poem/Poem/frontend/react/MetricProfiles.js b/poem/Poem/frontend/react/MetricProfiles.js index 1aac5429c..65168151a 100644 --- a/poem/Poem/frontend/react/MetricProfiles.js +++ b/poem/Poem/frontend/react/MetricProfiles.js @@ -481,7 +481,7 @@ const MetricProfilesForm = ({ infoview={historyview} submitperm={write_perm} extra_button={ - !addview && + !publicView && setDropdownOpen(!dropdownOpen)}> CSV diff --git a/poem/Poem/frontend/react/MetricTags.js b/poem/Poem/frontend/react/MetricTags.js index ad586fd93..afbbccac2 100644 --- a/poem/Poem/frontend/react/MetricTags.js +++ b/poem/Poem/frontend/react/MetricTags.js @@ -1,10 +1,11 @@ -import React, { useState } from "react" +import React, { useContext, useEffect, useState } from "react" import { useMutation, useQuery, useQueryClient } from "react-query" import { Link, useLocation, useParams, useNavigate } from "react-router-dom" import { fetchMetricTags, fetchMetricTemplates, fetchUserDetails } from "./QueryFunctions" import { BaseArgoTable, BaseArgoView, + CustomError, CustomReactSelect, DefaultColumnFilter, ErrorComponent, @@ -27,25 +28,46 @@ import { InputGroup, InputGroupText, Row, - Table + Table, + ButtonDropdown, + DropdownToggle, + DropdownMenu, + DropdownItem } from "reactstrap" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { faSearch,faPlus, faTimes } from '@fortawesome/free-solid-svg-icons'; -import { Controller, useForm, useWatch } from "react-hook-form" +import { Controller, FormProvider, useFieldArray, useForm, useFormContext, useWatch } from "react-hook-form" import { ErrorMessage } from '@hookform/error-message' import { yupResolver } from '@hookform/resolvers/yup' -import * as Yup from "yup" +import * as yup from "yup" import { ChangeViewPlaceholder, InputPlaceholder, ListViewPlaceholder } from "./Placeholders" +import PapaParse from 'papaparse'; +import { downloadCSV } from './FileDownload'; -const validationSchema = Yup.object().shape({ - name: Yup.string().required("This field is required") + +const validationSchema = yup.object().shape({ + name: yup.string().required("This field is required"), + view_metrics4tag: yup.array() + .of(yup.object().shape({ + name: yup.string() + .test("predefined_metrics", "Must be one of predefined metrics", function (value) { + if (value && this.options.context.metrictemplates.indexOf(value) == -1) + return false + + else + return true + }) + })) }) +const MetricTagsContext = React.createContext() + + export const MetricTagsList = (props) => { const location = useLocation(); const publicView = props.publicView @@ -80,7 +102,7 @@ export const MetricTagsList = (props) => { : row.value.map((metric, i) => - { metric } + { metric.name } ) } @@ -120,6 +142,141 @@ export const MetricTagsList = (props) => { } +const MetricsList = () => { + const context = useContext(MetricTagsContext) + + const { control, getValues, setValue, resetField, trigger, formState: { errors } } = useFormContext() + + const { fields, insert, remove } = useFieldArray({ control, name: "view_metrics4tag" }) + + const onRemove = (index) => { + let tmp_metrics4tag = [ ...context.metrics4tag ] + let origIndex = tmp_metrics4tag.findIndex(e => e.name == getValues(`view_metrics4tag.${index}.name`)) + tmp_metrics4tag.splice(origIndex, 1) + resetField("metrics4tag") + setValue("metrics4tag", tmp_metrics4tag) + remove(index) + trigger("view_metrics4tag") + } + + const onInsert = (index) => { + let tmp_metrics4tag = [ ...context.metrics4tag ] + let origIndex = tmp_metrics4tag.findIndex(e => e.name == getValues(`view_metrics4tag.${index}.name`)) + let new_element = { name: "" } + tmp_metrics4tag.splice(origIndex + 1, 0, new_element) + resetField("metrics4tag") + setValue("metrics4tag", tmp_metrics4tag) + insert(index + 1, new_element) + } + + return ( + + + + + + { + !context.publicView && + } + + + + + + + + { + fields.map((item, index) => + + + + + { + !context.publicView && + + } + + + ) + } + +
#Metric templateActions
+ + + + + } + /> +
+ { index + 1 } + + { + context.publicView ? + item.name + : + + { + let origIndex = getValues("metrics4tag").findIndex(met => met.name == getValues(`view_metrics4tag.${index}.name`) ) + setValue(`metrics4tag.${origIndex}.name`, e.value) + setValue(`view_metrics4tag.${index}.name`, e.value) + trigger(`view_metrics4tag.${index}.name`) + } } + options={ + context.allMetrics.map( + met => met.name + ).filter( + met => !context.metrics4tag.map(met => met.name).includes(met) + ).map( + option => new Object({ label: option, value: option }) + )} + value={ { label: field.value, value: field.value } } + /> + } + /> + } + { + errors?.view_metrics4tag?.[index]?.name && + + } + + + +
+ ) +} + + const MetricTagsForm = ({ name=undefined, tag=undefined, @@ -137,24 +294,49 @@ const MetricTagsForm = ({ const [modalFlag, setModalFlag] = useState(undefined); const [modalTitle, setModalTitle] = useState(undefined); const [modalMsg, setModalMsg] = useState(undefined); + const [dropdownOpen, setDropdownOpen] = useState(false); + const hiddenFileInput = React.useRef(null); const changeMutation = useMutation(async (values) => await backend.changeObject('/api/v2/internal/metrictags/', values)); const addMutation = useMutation(async (values) => await backend.addObject('/api/v2/internal/metrictags/', values)); const deleteMutation = useMutation(async () => await backend.deleteObject(`/api/v2/internal/metrictags/${name}`)) - const { control, getValues, setValue, handleSubmit, formState: { errors } } = useForm({ + const default_metrics4tag = tag?.metrics.length > 0 ? tag.metrics : [{ name: "" }] + + const methods = useForm({ defaultValues: { id: `${tag ? tag.id : ""}`, name: `${tag ? tag.name : ""}`, - metrics4tag: tag?.metrics.length > 0 ? tag.metrics : [""], + metrics4tag: default_metrics4tag, + view_metrics4tag: default_metrics4tag, searchItem: "" }, mode: "all", - resolver: yupResolver(validationSchema) + resolver: yupResolver(validationSchema), + context: { metrictemplates: publicView ? new Array() : allMetrics.map(metric => metric.name) } }) + const { control } = methods + const searchItem = useWatch({ control, name: "searchItem" }) const metrics4tag = useWatch({ control, name: "metrics4tag" }) + const view_metrics4tag = useWatch({ control, name: "view_metrics4tag" }) + + useEffect(() => { + if (view_metrics4tag.length === 0) { + methods.setValue("view_metrics4tag", [{ name: "" }]) + } + }, [view_metrics4tag]) + + useEffect(() => { + if (metrics4tag.length === 0) { + methods.setValue("metrics4tag", [{ name: "" }]) + } + }, [metrics4tag]) + + useEffect(() => { + methods.setValue("view_metrics4tag", metrics4tag.filter(e => e.name.toLowerCase().includes(searchItem.toLowerCase()))) + }, [searchItem]) const toggleAreYouSure = () => { setAreYouSureModal(!areYouSureModal) @@ -171,11 +353,11 @@ const MetricTagsForm = ({ } const doChange = () => { - let formValues = getValues() + let formValues = methods.getValues() const sendValues = new Object({ name: formValues.name, - metrics: formValues.metrics4tag.filter(met => met !== "") + metrics: formValues.metrics4tag.map(met => met.name).filter(met => met !== "") }) if (addview) @@ -277,180 +459,131 @@ const MetricTagsForm = ({ : undefined }} - toggle={toggleAreYouSure} - > - - - - - - Name - - - } - /> - - - { message } - - } - /> - - - - - - - - - - - - { - !publicView && + toggle={ toggleAreYouSure } + extra_button={ + setDropdownOpen(!dropdownOpen) }> + CSV + + { + let csvContent = [] + metrics4tag.sort().forEach((metric) => { + csvContent.push({ name: metric.name }) + }) + const content = PapaParse.unparse(csvContent) + let filename = `${name}.csv` + downloadCSV(content, filename) + }} + disabled={ addview } + > + Export + + { hiddenFileInput.current.click() }} + > + Import + + + { + PapaParse.parse(e.target.files[0], { + header: true, + complete: (results) => { + var imported = results.data; + // remove entries without keys if there is any + imported = imported.filter( + obj => "name" in obj && obj["name"] + ) + methods.resetField("view_metrics4tag") + methods.setValue("view_metrics4tag", imported.sort()) + methods.trigger("view_metrics4tag") + methods.resetField("searchItem") + methods.resetField("metrics4tag") + methods.setValue("metrics4tag", imported.sort()) } - - - - - - + + Name - } /> - - - { - metrics4tag.filter( - filteredRow => filteredRow.toLowerCase().includes(searchItem.toLowerCase()) - ).map((item, index) => - - - - - { - !publicView && - - } - - - ) - } - -
#Metric templateActions
- - + }) + }} + style={{display: 'none'}} + /> + + } + > + + + + +
- { index + 1 } - - { - publicView ? - item - : - - { - let tmpMetrics = getValues("metrics4tag") - tmpMetrics[index] = e.value - setValue("metrics4tag", tmpMetrics) - } } - options={ - allMetrics.map( - met => met.name - ).filter( - met => !metrics4tag.includes(met) - ).map( - option => new Object({ label: option, value: option }) - )} - value={ { label: item, value: item } } - /> - } - /> - } - - - -
-
- { - !publicView && -
- { - !addview ? - - : -
- } - -
- } - + + + { message } + + } + /> + + + +
+ + + + + + + { + !publicView && +
+ { + !addview ? + + : +
+ } + +
+ } + +
) } diff --git a/poem/Poem/frontend/react/MetricTemplates.js b/poem/Poem/frontend/react/MetricTemplates.js index fe8cb86fe..2bd45f536 100644 --- a/poem/Poem/frontend/react/MetricTemplates.js +++ b/poem/Poem/frontend/react/MetricTemplates.js @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { MetricForm} from './Metrics'; +import { MetricForm } from './Metrics'; import { Backend } from './DataManager'; import { NotifyOk, diff --git a/poem/Poem/frontend/react/Metrics.js b/poem/Poem/frontend/react/Metrics.js index 6ace548c4..a72f2745d 100644 --- a/poem/Poem/frontend/react/Metrics.js +++ b/poem/Poem/frontend/react/Metrics.js @@ -186,7 +186,7 @@ const InlineFields = ({ id={ `${fieldname}.${index}.key` } data-testid={ `${fieldname}.${index}.key` } className={ `form-control ${entry.isNew && "border-success"}` } - disabled={ readOnly || fieldname === "config" || (isPassive && entry.key === "PASSIVE") } + disabled={ readOnly || fieldname === "config" || fieldname === "dependency" || (isPassive && entry.key === "PASSIVE") } /> } /> @@ -201,7 +201,7 @@ const InlineFields = ({ id={ `${fieldname}.${index}.value` } data-testid={ `${fieldname}.${index}.value` } className={ `form-control ${entry.isNew && "border-success"} ${ errors?.config?.[index]?.value && "is-invalid" }` } - disabled={ readOnly || (isPassive && entry.key === "PASSIVE") || (fieldname === "config" && entry.key === "path" && isMetric) } + disabled={ readOnly || (isPassive && entry.key === "PASSIVE") || (fieldname === "config" && entry.key === "path" && isMetric) || fieldname === "dependency" } /> } /> @@ -398,6 +398,7 @@ export const ListOfMetrics = (props) => { NotifyWarn({ msg: data.warning, title: 'Deleted' }) setSelectAll(0); + setSelected({}); queryClient.invalidateQueries('metrictemplate'); queryClient.invalidateQueries('public_metrictemplate'); }, @@ -860,6 +861,7 @@ export const MetricForm = defaultValues: { id: initValues.id ? initValues.id : "", name: initValues.name, + profiles: initValues.profiles, probeversion: initValues.probeversion, type: initValues.type, group: initValues.group ? initValues.group: "", @@ -968,6 +970,25 @@ export const MetricForm = toggleAreYouSure() } + function onDeleteHandle() { + let profiles = getValues("profiles") + let name = getValues("name") + let msg = "" + + if (resourcename === "metric" && !isHistory && profiles.length > 0) { + msg =
+

Metric { name } is part of profile(s): { profiles.join(", ") }

+

ARE YOU SURE you want to delete it?

+
+ } else + msg = `Are you sure you want to delete ${resourcename_beautify}?` + + setModalMsg(msg) + setModalTitle(`Delete ${resourcename_beautify}`) + setModalFlag('delete') + toggleAreYouSure() + } + return ( - + { + (watchDependency.length >= 1 && watchDependency[0].key != "") && + + } { - setModalMsg(`Are you sure you want to delete ${resourcename_beautify}?`) - setModalTitle(`Delete ${resourcename_beautify}`) - setModalFlag('delete') - toggleAreYouSure() - }} + onClick={() => onDeleteHandle()} > Delete @@ -1685,6 +1704,8 @@ export const MetricChange = (props) => { const probe = probes ? probes.find(prb => prb.object_repr === metric.probeversion).fields : { package: "" }; + const emptyEntry = [ { key: "", value: "" } ] + return ( { probeexecutable: metric.probeexecutable, parent: metric.parent, config: metric.config, - attributes: metric.attribute, - dependency: metric.dependancy, - parameter: metric.parameter, - flags: metric.flags, - files: metric.files, - file_attributes: metric.files, - file_parameters: metric.fileparameter, + attributes: metric.attribute.length > 0 ? metric.attribute : emptyEntry, + dependency: metric.dependancy.length > 0 ? metric.dependancy : emptyEntry, + parameter: metric.parameter.length > 0 ? metric.parameter : emptyEntry, + flags: metric.flags.length > 0 ? metric.flags : emptyEntry, + file_attributes: metric.files.length > 0 ? metric.files : emptyEntry, + file_parameters: metric.fileparameter.length > 0 ? metric.fileparameter : emptyEntry, probe: probe, - tags: metric.tags + tags: metric.tags, + profiles: metric.profiles }} isTenantSchema={ true } groups={ groups } diff --git a/poem/Poem/frontend/react/Placeholders.js b/poem/Poem/frontend/react/Placeholders.js index 6bef27f69..0dfdfd94f 100644 --- a/poem/Poem/frontend/react/Placeholders.js +++ b/poem/Poem/frontend/react/Placeholders.js @@ -288,7 +288,6 @@ export const MetricFormPlaceholder = ( props ) => { -
parent
diff --git a/poem/Poem/frontend/react/UIElements.js b/poem/Poem/frontend/react/UIElements.js index 9a3e6b706..173d64043 100644 --- a/poem/Poem/frontend/react/UIElements.js +++ b/poem/Poem/frontend/react/UIElements.js @@ -123,6 +123,7 @@ link_title.set('users', 'Users'); link_title.set('yumrepos', 'YUM repos'); link_title.set("metricoverrides", "Metric configuration overrides") link_title.set("default_ports", "Default ports") +link_title.set("public_default_ports", "Public default ports") link_title.set("probecandidates", "Probe candidates") diff --git a/poem/Poem/frontend/react/__tests__/DefaultPorts.test.js b/poem/Poem/frontend/react/__tests__/DefaultPorts.test.js index 26d820115..bea2407d8 100644 --- a/poem/Poem/frontend/react/__tests__/DefaultPorts.test.js +++ b/poem/Poem/frontend/react/__tests__/DefaultPorts.test.js @@ -61,18 +61,30 @@ const mockDefaultPorts = [ ] -function renderView() { - const route = "/ui/administration/default_ports" - - return { - ...render( - - - - - - ) - } +function renderView(publicView=false) { + const route = `/ui/${publicView? "public_" : "administration/"}default_ports` + + if (publicView) + return { + ...render( + + + + + + ) + } + + else + return { + ...render( + + + + + + ) + } } @@ -119,6 +131,34 @@ describe("Test default ports list", () => { expect(table.getAllByTestId(/insert-/i)).toHaveLength(4) }) + test("Test that public page renders properly", async () => { + renderView(true); + + await waitFor(() => { + expect(screen.getAllByRole("columnheader")).toHaveLength(3) + }) + + expect(screen.getByRole("heading", { name: /port/i }).textContent).toBe("Default ports"); + + const table = within(screen.getByRole("table")) + expect(table.getByRole("columnheader", { name: "#" })).toBeInTheDocument() + expect(table.getByRole("columnheader", { name: "Port name" })).toBeInTheDocument() + expect(table.getByRole("columnheader", { name: "Port value" })).toBeInTheDocument() + + const rows = table.getAllByRole("row") + expect(rows).toHaveLength(6) + expect(screen.getAllByPlaceholderText(/search/i)).toHaveLength(2) + expect(rows[0].textContent).toBe("#Port namePort value") + // row 1 is the one with search fields + expect(rows[2].textContent).toBe("1BDII_PORT2170") + expect(rows[3].textContent).toBe("2GRAM_PORT2119") + expect(rows[4].textContent).toBe("3MYPROXY_PORT7512") + expect(rows[5].textContent).toBe("4SITE_BDII_PORT2170") + + expect(table.queryAllByTestId(/remove-/i)).toHaveLength(0) + expect(table.queryAllByTestId(/insert-/i)).toHaveLength(0) + }) + test("Test filter page by port name", async () => { renderView(); @@ -144,6 +184,31 @@ describe("Test default ports list", () => { expect(table.getAllByTestId(/insert-/i)).toHaveLength(2) }) + test("Test filter page by port name if public view", async () => { + renderView(true); + + await waitFor(() => { + expect(screen.getAllByRole("columnheader")).toHaveLength(3) + }) + + expect(screen.getByRole("heading", { name: /port/i }).textContent).toBe("Default ports"); + + fireEvent.change(screen.getAllByPlaceholderText(/search/i)[0], { target: { value: "BDII" } }) + + const table = within(screen.getByRole("table")) + + const rows = table.getAllByRole("row") + expect(rows).toHaveLength(4) + expect(screen.getAllByPlaceholderText(/search/i)).toHaveLength(2) + expect(rows[0].textContent).toBe("#Port namePort value") + // row 1 is the one with search fields + expect(rows[2].textContent).toBe("1BDII_PORT2170") + expect(rows[3].textContent).toBe("2SITE_BDII_PORT2170") + + expect(table.queryAllByTestId(/remove-/i)).toHaveLength(0) + expect(table.queryAllByTestId(/insert-/i)).toHaveLength(0) + }) + test("Test filter page by port value", async () => { renderView(); @@ -168,6 +233,30 @@ describe("Test default ports list", () => { expect(table.getAllByTestId(/insert-/i)).toHaveLength(1) }) + test("Test filter page by port value if public view", async () => { + renderView(true); + + await waitFor(() => { + expect(screen.getAllByRole("columnheader")).toHaveLength(3) + }) + + expect(screen.getByRole("heading", { name: /port/i }).textContent).toBe("Default ports"); + + fireEvent.change(screen.getAllByPlaceholderText(/search/i)[1], { target: { value: "75" } }) + + const table = within(screen.getByRole("table")) + + const rows = table.getAllByRole("row") + expect(rows).toHaveLength(3) + expect(screen.getAllByPlaceholderText(/search/i)).toHaveLength(2) + expect(rows[0].textContent).toBe("#Port namePort value") + // row 1 is the one with search fields + expect(rows[2].textContent).toBe("1MYPROXY_PORT7512") + + expect(table.queryAllByTestId(/remove-/i)).toHaveLength(0) + expect(table.queryAllByTestId(/insert-/i)).toHaveLength(0) + }) + test("Test adding new port", async () => { renderView(); diff --git a/poem/Poem/frontend/react/__tests__/MetricOverrides.test.js b/poem/Poem/frontend/react/__tests__/MetricOverrides.test.js index a871b57fc..52ef6e59a 100644 --- a/poem/Poem/frontend/react/__tests__/MetricOverrides.test.js +++ b/poem/Poem/frontend/react/__tests__/MetricOverrides.test.js @@ -6,6 +6,7 @@ import { MemoryRouter, Routes, Route } from 'react-router-dom'; import { Backend } from "../DataManager" import { MetricOverrideChange, MetricOverrideList } from "../MetricOverrides" import { NotificationManager } from "react-notifications" +import useEvent from '@testing-library/user-event'; jest.mock("../DataManager", () => { @@ -262,6 +263,7 @@ describe("Tests for metric configuration overrides addview", () => { expect(screen.queryByRole('button', { name: /history/i })).not.toBeInTheDocument(); expect(screen.queryByRole('button', { name: /clone/i })).not.toBeInTheDocument(); expect(screen.queryByRole('button', { name: /delete/i })).not.toBeInTheDocument(); + expect(screen.queryByRole("button", { name: /json/i })).toBeInTheDocument() }) test("Test add name", async () => { @@ -1307,6 +1309,78 @@ describe("Tests for metric configuration overrides addview", () => { }) }) + test("Test import json successfully", async () => { + renderAddView() + + await waitFor(() => { + expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() + }) + + fireEvent.click(screen.getByRole("button", { name: /json/i })) + fireEvent.click(screen.getByRole("menuitem", { name: /import/i })) + + const content = new Blob([JSON.stringify({ + global_attributes: [ + { + attribute: "ROBOT_CERT", + value: "/etc/sensu/certs/robotcert.pem" + }, + { + attribute: "ROBOT_KEY", + value: "/etc/sensu/certs/robotkey.pem" + } + ], + host_attributes: [ + { + hostname: "poem.devel.argo.grnet.gr", + attribute: "ARGO_TENANTS_TOKEN", + value: "$DEVEL_ARGO_TENANTS_TOKEN" + } + ], + metric_parameters: [ + { + hostname: "", + metric: "", + parameter: "", + value: "" + } + ] + })]) + + const file = new File([content], "test.json", { type: "application/json" }) + const input = screen.getByTestId("file_input") + + await waitFor(() => { + useEvent.upload(input, file) + }) + + await waitFor(() => { + expect(input.files[0]).toBe(file) + }) + + expect(input.files.item(0)).toBe(file) + expect(input.files).toHaveLength(1) + + await waitFor(() => { + fireEvent.load(screen.getByTestId("file_input")) + }) + + expect(screen.getByTestId("metric-override-form")).toHaveFormValues({ + name: "", + "globalAttributes.0.attribute": "ROBOT_CERT", + "globalAttributes.0.value": "/etc/sensu/certs/robotcert.pem", + "globalAttributes.1.attribute": "ROBOT_KEY", + "globalAttributes.1.value": "/etc/sensu/certs/robotkey.pem", + "hostAttributes.0.hostname": "poem.devel.argo.grnet.gr", + "hostAttributes.0.attribute": "ARGO_TENANTS_TOKEN", + "hostAttributes.0.value": "$DEVEL_ARGO_TENANTS_TOKEN", + "metricParameters.0.hostname": "", + "metricParameters.0.metric": "", + "metricParameters.0.parameter": "", + "metricParameters.0.value": "" + }) + }) + test("Test save metric override with just name", async () => { renderAddView() @@ -1732,6 +1806,7 @@ describe("Tests for metric configuration overrides changeview", () => { expect(screen.queryByRole('button', { name: /clone/i })).not.toBeInTheDocument(); expect(screen.getByRole('button', { name: /save/i })).toBeInTheDocument(); expect(screen.getByRole('button', { name: /delete/i })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /json/i })).toBeInTheDocument(); }) test("Test change global attributes", async () => { @@ -3141,6 +3216,307 @@ describe("Tests for metric configuration overrides changeview", () => { }) }) + test("Test import json successfully", async () => { + renderChangeView() + + await waitFor(() => { + expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() + }) + + fireEvent.click(screen.getByRole("button", { name: /json/i })) + fireEvent.click(screen.getByRole("menuitem", { name: /import/i })) + + const content = new Blob([JSON.stringify({ + global_attributes: [ + { + attribute: "ROBOT_CERT", + value: "/etc/sensu/certs/robotcert.pem" + }, + { + attribute: "ROBOT_KEY", + value: "/etc/sensu/certs/robotkey.pem" + } + ], + host_attributes: [ + { + hostname: "poem.devel.argo.grnet.gr", + attribute: "ARGO_TENANTS_TOKEN", + value: "$DEVEL_ARGO_TENANTS_TOKEN" + } + ], + metric_parameters: [ + { + hostname: "", + metric: "", + parameter: "", + value: "" + } + ] + })]) + + const file = new File([content], "test.json", { type: "application/json" }) + const input = screen.getByTestId("file_input") + + await waitFor(() => { + useEvent.upload(input, file) + }) + + await waitFor(() => { + expect(input.files[0]).toBe(file) + }) + + expect(input.files.item(0)).toBe(file) + expect(input.files).toHaveLength(1) + + await waitFor(() => { + fireEvent.load(screen.getByTestId("file_input")) + }) + + expect(screen.getByTestId("metric-override-form")).toHaveFormValues({ + name: "local", + "globalAttributes.0.attribute": "ROBOT_CERT", + "globalAttributes.0.value": "/etc/sensu/certs/robotcert.pem", + "globalAttributes.1.attribute": "ROBOT_KEY", + "globalAttributes.1.value": "/etc/sensu/certs/robotkey.pem", + "hostAttributes.0.hostname": "poem.devel.argo.grnet.gr", + "hostAttributes.0.attribute": "ARGO_TENANTS_TOKEN", + "hostAttributes.0.value": "$DEVEL_ARGO_TENANTS_TOKEN", + "metricParameters.0.hostname": "", + "metricParameters.0.metric": "", + "metricParameters.0.parameter": "", + "metricParameters.0.value": "" + }) + + fireEvent.click(screen.getByRole("button", { name: /save/i })) + await waitFor(() => { + expect(screen.getByRole('dialog', { title: /add/i })).toBeInTheDocument(); + }) + fireEvent.click(screen.getByRole('button', { name: /yes/i })); + + await waitFor(() => { + expect(mockChangeObject).toHaveBeenCalledWith( + "/api/v2/internal/metricconfiguration/", + { + id: "1", + name: "local", + global_attributes: [ + { + attribute: "ROBOT_CERT", + value: "/etc/sensu/certs/robotcert.pem" + }, + { + attribute: "ROBOT_KEY", + value: "/etc/sensu/certs/robotkey.pem" + } + ], + host_attributes: [ + { + hostname: "poem.devel.argo.grnet.gr", + attribute: "ARGO_TENANTS_TOKEN", + value: "$DEVEL_ARGO_TENANTS_TOKEN" + } + ], + metric_parameters: [ + { + hostname: "", + metric: "", + parameter: "", + value: "" + } + ] + } + ) + }) + }) + + test("Test export json successfully", async () => { + const helpers = require("../FileDownload") + jest.spyOn(helpers, "downloadJSON").mockReturnValueOnce(null) + + renderChangeView() + + await waitFor(() => { + expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() + }) + + fireEvent.click(screen.getByRole("button", { name: /json/i })) + fireEvent.click(screen.getByRole("menuitem", { name: /export/i })) + + const content = { + global_attributes: [ + { + attribute: "NAGIOS_ACTUAL_HOST_CERT", + value: "/etc/nagios/globus/hostcert.pem" + }, + { + attribute: "NAGIOS_ACTUAL_HOST_KEY", + value: "/etc/nagios/globus/hostkey.pem" + } + ], + host_attributes: [ + { + hostname: "mock.host.name", + attribute: "attr1", + value: "some-new-value" + } + ], + metric_parameters: [ + { + hostname: "eosccore.ui.argo.grnet.gr", + metric: "org.nagios.ARGOWeb-AR", + parameter: "-r", + value: "EOSC_Monitoring" + }, + { + hostname: "argo.eosc-portal.eu", + metric: "org.nagios.ARGOWeb-Status", + parameter: "-u", + value: "/eosc/report-status/Default/SERVICEGROUPS?accept=csv" + } + ] + } + + expect(helpers.downloadJSON).toHaveBeenCalledTimes(1) + expect(helpers.downloadJSON).toHaveBeenCalledWith(content, "local.json") + }) + + test("Test export json when form has been changed", async () => { + const helpers = require("../FileDownload") + jest.spyOn(helpers, "downloadJSON").mockReturnValueOnce(null) + + renderChangeView() + + await waitFor(() => { + expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() + }) + + await waitFor(() => { + fireEvent.change(screen.getByTestId("name"), { target: { value: "local_new" } }) + }) + + await waitFor(() => { + fireEvent.change(screen.getByTestId("globalAttributes.0.attribute"), { target: { value: "NAGIOS_REAL_HOST_CERT" } }) + }) + + await waitFor(() => { + fireEvent.change(screen.getByTestId("globalAttributes.0.value"), { target: { value: "/etc/nagios/globus/cert.pem" } }) + }) + + await waitFor(() => fireEvent.click(screen.getByTestId("globalAttributes.0.add"))) + + await waitFor(() => { + fireEvent.change(screen.getByTestId("globalAttributes.1.attribute"), { target: { value: "MOCK_ATTRIBUTE" } }) + }) + + await waitFor(() => { + fireEvent.change(screen.getByTestId("globalAttributes.1.value"), { target: { value: "mock-attribute-value" } }) + }) + + await waitFor(() => { + fireEvent.change(screen.getByTestId("globalAttributes.2.attribute"), { target: { value: "NAGIOS_REAL_HOST_KEY" } }) + }) + + await waitFor(() => { + fireEvent.change(screen.getByTestId("globalAttributes.2.value"), { target: { value: "/etc/nagios/globus/cert.key" } }) + }) + + await waitFor(() => { + fireEvent.change(screen.getByTestId("hostAttributes.0.hostname"), { target: { value: "host.foo.bar" } }) + }) + + await waitFor(() => { + fireEvent.change(screen.getByTestId("hostAttributes.0.attribute"), { target: { value: "FOOBAR" } }) + }) + + await waitFor(() => { + fireEvent.change(screen.getByTestId("hostAttributes.0.value"), { target: { value: "foo-bar" } }) + }) + + await waitFor(() => { + fireEvent.change(screen.getByTestId("metricParameters.0.hostname"), { target: { value: "sensu.cro-ngi" } }) + }) + + await waitFor(() => { + fireEvent.change(screen.getByTestId("metricParameters.0.metric"), { target: { value: "argo.AMSPublisher-Check" } }) + }) + + await waitFor(() => { + fireEvent.change(screen.getByTestId("metricParameters.0.parameter"), { target: { value: "-q" } }) + }) + + await waitFor(() => { + fireEvent.change(screen.getByTestId("metricParameters.0.value"), { target: { value: "w:metrics+g:published180 -c 10" } }) + }) + + await waitFor(() => fireEvent.click(screen.getByTestId("metricParameters.0.add"))) + + await waitFor(() => { + fireEvent.change(screen.getByTestId("metricParameters.1.hostname"), { target: { value: "epic5.storage.surfsara.nl" } }) + }) + + await waitFor(() => { + fireEvent.change(screen.getByTestId("metricParameters.1.metric"), { target: { value: "generic.tcp.connect" } }) + }) + + await waitFor(() => { + fireEvent.change(screen.getByTestId("metricParameters.1.parameter"), { target: { value: "-p" } }) + }) + + await waitFor(() => { + fireEvent.change(screen.getByTestId("metricParameters.1.value"), { target: { value: "8004" } }) + }) + + fireEvent.click(screen.getByRole("button", { name: /json/i })) + fireEvent.click(screen.getByRole("menuitem", { name: /export/i })) + + const content = { + global_attributes: [ + { + attribute: "NAGIOS_REAL_HOST_CERT", + value: "/etc/nagios/globus/cert.pem" + }, + { + attribute: "MOCK_ATTRIBUTE", + value: "mock-attribute-value" + }, + { + attribute: "NAGIOS_REAL_HOST_KEY", + value: "/etc/nagios/globus/cert.key" + } + ], + host_attributes: [ + { + hostname: "host.foo.bar", + attribute: "FOOBAR", + value: "foo-bar" + } + ], + metric_parameters: [ + { + hostname: "sensu.cro-ngi", + metric: "argo.AMSPublisher-Check", + parameter: "-q", + value: "w:metrics+g:published180 -c 10" + }, + { + hostname: "epic5.storage.surfsara.nl", + metric: "generic.tcp.connect", + parameter: "-p", + value: "8004" + }, + { + hostname: "argo.eosc-portal.eu", + metric: "org.nagios.ARGOWeb-Status", + parameter: "-u", + value: "/eosc/report-status/Default/SERVICEGROUPS?accept=csv" + } + ] + } + + expect(helpers.downloadJSON).toHaveBeenCalledTimes(1) + expect(helpers.downloadJSON).toHaveBeenCalledWith(content, "local.json") + }) + test("Test save metric override with only name", async () => { renderChangeView() diff --git a/poem/Poem/frontend/react/__tests__/MetricProfiles.test.js b/poem/Poem/frontend/react/__tests__/MetricProfiles.test.js index a34e8c080..3c07cf974 100644 --- a/poem/Poem/frontend/react/__tests__/MetricProfiles.test.js +++ b/poem/Poem/frontend/react/__tests__/MetricProfiles.test.js @@ -1466,6 +1466,7 @@ describe('Tests for metric profiles changeview', () => { expect(metricInstances.getAllByTestId(/insert-/i)).toHaveLength(2); expect(screen.queryByText('Must be one of predefined metrics')).not.toBeInTheDocument() + }) test('Test import csv with nonexisting metrics', async () => { @@ -3549,7 +3550,7 @@ describe('Tests for metric profile addview', () => { expect(screen.queryByRole('button', { name: /delete/i })).not.toBeInTheDocument(); expect(screen.queryByRole('button', { name: /clone/i })).not.toBeInTheDocument(); - expect(screen.queryByRole('button', { name: /csv/i })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: /csv/i })).toBeInTheDocument(); }) test('Test that page renders properly for combined tenant', async () => { @@ -3598,7 +3599,7 @@ describe('Tests for metric profile addview', () => { expect(screen.queryByRole('button', { name: /delete/i })).not.toBeInTheDocument(); expect(screen.queryByRole('button', { name: /clone/i })).not.toBeInTheDocument(); - expect(screen.queryByRole('button', { name: /csv/i })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: /csv/i })).toBeInTheDocument(); }) test("Test add main profile info", async () => { @@ -3745,6 +3746,249 @@ describe('Tests for metric profile addview', () => { expect(row1.getAllByText("Select...")).toHaveLength(2) }) + test("Test import csv successfully", async () => { + mockAddMetricProfile.mockReturnValueOnce( + Promise.resolve({ + status: { + message: 'Metric profile Created', + code: '200' + }, + data: { + id: 'va0ahsh6-6rs0-14ho-xlh9-wahso4hie7iv', + links: { + self: 'string' + } + } + }) + ) + + renderAddView() + + await waitFor(() => { + expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() + }) + + fireEvent.change(screen.getByTestId('name'), { target: { value: 'NEW_PROFILE' } }); + fireEvent.change(screen.getByLabelText(/description/i), { target: { value: 'New central ARGO_MON profile.' } }); + + await waitFor(() => { + selectEvent.select(screen.getAllByText("Select...")[0], 'ARGO') + }) + + fireEvent.click(screen.getByRole('button', { name: /csv/i })); + fireEvent.click(screen.getByRole('menuitem', { name: /import/i })); + + const csv = 'service,metric\r\norg.opensciencegrid.htcondorce,ch.cern.HTCondorCE-JobState\r\norg.opensciencegrid.htcondorce,ch.cern.HTCondorCE-JobSubmit\r\n'; + + const content = new Blob([csv], { type: 'text/csv;charset=UTF-8' }); + const file = new File([content], 'profile.csv', { type: 'text/csv;charset=UTF-8' }); + const input = screen.getByTestId('file_input'); + + await waitFor(() => { + useEvent.upload(input, file); + }) + + await waitFor(() => { + expect(input.files[0]).toStrictEqual(file) + }) + expect(input.files.item(0)).toStrictEqual(file) + expect(input.files).toHaveLength(1) + + await waitFor(() => { + fireEvent.load(screen.getByTestId('file_input')) + }) + + const metricInstances = within(screen.getByRole('table')); + const rows = metricInstances.getAllByRole('row'); + expect(rows).toHaveLength(4); + const row1 = within(rows[2]) + const row2 = within(rows[3]) + + expect(row1.getByText("org.opensciencegrid.htcondorce")).toBeInTheDocument() + expect(row1.getByText("ch.cern.HTCondorCE-JobState")).toBeInTheDocument() + expect(row2.getByText("org.opensciencegrid.htcondorce")).toBeInTheDocument() + expect(row2.getByText("ch.cern.HTCondorCE-JobSubmit")).toBeInTheDocument() + + expect(metricInstances.getAllByTestId(/remove-/i)).toHaveLength(2); + expect(metricInstances.getAllByTestId(/insert-/i)).toHaveLength(2); + + expect(screen.queryByText('Must be one of predefined metrics')).not.toBeInTheDocument() + + fireEvent.click(screen.getByRole('button', { name: /save/i })) + await waitFor(() => { + expect(screen.getByRole('dialog', { title: /add/i })).toBeInTheDocument(); + }) + fireEvent.click(screen.getByRole('button', { name: /yes/i })); + + await waitFor(() => { + expect(mockAddMetricProfile).toHaveBeenCalledWith({ + description: 'New central ARGO_MON profile.', + name: 'NEW_PROFILE', + services: [ + { + service: "org.opensciencegrid.htcondorce", + metrics: [ + "ch.cern.HTCondorCE-JobState", + "ch.cern.HTCondorCE-JobSubmit" + ] + } + ] + }) + }) + }) + + test("Test import csv with nonexisting metrics", async () => { + renderAddView() + + await waitFor(() => { + expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() + }) + + fireEvent.click(screen.getByRole('button', { name: /csv/i })); + fireEvent.click(screen.getByRole('menuitem', { name: /import/i })); + + const csv = 'service,metric\r\norg.opensciencegrid.htcondorce,ch.cern.HTCondorCE-CertValidity\r\norg.opensciencegrid.htcondorce,ch.cern.HTCondorCE-JobSubmit\r\n'; + + const content = new Blob([csv], { type: 'text/csv;charset=UTF-8' }); + const file = new File([content], 'profile.csv', { type: 'text/csv;charset=UTF-8' }); + const input = screen.getByTestId('file_input'); + + await waitFor(() => { + useEvent.upload(input, file); + }) + + await waitFor(() => { + expect(input.files[0]).toStrictEqual(file) + }) + expect(input.files.item(0)).toStrictEqual(file) + expect(input.files).toHaveLength(1) + + await waitFor(() => { + fireEvent.load(screen.getByTestId('file_input')) + }) + + const metricInstances = within(screen.getByRole('table')); + const rows = metricInstances.getAllByRole('row'); + expect(rows).toHaveLength(4); + const row1 = within(rows[2]) + const row2 = within(rows[3]) + + expect(row1.getByText("org.opensciencegrid.htcondorce")).toBeInTheDocument() + expect(row1.getByText("ch.cern.HTCondorCE-CertValidity")).toBeInTheDocument() + expect(row2.getByText("org.opensciencegrid.htcondorce")).toBeInTheDocument() + expect(row2.getByText("ch.cern.HTCondorCE-JobSubmit")).toBeInTheDocument() + + expect(metricInstances.getAllByTestId(/remove-/i)).toHaveLength(2); + expect(metricInstances.getAllByTestId(/insert-/i)).toHaveLength(2); + + expect(screen.queryByText('Must be one of predefined metrics')).toBeInTheDocument() + }) + + test("Test import csv, make some changes and save profile successfully", async () => { + mockAddMetricProfile.mockReturnValueOnce( + Promise.resolve({ + status: { + message: 'Metric profile Created', + code: '200' + }, + data: { + id: 'va0ahsh6-6rs0-14ho-xlh9-wahso4hie7iv', + links: { + self: 'string' + } + } + }) + ) + + renderAddView() + + await waitFor(() => { + expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() + }) + + fireEvent.change(screen.getByTestId('name'), { target: { value: 'NEW_PROFILE' } }); + fireEvent.change(screen.getByLabelText(/description/i), { target: { value: 'New central ARGO_MON profile.' } }); + + await waitFor(() => { + selectEvent.select(screen.getAllByText("Select...")[0], 'ARGO') + }) + + fireEvent.click(screen.getByRole('button', { name: /csv/i })); + fireEvent.click(screen.getByRole('menuitem', { name: /import/i })); + + const csv = 'service,metric\r\norg.opensciencegrid.htcondorce,ch.cern.HTCondorCE-JobState\r\norg.opensciencegrid.htcondorce,ch.cern.HTCondorCE-JobSubmit\r\n'; + + const content = new Blob([csv], { type: 'text/csv;charset=UTF-8' }); + const file = new File([content], 'profile.csv', { type: 'text/csv;charset=UTF-8' }); + const input = screen.getByTestId('file_input'); + + await waitFor(() => { + useEvent.upload(input, file); + }) + + await waitFor(() => { + expect(input.files[0]).toStrictEqual(file) + }) + expect(input.files.item(0)).toStrictEqual(file) + expect(input.files).toHaveLength(1) + + await waitFor(() => { + fireEvent.load(screen.getByTestId('file_input')) + }) + + await waitFor(() => { + expect(within(screen.getByRole("table")).getAllByRole("row")).toHaveLength(4) + }) + + fireEvent.click(screen.getByTestId("insert-0")) + + const metricInstances = within(screen.getByRole('table')); + var rows = metricInstances.getAllByRole('row'); + const row2 = within(rows[3]) + + await waitFor(() => { + selectEvent.select(row2.getAllByText("Select...")[0], "eu.argo.ams") + }) + + await waitFor(() => { + selectEvent.select(row2.getAllByText("Select...")[1], "argo.AMS-Check") + }) + + await waitFor(() => { + expect(screen.queryByText(/duplicated/i)).not.toBeInTheDocument() + }) + + expect(screen.queryByText('Must be one of predefined metrics')).not.toBeInTheDocument() + + fireEvent.click(screen.getByRole('button', { name: /save/i })) + await waitFor(() => { + expect(screen.getByRole('dialog', { title: /add/i })).toBeInTheDocument(); + }) + fireEvent.click(screen.getByRole('button', { name: /yes/i })); + + await waitFor(() => { + expect(mockAddMetricProfile).toHaveBeenCalledWith({ + description: 'New central ARGO_MON profile.', + name: 'NEW_PROFILE', + services: [ + { + service: "eu.argo.ams", + metrics: [ + "argo.AMS-Check" + ] + }, + { + service: "org.opensciencegrid.htcondorce", + metrics: [ + "ch.cern.HTCondorCE-JobState", + "ch.cern.HTCondorCE-JobSubmit" + ] + } + ] + }) + }) + }) + test("Test importing tuples from constituting tenants for combined tenant", async () => { renderAddView(true) diff --git a/poem/Poem/frontend/react/__tests__/MetricTags.test.js b/poem/Poem/frontend/react/__tests__/MetricTags.test.js index 10951d4cd..31b56f4d2 100644 --- a/poem/Poem/frontend/react/__tests__/MetricTags.test.js +++ b/poem/Poem/frontend/react/__tests__/MetricTags.test.js @@ -7,6 +7,7 @@ import { MetricTagsComponent, MetricTagsList } from "../MetricTags" import { Backend } from "../DataManager" import selectEvent from "react-select-event" import { NotificationManager } from "react-notifications" +import useEvent from '@testing-library/user-event'; jest.mock("../DataManager", () => { @@ -45,22 +46,27 @@ const mockListTags = [ { id: "3", name: "internal", - metrics: ["argo.AMSPublisher-Check"] + metrics: [ + { "name": "argo.AMSPublisher-Check" } + ] }, { id: "4", name: "harmonized", metrics: [ - "generic.certificate.validity", - "generic.http.connect", - "generic.tcp.connect", + { "name": "generic.certificate.validity" }, + { "name": "generic.http.connect" }, + { "name": "generic.tcp.connect" } ] }, { id: "2", name: "messaging", - metrics: ["argo.AMS-Check", "argo.AMSPublisher-Check"] - }, + metrics: [ + { "name": "argo.AMS-Check" }, + { "name": "argo.AMSPublisher-Check" } + ] + } ] const mockActiveSession = { @@ -445,6 +451,7 @@ describe("Test metric tags changeview", () => { expect(screen.getByRole("button", { name: /delete/i })).toBeInTheDocument() expect(screen.queryByRole("button", { name: /clone/i })).not.toBeInTheDocument() + expect(screen.getByRole("button", { name: /csv/i })).toBeInTheDocument() }) test("Test filter metrics", async () => { @@ -561,6 +568,49 @@ describe("Test metric tags changeview", () => { ) }) + test("Test change metric tags name in filtered view", async () => { + mockChangeObject.mockReturnValueOnce( + Promise.resolve({ ok: true, status: 201, statusText: "CREATED" }) + ) + + renderChangeView() + + await waitFor(() => { + expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() + }) + + fireEvent.change(screen.getByPlaceholderText(/search/i), { target: { value: "connect" } }) + + fireEvent.change(screen.getByTestId("name"), { target: { value: "test_tag" } }) + + await waitFor(() => { + expect(screen.getByTestId("form")).toHaveFormValues({name: "test_tag"}) + }) + + fireEvent.click(screen.getByRole('button', { name: /save/i })); + await waitFor(() => { + expect(screen.getByRole('dialog', { title: /change/i })).toBeInTheDocument(); + }) + fireEvent.click(screen.getByRole('button', { name: /yes/i })); + + await waitFor(() => { + expect(mockChangeObject).toHaveBeenCalledWith( + "/api/v2/internal/metrictags/", + { id: "4", name: "test_tag", metrics: ["generic.certificate.validity", "generic.http.connect", "generic.tcp.connect"] } + ) + }) + + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metrictags") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metric") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metrictemplate") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metrictags") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metric") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metrictemplate") + expect(NotificationManager.success).toHaveBeenCalledWith( + "Metric tag successfully changed", "Changed", 2000 + ) + }) + test("Test error changing metric tag with error message", async () => { mockChangeObject.mockImplementationOnce(() => { throw Error("400 BAD REQUEST: Metric tag with this name already exists.") @@ -654,17 +704,9 @@ describe("Test metric tags changeview", () => { expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() }) - await selectEvent.select(screen.getByText("generic.certificate.validity"), "argo.AMS-Check") - - fireEvent.click(screen.getByTestId("remove-1")) - - fireEvent.click(screen.getByTestId("insert-0")) - - const table = within(screen.getByRole("table")) - - const row2 = table.getAllByRole("row")[3] - - await selectEvent.select(within(row2).getByRole("combobox"), "argo.AMSPublisher-Check") + await waitFor(() => { + selectEvent.select(screen.getByText("generic.certificate.validity"), "argo.AMS-Check") + }) fireEvent.click(screen.getByRole('button', { name: /save/i })); await waitFor(() => { @@ -675,7 +717,7 @@ describe("Test metric tags changeview", () => { await waitFor(() => { expect(mockChangeObject).toHaveBeenCalledWith( "/api/v2/internal/metrictags/", - { id: "4", name: "harmonized", metrics: ["argo.AMS-Check", "argo.AMSPublisher-Check", "generic.tcp.connect"] } + { id: "4", name: "harmonized", metrics: ["argo.AMS-Check", "generic.http.connect", "generic.tcp.connect"] } ) }) @@ -690,14 +732,9 @@ describe("Test metric tags changeview", () => { ) }) - test("Test display warning messages", async () => { + test("Test change metrics for metric tag in filtered view", async () => { mockChangeObject.mockReturnValueOnce( - Promise.resolve({ - ok: true, - status: 201, - statusText: "CREATED", - detail: "Metric argo.AMSPublisher-Check does not exist." - }) + Promise.resolve({ ok: true, status: 201, statusText: "CREATED" }) ) renderChangeView() @@ -706,17 +743,9 @@ describe("Test metric tags changeview", () => { expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() }) - await selectEvent.select(screen.getByText("generic.certificate.validity"), "argo.AMS-Check") - - fireEvent.click(screen.getByTestId("remove-1")) - - fireEvent.click(screen.getByTestId("insert-0")) - - const table = within(screen.getByRole("table")) - - const row2 = table.getAllByRole("row")[3] + fireEvent.change(screen.getByPlaceholderText(/search/i), { target: { value: "connect" } }) - await selectEvent.select(within(row2).getByRole("combobox"), "argo.AMSPublisher-Check") + await selectEvent.select(screen.getByText("generic.http.connect"), "argo.AMS-Check") fireEvent.click(screen.getByRole('button', { name: /save/i })); await waitFor(() => { @@ -727,7 +756,7 @@ describe("Test metric tags changeview", () => { await waitFor(() => { expect(mockChangeObject).toHaveBeenCalledWith( "/api/v2/internal/metrictags/", - { id: "4", name: "harmonized", metrics: ["argo.AMS-Check", "argo.AMSPublisher-Check", "generic.tcp.connect"] } + { id: "4", name: "harmonized", metrics: ["generic.certificate.validity", "argo.AMS-Check", "generic.tcp.connect"] } ) }) @@ -740,25 +769,11 @@ describe("Test metric tags changeview", () => { expect(NotificationManager.success).toHaveBeenCalledWith( "Metric tag successfully changed", "Changed", 2000 ) - expect(NotificationManager.warning).toHaveBeenCalledWith( -
-

Metric argo.AMSPublisher-Check does not exist.

-

Click to dismiss.

-
, - "Warning", - 0, - expect.any(Function) - ) }) - test("Test display multiple warning messages", async () => { + test("Test delete metrics from metric tag", async () => { mockChangeObject.mockReturnValueOnce( - Promise.resolve({ - ok: true, - status: 201, - statusText: "CREATED", - detail: "Error syncing metric tags\nMetric argo.AMSPublisher-Check does not exist." - }) + Promise.resolve({ ok: true, status: 201, statusText: "CREATED" }) ) renderChangeView() @@ -767,18 +782,8 @@ describe("Test metric tags changeview", () => { expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() }) - await selectEvent.select(screen.getByText("generic.certificate.validity"), "argo.AMS-Check") - fireEvent.click(screen.getByTestId("remove-1")) - fireEvent.click(screen.getByTestId("insert-0")) - - const table = within(screen.getByRole("table")) - - const row2 = table.getAllByRole("row")[3] - - await selectEvent.select(within(row2).getByRole("combobox"), "argo.AMSPublisher-Check") - fireEvent.click(screen.getByRole('button', { name: /save/i })); await waitFor(() => { expect(screen.getByRole('dialog', { title: /change/i })).toBeInTheDocument(); @@ -788,7 +793,7 @@ describe("Test metric tags changeview", () => { await waitFor(() => { expect(mockChangeObject).toHaveBeenCalledWith( "/api/v2/internal/metrictags/", - { id: "4", name: "harmonized", metrics: ["argo.AMS-Check", "argo.AMSPublisher-Check", "generic.tcp.connect"] } + { id: "4", name: "harmonized", metrics: ["generic.certificate.validity", "generic.tcp.connect"] } ) }) @@ -801,30 +806,11 @@ describe("Test metric tags changeview", () => { expect(NotificationManager.success).toHaveBeenCalledWith( "Metric tag successfully changed", "Changed", 2000 ) - expect(NotificationManager.warning).toHaveBeenCalledWith( -
-

Metric argo.AMSPublisher-Check does not exist.

-

Click to dismiss.

-
, - "Warning", - 0, - expect.any(Function) - ) - - expect(NotificationManager.warning).toHaveBeenCalledWith( -
-

Error syncing metric tags

-

Click to dismiss.

-
, - "Warning", - 0, - expect.any(Function) - ) }) - test("Test delete metric tag", async () => { - mockDeleteObject.mockReturnValueOnce( - Promise.resolve({ ok: true, status: 204, statusText: "NO CONTENT" }) + test("Test delete metrics from metric tag if filtered view", async () => { + mockChangeObject.mockReturnValueOnce( + Promise.resolve({ ok: true, status: 201, statusText: "CREATED" }) ) renderChangeView() @@ -833,15 +819,20 @@ describe("Test metric tags changeview", () => { expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() }) - fireEvent.click(screen.getByRole("button", { name: /delete/i })) + fireEvent.change(screen.getByPlaceholderText(/search/i), { target: { value: "connect" } }) + + fireEvent.click(screen.getByTestId("remove-0")) + + fireEvent.click(screen.getByRole('button', { name: /save/i })); await waitFor(() => { - expect(screen.getByRole('dialog', { title: /delete/i })).toBeInTheDocument(); + expect(screen.getByRole('dialog', { title: /change/i })).toBeInTheDocument(); }) fireEvent.click(screen.getByRole('button', { name: /yes/i })); await waitFor(() => { - expect(mockDeleteObject).toHaveBeenCalledWith( - "/api/v2/internal/metrictags/harmonized" + expect(mockChangeObject).toHaveBeenCalledWith( + "/api/v2/internal/metrictags/", + { id: "4", name: "harmonized", metrics: ["generic.certificate.validity", "generic.tcp.connect"] } ) }) @@ -852,13 +843,13 @@ describe("Test metric tags changeview", () => { expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metric") expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metrictemplate") expect(NotificationManager.success).toHaveBeenCalledWith( - "Metric tag successfully deleted", "Deleted", 2000 + "Metric tag successfully changed", "Changed", 2000 ) }) - test("Test delete metric tag if changed name", async () => { - mockDeleteObject.mockReturnValueOnce( - Promise.resolve({ ok: true, status: 204, statusText: "NO CONTENT" }) + test("Test insert new metrics for metric tag", async () => { + mockChangeObject.mockReturnValueOnce( + Promise.resolve({ ok: true, status: 201, statusText: "CREATED" }) ) renderChangeView() @@ -867,17 +858,30 @@ describe("Test metric tags changeview", () => { expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() }) - fireEvent.change(screen.getByTestId("name"), { target: { value: "test_tag" } }) + fireEvent.click(screen.getByTestId("insert-0")) + + const table = within(screen.getByRole("table")) + + const row2 = table.getAllByRole("row")[3] - fireEvent.click(screen.getByRole("button", { name: /delete/i })) await waitFor(() => { - expect(screen.getByRole('dialog', { title: /delete/i })).toBeInTheDocument(); + selectEvent.select(within(row2).getByRole("combobox"), "argo.AMSPublisher-Check") + }) + + await waitFor(() => { + expect(screen.queryByText("Must be one of predefined metrics")).not.toBeInTheDocument() + }) + + fireEvent.click(screen.getByRole('button', { name: /save/i })); + await waitFor(() => { + expect(screen.getByRole('dialog', { title: /change/i })).toBeInTheDocument(); }) fireEvent.click(screen.getByRole('button', { name: /yes/i })); await waitFor(() => { - expect(mockDeleteObject).toHaveBeenCalledWith( - "/api/v2/internal/metrictags/harmonized" + expect(mockChangeObject).toHaveBeenCalledWith( + "/api/v2/internal/metrictags/", + { id: "4", name: "harmonized", metrics: ["generic.certificate.validity", "argo.AMSPublisher-Check", "generic.http.connect", "generic.tcp.connect"] } ) }) @@ -888,14 +892,14 @@ describe("Test metric tags changeview", () => { expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metric") expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metrictemplate") expect(NotificationManager.success).toHaveBeenCalledWith( - "Metric tag successfully deleted", "Deleted", 2000 + "Metric tag successfully changed", "Changed", 2000 ) }) - test("Test error deleting metric tag with message", async () => { - mockDeleteObject.mockImplementationOnce( () => { - throw Error("400 BAD REQUEST: There has been an error.") - }) + test("Test insert new metrics for metric tag in filtered view", async () => { + mockChangeObject.mockReturnValueOnce( + Promise.resolve({ ok: true, status: 201, statusText: "CREATED" }) + ) renderChangeView() @@ -903,79 +907,559 @@ describe("Test metric tags changeview", () => { expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() }) - fireEvent.click(screen.getByRole("button", { name: /delete/i })) + fireEvent.change(screen.getByPlaceholderText(/search/i), { target: { value: "connect" } }) + + fireEvent.click(screen.getByTestId("insert-0")) + + const table = within(screen.getByRole("table")) + + const row2 = table.getAllByRole("row")[3] + await waitFor(() => { - expect(screen.getByRole('dialog', { title: /delete/i })).toBeInTheDocument(); + selectEvent.select(within(row2).getByRole("combobox"), "argo.AMSPublisher-Check") + }) + + await waitFor(() => { + expect(screen.queryByText("Must be one of predefined metrics")).not.toBeInTheDocument() + }) + + fireEvent.click(screen.getByRole('button', { name: /save/i })); + await waitFor(() => { + expect(screen.getByRole('dialog', { title: /change/i })).toBeInTheDocument(); }) fireEvent.click(screen.getByRole('button', { name: /yes/i })); await waitFor(() => { - expect(mockDeleteObject).toHaveBeenCalledWith( - "/api/v2/internal/metrictags/harmonized" + expect(mockChangeObject).toHaveBeenCalledWith( + "/api/v2/internal/metrictags/", + { id: "4", name: "harmonized", metrics: ["generic.certificate.validity", "generic.http.connect", "argo.AMSPublisher-Check", "generic.tcp.connect"] } ) }) - expect(queryClient.invalidateQueries).not.toHaveBeenCalled() - expect(NotificationManager.error).toHaveBeenCalledWith( -
-

400 BAD REQUEST: There has been an error.

-

Click to dismiss.

-
, - "Error", - 0, - expect.any(Function) + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metrictags") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metric") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metrictemplate") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metrictags") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metric") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metrictemplate") + expect(NotificationManager.success).toHaveBeenCalledWith( + "Metric tag successfully changed", "Changed", 2000 ) }) - test("Test error deleting metric tag without message", async () => { - mockDeleteObject.mockImplementationOnce( () => { throw Error() } ) - + test("Test import csv successfully", async () => { renderChangeView() await waitFor(() => { expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() }) - fireEvent.click(screen.getByRole("button", { name: /delete/i })) + fireEvent.click(screen.getByRole("button", { name: /csv/i })) + fireEvent.click(screen.getByRole("menuitem", { name: /import/i })) + + const csv = 'name\r\nargo.AMS-Check\r\nargo.AMSPublisher-Check\r\n'; + + const content = new Blob([csv], { type: "text/csv;charset=UTF-8" }) + const file = new File([content], "harmonized.csv", { type: "text/csv;charset=UTF-8" }) + const input = screen.getByTestId("file_input") + + await waitFor(() => { + useEvent.upload(input, file) + }) + await waitFor(() => { - expect(screen.getByRole('dialog', { title: /delete/i })).toBeInTheDocument(); + expect(input.files[0]).toStrictEqual(file) }) - fireEvent.click(screen.getByRole('button', { name: /yes/i })); + expect(input.files.item(0)).toStrictEqual(file) + expect(input.files).toHaveLength(1) await waitFor(() => { - expect(mockDeleteObject).toHaveBeenCalledWith( - "/api/v2/internal/metrictags/harmonized" - ) + fireEvent.load(screen.getByTestId("file_input")) }) - expect(queryClient.invalidateQueries).not.toHaveBeenCalled() - expect(NotificationManager.error).toHaveBeenCalledWith( -
-

Error deleting metric tag

-

Click to dismiss.

-
, - "Error", - 0, - expect.any(Function) - ) - }) -}) + const nameField = screen.getByTestId("name") + expect(nameField.value).toBe("harmonized") + expect(nameField).toBeEnabled() -describe("Test metric tags addview", () => { - jest.spyOn(NotificationManager, "success") - jest.spyOn(NotificationManager, "error") - jest.spyOn(NotificationManager, "warning") - jest.spyOn(queryClient, "invalidateQueries") + expect(screen.getByPlaceholderText(/search/i)).toBeInTheDocument() + const table = within(screen.getByRole("table")) - beforeEach(() => { - Backend.mockImplementation(() => { - return { - fetchData: (path) => { - switch (path) { - case "/api/v2/internal/metrictemplates": - return Promise.resolve(mockMetricTemplates) - } + expect(table.getAllByRole("columnheader")).toHaveLength(3) + expect(table.getByRole("columnheader", { name: "#" })).toBeInTheDocument() + expect(table.getByRole("columnheader", { name: /metric/i }).textContent).toBe("Metric template") + expect(table.getByRole("columnheader", { name: /action/i }).textContent).toBe("Actions") + + await waitFor(() => { + expect(table.getAllByRole("row")).toHaveLength(4) + }) + expect(table.getAllByTestId(/remove-/i)).toHaveLength(2) + expect(table.getAllByTestId(/insert-/i)).toHaveLength(2) + + expect(table.queryByText("generic.certificate.validity")).not.toBeInTheDocument() + expect(table.queryByText("generic.http.connect")).not.toBeInTheDocument() + expect(table.queryByText("generic.tcp.connect")).not.toBeInTheDocument() + + expect(table.getByText("argo.AMS-Check")).toBeInTheDocument() + expect(table.getByText("argo.AMSPublisher-Check")).toBeInTheDocument() + + selectEvent.openMenu(table.getByText("argo.AMS-Check")) + expect(table.getAllByText("generic.certificate.validity")).toHaveLength(1) + expect(table.getAllByText("generic.http.connect")).toHaveLength(1) + expect(table.getAllByText("generic.tcp.connect")).toHaveLength(1) + expect(table.getAllByText("argo.AMS-Check")).toHaveLength(1) + expect(table.getAllByText("argo.AMSPublisher-Check")).toHaveLength(1) + + expect(screen.getByRole("button", { name: /delete/i })).toBeInTheDocument() + expect(screen.queryByRole("button", { name: /clone/i })).not.toBeInTheDocument() + }) + + test("Test import csv with nonexisting metrics", async () => { + renderChangeView() + + await waitFor(() => { + expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() + }) + + fireEvent.click(screen.getByRole("button", { name: /csv/i })) + fireEvent.click(screen.getByRole("menuitem", { name: /import/i })) + + const csv = 'name\r\nargo.AMS-Check\r\nargo.AMSPublisher-Check\r\nmock.metric.name\r\n'; + + const content = new Blob([csv], { type: "text/csv;charset=UTF-8" }) + const file = new File([content], "harmonized.csv", { type: "text/csv;charset=UTF-8" }) + const input = screen.getByTestId("file_input") + + await waitFor(() => { + useEvent.upload(input, file) + }) + + await waitFor(() => { + expect(input.files[0]).toStrictEqual(file) + }) + expect(input.files.item(0)).toStrictEqual(file) + expect(input.files).toHaveLength(1) + + await waitFor(() => { + fireEvent.load(screen.getByTestId("file_input")) + }) + + const nameField = screen.getByTestId("name") + + expect(nameField.value).toBe("harmonized") + expect(nameField).toBeEnabled() + + expect(screen.getByPlaceholderText(/search/i)).toBeInTheDocument() + const table = within(screen.getByRole("table")) + + expect(table.getAllByRole("columnheader")).toHaveLength(3) + expect(table.getByRole("columnheader", { name: "#" })).toBeInTheDocument() + expect(table.getByRole("columnheader", { name: /metric/i }).textContent).toBe("Metric template") + expect(table.getByRole("columnheader", { name: /action/i }).textContent).toBe("Actions") + + await waitFor(() => { + expect(table.getAllByRole("row")).toHaveLength(5) + }) + expect(table.getAllByTestId(/remove-/i)).toHaveLength(3) + expect(table.getAllByTestId(/insert-/i)).toHaveLength(3) + + expect(table.queryByText("generic.certificate.validity")).not.toBeInTheDocument() + expect(table.queryByText("generic.http.connect")).not.toBeInTheDocument() + expect(table.queryByText("generic.tcp.connect")).not.toBeInTheDocument() + + expect(table.getByText("argo.AMS-Check")).toBeInTheDocument() + expect(table.getByText("argo.AMSPublisher-Check")).toBeInTheDocument() + expect(table.getByText("mock.metric.name")).toBeInTheDocument() + + selectEvent.openMenu(table.getByText("argo.AMS-Check")) + expect(table.getAllByText("generic.certificate.validity")).toHaveLength(1) + expect(table.getAllByText("generic.http.connect")).toHaveLength(1) + expect(table.getAllByText("generic.tcp.connect")).toHaveLength(1) + expect(table.getAllByText("argo.AMS-Check")).toHaveLength(1) + expect(table.getAllByText("argo.AMSPublisher-Check")).toHaveLength(1) + + await waitFor(() => { + expect(screen.queryByText('Must be one of predefined metrics')).toBeInTheDocument() + }) + + expect(screen.getByRole("button", { name: /delete/i })).toBeInTheDocument() + expect(screen.queryByRole("button", { name: /clone/i })).not.toBeInTheDocument() + }) + + test("Test export csv successfully", async () => { + const helpers = require("../FileDownload") + jest.spyOn(helpers, "downloadCSV").mockReturnValueOnce(null) + + renderChangeView() + + await waitFor(() => { + expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() + }) + + fireEvent.click(screen.getByRole("button", { name: /csv/i })) + fireEvent.click(screen.getByRole("menuitem", { name: /export/i })) + + const content = "name\r\ngeneric.certificate.validity\r\ngeneric.http.connect\r\ngeneric.tcp.connect" + + expect(helpers.downloadCSV).toHaveBeenCalledTimes(1) + expect(helpers.downloadCSV).toHaveBeenCalledWith(content, "harmonized.csv") + }) + + test("Export csv when form has been changed", async () => { + const helpers = require("../FileDownload") + jest.spyOn(helpers, "downloadCSV").mockReturnValueOnce(null) + + renderChangeView() + + await waitFor(() => { + expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() + }) + + await waitFor(() => { + selectEvent.select(screen.getByText("generic.certificate.validity"), "argo.AMS-Check") + }) + + await waitFor(() => { + expect(screen.getByText("argo.AMS-Check")).toBeInTheDocument() + }) + + fireEvent.click(screen.getByTestId("remove-1")) + + fireEvent.click(screen.getByTestId("insert-0")) + + const table = within(screen.getByRole("table")) + + const row2 = table.getAllByRole("row")[3] + + await waitFor(() => { + selectEvent.select(within(row2).getByRole("combobox"), "argo.AMSPublisher-Check") + }) + + await waitFor(() => { + expect(screen.queryByText("Must be one of predefined metrics")).not.toBeInTheDocument() + }) + + fireEvent.click(screen.getByRole("button", { name: /csv/i })) + fireEvent.click(screen.getByRole("menuitem", { name: /export/i })) + + const content = "name\r\nargo.AMS-Check\r\nargo.AMSPublisher-Check\r\ngeneric.tcp.connect" + + expect(helpers.downloadCSV).toHaveBeenCalledTimes(1) + expect(helpers.downloadCSV).toHaveBeenCalledWith(content, "harmonized.csv") + }) + + test("Test display warning messages", async () => { + mockChangeObject.mockReturnValueOnce( + Promise.resolve({ + ok: true, + status: 201, + statusText: "CREATED", + detail: "Metric argo.AMSPublisher-Check does not exist." + }) + ) + + renderChangeView() + + await waitFor(() => { + expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() + }) + + await waitFor(() => { + selectEvent.select(screen.getByText("generic.certificate.validity"), "argo.AMS-Check") + }) + + await waitFor(() => { + expect(screen.getByText("argo.AMS-Check")).toBeInTheDocument() + }) + + fireEvent.click(screen.getByTestId("remove-1")) + + fireEvent.click(screen.getByTestId("insert-0")) + + const table = within(screen.getByRole("table")) + + const row2 = table.getAllByRole("row")[3] + + await waitFor(() => { + selectEvent.select(within(row2).getByRole("combobox"), "argo.AMSPublisher-Check") + }) + + await waitFor(() => { + expect(screen.getByText("argo.AMSPublisher-Check")).toBeInTheDocument() + }) + + fireEvent.click(screen.getByRole('button', { name: /save/i })); + await waitFor(() => { + expect(screen.getByRole('dialog', { title: /change/i })).toBeInTheDocument(); + }) + fireEvent.click(screen.getByRole('button', { name: /yes/i })); + + await waitFor(() => { + expect(mockChangeObject).toHaveBeenCalledWith( + "/api/v2/internal/metrictags/", + { id: "4", name: "harmonized", metrics: ["argo.AMS-Check", "argo.AMSPublisher-Check", "generic.tcp.connect"] } + ) + }) + + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metrictags") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metric") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metrictemplate") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metrictags") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metric") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metrictemplate") + expect(NotificationManager.success).toHaveBeenCalledWith( + "Metric tag successfully changed", "Changed", 2000 + ) + expect(NotificationManager.warning).toHaveBeenCalledWith( +
+

Metric argo.AMSPublisher-Check does not exist.

+

Click to dismiss.

+
, + "Warning", + 0, + expect.any(Function) + ) + }) + + test("Test display multiple warning messages", async () => { + mockChangeObject.mockReturnValueOnce( + Promise.resolve({ + ok: true, + status: 201, + statusText: "CREATED", + detail: "Error syncing metric tags\nMetric argo.AMSPublisher-Check does not exist." + }) + ) + + renderChangeView() + + await waitFor(() => { + expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() + }) + + await selectEvent.select(screen.getByText("generic.certificate.validity"), "argo.AMS-Check") + + await waitFor(() => { + expect(screen.getByText("argo.AMS-Check")).toBeInTheDocument() + }) + + fireEvent.click(screen.getByTestId("remove-1")) + + fireEvent.click(screen.getByTestId("insert-0")) + + const table = within(screen.getByRole("table")) + + const row2 = table.getAllByRole("row")[3] + + await waitFor(() => { + selectEvent.select(within(row2).getByRole("combobox"), "argo.AMSPublisher-Check") + }) + + await waitFor(() => { + expect(screen.getByText("argo.AMSPublisher-Check")).toBeInTheDocument() + }) + + await waitFor(() => { + expect(screen.queryByText("Must be one of predefined metrics")).not.toBeInTheDocument() + }) + + fireEvent.click(screen.getByRole('button', { name: /save/i })); + await waitFor(() => { + expect(screen.getByRole('dialog', { title: /change/i })).toBeInTheDocument(); + }) + fireEvent.click(screen.getByRole('button', { name: /yes/i })); + + await waitFor(() => { + expect(mockChangeObject).toHaveBeenCalledWith( + "/api/v2/internal/metrictags/", + { id: "4", name: "harmonized", metrics: ["argo.AMS-Check", "argo.AMSPublisher-Check", "generic.tcp.connect"] } + ) + }) + + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metrictags") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metric") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metrictemplate") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metrictags") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metric") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metrictemplate") + expect(NotificationManager.success).toHaveBeenCalledWith( + "Metric tag successfully changed", "Changed", 2000 + ) + expect(NotificationManager.warning).toHaveBeenCalledWith( +
+

Metric argo.AMSPublisher-Check does not exist.

+

Click to dismiss.

+
, + "Warning", + 0, + expect.any(Function) + ) + + expect(NotificationManager.warning).toHaveBeenCalledWith( +
+

Error syncing metric tags

+

Click to dismiss.

+
, + "Warning", + 0, + expect.any(Function) + ) + }) + + test("Test delete metric tag", async () => { + mockDeleteObject.mockReturnValueOnce( + Promise.resolve({ ok: true, status: 204, statusText: "NO CONTENT" }) + ) + + renderChangeView() + + await waitFor(() => { + expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() + }) + + fireEvent.click(screen.getByRole("button", { name: /delete/i })) + await waitFor(() => { + expect(screen.getByRole('dialog', { title: /delete/i })).toBeInTheDocument(); + }) + fireEvent.click(screen.getByRole('button', { name: /yes/i })); + + await waitFor(() => { + expect(mockDeleteObject).toHaveBeenCalledWith( + "/api/v2/internal/metrictags/harmonized" + ) + }) + + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metrictags") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metric") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metrictemplate") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metrictags") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metric") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metrictemplate") + expect(NotificationManager.success).toHaveBeenCalledWith( + "Metric tag successfully deleted", "Deleted", 2000 + ) + }) + + test("Test delete metric tag if changed name", async () => { + mockDeleteObject.mockReturnValueOnce( + Promise.resolve({ ok: true, status: 204, statusText: "NO CONTENT" }) + ) + + renderChangeView() + + await waitFor(() => { + expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() + }) + + fireEvent.change(screen.getByTestId("name"), { target: { value: "test_tag" } }) + + fireEvent.click(screen.getByRole("button", { name: /delete/i })) + await waitFor(() => { + expect(screen.getByRole('dialog', { title: /delete/i })).toBeInTheDocument(); + }) + fireEvent.click(screen.getByRole('button', { name: /yes/i })); + + await waitFor(() => { + expect(mockDeleteObject).toHaveBeenCalledWith( + "/api/v2/internal/metrictags/harmonized" + ) + }) + + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metrictags") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metric") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metrictemplate") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metrictags") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metric") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metrictemplate") + expect(NotificationManager.success).toHaveBeenCalledWith( + "Metric tag successfully deleted", "Deleted", 2000 + ) + }) + + test("Test error deleting metric tag with message", async () => { + mockDeleteObject.mockImplementationOnce( () => { + throw Error("400 BAD REQUEST: There has been an error.") + }) + + renderChangeView() + + await waitFor(() => { + expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() + }) + + fireEvent.click(screen.getByRole("button", { name: /delete/i })) + await waitFor(() => { + expect(screen.getByRole('dialog', { title: /delete/i })).toBeInTheDocument(); + }) + fireEvent.click(screen.getByRole('button', { name: /yes/i })); + + await waitFor(() => { + expect(mockDeleteObject).toHaveBeenCalledWith( + "/api/v2/internal/metrictags/harmonized" + ) + }) + + expect(queryClient.invalidateQueries).not.toHaveBeenCalled() + expect(NotificationManager.error).toHaveBeenCalledWith( +
+

400 BAD REQUEST: There has been an error.

+

Click to dismiss.

+
, + "Error", + 0, + expect.any(Function) + ) + }) + + test("Test error deleting metric tag without message", async () => { + mockDeleteObject.mockImplementationOnce( () => { throw Error() } ) + + renderChangeView() + + await waitFor(() => { + expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() + }) + + fireEvent.click(screen.getByRole("button", { name: /delete/i })) + await waitFor(() => { + expect(screen.getByRole('dialog', { title: /delete/i })).toBeInTheDocument(); + }) + fireEvent.click(screen.getByRole('button', { name: /yes/i })); + + await waitFor(() => { + expect(mockDeleteObject).toHaveBeenCalledWith( + "/api/v2/internal/metrictags/harmonized" + ) + }) + + expect(queryClient.invalidateQueries).not.toHaveBeenCalled() + expect(NotificationManager.error).toHaveBeenCalledWith( +
+

Error deleting metric tag

+

Click to dismiss.

+
, + "Error", + 0, + expect.any(Function) + ) + }) +}) + + +describe("Test metric tags addview", () => { + jest.spyOn(NotificationManager, "success") + jest.spyOn(NotificationManager, "error") + jest.spyOn(NotificationManager, "warning") + jest.spyOn(queryClient, "invalidateQueries") + + beforeEach(() => { + Backend.mockImplementation(() => { + return { + fetchData: (path) => { + switch (path) { + case "/api/v2/internal/metrictemplates": + return Promise.resolve(mockMetricTemplates) + } }, isActiveSession: () => Promise.resolve(mockActiveSession), addObject: mockAddObject @@ -1018,6 +1502,11 @@ describe("Test metric tags addview", () => { expect(table.queryByText(/generic/i)).not.toBeInTheDocument() expect(table.queryByText(/argo/i)).not.toBeInTheDocument() + expect(table.queryByText("generic.certificate.validity")).not.toBeInTheDocument() + expect(table.queryByText("generic.http.connect")).not.toBeInTheDocument() + expect(table.queryByText("generic.tcp.connect")).not.toBeInTheDocument() + expect(table.queryByText("argo.AMS-Check")).not.toBeInTheDocument() + expect(table.queryByText("argo.AMSPublisher-Check")).not.toBeInTheDocument() selectEvent.openMenu(input) expect(table.getByText("generic.certificate.validity")).toBeInTheDocument() expect(table.getByText("generic.http.connect")).toBeInTheDocument() @@ -1027,6 +1516,7 @@ describe("Test metric tags addview", () => { expect(screen.queryByRole("button", { name: /delete/i })).not.toBeInTheDocument() expect(screen.queryByRole("button", { name: /clone/i })).not.toBeInTheDocument() + expect(screen.getByRole("button", { name: /csv/i })).toBeInTheDocument() }) test("Test change metric tags name", async () => { @@ -1094,24 +1584,46 @@ describe("Test metric tags addview", () => { const row1 = table.getAllByRole("row")[2] const input1 = within(row1).getByRole("combobox") - await selectEvent.select(input1, "generic.certificate.validity") + await waitFor(() => { + selectEvent.select(input1, "generic.certificate.validity") + }) + + await waitFor(() => { + expect(table.queryByText("generic.http.connect")).not.toBeInTheDocument() + }) fireEvent.click(table.getByTestId("insert-0")) const row2 = table.getAllByRole("row")[3] const input2 = within(row2).getByRole("combobox") - await selectEvent.select(input2, "generic.http.connect") + await waitFor(() => { + selectEvent.select(input2, "generic.http.connect") + }) + + await waitFor(() => { + expect(table.queryByText("generic.tcp.connect")).not.toBeInTheDocument() + }) fireEvent.click(table.getByTestId("insert-1")) const row3 = table.getAllByRole("row")[4] const input3 = within(row3).getByRole("combobox") - await selectEvent.select(input3, "generic.tcp.connect") + await waitFor(() => { + selectEvent.select(input3, "generic.tcp.connect") + }) + + await waitFor(() => { + expect(table.getByText("generic.tcp.connect")).toBeInTheDocument() + }) fireEvent.click(table.getByTestId("remove-1")) + await waitFor(() => { + expect(table.queryByText("generic.http.connect")).not.toBeInTheDocument() + }) + await selectEvent.select(table.getByText("generic.tcp.connect"), "argo.AMS-Check") fireEvent.click(screen.getByRole('button', { name: /save/i })); @@ -1138,6 +1650,141 @@ describe("Test metric tags addview", () => { ) }) + test("Test import csv successfully", async () => { + mockAddObject.mockReturnValueOnce( + Promise.resolve({ ok: true, status: 201, statusText: "CREATED" }) + ) + + renderAddView() + + await waitFor(() => { + expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() + }) + + const csv = 'name\r\nargo.AMS-Check\r\nargo.AMSPublisher-Check\r\n'; + + const content = new Blob([csv], { type: "text/csv;charset=UTF-8" }) + const file = new File([content], "harmonized.csv", { type: "text/csv;charset=UTF-8" }) + const input = screen.getByTestId("file_input") + + await waitFor(() => { + useEvent.upload(input, file) + }) + + await waitFor(() => { + expect(input.files[0]).toStrictEqual(file) + }) + expect(input.files.item(0)).toStrictEqual(file) + expect(input.files).toHaveLength(1) + + await waitFor(() => { + fireEvent.load(screen.getByTestId("file_input")) + }) + + fireEvent.change(screen.getByTestId("name"), { target: { value: "test_tag" } }) + + fireEvent.click(screen.getByRole('button', { name: /save/i })); + await waitFor(() => { + expect(screen.getByRole('dialog', { title: /add/i })).toBeInTheDocument(); + }) + fireEvent.click(screen.getByRole('button', { name: /yes/i })); + + await waitFor(() => { + expect(mockAddObject).toHaveBeenCalledWith( + "/api/v2/internal/metrictags/", + { name: "test_tag", metrics: ["argo.AMS-Check", "argo.AMSPublisher-Check"] } + ) + }) + + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metrictags") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metric") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metrictemplate") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metrictags") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metric") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metrictemplate") + expect(NotificationManager.success).toHaveBeenCalledWith( + "Metric tag successfully added", "Added", 2000 + ) + }) + + test("Test import csv, make some changes, and save", async () => { + mockAddObject.mockReturnValueOnce( + Promise.resolve({ ok: true, status: 201, statusText: "CREATED" }) + ) + + renderAddView() + + await waitFor(() => { + expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() + }) + + const csv = 'name\r\nargo.AMS-Check\r\nargo.AMSPublisher-Check\r\n'; + + const content = new Blob([csv], { type: "text/csv;charset=UTF-8" }) + const file = new File([content], "harmonized.csv", { type: "text/csv;charset=UTF-8" }) + const input = screen.getByTestId("file_input") + + await waitFor(() => { + useEvent.upload(input, file) + }) + + await waitFor(() => { + expect(input.files[0]).toStrictEqual(file) + }) + expect(input.files.item(0)).toStrictEqual(file) + expect(input.files).toHaveLength(1) + + await waitFor(() => { + fireEvent.load(screen.getByTestId("file_input")) + }) + + fireEvent.change(screen.getByTestId("name"), { target: { value: "test_tag" } }) + + const table = within(screen.getByRole("table")) + + fireEvent.click(table.getByTestId("insert-1")) + + const row3 = table.getAllByRole("row")[4] + const input3 = within(row3).getByRole("combobox") + + await waitFor(() => { + selectEvent.select(input3, "generic.tcp.connect") + }) + + await waitFor(() => { + expect(table.getByText("generic.tcp.connect")).toBeInTheDocument() + }) + + fireEvent.click(table.getByTestId("remove-1")) + + await waitFor(() => { + expect(table.queryByText("argo.AMSPublisher-Check")).not.toBeInTheDocument() + }) + + fireEvent.click(screen.getByRole('button', { name: /save/i })); + await waitFor(() => { + expect(screen.getByRole('dialog', { title: /add/i })).toBeInTheDocument(); + }) + fireEvent.click(screen.getByRole('button', { name: /yes/i })); + + await waitFor(() => { + expect(mockAddObject).toHaveBeenCalledWith( + "/api/v2/internal/metrictags/", + { name: "test_tag", metrics: ["argo.AMS-Check", "generic.tcp.connect"] } + ) + }) + + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metrictags") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metric") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("metrictemplate") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metrictags") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metric") + expect(queryClient.invalidateQueries).toHaveBeenCalledWith("public_metrictemplate") + expect(NotificationManager.success).toHaveBeenCalledWith( + "Metric tag successfully added", "Added", 2000 + ) + }) + test("Test display warning messages", async () => { mockAddObject.mockReturnValueOnce( Promise.resolve({ @@ -1161,13 +1808,25 @@ describe("Test metric tags addview", () => { const row1 = table.getAllByRole("row")[2] const input1 = within(row1).getByRole("combobox") - await selectEvent.select(input1, "generic.tcp.connect") + await waitFor(() => { + selectEvent.select(input1, "generic.tcp.connect") + }) + + await waitFor(() => { + expect(table.getByText("generic.tcp.connect")).toBeInTheDocument() + }) fireEvent.click(table.getByTestId("insert-0")) const row2 = table.getAllByRole("row")[3] - await selectEvent.select(within(row2).getByRole("combobox"), "generic.http.connect") + await waitFor(() => { + selectEvent.select(within(row2).getByRole("combobox"), "generic.http.connect") + }) + + await waitFor(() => { + expect(table.getByText("generic.http.connect")).toBeInTheDocument() + }) fireEvent.click(screen.getByRole('button', { name: /save/i })); await waitFor(() => { @@ -1225,13 +1884,25 @@ describe("Test metric tags addview", () => { const row1 = table.getAllByRole("row")[2] const input1 = within(row1).getByRole("combobox") - await selectEvent.select(input1, "generic.tcp.connect") + await waitFor(() => { + selectEvent.select(input1, "generic.tcp.connect") + }) + + await waitFor(() => { + expect(table.getByText("generic.tcp.connect")).toBeInTheDocument() + }) fireEvent.click(table.getByTestId("insert-0")) const row2 = table.getAllByRole("row")[3] - await selectEvent.select(within(row2).getByRole("combobox"), "generic.http.connect") + await waitFor(() => { + selectEvent.select(within(row2).getByRole("combobox"), "generic.http.connect") + }) + + await waitFor(() => { + expect(table.getByText("generic.http.connect")).toBeInTheDocument() + }) fireEvent.click(screen.getByRole('button', { name: /save/i })); await waitFor(() => { diff --git a/poem/Poem/frontend/react/__tests__/MetricTemplates.test.js b/poem/Poem/frontend/react/__tests__/MetricTemplates.test.js index c6bfbe029..cb716dfdf 100644 --- a/poem/Poem/frontend/react/__tests__/MetricTemplates.test.js +++ b/poem/Poem/frontend/react/__tests__/MetricTemplates.test.js @@ -292,6 +292,76 @@ const mockMetricTemplate = { fileparameter: [] }; +const mockMetricTemplateWithDependency = { + id: 422, + name: "srce.gridproxy.validity", + mtype: "Active", + tags: [ + "test_tag1", + "test_tag2", + "internal" + ], + probeversion: "GridProxy-probe (0.2.0)", + description: "", + parent: "", + probeexecutable: "GridProxy-probe", + config: [ + { + key: "maxCheckAttempts", + value: "3" + }, + { + key: "timeout", + value: "30" + }, + { + key: "path", + value: "/usr/libexec/argo/probes/globus" + }, + { + key: "interval", + value: "15" + }, + { + key: "retryInterval", + value: "3" + } + ], + attribute: [ + { + key: "VONAME", + value: "--vo" + }, + { + key: "X509_USER_PROXY", + value: "-x" + } + ], + dependency: [ + { + key: "hr.srce.GridProxy-Get", + value: "0" + } + ], + flags: [ + { + key: "NOHOSTNAME", + value: "1" + }, + { + key: "VO", + value: "1" + }, + { + key: "NOPUBLISH", + value: "1" + } + ], + files: [], + parameter: [], + fileparameter: [] +} + const mockProbeVersions = [ { id: '3', @@ -377,7 +447,24 @@ const mockProbeVersions = [ date_created: '2020-12-31 08:57:15', comment: 'Newest version', version: '0.1.13' - } + }, + { + id: '64', + object_repr: "GridProxy-probe (0.2.0)", + fields: { + name: "GridProxy-probe", + version: "0.2.0", + package: "argo-probe-globus (0.2.0)", + description: "Probe for functional checking of MyProxy service.", + comment: "Harmonized version.", + repository: "https://github.com/ARGOeu-Metrics/argo-probe-globus", + docurl: "https://github.com/ARGOeu-Metrics/argo-probe-globus/blob/master/README.md" + }, + user: "poem", + date_created: "2019-12-09 09:24:20", + comment: "Initial version.", + version: "0.2.0" +}, ]; const mockPassiveMetricTemplate = { @@ -647,8 +734,11 @@ function renderTenantListView() { function renderChangeView(options = {}) { const passive = options.passive ? options.passive : false; const publicView = options.publicView ? options.publicView : false; + const withDependency = options.withDependency ? options.withDependency : false - const route = `/ui/${publicView ? 'public_' : ''}metrictemplates/${passive ? 'org.apel.APEL-Pub' : 'argo.AMS-Check'}`; + const metric = withDependency ? "srce.gridproxy.validity" : passive ? "org.apel.APEL-Pub" : "argo.AMS-Check" + + const route = `/ui/${publicView ? 'public_' : ''}metrictemplates/${metric}` if (publicView) return { @@ -1374,6 +1464,12 @@ describe('Test metric template changeview on SuperPOEM', () => { case '/api/v2/internal/public_metrictemplates/argo.AMS-Check': return Promise.resolve(mockMetricTemplate) + case '/api/v2/internal/metrictemplates/srce.gridproxy.validity': + return Promise.resolve(mockMetricTemplateWithDependency) + + case '/api/v2/internal/public_metrictemplates/srce.gridproxy.validity': + return Promise.resolve(mockMetricTemplateWithDependency) + case '/api/v2/internal/metrictemplates/org.apel.APEL-Pub': return Promise.resolve(mockPassiveMetricTemplate) @@ -1434,14 +1530,15 @@ describe('Test metric template changeview on SuperPOEM', () => { const configVal5 = screen.getByTestId('config.4.value'); const attributeKey = screen.getByTestId('attributes.0.key'); const attributeVal = screen.getByTestId('attributes.0.value') - const dependencyKey = screen.getByTestId('dependency.0.key'); - const dependencyVal = screen.getByTestId('dependency.0.value'); const parameterKey = screen.getByTestId('parameter.0.key'); const parameterVal = screen.getByTestId('parameter.0.value'); const flagKey = screen.getByTestId('flags.0.key'); const flagVal = screen.getByTestId('flags.0.value'); const parentField = screen.getByText(/select/i) + expect(screen.queryByTestId("dependency.0.key")).not.toBeInTheDocument() + expect(screen.queryByTestId("dependency.0.value")).not.toBeInTheDocument() + expect(nameField.value).toBe('argo.AMS-Check'); expect(typeField).toBeEnabled() @@ -1498,9 +1595,7 @@ describe('Test metric template changeview on SuperPOEM', () => { expect(screen.getByRole("heading", { name: /attributes/i })).toBeInTheDocument() expect(attributeKey.value).toBe('argo.ams_TOKEN'); expect(attributeVal.value).toBe('--token'); - expect(screen.getByRole("heading", { name: /dependency/i })).toBeInTheDocument() - expect(dependencyKey.value).toBe(''); - expect(dependencyVal.value).toBe(''); + expect(screen.queryByRole("heading", { name: /dependency/i })).not.toBeInTheDocument() expect(screen.getByRole("heading", { name: /parameter/i })).toBeInTheDocument() expect(parameterKey.value).toBe('--project'); expect(parameterVal.value).toBe('EGI'); @@ -1519,7 +1614,7 @@ describe('Test metric template changeview on SuperPOEM', () => { expect(screen.getByTestId("attributes.addnew")) expect(screen.queryByTestId("dependency")).not.toBeInTheDocument() - expect(screen.getByTestId("dependency.addnew")).toBeInTheDocument() + expect(screen.queryByTestId("dependency.addnew")).not.toBeInTheDocument() expect(screen.getByTestId("parameter.0.remove")).toBeInTheDocument() expect(screen.getByTestId("parameter.addnew")).toBeInTheDocument() @@ -1539,6 +1634,158 @@ describe('Test metric template changeview on SuperPOEM', () => { expect(screen.getByRole('button', { name: /delete/i })).toBeInTheDocument(); }) + test('Test that page renders properly if metric template with dependency', async () => { + renderChangeView({ withDependency: true }); + + await waitFor(() => { + expect(screen.getByRole('button', { name: /save/i })).toBeInTheDocument(); + }) + + expect(screen.getByRole('heading', { name: /change metric/i }).textContent).toBe('Change metric template'); + + const nameField = screen.getByTestId('name'); + const typeField = screen.getByText('Active') + const probeField = screen.getByText('GridProxy-probe (0.2.0)') + const packageField = screen.getByTestId('package'); + const descriptionField = screen.getByTestId('description'); + const groupField = screen.queryByText(/group/i) + const tagsElement = screen.getByLabelText('Tags:') + + const executableField = screen.getByTestId('probeexecutable'); + const configKey1 = screen.getByTestId('config.0.key'); + const configKey2 = screen.getByTestId('config.1.key'); + const configKey3 = screen.getByTestId('config.2.key'); + const configKey4 = screen.getByTestId('config.3.key'); + const configKey5 = screen.getByTestId('config.4.key'); + const configVal1 = screen.getByTestId('config.0.value'); + const configVal2 = screen.getByTestId('config.1.value'); + const configVal3 = screen.getByTestId('config.2.value'); + const configVal4 = screen.getByTestId('config.3.value'); + const configVal5 = screen.getByTestId('config.4.value'); + const attributeKey1 = screen.getByTestId('attributes.0.key'); + const attributeVal1 = screen.getByTestId('attributes.0.value') + const attributeKey2 = screen.getByTestId('attributes.1.key'); + const attributeVal2 = screen.getByTestId('attributes.1.value') + const dependencyKey = screen.getByTestId("dependency.0.key") + const dependencyVal = screen.getByTestId("dependency.0.value") + const parameterKey = screen.getByTestId('parameter.0.key'); + const parameterVal = screen.getByTestId('parameter.0.value'); + const flagKey1 = screen.getByTestId('flags.0.key'); + const flagVal1 = screen.getByTestId('flags.0.value'); + const flagKey2 = screen.getByTestId('flags.1.key'); + const flagVal2 = screen.getByTestId('flags.1.value'); + const flagKey3 = screen.getByTestId('flags.2.key'); + const flagVal3 = screen.getByTestId('flags.2.value'); + const parentField = screen.getByText(/select/i) + + expect(nameField.value).toBe("srce.gridproxy.validity"); + expect(typeField).toBeEnabled() + + expect(screen.queryByText('Passive')).not.toBeInTheDocument() + selectEvent.openMenu(typeField) + expect(screen.getByText('Passive')).toBeInTheDocument() + + expect(probeField).toBeEnabled() + + expect(screen.queryByText('ams-probe (0.1.11)')).not.toBeInTheDocument() + expect(screen.queryByText('ams-publisher-probe (0.1.11)')).not.toBeInTheDocument() + expect(screen.queryByText('ams-publisher-probe (0.1.12)')).not.toBeInTheDocument() + expect(screen.queryByText('ams-probe-new (0.1.13)')).not.toBeInTheDocument() + selectEvent.openMenu(probeField) + expect(screen.queryByText('ams-probe (0.1.11)')).toBeInTheDocument() + expect(screen.queryByText('ams-publisher-probe (0.1.11)')).toBeInTheDocument() + expect(screen.queryByText('ams-publisher-probe (0.1.12)')).toBeInTheDocument() + expect(screen.queryByText('ams-probe-new (0.1.13)')).toBeInTheDocument() + + expect(packageField.value).toBe('argo-probe-globus (0.2.0)') + expect(packageField).toBeDisabled(); + expect(descriptionField.value).toBe("") + expect(groupField).not.toBeInTheDocument(); + + expect(tagsElement).toBeInTheDocument() + expect(screen.getByText("test_tag1")).toBeInTheDocument() + expect(screen.getByText("test_tag2")).toBeInTheDocument() + expect(screen.queryByText("internal")).toBeInTheDocument() + expect(screen.queryByText("deprecated")).not.toBeInTheDocument() + + expect(screen.getByRole("heading", { name: /probe executable/i })).toBeInTheDocument() + expect(executableField.value).toBe('GridProxy-probe'); + expect(screen.getByRole("heading", { name: "config" })).toBeInTheDocument() + expect(configKey1.value).toBe('maxCheckAttempts'); + expect(configKey1).toBeDisabled() + expect(configVal1.value).toBe('3'); + expect(configVal1).toBeEnabled() + expect(configKey2.value).toBe('timeout'); + expect(configKey2).toBeDisabled() + expect(configVal2.value).toBe('30'); + expect(configVal2).toBeEnabled() + expect(configKey3.value).toBe('path'); + expect(configKey3).toBeDisabled() + expect(configVal3.value).toBe('/usr/libexec/argo/probes/globus'); + expect(configVal3).toBeEnabled() + expect(configKey4.value).toBe('interval'); + expect(configKey4).toBeDisabled() + expect(configVal4.value).toBe('15'); + expect(configVal4).toBeEnabled() + expect(configKey5.value).toBe('retryInterval'); + expect(configKey5).toBeDisabled() + expect(configVal5.value).toBe('3'); + expect(configVal5).toBeEnabled() + expect(screen.getByRole("heading", { name: /attributes/i })).toBeInTheDocument() + expect(attributeKey1.value).toBe('VONAME'); + expect(attributeVal1.value).toBe('--vo'); + expect(attributeKey2.value).toBe('X509_USER_PROXY'); + expect(attributeVal2.value).toBe('-x'); + expect(screen.getByRole("heading", { name: /dependency/i })).toBeInTheDocument() + expect(dependencyKey.value).toBe("hr.srce.GridProxy-Get") + expect(dependencyVal.value).toBe("0") + expect(dependencyKey).toBeDisabled() + expect(dependencyVal).toBeDisabled() + expect(screen.getByRole("heading", { name: /parameter/i })).toBeInTheDocument() + expect(parameterKey.value).toBe(""); + expect(parameterVal.value).toBe(""); + expect(screen.getByRole("heading", { name: /flags/i })).toBeInTheDocument() + expect(flagKey1.value).toBe("NOHOSTNAME") + expect(flagVal1.value).toBe("1") + expect(flagKey2.value).toBe("VO") + expect(flagVal2.value).toBe("1") + expect(flagKey3.value).toBe("NOPUBLISH") + expect(flagVal3.value).toBe("1") + + expect(screen.queryByTestId('config.0.remove')).not.toBeInTheDocument() + expect(screen.queryByTestId('config.1.remove')).not.toBeInTheDocument() + expect(screen.queryByTestId('config.2.remove')).not.toBeInTheDocument() + expect(screen.queryByTestId('config.3.remove')).not.toBeInTheDocument() + expect(screen.queryByTestId('config.4.remove')).not.toBeInTheDocument() + expect(screen.queryByTestId('config.addnew')).not.toBeInTheDocument() + + expect(screen.getByTestId("attributes.0.remove")).toBeInTheDocument() + expect(screen.getByTestId("attributes.1.remove")).toBeInTheDocument() + expect(screen.getByTestId("attributes.addnew")) + + expect(screen.queryByTestId("dependency.0.remove")).toBeInTheDocument() + expect(screen.queryByTestId("dependency.addnew")).not.toBeInTheDocument() + + expect(screen.queryByTestId("parameter.0.remove")).not.toBeInTheDocument() + expect(screen.getByTestId("parameter.addnew")).toBeInTheDocument() + + expect(screen.getByTestId("flags.0.remove")).toBeInTheDocument() + expect(screen.getByTestId("flags.1.remove")).toBeInTheDocument() + expect(screen.getByTestId("flags.2.remove")).toBeInTheDocument() + expect(screen.getByTestId("flags.addnew")).toBeInTheDocument() + + expect(screen.getByRole("heading", { name: /parent/i })).toBeInTheDocument() + expect(screen.queryByText('argo.AMS-Publisher')).not.toBeInTheDocument() + expect(screen.queryByText('org.apel.APEL-Pub')).not.toBeInTheDocument() + selectEvent.openMenu(parentField) + expect(screen.queryByText('argo.AMS-Publisher')).toBeInTheDocument() + expect(screen.queryByText('org.apel.APEL-Pub')).toBeInTheDocument() + + expect(screen.getByRole('button', { name: /history/i }).closest('a')).toHaveAttribute('href', '/ui/metrictemplates/srce.gridproxy.validity/history'); + expect(screen.getByRole('button', { name: /clone/i }).closest('a')).toHaveAttribute('href', '/ui/metrictemplates/srce.gridproxy.validity/clone'); + expect(screen.getByRole('button', { name: /delete/i })).toBeInTheDocument(); + }) + test('Test that public changeview for active metric template renders properly', async () => { renderChangeView({ publicView: true }); @@ -1576,8 +1823,8 @@ describe('Test metric template changeview on SuperPOEM', () => { const attributeKey = screen.getByTestId('attributes.0.key'); const attributeVal = screen.getByTestId('attributes.0.value') - const dependencyKey = screen.getByTestId('dependency.0.key'); - const dependencyVal = screen.getByTestId('dependency.0.value'); + expect(screen.queryByTestId("dependency.0.key")).not.toBeInTheDocument() + expect(screen.queryByTestId("dependency.0.value")).not.toBeInTheDocument() const parameterKey = screen.getByTestId('parameter.0.key'); const parameterVal = screen.getByTestId('parameter.0.value'); @@ -1652,11 +1899,7 @@ describe('Test metric template changeview on SuperPOEM', () => { expect(screen.queryByTestId('attributes.0.remove')).not.toBeInTheDocument(); expect(screen.queryByTestId('attributes.addnew')).not.toBeInTheDocument(); - expect(screen.getByRole("heading", { name: /dependency/i })).toBeInTheDocument() - expect(dependencyKey.value).toBe(''); - expect(dependencyKey).toBeDisabled() - expect(dependencyVal.value).toBe(''); - expect(dependencyVal).toBeDisabled() + expect(screen.queryByRole("heading", { name: /dependency/i })).not.toBeInTheDocument() expect(screen.queryByTestId('dependency.0.remove')).not.toBeInTheDocument(); expect(screen.queryByTestId('dependency.addnew')).not.toBeInTheDocument(); @@ -1686,6 +1929,173 @@ describe('Test metric template changeview on SuperPOEM', () => { expect(screen.queryByRole('button', { name: /delete/i })).not.toBeInTheDocument(); }) + test('Test that public changeview for active metric template renders properly if dependency', async () => { + renderChangeView({ publicView: true, withDependency: true }); + + await waitFor(() => { + expect(screen.getByTestId("name")).toBeInTheDocument() + }) + + expect(screen.getByRole('heading', { name: /metric template/i }).textContent).toBe('Metric template details'); + + const nameField = screen.getByTestId('name'); + const typeField = screen.getByTestId('mtype'); + const probeField = screen.getByTestId('probeversion') + const packageField = screen.getByTestId('package'); + const descriptionField = screen.getByTestId('description'); + const groupField = screen.queryByText(/group/i); + + const tagBadge1 = screen.getByText(/test_tag1/i); + const tagBadge2 = screen.getByText(/test_tag2/i); + const tagBadge3 = screen.queryByText("internal") + const tagBadge4 = screen.queryByText("deprecated") + + const executableField = screen.getByTestId('probeexecutable'); + + const configKey1 = screen.getByTestId('config.0.key'); + const configKey2 = screen.getByTestId('config.1.key'); + const configKey3 = screen.getByTestId('config.2.key'); + const configKey4 = screen.getByTestId('config.3.key'); + const configKey5 = screen.getByTestId('config.4.key'); + const configVal1 = screen.getByTestId('config.0.value'); + const configVal2 = screen.getByTestId('config.1.value'); + const configVal3 = screen.getByTestId('config.2.value'); + const configVal4 = screen.getByTestId('config.3.value'); + const configVal5 = screen.getByTestId('config.4.value'); + + const attributeKey1 = screen.getByTestId('attributes.0.key'); + const attributeVal1 = screen.getByTestId('attributes.0.value') + const attributeKey2 = screen.getByTestId('attributes.1.key'); + const attributeVal2 = screen.getByTestId('attributes.1.value') + + const dependencyKey = screen.getByTestId("dependency.0.key") + const dependencyVal = screen.getByTestId("dependency.0.value") + + const parameterKey = screen.getByTestId('parameter.0.key'); + const parameterVal = screen.getByTestId('parameter.0.value'); + + const flagKey1 = screen.getByTestId('flags.0.key') + const flagVal1 = screen.getByTestId('flags.0.value') + const flagKey2 = screen.getByTestId('flags.1.key') + const flagVal2 = screen.getByTestId('flags.1.value') + const flagKey3 = screen.getByTestId('flags.2.key') + const flagVal3 = screen.getByTestId('flags.2.value') + + const parentField = screen.getByTestId('parent'); + + expect(nameField.value).toBe('srce.gridproxy.validity'); + expect(nameField).toBeDisabled() + + expect(typeField.value).toBe('Active'); + expect(typeField).toBeDisabled() + expect(screen.queryByRole('option', { name: /active/i })).not.toBeInTheDocument() + expect(screen.queryByRole('option', { name: /passive/i })).not.toBeInTheDocument() + + expect(probeField.value).toBe('GridProxy-probe (0.2.0)'); + expect(probeField).toBeDisabled(); + + expect(packageField.value).toBe('argo-probe-globus (0.2.0)') + expect(packageField).toBeDisabled(); + + expect(descriptionField.value).toBe("") + expect(descriptionField).toBeDisabled(); + + expect(groupField).not.toBeInTheDocument(); + + expect(tagBadge1.textContent).toBe('test_tag1') + expect(tagBadge2.textContent).toBe('test_tag2') + expect(tagBadge3.textContent).toBe("internal") + expect(tagBadge4).not.toBeInTheDocument() + + expect(screen.getByRole("heading", { name: /probe executable/i })).toBeInTheDocument() + expect(executableField.value).toBe("GridProxy-probe") + expect(executableField).toBeDisabled() + + expect(screen.getByRole("heading", { name: "config" })).toBeInTheDocument() + expect(configKey1.value).toBe('maxCheckAttempts'); + expect(configKey1).toBeDisabled() + expect(configVal1.value).toBe('3'); + expect(configVal1).toBeDisabled() + expect(screen.queryByTestId('config.0.remove')).not.toBeInTheDocument(); + expect(configKey2.value).toBe('timeout'); + expect(configKey2).toBeDisabled() + expect(configVal2.value).toBe('30'); + expect(configVal2).toBeDisabled() + expect(screen.queryByTestId('config.1.remove')).not.toBeInTheDocument(); + expect(configKey3.value).toBe('path'); + expect(configKey3).toBeDisabled() + expect(configVal3.value).toBe('/usr/libexec/argo/probes/globus'); + expect(configVal3).toBeDisabled() + expect(screen.queryByTestId('config.2.remove')).not.toBeInTheDocument(); + expect(configKey4.value).toBe('interval'); + expect(configKey4).toBeDisabled() + expect(configVal4.value).toBe('15'); + expect(configVal4).toBeDisabled() + expect(screen.queryByTestId('config.3.remove')).not.toBeInTheDocument(); + expect(configKey5.value).toBe('retryInterval'); + expect(configKey5).toBeDisabled() + expect(configVal5.value).toBe('3'); + expect(configVal5).toBeDisabled() + expect(screen.queryByTestId('config.4.remove')).not.toBeInTheDocument(); + expect(screen.queryByTestId('config.addnew')).not.toBeInTheDocument(); + + expect(screen.getByRole("heading", { name: /attributes/i })).toBeInTheDocument() + expect(attributeKey1.value).toBe("VONAME"); + expect(attributeKey1).toBeDisabled() + expect(attributeVal1.value).toBe('--vo'); + expect(attributeVal1).toBeDisabled() + expect(attributeKey2.value).toBe("X509_USER_PROXY") + expect(attributeKey2).toBeDisabled() + expect(attributeVal2.value).toBe("-x") + expect(attributeVal2).toBeDisabled() + expect(screen.queryByTestId('attributes.0.remove')).not.toBeInTheDocument(); + expect(screen.queryByTestId('attributes.1.remove')).not.toBeInTheDocument(); + expect(screen.queryByTestId('attributes.addnew')).not.toBeInTheDocument(); + + expect(screen.getByRole("heading", { name: /dependency/i })).toBeInTheDocument() + expect(dependencyKey.value).toBe("hr.srce.GridProxy-Get") + expect(dependencyKey).toBeDisabled() + expect(dependencyVal.value).toBe("0") + expect(dependencyVal).toBeDisabled() + expect(screen.queryByTestId('dependency.0.remove')).not.toBeInTheDocument(); + expect(screen.queryByTestId('dependency.addnew')).not.toBeInTheDocument(); + + expect(screen.getByRole("heading", { name: /parameter/i })).toBeInTheDocument() + expect(parameterKey.value).toBe("") + expect(parameterKey).toBeDisabled() + expect(parameterVal.value).toBe("") + expect(parameterVal).toBeDisabled() + expect(screen.queryByTestId('parameter.0.remove')).not.toBeInTheDocument(); + expect(screen.queryByTestId('parameter.addnew')).not.toBeInTheDocument(); + + expect(screen.getByRole("heading", { name: /flags/i })).toBeInTheDocument() + expect(flagKey1.value).toBe("NOHOSTNAME") + expect(flagKey1).toBeDisabled() + expect(flagVal1.value).toBe("1") + expect(flagVal1).toBeDisabled() + expect(flagKey2.value).toBe("VO") + expect(flagKey2).toBeDisabled() + expect(flagVal2.value).toBe("1") + expect(flagVal2).toBeDisabled() + expect(flagKey3.value).toBe("NOPUBLISH") + expect(flagKey3).toBeDisabled() + expect(flagVal3.value).toBe("1") + expect(flagVal3).toBeDisabled() + expect(screen.queryByTestId('flags.0.remove')).not.toBeInTheDocument(); + expect(screen.queryByTestId('flags.1.remove')).not.toBeInTheDocument(); + expect(screen.queryByTestId('flags.2.remove')).not.toBeInTheDocument(); + expect(screen.queryByTestId('flags.addnew')).not.toBeInTheDocument(); + + expect(screen.getByRole("heading", { name: /parent/i })).toBeInTheDocument() + expect(parentField.value).toBe(''); + expect(parentField).toBeDisabled() + + expect(screen.getByRole('button', { name: /history/i }).closest('a')).toHaveAttribute('href', '/ui/public_metrictemplates/srce.gridproxy.validity/history'); + expect(screen.queryByRole('button', { name: /clone/i })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: /save/i })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: /delete/i })).not.toBeInTheDocument(); + }) + test("Test change main metric template info", async () => { mockChangeObject.mockReturnValueOnce( Promise.resolve({ ok: true, status: 200 }) @@ -1895,75 +2305,6 @@ describe('Test metric template changeview on SuperPOEM', () => { ) }) - test("Test change dependency", async () => { - mockChangeObject.mockReturnValueOnce( - Promise.resolve({ ok: true, status: 200 }) - ) - - renderChangeView(); - - await waitFor(() => { - expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() - }) - - fireEvent.change(screen.getByTestId('dependency.0.key'), { target: { value: 'test.AMS-Check' } }); - fireEvent.change(screen.getByTestId('dependency.0.value'), { target: { value: '1' } }); - - fireEvent.click(screen.getByTestId("dependency.addnew")) - fireEvent.change(screen.getByTestId('dependency.1.key'), { target: { value: 'test2.AMS-Check' } }); - fireEvent.change(screen.getByTestId('dependency.1.value'), { target: { value: '0' } }); - - fireEvent.click(screen.getByRole('button', { name: /save/i })); - await waitFor(() => { - expect(screen.getByRole('dialog', { title: /change/i })).toBeInTheDocument(); - }) - fireEvent.click(screen.getByRole('button', { name: /yes/i })); - - await waitFor(() => { - expect(mockChangeObject).toHaveBeenCalledWith( - "/api/v2/internal/metrictemplates/", - { - id: "1", - name: "argo.AMS-Check", - mtype: "Active", - tags: ["test_tag1", "test_tag2"], - description: "Some description of argo.AMS-Check metric template.", - probeversion: "ams-probe (0.1.12)", - parent: "", - probeexecutable: "ams-probe", - config: [ - { key: "maxCheckAttempts", value: "4" }, - { key: "timeout", value: "70" }, - { key: "path", value: "/usr/libexec/argo-monitoring/" }, - { key: "interval", value: "5" }, - { key: "retryInterval", value: "3" } - ], - attribute: [ - { key: "argo.ams_TOKEN", value: "--token" } - ], - dependency: [ - { key: "test.AMS-Check", value: "1" }, - { key: "test2.AMS-Check", value: "0", isNew: true } - ], - parameter: [ - { key: "--project", value: "EGI" } - ], - flags: [ - { key: "OBSESS", value: "1" } - ], - files: [{ key: "", value: "" }], - fileparameter: [{ key: "", value: "" }] - } - ) - }) - - expect(queryClient.invalidateQueries).toHaveBeenCalledWith('metrictemplate'); - expect(queryClient.invalidateQueries).toHaveBeenCalledWith('public_metrictemplate'); - expect(NotificationManager.success).toHaveBeenCalledWith( - 'Metric template successfully changed', 'Changed', 2000 - ) - }) - test("Test change parameter", async () => { mockChangeObject.mockReturnValueOnce( Promise.resolve({ ok: true, status: 200 }) @@ -2128,9 +2469,6 @@ describe('Test metric template changeview on SuperPOEM', () => { fireEvent.change(screen.getByTestId('attributes.1.key'), { target: { value: 'ATTRIBUTE' } }); fireEvent.change(screen.getByTestId('attributes.1.value'), { target: { value: '--meh' } }); - fireEvent.change(screen.getByTestId("dependency.0.key"), { target: { value: 'some-dep' } }); - fireEvent.change(screen.getByTestId("dependency.0.value"), { target: { value: 'some-dep-value' } }); - fireEvent.click(screen.getByTestId('parameter.0.remove')); fireEvent.click(screen.getByTestId('flags.addnew')); @@ -2166,9 +2504,7 @@ describe('Test metric template changeview on SuperPOEM', () => { { key: 'argo.ams_TOKEN', value: '--token' }, { key: 'ATTRIBUTE', value: '--meh', isNew: true } ], - 'dependency': [ - { key: 'some-dep', value: 'some-dep-value' } - ], + 'dependency': [{ key: "", value: "" }], 'parameter': [{ key: '', value: '' }], 'flags': [ { key: 'OBSESS', value: '1' }, @@ -2217,9 +2553,6 @@ describe('Test metric template changeview on SuperPOEM', () => { fireEvent.change(screen.getByTestId('attributes.1.key'), { target: { value: 'ATTRIBUTE' } }); fireEvent.change(screen.getByTestId('attributes.1.value'), { target: { value: '--meh' } }); - fireEvent.change(screen.getByTestId("dependency.0.key"), { target: { value: 'some-dep' } }); - fireEvent.change(screen.getByTestId("dependency.0.value"), { target: { value: 'some-dep-value' } }); - fireEvent.click(screen.getByTestId('parameter.0.remove')); fireEvent.click(screen.getByTestId('flags.addnew')); @@ -2255,9 +2588,7 @@ describe('Test metric template changeview on SuperPOEM', () => { { key: 'argo.ams_TOKEN', value: '--token' }, { key: 'ATTRIBUTE', value: '--meh', isNew: true } ], - 'dependency': [ - { key: 'some-dep', value: 'some-dep-value' } - ], + 'dependency': [{ key: "", value: "" }], 'parameter': [{ key: '', value: '' }], 'flags': [ { key: 'OBSESS', value: '1' }, @@ -2702,8 +3033,6 @@ describe('Test metric template changeview on SuperPOEM', () => { const configVal5 = screen.getByTestId('config.4.value'); const attributeKey = screen.getByTestId('attributes.0.key'); const attributeVal = screen.getByTestId('attributes.0.value') - const dependencyKey = screen.getByTestId('dependency.0.key'); - const dependencyVal = screen.getByTestId('dependency.0.value'); const parameterKey = screen.getByTestId('parameter.0.key'); const parameterVal = screen.getByTestId('parameter.0.value'); const flagKey1 = screen.getByTestId('flags.0.key'); @@ -2712,6 +3041,9 @@ describe('Test metric template changeview on SuperPOEM', () => { const flagVal2 = screen.queryByTestId('flags.1.value'); const parentField = screen.getAllByText(/select/i)[2] + expect(screen.queryByTestId("dependency.0.key")).not.toBeInTheDocument() + expect(screen.queryByTestId("dependency.0.value")).not.toBeInTheDocument() + expect(nameField.value).toBe('org.apel.APEL-Pub'); expect(probeField).toBeEnabled(); @@ -2763,10 +3095,6 @@ describe('Test metric template changeview on SuperPOEM', () => { expect(attributeKey).not.toHaveAttribute('hidden'); expect(attributeVal.value).toBe(''); expect(attributeVal).not.toHaveAttribute('hidden'); - expect(dependencyKey.value).toBe(''); - expect(dependencyKey).not.toHaveAttribute('hidden'); - expect(dependencyVal.value).toBe(''); - expect(dependencyVal).not.toHaveAttribute('hidden'); expect(parameterKey.value).toBe(''); expect(parameterKey).not.toHaveAttribute('hidden'); expect(parameterVal.value).toBe(''); @@ -2943,8 +3271,8 @@ describe('Test metric template changeview on SuperPOEM', () => { expect(configVal5a).toBeEnabled() expect(attributeKey2.value).toBe('argo.ams_TOKEN'); expect(attributeVal2.value).toBe('--token'); - expect(dependencyKey2.value).toBe(''); - expect(dependencyVal2.value).toBe(''); + expect(dependencyKey2).not.toBeInTheDocument() + expect(dependencyVal2).not.toBeInTheDocument() expect(parameterKey2.value).toBe('--project'); expect(parameterVal2.value).toBe('EGI') expect(flagKey1a.value).toBe('OBSESS'); @@ -3070,14 +3398,15 @@ describe('Test metric template addview on SuperPOEM', () => { const configVal5 = screen.getByTestId('config.4.value'); const attributeKey = screen.getByTestId('attributes.0.key'); const attributeVal = screen.getByTestId('attributes.0.value') - const dependencyKey = screen.getByTestId('dependency.0.key'); - const dependencyVal = screen.getByTestId('dependency.0.value'); const parameterKey = screen.getByTestId('parameter.0.key'); const parameterVal = screen.getByTestId('parameter.0.value'); const flagKey = screen.getByTestId('flags.0.key'); const flagVal = screen.getByTestId('flags.0.value'); const parentField = screen.getAllByText(/select/i)[2] + expect(screen.queryByTestId("dependency.0.key")).not.toBeInTheDocument() + expect(screen.queryByTestId("dependency.0.value")).not.toBeInTheDocument() + expect(nameField.value).toBe(''); expect(typeField).toBeEnabled() @@ -3132,8 +3461,6 @@ describe('Test metric template addview on SuperPOEM', () => { expect(configVal5).toBeEnabled() expect(attributeKey.value).toBe(''); expect(attributeVal.value).toBe(''); - expect(dependencyKey.value).toBe(''); - expect(dependencyVal.value).toBe(''); expect(parameterKey.value).toBe(''); expect(parameterVal.value).toBe(''); expect(flagKey.value).toBe(''); @@ -3175,8 +3502,6 @@ describe('Test metric template addview on SuperPOEM', () => { "config.4.value": "", "attributes.0.key": "", "attributes.0.value": "", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "", "parameter.0.value": "", "flags.0.key": "", @@ -3206,8 +3531,6 @@ describe('Test metric template addview on SuperPOEM', () => { "config.4.value": "", "attributes.0.key": "", "attributes.0.value": "", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "", "parameter.0.value": "", "flags.0.key": "", @@ -3238,8 +3561,6 @@ describe('Test metric template addview on SuperPOEM', () => { "config.4.value": "", "attributes.0.key": "", "attributes.0.value": "", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "", "parameter.0.value": "", "flags.0.key": "", @@ -3268,8 +3589,6 @@ describe('Test metric template addview on SuperPOEM', () => { "config.4.value": "1", "attributes.0.key": "", "attributes.0.value": "", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "", "parameter.0.value": "", "flags.0.key": "", @@ -3300,8 +3619,6 @@ describe('Test metric template addview on SuperPOEM', () => { "config.4.value": "", "attributes.0.key": "", "attributes.0.value": "", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "", "parameter.0.value": "", "flags.0.key": "", @@ -3327,8 +3644,6 @@ describe('Test metric template addview on SuperPOEM', () => { "config.4.value": "", "attributes.0.key": "attribute1", "attributes.0.value": "120", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "", "parameter.0.value": "", "flags.0.key": "", @@ -3357,8 +3672,6 @@ describe('Test metric template addview on SuperPOEM', () => { "attributes.0.value": "120", "attributes.1.key": "attribute2", "attributes.1.value": "value3", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "", "parameter.0.value": "", "flags.0.key": "", @@ -3383,8 +3696,6 @@ describe('Test metric template addview on SuperPOEM', () => { "config.4.value": "", "attributes.0.key": "attribute1", "attributes.0.value": "120", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "", "parameter.0.value": "", "flags.0.key": "", @@ -3410,8 +3721,6 @@ describe('Test metric template addview on SuperPOEM', () => { "config.4.value": "", "attributes.0.key": "ATTRIBUTE", "attributes.0.value": "123", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "", "parameter.0.value": "", "flags.0.key": "", @@ -3436,175 +3745,6 @@ describe('Test metric template addview on SuperPOEM', () => { "config.4.value": "", "attributes.0.key": "", "attributes.0.value": "", - "dependency.0.key": "", - "dependency.0.value": "", - "parameter.0.key": "", - "parameter.0.value": "", - "flags.0.key": "", - "flags.0.value": "" - }) - }) - - test("Test add dependency", async () => { - renderAddView(); - - await waitFor(() => { - expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument() - }) - - expect(screen.getByTestId("metric-form")).toHaveFormValues({ - name: "", - description: "", - probeexecutable: "", - "config.0.key": "maxCheckAttempts", - "config.0.value": "", - "config.1.key": "timeout", - "config.1.value": "", - "config.2.key": "path", - "config.2.value": "", - "config.3.key": "interval", - "config.3.value": "", - "config.4.key": "retryInterval", - "config.4.value": "", - "attributes.0.key": "", - "attributes.0.value": "", - "dependency.0.key": "", - "dependency.0.value": "", - "parameter.0.key": "", - "parameter.0.value": "", - "flags.0.key": "", - "flags.0.value": "" - }) - - fireEvent.change(screen.getByTestId("dependency.0.key"), { target: { value: 'test.AMS-Check' } }); - fireEvent.change(screen.getByTestId("dependency.0.value"), { target: { value: '1' } }); - - expect(screen.getByTestId("metric-form")).toHaveFormValues({ - name: "", - description: "", - probeexecutable: "", - "config.0.key": "maxCheckAttempts", - "config.0.value": "", - "config.1.key": "timeout", - "config.1.value": "", - "config.2.key": "path", - "config.2.value": "", - "config.3.key": "interval", - "config.3.value": "", - "config.4.key": "retryInterval", - "config.4.value": "", - "attributes.0.key": "", - "attributes.0.value": "", - "dependency.0.key": "test.AMS-Check", - "dependency.0.value": "1", - "parameter.0.key": "", - "parameter.0.value": "", - "flags.0.key": "", - "flags.0.value": "" - }) - - fireEvent.click(screen.getByTestId("dependency.addnew")) - fireEvent.change(screen.getByTestId("dependency.1.key"), { target: { value: 'generic.http.connect' } }); - fireEvent.change(screen.getByTestId("dependency.1.value"), { target: { value: '0' } }); - - expect(screen.getByTestId("metric-form")).toHaveFormValues({ - name: "", - description: "", - probeexecutable: "", - "config.0.key": "maxCheckAttempts", - "config.0.value": "", - "config.1.key": "timeout", - "config.1.value": "", - "config.2.key": "path", - "config.2.value": "", - "config.3.key": "interval", - "config.3.value": "", - "config.4.key": "retryInterval", - "config.4.value": "", - "attributes.0.key": "", - "attributes.0.value": "", - "dependency.0.key": "test.AMS-Check", - "dependency.0.value": "1", - "dependency.1.key": "generic.http.connect", - "dependency.1.value": "0", - "parameter.0.key": "", - "parameter.0.value": "", - "flags.0.key": "", - "flags.0.value": "" - }) - - fireEvent.click(screen.getByTestId("dependency.0.remove")) - - expect(screen.getByTestId("metric-form")).toHaveFormValues({ - name: "", - description: "", - probeexecutable: "", - "config.0.key": "maxCheckAttempts", - "config.0.value": "", - "config.1.key": "timeout", - "config.1.value": "", - "config.2.key": "path", - "config.2.value": "", - "config.3.key": "interval", - "config.3.value": "", - "config.4.key": "retryInterval", - "config.4.value": "", - "attributes.0.key": "", - "attributes.0.value": "", - "dependency.0.key": "generic.http.connect", - "dependency.0.value": "0", - "parameter.0.key": "", - "parameter.0.value": "", - "flags.0.key": "", - "flags.0.value": "" - }) - - fireEvent.change(screen.getByTestId("dependency.0.value"), { target: { value: '1' } }); - - expect(screen.getByTestId("metric-form")).toHaveFormValues({ - name: "", - description: "", - probeexecutable: "", - "config.0.key": "maxCheckAttempts", - "config.0.value": "", - "config.1.key": "timeout", - "config.1.value": "", - "config.2.key": "path", - "config.2.value": "", - "config.3.key": "interval", - "config.3.value": "", - "config.4.key": "retryInterval", - "config.4.value": "", - "attributes.0.key": "", - "attributes.0.value": "", - "dependency.0.key": "generic.http.connect", - "dependency.0.value": "1", - "parameter.0.key": "", - "parameter.0.value": "", - "flags.0.key": "", - "flags.0.value": "" - }) - - fireEvent.click(screen.getByTestId("dependency.0.remove")) - - expect(screen.getByTestId("metric-form")).toHaveFormValues({ - name: "", - description: "", - probeexecutable: "", - "config.0.key": "maxCheckAttempts", - "config.0.value": "", - "config.1.key": "timeout", - "config.1.value": "", - "config.2.key": "path", - "config.2.value": "", - "config.3.key": "interval", - "config.3.value": "", - "config.4.key": "retryInterval", - "config.4.value": "", - "attributes.0.key": "", - "attributes.0.value": "", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "", "parameter.0.value": "", "flags.0.key": "", @@ -3635,8 +3775,6 @@ describe('Test metric template addview on SuperPOEM', () => { "config.4.value": "", "attributes.0.key": "", "attributes.0.value": "", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "", "parameter.0.value": "", "flags.0.key": "", @@ -3662,8 +3800,6 @@ describe('Test metric template addview on SuperPOEM', () => { "config.4.value": "", "attributes.0.key": "", "attributes.0.value": "", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "-vv", "parameter.0.value": "", "flags.0.key": "", @@ -3690,8 +3826,6 @@ describe('Test metric template addview on SuperPOEM', () => { "config.4.value": "", "attributes.0.key": "", "attributes.0.value": "", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "-vv", "parameter.0.value": "", "parameter.1.key": "-p", @@ -3718,8 +3852,6 @@ describe('Test metric template addview on SuperPOEM', () => { "config.4.value": "", "attributes.0.key": "", "attributes.0.value": "", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "-p", "parameter.0.value": "443", "flags.0.key": "", @@ -3744,8 +3876,6 @@ describe('Test metric template addview on SuperPOEM', () => { "config.4.value": "", "attributes.0.key": "", "attributes.0.value": "", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "-p", "parameter.0.value": "80", "flags.0.key": "", @@ -3770,8 +3900,6 @@ describe('Test metric template addview on SuperPOEM', () => { "config.4.value": "", "attributes.0.key": "", "attributes.0.value": "", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "", "parameter.0.value": "", "flags.0.key": "", @@ -3802,8 +3930,6 @@ describe('Test metric template addview on SuperPOEM', () => { "config.4.value": "", "attributes.0.key": "", "attributes.0.value": "", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "", "parameter.0.value": "", "flags.0.key": "", @@ -3829,8 +3955,6 @@ describe('Test metric template addview on SuperPOEM', () => { "config.4.value": "", "attributes.0.key": "", "attributes.0.value": "", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "", "parameter.0.value": "", "flags.0.key": "OBSESS", @@ -3857,8 +3981,6 @@ describe('Test metric template addview on SuperPOEM', () => { "config.4.value": "", "attributes.0.key": "", "attributes.0.value": "", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "", "parameter.0.value": "", "flags.0.key": "OBSESS", @@ -3885,8 +4007,6 @@ describe('Test metric template addview on SuperPOEM', () => { "config.4.value": "", "attributes.0.key": "", "attributes.0.value": "", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "", "parameter.0.value": "", "flags.0.key": "NOHOSTNAME", @@ -3911,8 +4031,6 @@ describe('Test metric template addview on SuperPOEM', () => { "config.4.value": "", "attributes.0.key": "", "attributes.0.value": "", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "", "parameter.0.value": "", "flags.0.key": "NOHOSTNAME", @@ -3937,8 +4055,6 @@ describe('Test metric template addview on SuperPOEM', () => { "config.4.value": "", "attributes.0.key": "", "attributes.0.value": "", - "dependency.0.key": "", - "dependency.0.value": "", "parameter.0.key": "", "parameter.0.value": "", "flags.0.key": "", @@ -4516,14 +4632,15 @@ describe('Test metric template cloneview on SuperPOEM', () => { const configVal5 = screen.getByTestId('config.4.value'); const attributeKey = screen.getByTestId('attributes.0.key'); const attributeVal = screen.getByTestId('attributes.0.value') - const dependencyKey = screen.getByTestId('dependency.0.key'); - const dependencyVal = screen.getByTestId('dependency.0.value'); const parameterKey = screen.getByTestId('parameter.0.key'); const parameterVal = screen.getByTestId('parameter.0.value'); const flagKey = screen.getByTestId('flags.0.key'); const flagVal = screen.getByTestId('flags.0.value'); const parentField = screen.getByText(/select/i) + expect(screen.queryByTestId("dependency.0.key")).not.toBeInTheDocument() + expect(screen.queryByTestId("dependency.0.value")).not.toBeInTheDocument() + expect(nameField.value).toBe('argo.AMS-Check'); expect(typeField).toBeEnabled() @@ -4577,8 +4694,6 @@ describe('Test metric template cloneview on SuperPOEM', () => { expect(configVal5).toBeEnabled() expect(attributeKey.value).toBe('argo.ams_TOKEN'); expect(attributeVal.value).toBe('--token'); - expect(dependencyKey.value).toBe(''); - expect(dependencyVal.value).toBe(''); expect(parameterKey.value).toBe('--project'); expect(parameterVal.value).toBe('EGI'); expect(flagKey.value).toBe('OBSESS'); @@ -4911,14 +5026,15 @@ describe('Test metric template detail view on tenant POEM', () => { const configVal5 = screen.getByTestId('config.4.value'); const attributeKey = screen.getByTestId('attributes.0.key'); const attributeVal = screen.getByTestId('attributes.0.value') - const dependencyKey = screen.getByTestId('dependency.0.key'); - const dependencyVal = screen.getByTestId('dependency.0.value'); const parameterKey = screen.getByTestId('parameter.0.key'); const parameterVal = screen.getByTestId('parameter.0.value'); const flagKey = screen.getByTestId('flags.0.key'); const flagVal = screen.getByTestId('flags.0.value'); const parentField = screen.getByTestId('parent'); + expect(screen.queryByTestId("dependency.0.key")).not.toBeInTheDocument() + expect(screen.queryByTestId("dependency.0.value")).not.toBeInTheDocument() + expect(nameField.value).toBe('argo.AMS-Check'); expect(nameField).toBeDisabled() expect(typeField.value).toBe('Active'); @@ -4967,10 +5083,6 @@ describe('Test metric template detail view on tenant POEM', () => { expect(attributeVal).toBeDisabled() expect(screen.queryByTestId('attributes.0.remove')).not.toBeInTheDocument(); expect(screen.queryByTestId('attributes.addnew')).not.toBeInTheDocument(); - expect(dependencyKey.value).toBe(''); - expect(dependencyKey).toBeDisabled() - expect(dependencyVal.value).toBe(''); - expect(dependencyVal).toBeDisabled() expect(screen.queryByTestId('dependency.0.remove')).not.toBeInTheDocument(); expect(screen.queryByTestId('dependency.addnew')).not.toBeInTheDocument(); expect(parameterKey.value).toBe('--project'); @@ -5022,7 +5134,7 @@ describe('Test metric template version detail view', () => { renderVersionDetailsView(); await waitFor(() => { - expect(screen.getByTestId("dependency.0.key")).toBeInTheDocument() + expect(screen.getByTestId("config.0.key")).toBeInTheDocument() }) const nameField = screen.getByTestId('name'); @@ -5044,14 +5156,15 @@ describe('Test metric template version detail view', () => { const configVal5 = screen.getByTestId('config.4.value'); const attributeKey = screen.getByTestId('attributes.0.key'); const attributeVal = screen.getByTestId('attributes.0.value') - const dependencyKey = screen.getByTestId('dependency.0.key'); - const dependencyVal = screen.getByTestId('dependency.0.value'); const parameterKey = screen.getByTestId('parameter.0.key'); const parameterVal = screen.getByTestId('parameter.0.value'); const flagKey = screen.getByTestId('flags.0.key'); const flagVal = screen.getByTestId('flags.0.value'); const parentField = screen.getByTestId('parent'); + expect(screen.queryByTestId("dependency.0.key")).not.toBeInTheDocument() + expect(screen.queryByTestId("dependency.0.value")).not.toBeInTheDocument() + expect(nameField.value).toBe('argo.AMS-Check'); expect(nameField).toBeDisabled() expect(typeField.value).toBe('Active'); @@ -5100,10 +5213,6 @@ describe('Test metric template version detail view', () => { expect(attributeVal).toBeDisabled() expect(screen.queryByTestId('attributes.0.remove')).not.toBeInTheDocument(); expect(screen.queryByTestId('attributes.addnew')).not.toBeInTheDocument(); - expect(dependencyKey.value).toBe(''); - expect(dependencyKey).toBeDisabled() - expect(dependencyVal.value).toBe(''); - expect(dependencyVal).toBeDisabled() expect(screen.queryByTestId('dependency.0.remove')).not.toBeInTheDocument(); expect(screen.queryByTestId('dependency.addnew')).not.toBeInTheDocument(); expect(parameterKey.value).toBe('--project'); diff --git a/poem/Poem/frontend/react/__tests__/Metrics.test.js b/poem/Poem/frontend/react/__tests__/Metrics.test.js index 65f91871c..f628cb473 100644 --- a/poem/Poem/frontend/react/__tests__/Metrics.test.js +++ b/poem/Poem/frontend/react/__tests__/Metrics.test.js @@ -15,6 +15,7 @@ const mockListOfMetrics = [ name: 'argo.AMS-Check', mtype: 'Active', tags: ['test_tag1', 'test_tag2'], + profiles: ["ARGO_MON", "TEST_PROFILE"], probeversion: 'ams-probe (0.1.12)', group: 'EGI', description: 'Description of argo.AMS-Check metric', @@ -44,6 +45,7 @@ const mockListOfMetrics = [ name: 'argo.AMS-Publisher', mtype: 'Active', tags: ['internal'], + profiles: ["ARGO_MON_INTERNAL"], probeversion: 'ams-publisher-probe (0.1.12)', group: 'EGI', description: '', @@ -74,6 +76,7 @@ const mockListOfMetrics = [ name: 'org.apel.APEL-Pub', mtype: 'Passive', tags: [], + profiles: [], probeversion: '', group: 'ARGOTEST', description: '', @@ -97,6 +100,7 @@ const mockMetric = { name: 'argo.AMS-Check', mtype: 'Active', tags: ['test_tag1', 'test_tag2'], + profiles: ["ARGO_MON", "TEST_PROFILE"], probeversion: 'ams-probe (0.1.12)', group: 'EGI', description: 'Description of argo.AMS-Check metric', @@ -119,14 +123,87 @@ const mockMetric = { parameter: [ { key: '--project', value: 'EGI' } ], + files: [], fileparameter: [] }; +const mockMetricWithDependency = { + id: 422, + name: "srce.gridproxy.validity", + mtype: "Active", + tags: [ + "test_tag1", + "test_tag2", + "internal" + ], + probeversion: "GridProxy-probe (0.2.0)", + group: "EGI", + description: "", + parent: "", + probeexecutable: "GridProxy-probe", + config: [ + { + key: "maxCheckAttempts", + value: "3" + }, + { + key: "timeout", + value: "30" + }, + { + key: "path", + value: "/usr/libexec/argo/probes/globus" + }, + { + key: "interval", + value: "15" + }, + { + key: "retryInterval", + value: "3" + } + ], + attribute: [ + { + key: "VONAME", + value: "--vo" + }, + { + key: "X509_USER_PROXY", + value: "-x" + } + ], + dependancy: [ + { + key: "hr.srce.GridProxy-Get", + value: "0" + } + ], + flags: [ + { + key: "NOHOSTNAME", + value: "1" + }, + { + key: "VO", + value: "1" + }, + { + key: "NOPUBLISH", + value: "1" + } + ], + files: [], + parameter: [], + fileparameter: [] +} + const mockPassiveMetric = { id: 2, name: 'org.apel.APEL-Pub', mtype: 'Passive', tags: [], + profiles: [], probeversion: '', group: 'ARGOTEST', description: '', @@ -162,6 +239,24 @@ const mockProbe = [{ version: '0.1.12' }]; +const mockProbe2 = [{ + id: '64', + object_repr: "GridProxy-probe (0.2.0)", + fields: { + name: "GridProxy-probe", + version: "0.2.0", + package: "argo-probe-globus (0.2.0)", + description: "Probe for functional checking of MyProxy service.", + comment: "Harmonized version.", + repository: "https://github.com/ARGOeu-Metrics/argo-probe-globus", + docurl: "https://github.com/ARGOeu-Metrics/argo-probe-globus/blob/master/README.md" + }, + user: "poem", + date_created: "2019-12-09 09:24:20", + comment: "Initial version.", + version: "0.2.0" +}] + const mockUserGroups = { 'aggregations': ['EGI'], 'metrics': ['EGI', 'ARGOTEST'], @@ -699,17 +794,29 @@ describe('Tests for metric change', () => { case '/api/v2/internal/metric/org.apel.APEL-Pub': return Promise.resolve(mockPassiveMetric) + case "/api/v2/internal/metric/srce.gridproxy.validity": + return Promise.resolve(mockMetricWithDependency) + case '/api/v2/internal/public_metric/argo.AMS-Check': return Promise.resolve(mockMetric) case '/api/v2/internal/public_metric/org.apel.APEL-Pub': return Promise.resolve(mockPassiveMetric) + case "/api/v2/internal/public_metric/srce.gridproxy.validity": + return Promise.resolve(mockMetricWithDependency) + case '/api/v2/internal/version/probe/ams-probe': return Promise.resolve(mockProbe) + + case "/api/v2/internal/version/probe/GridProxy-probe": + return Promise.resolve(mockProbe2) case '/api/v2/internal/public_version/probe/ams-probe': return Promise.resolve(mockProbe) + + case "/api/v2/internal/public_version/probe/GridProxy-probe": + return Promise.resolve(mockProbe2) } }, isActiveSession: () => Promise.resolve(mockActiveSession), @@ -812,13 +919,8 @@ describe('Tests for metric change', () => { expect(attributeVal.value).toBe('--token'); expect(attributeVal).toBeDisabled() - const dependencyKey = screen.getByTestId('dependency.0.key'); - expect(dependencyKey.value).toBe(''); - expect(dependencyKey).toBeDisabled() - - const dependencyVal = screen.getByTestId('dependency.0.value'); - expect(dependencyVal.value).toBe(''); - expect(dependencyVal).toBeDisabled() + expect(screen.queryByTestId("dependency.0.key")).not.toBeInTheDocument() + expect(screen.queryByTestId("dependency.0.value")).not.toBeInTheDocument() const parameterKey = screen.getByTestId('parameter.0.key'); expect(parameterKey.value).toBe('--project'); @@ -845,6 +947,159 @@ describe('Tests for metric change', () => { expect(screen.getByRole('button', { name: /delete/i })).toBeInTheDocument(); }) + test('Test that page renders properly if metric with dependency', async () => { + renderChangeView({ route: "/ui/metrics/srce.gridproxy.validity" }) + + await waitFor(() => { + expect(screen.getByRole('button', { name: /save/i })).toBeInTheDocument(); + }) + + expect(screen.getByRole('heading', { name: /change metric/i }).textContent).toBe('Change metric'); + + const nameField = screen.getByTestId('name'); + expect(nameField.value).toBe("srce.gridproxy.validity") + expect(nameField).toBeDisabled() + + const typeField = screen.getByTestId('mtype'); + expect(typeField.value).toBe('Active'); + expect(typeField).toBeDisabled() + + const probeField = screen.getByTestId('probeversion'); + expect(probeField.value).toBe('GridProxy-probe (0.2.0)'); + expect(probeField).toBeDisabled(); + + const packageField = screen.getByTestId('package'); + expect(packageField.value).toBe('argo-probe-globus (0.2.0)'); + expect(packageField).toBeDisabled(); + + expect(screen.queryAllByText(/test_tag/i)).toHaveLength(2); + expect(screen.getByText("internal")).toBeInTheDocument() + + const descriptionField = screen.getByTestId('description'); + expect(descriptionField.value).toBe("") + expect(descriptionField).toBeDisabled(); + + const groupField = screen.getByText('EGI'); + expect(groupField).toBeEnabled() + + expect(screen.queryByText('ARGOTEST')).not.toBeInTheDocument() + selectEvent.openMenu(groupField) + expect(screen.getByText('ARGOTEST')).toBeInTheDocument() + + expect(screen.getByRole('heading', { name: /metric configuration/i })).toBeInTheDocument() + + expect(screen.getByRole('heading', { name: /executable/i }).textContent).toBe('probe executable'); + const executableField = screen.getByTestId('probeexecutable'); + expect(executableField.value).toBe('GridProxy-probe'); + expect(executableField).toBeDisabled() + + const configKey1 = screen.getByTestId('config.0.key'); + expect(configKey1.value).toBe('maxCheckAttempts'); + expect(configKey1).toBeDisabled() + + const configKey2 = screen.getByTestId('config.1.key'); + expect(configKey2.value).toBe('timeout'); + expect(configKey2).toBeDisabled() + + const configKey3 = screen.getByTestId('config.2.key'); + expect(configKey3.value).toBe('path'); + expect(configKey3).toBeDisabled() + + const configKey4 = screen.getByTestId('config.3.key'); + expect(configKey4.value).toBe('interval'); + expect(configKey4).toBeDisabled() + + const configKey5 = screen.getByTestId('config.4.key'); + expect(configKey5.value).toBe('retryInterval'); + expect(configKey5).toBeDisabled() + + const configVal1 = screen.getByTestId('config.0.value'); + expect(configVal1.value).toBe('3'); + expect(configVal1).not.toBeDisabled() + + const configVal2 = screen.getByTestId('config.1.value'); + expect(configVal2.value).toBe('30'); + expect(configVal2).not.toBeDisabled() + + const configVal3 = screen.getByTestId('config.2.value'); + expect(configVal3.value).toBe("/usr/libexec/argo/probes/globus") + expect(configVal3).toBeDisabled() + + const configVal4 = screen.getByTestId('config.3.value'); + expect(configVal4.value).toBe('15') + expect(configVal4).not.toBeDisabled() + + const configVal5 = screen.getByTestId('config.4.value'); + expect(configVal5.value).toBe("3") + expect(configVal5).not.toBeDisabled() + + const attributeKey1 = screen.getByTestId('attributes.0.key'); + expect(attributeKey1.value).toBe("VONAME"); + expect(attributeKey1).toBeDisabled() + + const attributeVal1 = screen.getByTestId('attributes.0.value') + expect(attributeVal1.value).toBe('--vo'); + expect(attributeVal1).toBeDisabled() + + const attributeKey2 = screen.getByTestId("attributes.1.key") + expect(attributeKey2.value).toBe("X509_USER_PROXY") + expect(attributeKey2).toBeDisabled() + + const attributeVal2 = screen.getByTestId("attributes.1.value") + expect(attributeVal2.value).toBe("-x") + expect(attributeVal2).toBeDisabled() + + expect(screen.queryByTestId("attributes.2.key")).not.toBeInTheDocument() + expect(screen.queryByTestId("attributes.2.value")).not.toBeInTheDocument() + + const dependencyKey = screen.getByTestId("dependency.0.key") + expect(dependencyKey.value).toBe("hr.srce.GridProxy-Get") + expect(dependencyKey).toBeDisabled() + + const dependencyVal= screen.getByTestId("dependency.0.value") + expect(dependencyVal.value).toBe("0") + expect(dependencyVal).toBeDisabled() + + const parameterKey = screen.getByTestId("parameter.0.key") + expect(parameterKey.value).toBe("") + expect(parameterKey).toBeDisabled() + + const parameterVal = screen.getByTestId("parameter.0.value") + expect(parameterVal.value).toBe("") + expect(parameterVal).toBeDisabled() + + const flagKey1 = screen.getByTestId('flags.0.key'); + expect(flagKey1.value).toBe('NOHOSTNAME'); + expect(flagKey1).toBeDisabled() + + const flagVal1 = screen.getByTestId('flags.0.value'); + expect(flagVal1.value).toBe('1'); + expect(flagVal1).toBeDisabled() + + const flagKey2 = screen.getByTestId("flags.1.key") + expect(flagKey2.value).toBe("VO") + expect(flagKey2).toBeDisabled() + + const flagVal2 = screen.getByTestId("flags.1.value") + expect(flagVal2.value).toBe("1") + + const flagKey3 = screen.getByTestId("flags.2.key") + expect(flagKey3.value).toBe("NOPUBLISH") + expect(flagKey3).toBeDisabled() + + const flagVal3 = screen.getByTestId("flags.2.value") + expect(flagVal3.value).toBe("1") + expect(flagVal3).toBeDisabled() + + const parentField = screen.getByTestId('parent'); + expect(parentField.value).toBe(''); + expect(parentField).toBeDisabled() + + expect(screen.getByRole('button', { name: /history/i }).closest('a')).toHaveAttribute('href', '/ui/metrics/srce.gridproxy.validity/history') + expect(screen.queryByRole("button", { name: /clone/i })).not.toBeInTheDocument() + expect(screen.getByRole('button', { name: /delete/i })).toBeInTheDocument(); + }) + test('Test that public page renders properly', async () => { Backend.mockImplementationOnce(() => { return { @@ -868,7 +1123,7 @@ describe('Tests for metric change', () => { renderChangeView({ publicView: true }); await waitFor(() => { - expect(screen.getByTestId("dependency.0.key")).toBeInTheDocument() + expect(screen.getByTestId("config.0.key")).toBeInTheDocument() }) expect(screen.getByRole('heading', { name: /details/i }).textContent).toBe('Metric details'); @@ -956,13 +1211,8 @@ describe('Tests for metric change', () => { expect(attributeVal.value).toBe('--token'); expect(attributeVal).toBeDisabled() - const dependencyKey = screen.getByTestId('dependency.0.key'); - expect(dependencyKey.value).toBe(''); - expect(dependencyKey).toBeDisabled() - - const dependencyVal = screen.getByTestId('dependency.0.value'); - expect(dependencyVal.value).toBe(''); - expect(dependencyVal).toBeDisabled() + expect(screen.queryByTestId("dependency.0.key")).not.toBeInTheDocument() + expect(screen.queryByTestId("dependency.0.value")).not.toBeInTheDocument() const parameterKey = screen.getByTestId('parameter.0.key'); expect(parameterKey.value).toBe('--project'); @@ -990,6 +1240,174 @@ describe('Tests for metric change', () => { expect(screen.queryByRole("button", { name: /clone/i })).not.toBeInTheDocument() }) + test('Test that public page renders properly if metric with dependency', async () => { + Backend.mockImplementationOnce(() => { + return { + fetchData: (path) => { + switch (path) { + case '/api/v2/internal/public_metric/srce.gridproxy.validity': + return Promise.resolve(mockMetricWithDependency) + + case '/api/v2/internal/public_version/probe/GridProxy-probe': + return Promise.resolve(mockProbe2) + } + }, + isActiveSession: () => Promise.resolve({ + json: () => Promise.resolve({ detail: 'Authentication credentials were not provided.'}), + status: 403, + statusText: 'FORBIDDEN' + }) + } + }) + renderChangeView({ publicView: true, route: "/ui/public_metrics/srce.gridproxy.validity" }) + + await waitFor(() => { + expect(screen.getByTestId("config.0.key")).toBeInTheDocument() + }) + + expect(screen.getByRole('heading', { name: /details/i }).textContent).toBe('Metric details'); + + const nameField = screen.getByTestId('name'); + expect(nameField.value).toBe("srce.gridproxy.validity") + expect(nameField).toBeDisabled() + + const typeField = screen.getByTestId('mtype'); + expect(typeField.value).toBe('Active'); + expect(typeField).toBeDisabled() + + const probeField = screen.getByTestId('probeversion'); + expect(probeField.value).toBe('GridProxy-probe (0.2.0)'); + expect(probeField).toBeDisabled(); + + const packageField = screen.getByTestId('package'); + expect(packageField.value).toBe('argo-probe-globus (0.2.0)'); + expect(packageField).toBeDisabled(); + + expect(screen.queryAllByText(/test_tag/i)).toHaveLength(2); + expect(screen.getByText("internal")).toBeInTheDocument() + + const descriptionField = screen.getByTestId('description'); + expect(descriptionField.value).toBe("") + expect(descriptionField).toBeDisabled(); + + const groupField = screen.getByTestId("group") + expect(groupField.value).toBe("EGI") + expect(groupField).toBeDisabled() + + expect(screen.getByRole('heading', { name: /metric configuration/i })).toBeInTheDocument() + + expect(screen.getByRole('heading', { name: /executable/i }).textContent).toBe('probe executable'); + const executableField = screen.getByTestId('probeexecutable'); + expect(executableField.value).toBe('GridProxy-probe'); + expect(executableField).toBeDisabled() + + const configKey1 = screen.getByTestId('config.0.key'); + expect(configKey1.value).toBe('maxCheckAttempts'); + expect(configKey1).toBeDisabled() + + const configKey2 = screen.getByTestId('config.1.key'); + expect(configKey2.value).toBe('timeout'); + expect(configKey2).toBeDisabled() + + const configKey3 = screen.getByTestId('config.2.key'); + expect(configKey3.value).toBe('path'); + expect(configKey3).toBeDisabled() + + const configKey4 = screen.getByTestId('config.3.key'); + expect(configKey4.value).toBe('interval'); + expect(configKey4).toBeDisabled() + + const configKey5 = screen.getByTestId('config.4.key'); + expect(configKey5.value).toBe('retryInterval'); + expect(configKey5).toBeDisabled() + + const configVal1 = screen.getByTestId('config.0.value'); + expect(configVal1.value).toBe('3'); + expect(configVal1).toBeDisabled() + + const configVal2 = screen.getByTestId('config.1.value'); + expect(configVal2.value).toBe('30'); + expect(configVal2).toBeDisabled() + + const configVal3 = screen.getByTestId('config.2.value'); + expect(configVal3.value).toBe("/usr/libexec/argo/probes/globus") + expect(configVal3).toBeDisabled() + + const configVal4 = screen.getByTestId('config.3.value'); + expect(configVal4.value).toBe('15') + expect(configVal4).toBeDisabled() + + const configVal5 = screen.getByTestId('config.4.value'); + expect(configVal5.value).toBe("3") + expect(configVal5).toBeDisabled() + + const attributeKey1 = screen.getByTestId('attributes.0.key'); + expect(attributeKey1.value).toBe("VONAME"); + expect(attributeKey1).toBeDisabled() + + const attributeVal1 = screen.getByTestId('attributes.0.value') + expect(attributeVal1.value).toBe('--vo'); + expect(attributeVal1).toBeDisabled() + + const attributeKey2 = screen.getByTestId("attributes.1.key") + expect(attributeKey2.value).toBe("X509_USER_PROXY") + expect(attributeKey2).toBeDisabled() + + const attributeVal2 = screen.getByTestId("attributes.1.value") + expect(attributeVal2.value).toBe("-x") + expect(attributeVal2).toBeDisabled() + + expect(screen.queryByTestId("attributes.2.key")).not.toBeInTheDocument() + expect(screen.queryByTestId("attributes.2.value")).not.toBeInTheDocument() + + const dependencyKey = screen.getByTestId("dependency.0.key") + expect(dependencyKey.value).toBe("hr.srce.GridProxy-Get") + expect(dependencyKey).toBeDisabled() + + const dependencyVal= screen.getByTestId("dependency.0.value") + expect(dependencyVal.value).toBe("0") + expect(dependencyVal).toBeDisabled() + + const parameterKey = screen.getByTestId("parameter.0.key") + expect(parameterKey.value).toBe("") + expect(parameterKey).toBeDisabled() + + const parameterVal = screen.getByTestId("parameter.0.value") + expect(parameterVal.value).toBe("") + expect(parameterVal).toBeDisabled() + + const flagKey1 = screen.getByTestId('flags.0.key'); + expect(flagKey1.value).toBe('NOHOSTNAME'); + expect(flagKey1).toBeDisabled() + + const flagVal1 = screen.getByTestId('flags.0.value'); + expect(flagVal1.value).toBe('1'); + expect(flagVal1).toBeDisabled() + + const flagKey2 = screen.getByTestId("flags.1.key") + expect(flagKey2.value).toBe("VO") + expect(flagKey2).toBeDisabled() + + const flagVal2 = screen.getByTestId("flags.1.value") + expect(flagVal2.value).toBe("1") + + const flagKey3 = screen.getByTestId("flags.2.key") + expect(flagKey3.value).toBe("NOPUBLISH") + expect(flagKey3).toBeDisabled() + + const flagVal3 = screen.getByTestId("flags.2.value") + expect(flagVal3.value).toBe("1") + expect(flagVal3).toBeDisabled() + + const parentField = screen.getByTestId('parent'); + expect(parentField.value).toBe(''); + expect(parentField).toBeDisabled() + + expect(screen.queryByRole('button', { name: /history/i })).not.toBeInTheDocument() + expect(screen.queryByRole("button", { name: /clone/i })).not.toBeInTheDocument() + expect(screen.queryByRole('button', { name: /delete/i })).not.toBeInTheDocument() + }) + test('Test that passive metric changeview renders properly', async () => { renderChangeView({ route: '/ui/metrics/org.apel.APEL-Pub' }); @@ -1332,7 +1750,6 @@ describe('Tests for metric change', () => { attribute: mockMetric.attribute, dependancy: mockMetric.dependancy, flags: mockMetric.flags, - files: mockMetric.files, parameter: mockMetric.parameter } ) @@ -1461,7 +1878,7 @@ describe('Tests for metric change', () => { renderChangeView(); await waitFor(() => { - expect(screen.getByTestId("dependency.0.key")).toBeInTheDocument() + expect(screen.getByTestId("config.0.key")).toBeInTheDocument() }) const nameField = screen.getByTestId('name'); @@ -1483,13 +1900,14 @@ describe('Tests for metric change', () => { const configVal5 = screen.getByTestId('config.4.value'); const attributeKey = screen.getByTestId('attributes.0.key'); const attributeVal = screen.getByTestId('attributes.0.value') - const dependencyKey = screen.getByTestId('dependency.0.key'); - const dependencyVal = screen.getByTestId('dependency.0.value'); const parameterKey = screen.getByTestId('parameter.0.key'); const parameterVal = screen.getByTestId('parameter.0.value'); const flagKey = screen.getByTestId('flags.0.key'); const flagVal = screen.getByTestId('flags.0.value'); const parentField = screen.getByTestId('parent'); + + expect(screen.queryByTestId("dependency.0.key")).not.toBeInTheDocument() + expect(screen.queryByTestId("dependency.0.value")).not.toBeInTheDocument() expect(screen.getByRole('alert').textContent).toBe( 'This is a read-only instance, please request the corresponding permissions to perform any changes in this form.' @@ -1539,10 +1957,6 @@ describe('Tests for metric change', () => { expect(attributeKey).toBeDisabled() expect(attributeVal.value).toBe('--token'); expect(attributeVal).toBeDisabled() - expect(dependencyKey.value).toBe(''); - expect(dependencyKey).toBeDisabled() - expect(dependencyVal.value).toBe(''); - expect(dependencyVal).toBeDisabled() expect(parameterKey.value).toBe('--project'); expect(parameterKey).toBeDisabled() expect(parameterVal.value).toBe('EGI'); @@ -1581,7 +1995,7 @@ describe('Tests for metric history', () => { renderVersionDetailsView(); await waitFor(() => { - expect(screen.getByTestId("dependency.0.key")).toBeInTheDocument() + expect(screen.getByTestId("config.0.key")).toBeInTheDocument() }) expect(screen.getByRole('heading', { name: /argo/i }).textContent).toBe('argo.AMS-Check (2020-11-30 13:23:48)'); @@ -1605,14 +2019,15 @@ describe('Tests for metric history', () => { const configVal5 = screen.getByTestId('config.4.value'); const attributeKey = screen.getByTestId('attributes.0.key'); const attributeVal = screen.getByTestId('attributes.0.value') - const dependencyKey = screen.getByTestId('dependency.0.key'); - const dependencyVal = screen.getByTestId('dependency.0.value'); const parameterKey = screen.getByTestId('parameter.0.key'); const parameterVal = screen.getByTestId('parameter.0.value'); const flagKey = screen.getByTestId('flags.0.key'); const flagVal = screen.getByTestId('flags.0.value'); const parentField = screen.getByTestId('parent'); + expect(screen.queryByTestId("dependency.0.key")).not.toBeInTheDocument() + expect(screen.queryByTestId("dependency.0.value")).not.toBeInTheDocument() + expect(nameField.value).toBe('argo.AMS-Check'); expect(nameField).toBeDisabled() expect(typeField.value).toBe('Active'); @@ -1654,10 +2069,6 @@ describe('Tests for metric history', () => { expect(attributeKey).toBeDisabled() expect(attributeVal.value).toBe('--token'); expect(attributeVal).toBeDisabled() - expect(dependencyKey.value).toBe(''); - expect(dependencyKey).toBeDisabled() - expect(dependencyVal.value).toBe(''); - expect(dependencyVal).toBeDisabled() expect(parameterKey.value).toBe('--project'); expect(parameterKey).toBeDisabled() expect(parameterVal.value).toBe('EGI'); diff --git a/poem/Poem/helpers/metrics_helpers.py b/poem/Poem/helpers/metrics_helpers.py index c350fe4a3..d8bb25a30 100644 --- a/poem/Poem/helpers/metrics_helpers.py +++ b/poem/Poem/helpers/metrics_helpers.py @@ -386,9 +386,6 @@ def sync_metrics(tenant, user): key for key in get_metrics_in_profiles(tenant=tenant) ] metrics = poem_models.Metric.objects.all().values_list("name", flat=True) - internal = admin_models.MetricTemplate.objects.filter( - tags__name="internal" - ).values_list("name", flat=True) missing_metrics = list(set(metrics_in_profiles).difference(set(metrics))) extra_metrics = list(set(metrics).difference(set(metrics_in_profiles))) @@ -399,15 +396,14 @@ def sync_metrics(tenant, user): deleted = list() for metric in extra_metrics: - if metric not in internal: - m = poem_models.Metric.objects.get(name=metric) - poem_models.TenantHistory.objects.filter( - object_id=m.id, - content_type=ContentType.objects.get_for_model( - poem_models.Metric - ) - ).delete() - m.delete() - deleted.append(metric) + m = poem_models.Metric.objects.get(name=metric) + poem_models.TenantHistory.objects.filter( + object_id=m.id, + content_type=ContentType.objects.get_for_model( + poem_models.Metric + ) + ).delete() + m.delete() + deleted.append(metric) return imported, warn, err, unavailable, deleted diff --git a/poem/Poem/poem/management/commands/import_internal_metrics.py b/poem/Poem/poem/management/commands/import_internal_metrics.py index cea27f5e0..6b4b3ef4b 100644 --- a/poem/Poem/poem/management/commands/import_internal_metrics.py +++ b/poem/Poem/poem/management/commands/import_internal_metrics.py @@ -15,7 +15,8 @@ def handle(self, *args, **kwargs): tenant = connection.tenant internal_metrics = [ mt.name for mt in admin_models.MetricTemplate.objects.all() - if 'internal' in [tag.name for tag in mt.tags.all()] + if 'internal' in [tag.name for tag in mt.tags.all()] and + "eol" not in [tag.name for tag in mt.tags.all()] ] if len(internal_metrics) > 0: try: @@ -32,8 +33,8 @@ def handle(self, *args, **kwargs): get_user_model().DoesNotExist, NoSectionError, NoOptionError ): self.stderr.write( - 'Super user for tenant {} is not defined.\n' - 'Internal metrics not imported.'.format(tenant.name) + f'Super user for tenant {tenant.name} is not defined.\n' + f'Internal metrics not imported.' ) else: