@oneuptime/common 12.0.9 → 12.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/NetworkDevice.ts +44 -0
- package/Models/DatabaseModels/NetworkDeviceLink.ts +116 -0
- package/Models/DatabaseModels/RunnerJob.ts +17 -2
- package/Server/API/LlmProviderAPI.ts +8 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.ts +52 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Services/NetworkDeviceLabelRuleEngineService.ts +240 -0
- package/Server/Services/NetworkDeviceLinkService.ts +338 -2
- package/Server/Services/NetworkDeviceService.ts +265 -12
- package/Server/Services/RunnerJobService.ts +24 -1
- package/Server/Types/Billing/MeteredPlan/AllMeteredPlans.ts +19 -5
- package/Server/Types/Database/JSONColumnQuery.ts +42 -4
- package/Server/Utils/LLM/LLMService.ts +108 -19
- package/Server/Utils/Monitor/MonitorAlert.ts +22 -115
- package/Server/Utils/Monitor/MonitorIncident.ts +7 -337
- package/Server/Utils/Monitor/MonitorMaintenanceSuppression.ts +27 -2
- package/Server/Utils/Monitor/SeriesResourceLabels.ts +32 -0
- package/Server/Utils/Monitor/SeriesResourceLinker.ts +469 -0
- package/Server/Utils/SSRFProtection.ts +5 -4
- package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +186 -38
- package/Tests/App/AdminDashboard/AdminUsersBulkAddToProject.test.tsx +955 -0
- package/Tests/App/AdminDashboard/BulkAddUsersToProject.test.ts +778 -0
- package/Tests/App/AdminDashboard/BulkAddUsersToProjectModal.test.tsx +1051 -0
- package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +420 -112
- package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +81 -6
- package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +543 -0
- package/Tests/App/Dashboard/PayAsYouGoFreePlan.test.ts +125 -0
- package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +612 -0
- package/Tests/App/Dashboard/SloWidgetChartRendering.test.tsx +536 -0
- package/Tests/App/Dashboard/UserElementEmail.test.tsx +447 -0
- package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +284 -0
- package/Tests/Models/AnalyticsModels/CommonModel.test.ts +312 -0
- package/Tests/Server/Infrastructure/Postgres/DeviceRoleAndDeclaredLinkParentMigration.test.ts +250 -0
- package/Tests/Server/Infrastructure/Postgres/EpisodeMemberNotifyIndexesMigration.test.ts +46 -18
- package/Tests/Server/Services/NetworkDeviceLabelRuleRun.test.ts +618 -0
- package/Tests/Server/Services/NetworkDeviceLinkParentValidation.test.ts +1043 -0
- package/Tests/Server/Services/NetworkSiteAssignmentRuleRun.test.ts +706 -0
- package/Tests/Server/Services/RunnerJobEnqueue.test.ts +412 -0
- package/Tests/Server/Types/Billing/PayAsYouGoMeteredPricing.test.ts +125 -0
- package/Tests/Server/Types/Database/JSONColumnQuery.test.ts +508 -0
- package/Tests/Server/Utils/AI/LLMServiceRequestPolicy.test.ts +2 -2
- package/Tests/Server/Utils/AI/LLMServiceRetryPolicy.test.ts +450 -0
- package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +556 -0
- package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +547 -0
- package/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.ts +30 -0
- package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +699 -0
- package/Tests/Server/Utils/SeriesResourceLabels.test.ts +55 -0
- package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +668 -0
- package/Tests/Types/API/URLMalformedTolerance.test.ts +301 -0
- package/Tests/Types/API/URLTelScheme.test.ts +261 -0
- package/Tests/Types/API/URLUnsupportedScheme.test.ts +148 -0
- package/Tests/Types/Billing/PayAsYouGoPricing.test.ts +64 -0
- package/Tests/Types/CustomField/CustomFieldTypeCorrespondence.test.ts +107 -0
- package/Tests/Types/Monitor/MonitorType.test.ts +62 -0
- package/Tests/Types/NetworkAutomation/RuleRunResult.test.ts +184 -0
- package/Tests/Types/Runbook/RunbookStepType.test.ts +124 -0
- package/Tests/Types/Workflow/BaseModelComponents.test.ts +17 -0
- package/Tests/UI/Components/Button.test.tsx +279 -0
- package/Tests/UI/Components/Charts/ChartXAxisDataKey.test.tsx +256 -0
- package/Tests/UI/Components/Charts/DataPointUtil.test.ts +323 -0
- package/Tests/UI/Components/DictionaryValuePlaceholderAndSuggestions.test.tsx +595 -0
- package/Tests/UI/Components/Icon.test.tsx +57 -0
- package/Tests/UI/Components/List.test.tsx +20 -3
- package/Tests/UI/Components/LogsSavedViewsDropdown.test.tsx +304 -0
- package/Tests/UI/Components/LogsViewerClearSavedView.test.tsx +179 -0
- package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +349 -1
- package/Tests/UI/Components/Pagination.test.tsx +721 -146
- package/Tests/UI/Components/PaginationIntegration.test.tsx +386 -0
- package/Tests/UI/Components/PaginationUtil.test.ts +417 -0
- package/Tests/UI/Components/TelemetrySavedViewsDropdown.test.tsx +343 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnControl.test.ts +289 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnOperators.test.ts +271 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnRow.test.ts +119 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.test.ts +968 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditor.render.test.tsx +766 -0
- package/Tests/UI/Utils/RunNetworkRule.test.ts +320 -0
- package/Tests/Utils/APIRetryPolicy.test.ts +823 -0
- package/Tests/Utils/Monitor/NetworkDeviceRoleUtil.test.ts +502 -0
- package/Tests/Utils/Monitor/NetworkTopologyDeclaredHierarchy.test.ts +759 -0
- package/Tests/Utils/NetworkAutomation/RuleRunSummary.test.ts +286 -0
- package/Tests/Utils/Schema/BaseSchema.test.ts +651 -0
- package/Types/API/Protocol.ts +9 -0
- package/Types/API/URL.ts +286 -20
- package/Types/Billing/PayAsYouGoPricing.ts +47 -0
- package/Types/CustomField/CustomFieldType.ts +28 -0
- package/Types/Monitor/MonitorType.ts +11 -0
- package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +13 -0
- package/Types/NetworkAutomation/RuleRunResult.ts +121 -0
- package/Types/Runbook/RunbookStepType.ts +23 -0
- package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +7 -0
- package/UI/Components/Button/Button.tsx +14 -22
- package/UI/Components/Charts/Area/AreaChart.tsx +4 -2
- package/UI/Components/Charts/Bar/BarChart.tsx +4 -2
- package/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.ts +13 -0
- package/UI/Components/Charts/Line/LineChart.tsx +4 -2
- package/UI/Components/Charts/Types/XAxis/XAxis.ts +6 -0
- package/UI/Components/Charts/Utils/DataPoint.ts +15 -12
- package/UI/Components/Dictionary/Dictionary.tsx +4 -39
- package/UI/Components/Icon/Icon.tsx +1 -1
- package/UI/Components/LogsViewer/LogsViewer.tsx +5 -1
- package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +8 -0
- package/UI/Components/LogsViewer/components/LogsPagination.tsx +29 -91
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +2 -0
- package/UI/Components/LogsViewer/components/SavedViewsDropdown.tsx +66 -1
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +55 -0
- package/UI/Components/Pagination/Pagination.tsx +373 -328
- package/UI/Components/Pagination/PaginationUtil.ts +263 -0
- package/UI/Components/Pagination/Screenshots/README.md +15 -0
- package/UI/Components/Pagination/Screenshots/pagination-before-after.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-go-to-page-modal.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-long-list.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-mobile.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-telemetry.png +0 -0
- package/UI/Components/TelemetryViewer/components/SavedViewsDropdown.tsx +66 -1
- package/UI/Components/TelemetryViewer/components/TelemetryPagination.tsx +38 -92
- package/UI/Components/TextArea/TextArea.tsx +3 -0
- package/UI/Components/Workflow/ArgumentsForm.tsx +18 -1
- package/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.tsx +177 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.tsx +243 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnControl.ts +255 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.tsx +178 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnOperators.ts +206 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnRow.ts +157 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.ts +517 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnValueInput.tsx +482 -0
- package/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.tsx +131 -0
- package/UI/Components/Workflow/ColumnEditor/ModelRecordForm.tsx +163 -0
- package/UI/Components/Workflow/ModelColumnEditor.tsx +284 -100
- package/UI/Utils/NetworkAutomation/RunNetworkRule.ts +122 -0
- package/Utils/API.ts +243 -36
- package/Utils/Monitor/NetworkDeviceRoleUtil.ts +45 -0
- package/Utils/Monitor/NetworkTopologyUtil.ts +42 -2
- package/Utils/NetworkAutomation/RuleRunSummary.ts +147 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +45 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js +115 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RunnerJob.js +17 -2
- package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
- package/build/dist/Server/API/LlmProviderAPI.js +9 -1
- package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.js +33 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js +202 -0
- package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceLinkService.js +253 -1
- package/build/dist/Server/Services/NetworkDeviceLinkService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +223 -12
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/RunnerJobService.js +20 -2
- package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
- package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js +12 -5
- package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js.map +1 -1
- package/build/dist/Server/Types/Database/JSONColumnQuery.js +40 -4
- package/build/dist/Server/Types/Database/JSONColumnQuery.js.map +1 -1
- package/build/dist/Server/Utils/LLM/LLMService.js +78 -22
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +22 -98
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +7 -254
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js +14 -2
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +27 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +285 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -0
- package/build/dist/Server/Utils/SSRFProtection.js +5 -4
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +128 -35
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
- package/build/dist/Types/API/Protocol.js +9 -0
- package/build/dist/Types/API/Protocol.js.map +1 -1
- package/build/dist/Types/API/URL.js +241 -18
- package/build/dist/Types/API/URL.js.map +1 -1
- package/build/dist/Types/Billing/PayAsYouGoPricing.js +41 -0
- package/build/dist/Types/Billing/PayAsYouGoPricing.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldType.js +26 -0
- package/build/dist/Types/CustomField/CustomFieldType.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorType.js +10 -0
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js +39 -0
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookStepType.js +21 -0
- package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
- package/build/dist/UI/Components/Button/Button.js +10 -16
- package/build/dist/UI/Components/Button/Button.js.map +1 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +2 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +2 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.js +12 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.js.map +1 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js +2 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js +17 -10
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
- package/build/dist/UI/Components/Dictionary/Dictionary.js +4 -18
- package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +1 -1
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +3 -3
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +5 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js +14 -44
- package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js +20 -2
- package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +42 -0
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Components/Pagination/Pagination.js +174 -199
- package/build/dist/UI/Components/Pagination/Pagination.js.map +1 -1
- package/build/dist/UI/Components/Pagination/PaginationUtil.js +160 -0
- package/build/dist/UI/Components/Pagination/PaginationUtil.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js +20 -2
- package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryPagination.js +23 -47
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryPagination.js.map +1 -1
- package/build/dist/UI/Components/TextArea/TextArea.js +1 -1
- package/build/dist/UI/Components/TextArea/TextArea.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js +14 -2
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.js +86 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.js +119 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnControl.js +206 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnControl.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.js +57 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnOperators.js +150 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnOperators.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRow.js +67 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRow.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.js +309 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnValueInput.js +204 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnValueInput.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js +66 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelRecordForm.js +83 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelRecordForm.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +187 -68
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -1
- package/build/dist/UI/Utils/NetworkAutomation/RunNetworkRule.js +75 -0
- package/build/dist/UI/Utils/NetworkAutomation/RunNetworkRule.js.map +1 -0
- package/build/dist/Utils/API.js +175 -34
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js +37 -0
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +25 -3
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/NetworkAutomation/RuleRunSummary.js +78 -0
- package/build/dist/Utils/NetworkAutomation/RuleRunSummary.js.map +1 -0
- package/package.json +1 -1
|
@@ -3,9 +3,12 @@ import { NetworkTopologyDeviceRole } from "../../../Types/Monitor/SnmpMonitor/Ne
|
|
|
3
3
|
import {
|
|
4
4
|
DEVICE_ROLES_IN_LEGEND_ORDER,
|
|
5
5
|
DEVICE_ROLE_LABELS,
|
|
6
|
+
DeviceRoleSignals,
|
|
6
7
|
classifyDeviceRole,
|
|
7
8
|
classifyEndpointRole,
|
|
8
9
|
labelForDeviceRole,
|
|
10
|
+
parseDeviceRoleOverride,
|
|
11
|
+
resolveDeviceRole,
|
|
9
12
|
} from "../../../Utils/Monitor/NetworkDeviceRoleUtil";
|
|
10
13
|
|
|
11
14
|
/*
|
|
@@ -512,3 +515,502 @@ describe("labels", () => {
|
|
|
512
515
|
);
|
|
513
516
|
});
|
|
514
517
|
});
|
|
518
|
+
|
|
519
|
+
/*
|
|
520
|
+
* The operator's override (NetworkDevice.deviceRole), from issue #3192.
|
|
521
|
+
*
|
|
522
|
+
* The classifier above only ever sees SNMP, so a device that answers
|
|
523
|
+
* nothing but ping is unclassifiable forever — it is drawn "unknown" and
|
|
524
|
+
* no amount of re-polling changes that. The override is the operator
|
|
525
|
+
* saying what the box IS, which is the only statement about a role in the
|
|
526
|
+
* system that is not an inference, so it has to beat every tier of
|
|
527
|
+
* evidence. Everything below is about the two halves of that promise:
|
|
528
|
+
* parseDeviceRoleOverride deciding whether a stored string is a
|
|
529
|
+
* declaration at all, and resolveDeviceRole deciding who wins when it is.
|
|
530
|
+
*/
|
|
531
|
+
|
|
532
|
+
/*
|
|
533
|
+
* A real switch, described by every tier at once: named product family in
|
|
534
|
+
* the model, the vendor's boilerplate in sysDescr, Cisco's arc in the OID,
|
|
535
|
+
* and a hostname that follows the convention. Nothing about this device is
|
|
536
|
+
* ambiguous, which is exactly why it is the right device to point an
|
|
537
|
+
* override at — if the override wins here it wins anywhere.
|
|
538
|
+
*/
|
|
539
|
+
const CATALYST_SWITCH_SIGNALS: DeviceRoleSignals = {
|
|
540
|
+
vendor: "Cisco Systems",
|
|
541
|
+
deviceModel: "WS-C2960X-48FPD-L",
|
|
542
|
+
sysDescr: "Cisco IOS Software, C2960X Software, Catalyst 2960X switch",
|
|
543
|
+
sysObjectId: "1.3.6.1.4.1.9.1.1745",
|
|
544
|
+
sysName: "idf2-sw-3",
|
|
545
|
+
name: "idf2-sw-3",
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
describe("parseDeviceRoleOverride — the values it accepts", () => {
|
|
549
|
+
test("every role a legend can show round-trips through the column", () => {
|
|
550
|
+
/*
|
|
551
|
+
* The column is what the picker writes and what the topology builder
|
|
552
|
+
* reads back, so the set it accepts has to be exactly the set the UI
|
|
553
|
+
* offers. Looping the legend rather than listing the roles means a new
|
|
554
|
+
* role cannot be added to the type without this test covering it.
|
|
555
|
+
*/
|
|
556
|
+
for (const role of DEVICE_ROLES_IN_LEGEND_ORDER) {
|
|
557
|
+
const parsed: NetworkTopologyDeviceRole | undefined =
|
|
558
|
+
parseDeviceRoleOverride(role);
|
|
559
|
+
expect(parsed).toBe(role);
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
test("matching ignores the case the value was stored in", () => {
|
|
564
|
+
/*
|
|
565
|
+
* Values arrive from a form, an API caller and a seed script, and none
|
|
566
|
+
* of them agree about casing.
|
|
567
|
+
*/
|
|
568
|
+
expect(parseDeviceRoleOverride("switch")).toBe("switch");
|
|
569
|
+
expect(parseDeviceRoleOverride("SWITCH")).toBe("switch");
|
|
570
|
+
expect(parseDeviceRoleOverride("Switch")).toBe("switch");
|
|
571
|
+
expect(parseDeviceRoleOverride("sWiTcH")).toBe("switch");
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
test("the camelCase roles survive every casing, which a one-sided compare would break", () => {
|
|
575
|
+
/*
|
|
576
|
+
* wirelessAccessPoint and loadBalancer are the only two role keys with
|
|
577
|
+
* an internal capital, so they are the two a naive
|
|
578
|
+
* `role === value.toLowerCase()` would silently drop — the stored value
|
|
579
|
+
* would look valid, parse to undefined, and the classifier would
|
|
580
|
+
* quietly overrule the operator. Both sides have to be lowercased.
|
|
581
|
+
*/
|
|
582
|
+
expect(parseDeviceRoleOverride("wirelessAccessPoint")).toBe(
|
|
583
|
+
"wirelessAccessPoint",
|
|
584
|
+
);
|
|
585
|
+
expect(parseDeviceRoleOverride("wirelessaccesspoint")).toBe(
|
|
586
|
+
"wirelessAccessPoint",
|
|
587
|
+
);
|
|
588
|
+
expect(parseDeviceRoleOverride("WIRELESSACCESSPOINT")).toBe(
|
|
589
|
+
"wirelessAccessPoint",
|
|
590
|
+
);
|
|
591
|
+
expect(parseDeviceRoleOverride("WirelessAccessPoint")).toBe(
|
|
592
|
+
"wirelessAccessPoint",
|
|
593
|
+
);
|
|
594
|
+
|
|
595
|
+
expect(parseDeviceRoleOverride("loadBalancer")).toBe("loadBalancer");
|
|
596
|
+
expect(parseDeviceRoleOverride("loadbalancer")).toBe("loadBalancer");
|
|
597
|
+
expect(parseDeviceRoleOverride("LOADBALANCER")).toBe("loadBalancer");
|
|
598
|
+
expect(parseDeviceRoleOverride("LoadBalancer")).toBe("loadBalancer");
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
test("surrounding whitespace is trimmed off before matching", () => {
|
|
602
|
+
// Copy-paste into a text field is the normal way a stray space arrives.
|
|
603
|
+
expect(parseDeviceRoleOverride(" router ")).toBe("router");
|
|
604
|
+
expect(parseDeviceRoleOverride("\tswitch")).toBe("switch");
|
|
605
|
+
expect(parseDeviceRoleOverride("firewall\n")).toBe("firewall");
|
|
606
|
+
expect(parseDeviceRoleOverride(" loadBalancer ")).toBe("loadBalancer");
|
|
607
|
+
expect(parseDeviceRoleOverride("\n wirelessaccesspoint \t ")).toBe(
|
|
608
|
+
"wirelessAccessPoint",
|
|
609
|
+
);
|
|
610
|
+
});
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
describe("parseDeviceRoleOverride — the values it refuses", () => {
|
|
614
|
+
test("absent, empty and blank all mean no override rather than a role", () => {
|
|
615
|
+
/*
|
|
616
|
+
* Undefined is the answer that lets the classifier run, so every way of
|
|
617
|
+
* saying "the operator never filled this in" — a null column, an empty
|
|
618
|
+
* string from a cleared form, a field containing only spaces — has to
|
|
619
|
+
* land on it.
|
|
620
|
+
*/
|
|
621
|
+
expect(parseDeviceRoleOverride(undefined)).toBeUndefined();
|
|
622
|
+
expect(parseDeviceRoleOverride(null)).toBeUndefined();
|
|
623
|
+
expect(parseDeviceRoleOverride("")).toBeUndefined();
|
|
624
|
+
expect(parseDeviceRoleOverride(" ")).toBeUndefined();
|
|
625
|
+
expect(parseDeviceRoleOverride("\t\n ")).toBeUndefined();
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
test('"unknown" is deliberately refused even though it is a real role', () => {
|
|
629
|
+
/*
|
|
630
|
+
* This is the one refusal that is a design decision rather than a
|
|
631
|
+
* validation failure. Empty already means "classify it", so storing
|
|
632
|
+
* "unknown" could only mean the different and much less useful thing:
|
|
633
|
+
* pin this device to the neutral shape and switch the classifier off
|
|
634
|
+
* for good. An operator who does not know what a box is wants the
|
|
635
|
+
* classifier to keep trying, not to be silenced — so "unknown" is not
|
|
636
|
+
* in DEVICE_ROLES_IN_LEGEND_ORDER and parses as no override at all.
|
|
637
|
+
*/
|
|
638
|
+
expect(parseDeviceRoleOverride("unknown")).toBeUndefined();
|
|
639
|
+
expect(parseDeviceRoleOverride("Unknown")).toBeUndefined();
|
|
640
|
+
expect(parseDeviceRoleOverride("UNKNOWN")).toBeUndefined();
|
|
641
|
+
expect(parseDeviceRoleOverride(" unknown ")).toBeUndefined();
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
test("unrecognised text is refused rather than partially matched", () => {
|
|
645
|
+
/*
|
|
646
|
+
* The match is whole-string equality on purpose: a prefix or substring
|
|
647
|
+
* rule would let "rout" or "router-ish" mean router, and an override
|
|
648
|
+
* that quietly means something adjacent to what was typed is worse
|
|
649
|
+
* than one that does nothing.
|
|
650
|
+
*/
|
|
651
|
+
expect(parseDeviceRoleOverride("banana")).toBeUndefined();
|
|
652
|
+
expect(parseDeviceRoleOverride("rout")).toBeUndefined();
|
|
653
|
+
expect(parseDeviceRoleOverride("router-ish")).toBeUndefined();
|
|
654
|
+
expect(parseDeviceRoleOverride("routers")).toBeUndefined();
|
|
655
|
+
expect(parseDeviceRoleOverride("sw")).toBeUndefined();
|
|
656
|
+
expect(parseDeviceRoleOverride("core-sw-1")).toBeUndefined();
|
|
657
|
+
expect(parseDeviceRoleOverride("42")).toBeUndefined();
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
test("only the surrounding whitespace is forgiven, never the internal kind", () => {
|
|
661
|
+
/*
|
|
662
|
+
* Trimming the ends is a paste artefact; rewriting the middle would be
|
|
663
|
+
* guessing. "wireless access point" is prose, not the stored value, so
|
|
664
|
+
* it is refused — while the same letters with only outer padding are
|
|
665
|
+
* accepted (covered above).
|
|
666
|
+
*/
|
|
667
|
+
expect(parseDeviceRoleOverride("wireless access point")).toBeUndefined();
|
|
668
|
+
expect(parseDeviceRoleOverride("wireless accesspoint")).toBeUndefined();
|
|
669
|
+
expect(parseDeviceRoleOverride("wireless-access-point")).toBeUndefined();
|
|
670
|
+
expect(parseDeviceRoleOverride("wireless_access_point")).toBeUndefined();
|
|
671
|
+
expect(parseDeviceRoleOverride("load balancer")).toBeUndefined();
|
|
672
|
+
expect(parseDeviceRoleOverride("load-balancer")).toBeUndefined();
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
test("the display labels are not storable values", () => {
|
|
676
|
+
/*
|
|
677
|
+
* DEVICE_ROLE_LABELS is what a human reads; the column holds the role
|
|
678
|
+
* key. The multi-word labels are the ones that would break if a UI ever
|
|
679
|
+
* wrote the label back, so they must not silently parse.
|
|
680
|
+
*/
|
|
681
|
+
expect(
|
|
682
|
+
parseDeviceRoleOverride(DEVICE_ROLE_LABELS.wirelessAccessPoint),
|
|
683
|
+
).toBe(undefined);
|
|
684
|
+
expect(parseDeviceRoleOverride(DEVICE_ROLE_LABELS.loadBalancer)).toBe(
|
|
685
|
+
undefined,
|
|
686
|
+
);
|
|
687
|
+
expect(parseDeviceRoleOverride(DEVICE_ROLE_LABELS.phone)).toBe(undefined);
|
|
688
|
+
expect(parseDeviceRoleOverride(DEVICE_ROLE_LABELS.unknown)).toBe(undefined);
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
describe("parseDeviceRoleOverride — determinism", () => {
|
|
693
|
+
test("the same value parses the same way however many times it is asked", () => {
|
|
694
|
+
/*
|
|
695
|
+
* Cheap insurance against any future implementation that reaches for a
|
|
696
|
+
* shared regex (whose lastIndex is stateful) or a memo keyed on
|
|
697
|
+
* something mutable: the same input must not answer differently on the
|
|
698
|
+
* second call, or a topology would depend on how many devices were
|
|
699
|
+
* built before it.
|
|
700
|
+
*/
|
|
701
|
+
const values: Array<string> = [
|
|
702
|
+
"switch",
|
|
703
|
+
"SWITCH",
|
|
704
|
+
"unknown",
|
|
705
|
+
"banana",
|
|
706
|
+
"loadBalancer",
|
|
707
|
+
"switch",
|
|
708
|
+
];
|
|
709
|
+
const firstPass: Array<NetworkTopologyDeviceRole | undefined> = values.map(
|
|
710
|
+
(value: string) => {
|
|
711
|
+
return parseDeviceRoleOverride(value);
|
|
712
|
+
},
|
|
713
|
+
);
|
|
714
|
+
const secondPass: Array<NetworkTopologyDeviceRole | undefined> = values.map(
|
|
715
|
+
(value: string) => {
|
|
716
|
+
return parseDeviceRoleOverride(value);
|
|
717
|
+
},
|
|
718
|
+
);
|
|
719
|
+
|
|
720
|
+
expect(secondPass).toEqual(firstPass);
|
|
721
|
+
expect(firstPass).toEqual([
|
|
722
|
+
"switch",
|
|
723
|
+
"switch",
|
|
724
|
+
undefined,
|
|
725
|
+
undefined,
|
|
726
|
+
"loadBalancer",
|
|
727
|
+
"switch",
|
|
728
|
+
]);
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
test("parsing the legend backwards gives the same answers as parsing it forwards", () => {
|
|
732
|
+
/*
|
|
733
|
+
* The order the caller happens to iterate in must not leak into the
|
|
734
|
+
* result — the same guarantee the topology builder relies on when it
|
|
735
|
+
* resolves devices in whatever order the database returned them.
|
|
736
|
+
*/
|
|
737
|
+
const forwards: Array<NetworkTopologyDeviceRole | undefined> =
|
|
738
|
+
DEVICE_ROLES_IN_LEGEND_ORDER.map((role: NetworkTopologyDeviceRole) => {
|
|
739
|
+
return parseDeviceRoleOverride(role);
|
|
740
|
+
});
|
|
741
|
+
const backwards: Array<NetworkTopologyDeviceRole | undefined> = [
|
|
742
|
+
...DEVICE_ROLES_IN_LEGEND_ORDER,
|
|
743
|
+
]
|
|
744
|
+
.reverse()
|
|
745
|
+
.map((role: NetworkTopologyDeviceRole) => {
|
|
746
|
+
return parseDeviceRoleOverride(role);
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
expect(backwards).toEqual([...forwards].reverse());
|
|
750
|
+
expect(forwards).toEqual([...DEVICE_ROLES_IN_LEGEND_ORDER]);
|
|
751
|
+
});
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
describe("resolveDeviceRole — a stored override outranks the evidence", () => {
|
|
755
|
+
test("an override beats a sysDescr that names the product family outright", () => {
|
|
756
|
+
/*
|
|
757
|
+
* The device is unmistakably a Catalyst switch by every tier the
|
|
758
|
+
* classifier has. The operator says it is a camera; the operator wins,
|
|
759
|
+
* because the classifier is inferring and the operator is not. (In
|
|
760
|
+
* practice this is a re-used chassis, a lab rig, or a device the
|
|
761
|
+
* operator wants grouped with the cameras on the map.)
|
|
762
|
+
*/
|
|
763
|
+
expect(resolveDeviceRole("camera", CATALYST_SWITCH_SIGNALS)).toBe("camera");
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
test("an override beats every tier of evidence, one role at a time", () => {
|
|
767
|
+
/*
|
|
768
|
+
* Exhaustive rather than illustrative: whichever role an operator
|
|
769
|
+
* picks, that role is what gets drawn, even on the least ambiguous
|
|
770
|
+
* device in these tests.
|
|
771
|
+
*/
|
|
772
|
+
for (const role of DEVICE_ROLES_IN_LEGEND_ORDER) {
|
|
773
|
+
expect(resolveDeviceRole(role, CATALYST_SWITCH_SIGNALS)).toBe(role);
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
test("an override beats a matching vendor arc and a matching hostname too", () => {
|
|
778
|
+
// Fortinet's arc plus a "fw" hostname would both say firewall.
|
|
779
|
+
expect(
|
|
780
|
+
resolveDeviceRole("loadBalancer", {
|
|
781
|
+
sysObjectId: "1.3.6.1.4.1.12356.101.1.1",
|
|
782
|
+
sysDescr: "FortiGate-60F v7.2.5",
|
|
783
|
+
name: "edge-fw01",
|
|
784
|
+
}),
|
|
785
|
+
).toBe("loadBalancer");
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
test("the override is honoured in whatever casing it was stored in", () => {
|
|
789
|
+
expect(resolveDeviceRole("CAMERA", CATALYST_SWITCH_SIGNALS)).toBe("camera");
|
|
790
|
+
expect(
|
|
791
|
+
resolveDeviceRole(" wirelessaccesspoint ", CATALYST_SWITCH_SIGNALS),
|
|
792
|
+
).toBe("wirelessAccessPoint");
|
|
793
|
+
});
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
describe("resolveDeviceRole — the classifier runs when there is no override", () => {
|
|
797
|
+
test("absent, null, empty and blank overrides all hand back to the classifier", () => {
|
|
798
|
+
expect(resolveDeviceRole(undefined, CATALYST_SWITCH_SIGNALS)).toBe(
|
|
799
|
+
"switch",
|
|
800
|
+
);
|
|
801
|
+
expect(resolveDeviceRole(null, CATALYST_SWITCH_SIGNALS)).toBe("switch");
|
|
802
|
+
expect(resolveDeviceRole("", CATALYST_SWITCH_SIGNALS)).toBe("switch");
|
|
803
|
+
expect(resolveDeviceRole(" ", CATALYST_SWITCH_SIGNALS)).toBe("switch");
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
test("an unrecognised override is ignored, not treated as a shrug", () => {
|
|
807
|
+
/*
|
|
808
|
+
* Garbage in the column must not disable the classifier — a typo or a
|
|
809
|
+
* value written by an older client should degrade to the behaviour the
|
|
810
|
+
* device had before the column existed.
|
|
811
|
+
*/
|
|
812
|
+
expect(resolveDeviceRole("banana", CATALYST_SWITCH_SIGNALS)).toBe("switch");
|
|
813
|
+
expect(resolveDeviceRole("rout", CATALYST_SWITCH_SIGNALS)).toBe("switch");
|
|
814
|
+
expect(resolveDeviceRole("router-ish", CATALYST_SWITCH_SIGNALS)).toBe(
|
|
815
|
+
"switch",
|
|
816
|
+
);
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
test('a stored "unknown" leaves the classifier in charge — the refusal, seen end to end', () => {
|
|
820
|
+
/*
|
|
821
|
+
* The payoff of parseDeviceRoleOverride refusing "unknown": the device
|
|
822
|
+
* is still classified as a switch rather than being pinned neutral.
|
|
823
|
+
*/
|
|
824
|
+
expect(resolveDeviceRole("unknown", CATALYST_SWITCH_SIGNALS)).toBe(
|
|
825
|
+
"switch",
|
|
826
|
+
);
|
|
827
|
+
/*
|
|
828
|
+
* And on a device with nothing to go on, the classifier's own answer is
|
|
829
|
+
* "unknown" anyway, so the two paths agree where it matters.
|
|
830
|
+
*/
|
|
831
|
+
expect(resolveDeviceRole("unknown", {})).toBe("unknown");
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
test("the classifier's full precedence chain still applies underneath", () => {
|
|
835
|
+
// A spot check that resolveDeviceRole is a wrapper, not a second opinion.
|
|
836
|
+
expect(
|
|
837
|
+
resolveDeviceRole(undefined, {
|
|
838
|
+
sysDescr: "Linux gw-1 5.4.0-91-generic x86_64",
|
|
839
|
+
name: "gw-1",
|
|
840
|
+
}),
|
|
841
|
+
).toBe("router");
|
|
842
|
+
expect(
|
|
843
|
+
resolveDeviceRole("", { sysObjectId: "1.3.6.1.4.1.3375.2.1.3" }),
|
|
844
|
+
).toBe("loadBalancer");
|
|
845
|
+
});
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
describe("resolveDeviceRole — the ping-only device from issue #3192", () => {
|
|
849
|
+
test("a device with no signals and no override is unknown, as it always was", () => {
|
|
850
|
+
/*
|
|
851
|
+
* The starting position the issue describes: discovery imported a box
|
|
852
|
+
* that answers ICMP and nothing else, so there is no sysDescr, no
|
|
853
|
+
* sysObjectId, no model — and the map draws a neutral node.
|
|
854
|
+
*/
|
|
855
|
+
expect(resolveDeviceRole(undefined, {})).toBe("unknown");
|
|
856
|
+
expect(
|
|
857
|
+
resolveDeviceRole(undefined, {
|
|
858
|
+
sysDescr: "",
|
|
859
|
+
sysObjectId: "",
|
|
860
|
+
deviceModel: undefined,
|
|
861
|
+
name: "10.20.30.40",
|
|
862
|
+
}),
|
|
863
|
+
).toBe("unknown");
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
test("a device with no signals but an override is drawn as the operator declared", () => {
|
|
867
|
+
/*
|
|
868
|
+
* The whole point of the feature. Nothing SNMP could ever say about
|
|
869
|
+
* this device will change, so the override is the only way it will ever
|
|
870
|
+
* be anything other than an anonymous circle — and it works with an
|
|
871
|
+
* entirely empty evidence bundle, which is the state a ping-only
|
|
872
|
+
* device is permanently in.
|
|
873
|
+
*/
|
|
874
|
+
expect(resolveDeviceRole("router", {})).toBe("router");
|
|
875
|
+
expect(resolveDeviceRole("switch", {})).toBe("switch");
|
|
876
|
+
expect(resolveDeviceRole("firewall", {})).toBe("firewall");
|
|
877
|
+
expect(resolveDeviceRole("wirelessAccessPoint", {})).toBe(
|
|
878
|
+
"wirelessAccessPoint",
|
|
879
|
+
);
|
|
880
|
+
});
|
|
881
|
+
|
|
882
|
+
test("an override on a ping-only device survives an uninformative hostname", () => {
|
|
883
|
+
/*
|
|
884
|
+
* "device-42" and a bare IP are what these imports are usually called;
|
|
885
|
+
* neither is a hostname convention the classifier can read, and neither
|
|
886
|
+
* gets to argue with the declaration.
|
|
887
|
+
*/
|
|
888
|
+
expect(resolveDeviceRole("router", { name: "device-42" })).toBe("router");
|
|
889
|
+
expect(resolveDeviceRole("printer", { name: "10.20.30.40" })).toBe(
|
|
890
|
+
"printer",
|
|
891
|
+
);
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
test("an override still wins once the device later starts answering SNMP", () => {
|
|
895
|
+
/*
|
|
896
|
+
* A ping-only device that is given credentials later suddenly has
|
|
897
|
+
* evidence. The declaration must not be quietly reversed by the first
|
|
898
|
+
* successful walk — the operator has to be the one to change it.
|
|
899
|
+
*/
|
|
900
|
+
expect(
|
|
901
|
+
resolveDeviceRole("router", {
|
|
902
|
+
sysDescr: "Cisco IOS Software, Catalyst 2960X switch",
|
|
903
|
+
deviceModel: "WS-C2960X-48FPD-L",
|
|
904
|
+
}),
|
|
905
|
+
).toBe("router");
|
|
906
|
+
});
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
describe("resolveDeviceRole — determinism and totality", () => {
|
|
910
|
+
test("the order the signal fields were assigned in does not change the answer", () => {
|
|
911
|
+
/*
|
|
912
|
+
* The builder assembles these objects field by field from several
|
|
913
|
+
* sources, so the key insertion order differs between call sites. It
|
|
914
|
+
* must not be observable.
|
|
915
|
+
*/
|
|
916
|
+
const oneOrder: DeviceRoleSignals = {
|
|
917
|
+
name: "idf2-sw-3",
|
|
918
|
+
sysDescr: "Cisco IOS Software, Catalyst 2960X switch",
|
|
919
|
+
vendor: "Cisco Systems",
|
|
920
|
+
deviceModel: "WS-C2960X-48FPD-L",
|
|
921
|
+
};
|
|
922
|
+
const anotherOrder: DeviceRoleSignals = {
|
|
923
|
+
deviceModel: "WS-C2960X-48FPD-L",
|
|
924
|
+
vendor: "Cisco Systems",
|
|
925
|
+
sysDescr: "Cisco IOS Software, Catalyst 2960X switch",
|
|
926
|
+
name: "idf2-sw-3",
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
expect(resolveDeviceRole(undefined, oneOrder)).toBe(
|
|
930
|
+
resolveDeviceRole(undefined, anotherOrder),
|
|
931
|
+
);
|
|
932
|
+
expect(resolveDeviceRole("camera", oneOrder)).toBe(
|
|
933
|
+
resolveDeviceRole("camera", anotherOrder),
|
|
934
|
+
);
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
test("resolving does not mutate the signals it was handed", () => {
|
|
938
|
+
/*
|
|
939
|
+
* The same signals object is read again when the node is rendered, so a
|
|
940
|
+
* normalisation written back in place would make the second read differ
|
|
941
|
+
* from the first.
|
|
942
|
+
*/
|
|
943
|
+
const signals: DeviceRoleSignals = { ...CATALYST_SWITCH_SIGNALS };
|
|
944
|
+
resolveDeviceRole("camera", signals);
|
|
945
|
+
resolveDeviceRole(undefined, signals);
|
|
946
|
+
|
|
947
|
+
expect(signals).toEqual(CATALYST_SWITCH_SIGNALS);
|
|
948
|
+
});
|
|
949
|
+
|
|
950
|
+
test("resolving the same device repeatedly, interleaved with others, is stable", () => {
|
|
951
|
+
const overrides: Array<string | undefined | null> = [
|
|
952
|
+
"camera",
|
|
953
|
+
undefined,
|
|
954
|
+
"unknown",
|
|
955
|
+
null,
|
|
956
|
+
"camera",
|
|
957
|
+
"banana",
|
|
958
|
+
];
|
|
959
|
+
const firstPass: Array<NetworkTopologyDeviceRole> = overrides.map(
|
|
960
|
+
(override: string | undefined | null) => {
|
|
961
|
+
return resolveDeviceRole(override, CATALYST_SWITCH_SIGNALS);
|
|
962
|
+
},
|
|
963
|
+
);
|
|
964
|
+
const secondPass: Array<NetworkTopologyDeviceRole> = [...overrides]
|
|
965
|
+
.reverse()
|
|
966
|
+
.map((override: string | undefined | null) => {
|
|
967
|
+
return resolveDeviceRole(override, CATALYST_SWITCH_SIGNALS);
|
|
968
|
+
});
|
|
969
|
+
|
|
970
|
+
expect(firstPass).toEqual([
|
|
971
|
+
"camera",
|
|
972
|
+
"switch",
|
|
973
|
+
"switch",
|
|
974
|
+
"switch",
|
|
975
|
+
"camera",
|
|
976
|
+
"switch",
|
|
977
|
+
]);
|
|
978
|
+
expect(secondPass).toEqual([...firstPass].reverse());
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
test("it is total: every override and signal combination yields a known role", () => {
|
|
982
|
+
/*
|
|
983
|
+
* resolveDeviceRole feeds NetworkTopologyNode.role, which the map uses
|
|
984
|
+
* to pick a shape and a legend entry, so it can never hand back
|
|
985
|
+
* undefined or a string outside the union.
|
|
986
|
+
*/
|
|
987
|
+
const knownRoles: Array<string> = Object.keys(DEVICE_ROLE_LABELS);
|
|
988
|
+
const overrides: Array<string | undefined | null> = [
|
|
989
|
+
undefined,
|
|
990
|
+
null,
|
|
991
|
+
"",
|
|
992
|
+
" ",
|
|
993
|
+
"unknown",
|
|
994
|
+
"banana",
|
|
995
|
+
"switch",
|
|
996
|
+
"loadBalancer",
|
|
997
|
+
"WIRELESSACCESSPOINT",
|
|
998
|
+
];
|
|
999
|
+
const bundles: Array<DeviceRoleSignals> = [
|
|
1000
|
+
{},
|
|
1001
|
+
{ name: "device-42" },
|
|
1002
|
+
CATALYST_SWITCH_SIGNALS,
|
|
1003
|
+
{ sysDescr: "Linux app-3 5.4.0-91-generic x86_64", name: "app-3" },
|
|
1004
|
+
];
|
|
1005
|
+
|
|
1006
|
+
for (const override of overrides) {
|
|
1007
|
+
for (const bundle of bundles) {
|
|
1008
|
+
const role: NetworkTopologyDeviceRole = resolveDeviceRole(
|
|
1009
|
+
override,
|
|
1010
|
+
bundle,
|
|
1011
|
+
);
|
|
1012
|
+
expect(knownRoles).toContain(role);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
});
|
|
1016
|
+
});
|