@oneuptime/common 12.0.34 → 13.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/GlobalConfig.ts +12 -12
- package/Models/DatabaseModels/InventoryItem.ts +44 -1
- package/Models/DatabaseModels/NetworkDevice.ts +126 -0
- package/Server/EnvironmentConfig.ts +47 -16
- package/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Infrastructure/Redis.ts +8 -8
- package/Server/Infrastructure/Status.ts +10 -4
- package/Server/Services/DatabaseService.ts +65 -13
- package/Server/Services/InventoryItemService.ts +31 -1
- package/Server/Services/MonitorTemplateService.ts +140 -19
- package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +349 -41
- package/Server/Services/NetworkDeviceDiscoveryScanService.ts +87 -2
- package/Server/Services/NetworkDeviceService.ts +69 -0
- package/Server/Services/UserNotificationRuleService.ts +76 -25
- package/Server/Types/Database/Permissions/ReadPermission.ts +45 -5
- package/Server/Types/Database/QueryUtil.ts +56 -0
- package/Server/Types/Markdown.ts +93 -1
- package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +82 -17
- package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +10 -1
- package/Server/Utils/EmailRollup/EmailRollupRenderer.ts +2 -2
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +82 -35
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +31 -5
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +202 -37
- package/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.ts +82 -9
- package/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.ts +16 -7
- package/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.ts +29 -17
- package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +107 -48
- package/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.ts +158 -0
- package/Server/Utils/Monitor/NetworkInventoryUtil.ts +27 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +11 -1
- package/Server/Utils/Telemetry/TelemetryEntity.ts +83 -9
- package/Tests/App/Dashboard/AddNeighborToMonitoringModal.test.tsx +32 -0
- package/Tests/App/Dashboard/AdoptedDeviceDrawer.test.tsx +374 -0
- package/Tests/App/Dashboard/DashboardLabelVariables.test.tsx +580 -0
- package/Tests/App/Dashboard/DashboardMonitorLabelVariable.test.tsx +438 -0
- package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +5 -2
- package/Tests/App/Dashboard/DeviceAttachmentCard.test.tsx +273 -0
- package/Tests/App/Dashboard/DiscoveryReviewInventoryRefresh.test.tsx +505 -0
- package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +163 -0
- package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +401 -0
- package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +232 -0
- package/Tests/App/Dashboard/InventoryItems.test.tsx +201 -0
- package/Tests/App/Dashboard/InventoryTypeAndStatusFacets.test.tsx +798 -0
- package/Tests/App/Dashboard/NetworkTopologyLiveView.test.tsx +320 -0
- package/Tests/App/Dashboard/NetworkTopologyToolbar.test.tsx +275 -0
- package/Tests/App/Dashboard/OnCallCalendarFeedEmptyState.test.tsx +975 -0
- package/Tests/App/Dashboard/OnCallCalendarFeedPlanGate.test.tsx +63 -0
- package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +626 -0
- package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +348 -0
- package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +331 -0
- package/Tests/App/Dashboard/UserSettingsEmailPreferences.test.tsx +603 -0
- package/Tests/App/Dashboard/UserSettingsNotificationSettings.test.tsx +324 -0
- package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +13 -4
- package/Tests/App/StatusPage/StatusPageOidcOrigin.test.tsx +223 -0
- package/Tests/Models/InventoryItemStatus.test.ts +147 -0
- package/Tests/Models/NetworkDeviceMacAddressColumn.test.ts +294 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +129 -0
- package/Tests/Server/EnvironmentConfigValkey.test.ts +227 -0
- package/Tests/Server/Infrastructure/Postgres/AddMacAddressToNetworkDeviceMigration.test.ts +365 -0
- package/Tests/Server/Infrastructure/Postgres/CustomFieldValueMappingMigration.test.ts +62 -23
- package/Tests/Server/Infrastructure/ValkeyDeploymentConfig.test.ts +265 -0
- package/Tests/Server/Middleware/HttpMetricsMiddleware.test.ts +382 -0
- package/Tests/Server/Middleware/SCIMAuthorization.test.ts +525 -0
- package/Tests/Server/Services/DatabaseServiceAggregateBy.test.ts +291 -56
- package/Tests/Server/Services/DatabaseServiceSortTiebreaker.test.ts +151 -0
- package/Tests/Server/Services/InventoryItemManualCreate.test.ts +105 -0
- package/Tests/Server/Services/InventoryItemPromotionGate.test.ts +178 -0
- package/Tests/Server/Services/MonitorTemplateServiceCustomFieldSync.test.ts +502 -0
- package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +1133 -81
- package/Tests/Server/Services/NetworkDeviceDiscoveryScanRegistration.test.ts +520 -0
- package/Tests/Server/Services/NetworkDeviceMacAddressNormalization.test.ts +572 -0
- package/Tests/Server/Types/Database/InventoryStatusPostgres.test.ts +368 -0
- package/Tests/Server/Types/Database/Permissions/ReadBlockPermission.test.ts +261 -14
- package/Tests/Server/Types/Database/QueryUtilIncludesAnyOfGroups.test.ts +169 -0
- package/Tests/Server/Types/Database/QueryUtilIncludesAnyOfGroupsPostgres.test.ts +297 -0
- package/Tests/Server/Types/Markdown.test.ts +194 -1
- package/Tests/Server/Types/Workflow/Components/IncomingWebhookUtils.test.ts +279 -0
- package/Tests/Server/Types/Workflow/Components/TextToJson.test.ts +212 -0
- package/Tests/Server/Utils/CustomField/CustomFieldMappingRegistry.test.ts +436 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +214 -0
- package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerBehaviour.test.ts +1 -1
- package/Tests/Server/Utils/EmailRollup/EmailRollupRenderer.test.ts +10 -3
- package/Tests/Server/Utils/EventLoop.test.ts +359 -0
- package/Tests/Server/Utils/JsonWebToken.test.ts +30 -0
- package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +242 -0
- package/Tests/Server/Utils/Monitor/Criteria/CompareCriteriaAggregation.test.ts +13 -7
- package/Tests/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.test.ts +29 -0
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +30 -6
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +246 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEmailEvalLogAgreement.test.ts +280 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +224 -10
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +570 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaExpectationBuilderUnits.test.ts +30 -5
- package/Tests/Server/Utils/Monitor/MonitorCriteriaMessageBuilderUnits.test.ts +68 -21
- package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +36 -19
- package/Tests/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.test.ts +573 -0
- package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +317 -0
- package/Tests/Server/Utils/Telemetry/ContextSpanProcessor.test.ts +281 -0
- package/Tests/Server/Utils/Telemetry/InventoryDuplicateIdentity.test.ts +316 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +111 -8
- package/Tests/Types/Database/IncludesAnyOfGroups.test.ts +106 -0
- package/Tests/Types/JSONFunctions.test.ts +40 -0
- package/Tests/Types/NetworkAutomation/AutoImportRunMerge.test.ts +235 -0
- package/Tests/Types/NetworkAutomation/RuleRunResult.test.ts +66 -0
- package/Tests/Types/NetworkAutomation/RuleRunResultDescribe.test.ts +309 -1
- package/Tests/Types/Telemetry/InventoryLiveness.test.ts +99 -0
- package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +160 -0
- package/Tests/UI/ReactRouterSingletonBuild.test.ts +133 -0
- package/Tests/UI/Rum/ReplayHeader.test.tsx +10 -2
- package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +514 -0
- package/Tests/UI/Rum/ReplayTimeFormat.test.ts +94 -0
- package/Tests/UI/Rum/ReplayUi.test.tsx +559 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +5 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +1 -0
- package/Tests/Utils/Dashboard/Components/DashboardMonitorListComponent.test.ts +11 -1
- package/Tests/Utils/Dashboard/LabelVariable.test.ts +257 -0
- package/Tests/Utils/Dashboard/VariableUrlState.test.ts +144 -14
- package/Tests/Utils/MetricUnitUtil.test.ts +81 -0
- package/Tests/Utils/Monitor/DeviceMacLearningUtil.test.ts +497 -0
- package/Tests/Utils/Monitor/MetricValueFormatter.test.ts +546 -0
- package/Tests/Utils/Monitor/MonitorTemplateCustomFieldUtil.test.ts +218 -0
- package/Tests/Utils/Monitor/NetworkTopologyEndpointAdoption.test.ts +1755 -0
- package/Tests/Utils/NetworkAutomation/AutoImportRunChain.test.ts +320 -0
- package/Types/BaseDatabase/IncludesAnyOfGroups.ts +77 -0
- package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardMonitorListComponent.ts +1 -0
- package/Types/Dashboard/DashboardVariable.ts +8 -0
- package/Types/JSON.ts +4 -0
- package/Types/JSONFunctions.ts +16 -0
- package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +13 -0
- package/Types/NetworkAutomation/RuleRunResult.ts +402 -13
- package/Types/SerializableObjectDictionary.ts +4 -0
- package/Types/Telemetry/InventoryLiveness.ts +56 -0
- package/UI/Components/CSVFileUpload/CSVFileUpload.tsx +9 -2
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +46 -21
- package/UI/Components/Dashboard/DashboardVariableControl.tsx +285 -0
- package/UI/esbuild-config.js +6 -0
- package/Utils/Dashboard/Components/DashboardMonitorListComponent.ts +12 -1
- package/Utils/Dashboard/LabelVariable.ts +152 -0
- package/Utils/Dashboard/VariableUrlState.ts +27 -5
- package/Utils/MetricUnitUtil.ts +34 -0
- package/Utils/Monitor/DeviceMacLearningUtil.ts +177 -0
- package/Utils/Monitor/MetricValueFormatter.ts +342 -0
- package/Utils/Monitor/MonitorTemplateCustomFieldUtil.ts +124 -0
- package/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.ts +7 -2
- package/Utils/Monitor/NetworkTopologyUtil.ts +693 -0
- package/Utils/NetworkAutomation/AutoImportRunChain.ts +138 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +12 -12
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/InventoryItem.js +34 -1
- package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +128 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +44 -12
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.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/Infrastructure/Redis.js +8 -8
- package/build/dist/Server/Infrastructure/Redis.js.map +1 -1
- package/build/dist/Server/Infrastructure/Status.js +10 -4
- package/build/dist/Server/Infrastructure/Status.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +58 -13
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/InventoryItemService.js +28 -1
- package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +116 -17
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +270 -37
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +65 -0
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +54 -0
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +60 -25
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js +29 -4
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryUtil.js +36 -0
- package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +75 -0
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +53 -18
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
- package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +10 -1
- package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -1
- package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js +2 -2
- package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +58 -35
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +29 -5
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +166 -35
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js +58 -9
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.js +9 -3
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.js +26 -12
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +74 -33
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.js +114 -0
- package/build/dist/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +25 -0
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +9 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +70 -13
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
- package/build/dist/Types/BaseDatabase/IncludesAnyOfGroups.js +54 -0
- package/build/dist/Types/BaseDatabase/IncludesAnyOfGroups.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardVariable.js +1 -0
- package/build/dist/Types/Dashboard/DashboardVariable.js.map +1 -1
- package/build/dist/Types/JSON.js +1 -0
- package/build/dist/Types/JSON.js.map +1 -1
- package/build/dist/Types/JSONFunctions.js +13 -0
- package/build/dist/Types/JSONFunctions.js.map +1 -1
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js +291 -6
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -1
- package/build/dist/Types/SerializableObjectDictionary.js +4 -0
- package/build/dist/Types/SerializableObjectDictionary.js.map +1 -1
- package/build/dist/Types/Telemetry/InventoryLiveness.js +37 -0
- package/build/dist/Types/Telemetry/InventoryLiveness.js.map +1 -0
- package/build/dist/UI/Components/CSVFileUpload/CSVFileUpload.js +7 -0
- package/build/dist/UI/Components/CSVFileUpload/CSVFileUpload.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +9 -2
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
- package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js +137 -0
- package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js +9 -1
- package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js.map +1 -1
- package/build/dist/Utils/Dashboard/LabelVariable.js +96 -0
- package/build/dist/Utils/Dashboard/LabelVariable.js.map +1 -0
- package/build/dist/Utils/Dashboard/VariableUrlState.js +19 -4
- package/build/dist/Utils/Dashboard/VariableUrlState.js.map +1 -1
- package/build/dist/Utils/MetricUnitUtil.js +26 -0
- package/build/dist/Utils/MetricUnitUtil.js.map +1 -1
- package/build/dist/Utils/Monitor/DeviceMacLearningUtil.js +112 -0
- package/build/dist/Utils/Monitor/DeviceMacLearningUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MetricValueFormatter.js +276 -0
- package/build/dist/Utils/Monitor/MetricValueFormatter.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorTemplateCustomFieldUtil.js +101 -0
- package/build/dist/Utils/Monitor/MonitorTemplateCustomFieldUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js +7 -2
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +484 -0
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/NetworkAutomation/AutoImportRunChain.js +59 -0
- package/build/dist/Utils/NetworkAutomation/AutoImportRunChain.js.map +1 -0
- package/jest.config.json +2 -0
- package/package.json +1 -1
- package/test-setup.sh +9 -9
- package/tsconfig.json +51 -11
- package/Tests/App/Dashboard/NotificationEmailPreferences.test.tsx +0 -399
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
import MetricValueFormatter from "../../../Utils/Monitor/MetricValueFormatter";
|
|
2
|
+
import ValueFormatter from "../../../Utils/ValueFormatter";
|
|
3
|
+
import { describe, expect, test } from "@jest/globals";
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
* How a metric value is written into an alert or incident.
|
|
7
|
+
*
|
|
8
|
+
* The behaviour has three tiers and every test below belongs to one:
|
|
9
|
+
*
|
|
10
|
+
* 1. The unit names a SCALE LADDER (bytes, seconds and their prefixed
|
|
11
|
+
* members) — the value is rescaled the way the dashboard rescales it,
|
|
12
|
+
* so 1073741824 By reads "1.07 GB".
|
|
13
|
+
* 2. The unit names a dimension but NO ladder (cores, Celsius, ops) —
|
|
14
|
+
* the unit symbol is appended and the digits are left alone,
|
|
15
|
+
* because an
|
|
16
|
+
* alert reader needs to tell 1500 restarts from 1549 and "1.5K" does
|
|
17
|
+
* not let them.
|
|
18
|
+
* 3. The unit names NO dimension ("", "1" on a counter, "{restarts}",
|
|
19
|
+
* the catalogs' "count") — the number stands on its own.
|
|
20
|
+
*/
|
|
21
|
+
describe("MetricValueFormatter", () => {
|
|
22
|
+
describe("format — units with a scale ladder", () => {
|
|
23
|
+
test("scales bytes the way the dashboard does", () => {
|
|
24
|
+
expect(MetricValueFormatter.format({ value: 0, unit: "By" })).toBe("0 B");
|
|
25
|
+
expect(MetricValueFormatter.format({ value: 512, unit: "By" })).toBe(
|
|
26
|
+
"512 B",
|
|
27
|
+
);
|
|
28
|
+
expect(MetricValueFormatter.format({ value: 1000, unit: "By" })).toBe(
|
|
29
|
+
"1 KB",
|
|
30
|
+
);
|
|
31
|
+
expect(
|
|
32
|
+
MetricValueFormatter.format({ value: 1048576, unit: "bytes" }),
|
|
33
|
+
).toBe("1.05 MB");
|
|
34
|
+
expect(
|
|
35
|
+
MetricValueFormatter.format({ value: 1073741824, unit: "By" }),
|
|
36
|
+
).toBe("1.07 GB");
|
|
37
|
+
expect(
|
|
38
|
+
MetricValueFormatter.format({ value: 1099511627776, unit: "bytes" }),
|
|
39
|
+
).toBe("1.1 TB");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("scales time units", () => {
|
|
43
|
+
expect(MetricValueFormatter.format({ value: 750, unit: "ms" })).toBe(
|
|
44
|
+
"750 ms",
|
|
45
|
+
);
|
|
46
|
+
expect(MetricValueFormatter.format({ value: 1500, unit: "ms" })).toBe(
|
|
47
|
+
"1.5 sec",
|
|
48
|
+
);
|
|
49
|
+
expect(
|
|
50
|
+
MetricValueFormatter.format({ value: 3661, unit: "seconds" }),
|
|
51
|
+
).toBe("1.02 hours");
|
|
52
|
+
expect(MetricValueFormatter.format({ value: 1500, unit: "ns" })).toBe(
|
|
53
|
+
"1.5 µs",
|
|
54
|
+
);
|
|
55
|
+
expect(MetricValueFormatter.format({ value: 0, unit: "seconds" })).toBe(
|
|
56
|
+
"0 sec",
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
/*
|
|
61
|
+
* A user who types a threshold in "GB" makes every sample arrive
|
|
62
|
+
* carrying the unit "GB". ValueFormatter has a ladder for "bytes" but
|
|
63
|
+
* not for "GB", so on its own it rendered 2500 of them as "2.5K GB".
|
|
64
|
+
* The value is restated in the family's base unit first.
|
|
65
|
+
*/
|
|
66
|
+
test("rescales a PREFIXED unit through its family base", () => {
|
|
67
|
+
expect(MetricValueFormatter.format({ value: 2500, unit: "GB" })).toBe(
|
|
68
|
+
"2.5 TB",
|
|
69
|
+
);
|
|
70
|
+
expect(MetricValueFormatter.format({ value: 2, unit: "GB" })).toBe(
|
|
71
|
+
"2 GB",
|
|
72
|
+
);
|
|
73
|
+
expect(MetricValueFormatter.format({ value: 0.5, unit: "MB" })).toBe(
|
|
74
|
+
"500 KB",
|
|
75
|
+
);
|
|
76
|
+
expect(MetricValueFormatter.format({ value: 1500, unit: "KB" })).toBe(
|
|
77
|
+
"1.5 MB",
|
|
78
|
+
);
|
|
79
|
+
expect(MetricValueFormatter.format({ value: 36, unit: "hours" })).toBe(
|
|
80
|
+
"1.5 days",
|
|
81
|
+
);
|
|
82
|
+
expect(MetricValueFormatter.format({ value: 5, unit: "min" })).toBe(
|
|
83
|
+
"5 min",
|
|
84
|
+
);
|
|
85
|
+
expect(MetricValueFormatter.format({ value: 90, unit: "min" })).toBe(
|
|
86
|
+
"1.5 hours",
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("scales the numerator of a rate unit and keeps the denominator", () => {
|
|
91
|
+
expect(
|
|
92
|
+
MetricValueFormatter.format({ value: 1500000, unit: "By/s" }),
|
|
93
|
+
).toBe("1.5 MB/s");
|
|
94
|
+
expect(MetricValueFormatter.format({ value: 2.5, unit: "GB/s" })).toBe(
|
|
95
|
+
"2.5 GB/s",
|
|
96
|
+
);
|
|
97
|
+
expect(MetricValueFormatter.format({ value: 2000, unit: "GB/s" })).toBe(
|
|
98
|
+
"2 TB/s",
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("a rate whose numerator has no ladder keeps its digits", () => {
|
|
103
|
+
expect(
|
|
104
|
+
MetricValueFormatter.format({ value: 1500, unit: "requests/s" }),
|
|
105
|
+
).toBe("1500 requests/s");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("negatives keep their sign through the ladder", () => {
|
|
109
|
+
expect(MetricValueFormatter.format({ value: -2048, unit: "bytes" })).toBe(
|
|
110
|
+
"-2.05 KB",
|
|
111
|
+
);
|
|
112
|
+
expect(MetricValueFormatter.format({ value: -1500, unit: "ms" })).toBe(
|
|
113
|
+
"-1.5 sec",
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe("format — percent and fraction metrics", () => {
|
|
119
|
+
test("percent units render inline with two decimals", () => {
|
|
120
|
+
expect(MetricValueFormatter.format({ value: 87.5, unit: "%" })).toBe(
|
|
121
|
+
"87.50%",
|
|
122
|
+
);
|
|
123
|
+
expect(MetricValueFormatter.format({ value: 100, unit: "percent" })).toBe(
|
|
124
|
+
"100.00%",
|
|
125
|
+
);
|
|
126
|
+
expect(MetricValueFormatter.format({ value: 2.04, unit: "pct" })).toBe(
|
|
127
|
+
"2.04%",
|
|
128
|
+
);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
/*
|
|
132
|
+
* The flagship case: a ratio metric whose samples are fractions in
|
|
133
|
+
* [0, 1] carrying OTel's dimensionless "1". The alert used to say
|
|
134
|
+
* "0.06 1"; the dashboard has always said "5.85%".
|
|
135
|
+
*/
|
|
136
|
+
test("a [0,1] fraction on a ratio metric becomes a percentage", () => {
|
|
137
|
+
expect(
|
|
138
|
+
MetricValueFormatter.format({
|
|
139
|
+
value: 0.0585,
|
|
140
|
+
unit: "1",
|
|
141
|
+
metricName: "system.cpu.utilization",
|
|
142
|
+
}),
|
|
143
|
+
).toBe("5.85%");
|
|
144
|
+
expect(
|
|
145
|
+
MetricValueFormatter.format({
|
|
146
|
+
value: 0.2534,
|
|
147
|
+
unit: "1",
|
|
148
|
+
metricName: "db.client.connection.usage_ratio",
|
|
149
|
+
}),
|
|
150
|
+
).toBe("25.34%");
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
/*
|
|
154
|
+
* kubeletstats reports these two as CPU CORES despite the
|
|
155
|
+
* `.utilization` name, and ValueFormatter.isFractionMetric excludes
|
|
156
|
+
* them by exact name. Four other places in the repo depend on that
|
|
157
|
+
* exclusion; this pins that the alert path honours it too, so 0.18
|
|
158
|
+
* cores never reads as "18.31%".
|
|
159
|
+
*/
|
|
160
|
+
test("the kubeletstats cores gauges are NOT treated as fractions", () => {
|
|
161
|
+
expect(
|
|
162
|
+
MetricValueFormatter.format({
|
|
163
|
+
value: 0.1831,
|
|
164
|
+
unit: "1",
|
|
165
|
+
metricName: "k8s.pod.cpu.utilization",
|
|
166
|
+
}),
|
|
167
|
+
).toBe("0.18");
|
|
168
|
+
expect(
|
|
169
|
+
MetricValueFormatter.format({
|
|
170
|
+
value: 0.1831,
|
|
171
|
+
unit: "1",
|
|
172
|
+
metricName: "k8s.node.cpu.utilization",
|
|
173
|
+
}),
|
|
174
|
+
).toBe("0.18");
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("a fraction unit with no metric name stays a bare number", () => {
|
|
178
|
+
expect(MetricValueFormatter.format({ value: 0.25, unit: "1" })).toBe(
|
|
179
|
+
"0.25",
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
/*
|
|
184
|
+
* The catalogs spell the dimensionless fraction "ratio". It means what
|
|
185
|
+
* "1" means, so it needs the same metric-name signal before it can be
|
|
186
|
+
* shown as a percentage.
|
|
187
|
+
*/
|
|
188
|
+
test("the catalogs' 'ratio' behaves exactly like '1'", () => {
|
|
189
|
+
expect(
|
|
190
|
+
MetricValueFormatter.format({
|
|
191
|
+
value: 0.97,
|
|
192
|
+
unit: "ratio",
|
|
193
|
+
metricName: "pve_cpu_usage_ratio",
|
|
194
|
+
}),
|
|
195
|
+
).toBe("97.00%");
|
|
196
|
+
expect(MetricValueFormatter.format({ value: 0.97, unit: "ratio" })).toBe(
|
|
197
|
+
"0.97",
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
/*
|
|
202
|
+
* Values reaching this formatter have ALREADY been converted into the
|
|
203
|
+
* threshold unit by MetricMonitorCriteria, so a ratio metric whose
|
|
204
|
+
* threshold is in "%" arrives pre-multiplied. Re-applying the fraction
|
|
205
|
+
* heuristic there would report 6% as 600%.
|
|
206
|
+
*/
|
|
207
|
+
test("does not double-scale a value already converted into percent", () => {
|
|
208
|
+
expect(
|
|
209
|
+
MetricValueFormatter.format({
|
|
210
|
+
value: 6,
|
|
211
|
+
unit: "%",
|
|
212
|
+
metricName: "system.cpu.utilization",
|
|
213
|
+
}),
|
|
214
|
+
).toBe("6.00%");
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe("format — units with a dimension but no ladder", () => {
|
|
219
|
+
test("appends the unit symbol and leaves every digit alone", () => {
|
|
220
|
+
expect(MetricValueFormatter.format({ value: 0.42, unit: "cores" })).toBe(
|
|
221
|
+
"0.42 cores",
|
|
222
|
+
);
|
|
223
|
+
expect(
|
|
224
|
+
MetricValueFormatter.format({ value: 1234567, unit: "cores" }),
|
|
225
|
+
).toBe("1234567 cores");
|
|
226
|
+
expect(MetricValueFormatter.format({ value: 95, unit: "Cel" })).toBe(
|
|
227
|
+
"95 °C",
|
|
228
|
+
);
|
|
229
|
+
expect(MetricValueFormatter.format({ value: 1500, unit: "kbit" })).toBe(
|
|
230
|
+
"1500 kbit",
|
|
231
|
+
);
|
|
232
|
+
expect(MetricValueFormatter.format({ value: 1024, unit: "KiBy" })).toBe(
|
|
233
|
+
"1024 KiB",
|
|
234
|
+
);
|
|
235
|
+
expect(MetricValueFormatter.format({ value: 4200, unit: "ops" })).toBe(
|
|
236
|
+
"4200 ops",
|
|
237
|
+
);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
/*
|
|
241
|
+
* This is the whole reason the formatter is not a bare call to
|
|
242
|
+
* ValueFormatter: on a chart axis "1.23M cores" is right, in an alert
|
|
243
|
+
* the exact count is the thing the reader came for.
|
|
244
|
+
*/
|
|
245
|
+
test("does NOT abbreviate large numbers the way a chart axis would", () => {
|
|
246
|
+
expect(ValueFormatter.formatValue(1234567, "cores")).toBe("1.23M cores");
|
|
247
|
+
expect(
|
|
248
|
+
MetricValueFormatter.format({ value: 1234567, unit: "cores" }),
|
|
249
|
+
).toBe("1234567 cores");
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test("rounds a non-integer to two decimals", () => {
|
|
253
|
+
expect(MetricValueFormatter.format({ value: 3.14159, unit: "ops" })).toBe(
|
|
254
|
+
"3.14 ops",
|
|
255
|
+
);
|
|
256
|
+
expect(MetricValueFormatter.format({ value: 2.5, unit: "ops" })).toBe(
|
|
257
|
+
"2.5 ops",
|
|
258
|
+
);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
describe("format — units that name no dimension", () => {
|
|
263
|
+
test("no unit at all leaves the number exactly as it was", () => {
|
|
264
|
+
expect(MetricValueFormatter.format({ value: 97 })).toBe("97");
|
|
265
|
+
expect(MetricValueFormatter.format({ value: 5000 })).toBe("5000");
|
|
266
|
+
expect(MetricValueFormatter.format({ value: 250, unit: "" })).toBe("250");
|
|
267
|
+
expect(MetricValueFormatter.format({ value: 12, unit: null })).toBe("12");
|
|
268
|
+
expect(MetricValueFormatter.format({ value: 12, unit: " " })).toBe(
|
|
269
|
+
"12",
|
|
270
|
+
);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test("rounds a bare non-integer to two decimals", () => {
|
|
274
|
+
// A count metric arrives as a float from an aggregation.
|
|
275
|
+
expect(MetricValueFormatter.format({ value: 2.3333333333333335 })).toBe(
|
|
276
|
+
"2.33",
|
|
277
|
+
);
|
|
278
|
+
expect(MetricValueFormatter.format({ value: 0.9712 })).toBe("0.97");
|
|
279
|
+
// Trailing zeros are dropped: 1.50 reads "1.5".
|
|
280
|
+
expect(MetricValueFormatter.format({ value: 1.5 })).toBe("1.5");
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("UCUM annotation-only units are dropped", () => {
|
|
284
|
+
expect(
|
|
285
|
+
MetricValueFormatter.format({ value: 1500, unit: "{restarts}" }),
|
|
286
|
+
).toBe("1500");
|
|
287
|
+
expect(MetricValueFormatter.format({ value: 42, unit: "{cpu}" })).toBe(
|
|
288
|
+
"42",
|
|
289
|
+
);
|
|
290
|
+
expect(MetricValueFormatter.format({ value: 7, unit: "{packets}" })).toBe(
|
|
291
|
+
"7",
|
|
292
|
+
);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test("the catalogs' 'count' is dropped like the dimensionless '1'", () => {
|
|
296
|
+
expect(MetricValueFormatter.format({ value: 250, unit: "count" })).toBe(
|
|
297
|
+
"250",
|
|
298
|
+
);
|
|
299
|
+
expect(MetricValueFormatter.format({ value: 12, unit: "COUNT" })).toBe(
|
|
300
|
+
"12",
|
|
301
|
+
);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test("the dimensionless '1' on a counter is dropped", () => {
|
|
305
|
+
expect(
|
|
306
|
+
MetricValueFormatter.format({
|
|
307
|
+
value: 0.31,
|
|
308
|
+
unit: "1",
|
|
309
|
+
metricName: "browser.cumulative_layout_shift",
|
|
310
|
+
}),
|
|
311
|
+
).toBe("0.31");
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
/*
|
|
316
|
+
* Three Proxmox catalog entries declare `unit: "seconds"` for a value
|
|
317
|
+
* that is a Unix epoch, not a duration. Run through the time ladder,
|
|
318
|
+
* an epoch of 1750000000 renders "20.25K days" — which does not just
|
|
319
|
+
* fail to help, it actively misleads: a replication job that synced a
|
|
320
|
+
* minute ago reads as 55 years stale.
|
|
321
|
+
*/
|
|
322
|
+
describe("format — metrics whose 'seconds' is a point in time", () => {
|
|
323
|
+
test("epoch timestamps keep their digits instead of becoming durations", () => {
|
|
324
|
+
for (const metricName of [
|
|
325
|
+
"pve_replication_last_sync_timestamp_seconds",
|
|
326
|
+
"pve_replication_last_try_timestamp_seconds",
|
|
327
|
+
"pve_replication_next_sync_timestamp_seconds",
|
|
328
|
+
]) {
|
|
329
|
+
expect(
|
|
330
|
+
MetricValueFormatter.format({
|
|
331
|
+
value: 1750000000,
|
|
332
|
+
unit: "seconds",
|
|
333
|
+
metricName: metricName,
|
|
334
|
+
}),
|
|
335
|
+
).toBe("1750000000 s");
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
test("the ladder is what would otherwise mislead", () => {
|
|
340
|
+
expect(ValueFormatter.formatValue(1750000000, "seconds")).toBe(
|
|
341
|
+
"20.25K days",
|
|
342
|
+
);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
test("recognises the shapes a timestamp metric name takes", () => {
|
|
346
|
+
expect(
|
|
347
|
+
MetricValueFormatter.isAbsoluteTimestampMetric(
|
|
348
|
+
"pve_replication_last_sync_timestamp_seconds",
|
|
349
|
+
),
|
|
350
|
+
).toBe(true);
|
|
351
|
+
expect(
|
|
352
|
+
MetricValueFormatter.isAbsoluteTimestampMetric(
|
|
353
|
+
"process.start.timestamp",
|
|
354
|
+
),
|
|
355
|
+
).toBe(true);
|
|
356
|
+
expect(
|
|
357
|
+
MetricValueFormatter.isAbsoluteTimestampMetric("build_timestamps"),
|
|
358
|
+
).toBe(true);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
/*
|
|
362
|
+
* Narrow on purpose. A genuine duration whose name merely mentions
|
|
363
|
+
* time must still scale — this is the control that stops the carve-out
|
|
364
|
+
* from swallowing the common case.
|
|
365
|
+
*/
|
|
366
|
+
test("does not swallow genuine durations", () => {
|
|
367
|
+
expect(
|
|
368
|
+
MetricValueFormatter.isAbsoluteTimestampMetric(
|
|
369
|
+
"http.server.request.duration",
|
|
370
|
+
),
|
|
371
|
+
).toBe(false);
|
|
372
|
+
expect(
|
|
373
|
+
MetricValueFormatter.isAbsoluteTimestampMetric(
|
|
374
|
+
"timestamp_delta_seconds",
|
|
375
|
+
),
|
|
376
|
+
).toBe(false);
|
|
377
|
+
expect(MetricValueFormatter.isAbsoluteTimestampMetric(undefined)).toBe(
|
|
378
|
+
false,
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
expect(
|
|
382
|
+
MetricValueFormatter.format({
|
|
383
|
+
value: 3661,
|
|
384
|
+
unit: "seconds",
|
|
385
|
+
metricName: "http.server.request.duration",
|
|
386
|
+
}),
|
|
387
|
+
).toBe("1.02 hours");
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
test("only suppresses the ladder, not the unit label", () => {
|
|
391
|
+
expect(
|
|
392
|
+
MetricValueFormatter.getReadableUnit(
|
|
393
|
+
"seconds",
|
|
394
|
+
"pve_replication_last_sync_timestamp_seconds",
|
|
395
|
+
),
|
|
396
|
+
).toBe("Seconds");
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
describe("format — non-finite values", () => {
|
|
401
|
+
/*
|
|
402
|
+
* ValueFormatter has no non-finite guard outside its percent branch:
|
|
403
|
+
* formatLargeNumber divides Infinity by 1e15, gets Infinity, and
|
|
404
|
+
* appends the "P" suffix. Anything routed through it would report a
|
|
405
|
+
* broken metric as "InfinityP PB" in an on-call email.
|
|
406
|
+
*/
|
|
407
|
+
test("are reported verbatim, never through the scaling ladder", () => {
|
|
408
|
+
expect(ValueFormatter.formatValue(Infinity, "By")).toBe("InfinityP PB");
|
|
409
|
+
|
|
410
|
+
expect(MetricValueFormatter.format({ value: NaN, unit: "By" })).toBe(
|
|
411
|
+
"NaN",
|
|
412
|
+
);
|
|
413
|
+
expect(MetricValueFormatter.format({ value: Infinity, unit: "By" })).toBe(
|
|
414
|
+
"Infinity",
|
|
415
|
+
);
|
|
416
|
+
expect(
|
|
417
|
+
MetricValueFormatter.format({ value: -Infinity, unit: "seconds" }),
|
|
418
|
+
).toBe("-Infinity");
|
|
419
|
+
expect(MetricValueFormatter.format({ value: NaN })).toBe("NaN");
|
|
420
|
+
expect(MetricValueFormatter.format({ value: NaN, unit: "%" })).toBe(
|
|
421
|
+
"NaN",
|
|
422
|
+
);
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
describe("hasDisplayableUnit", () => {
|
|
427
|
+
test("true for units that name a dimension", () => {
|
|
428
|
+
for (const unit of [
|
|
429
|
+
"By",
|
|
430
|
+
"bytes",
|
|
431
|
+
"GB",
|
|
432
|
+
"ms",
|
|
433
|
+
"seconds",
|
|
434
|
+
"%",
|
|
435
|
+
"percent",
|
|
436
|
+
"cores",
|
|
437
|
+
"Cel",
|
|
438
|
+
"ops",
|
|
439
|
+
"By/s",
|
|
440
|
+
]) {
|
|
441
|
+
expect(MetricValueFormatter.hasDisplayableUnit(unit)).toBe(true);
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
test("false for units that do not", () => {
|
|
446
|
+
for (const unit of [
|
|
447
|
+
undefined,
|
|
448
|
+
null,
|
|
449
|
+
"",
|
|
450
|
+
" ",
|
|
451
|
+
"1",
|
|
452
|
+
"count",
|
|
453
|
+
"counts",
|
|
454
|
+
"{restarts}",
|
|
455
|
+
"{}",
|
|
456
|
+
]) {
|
|
457
|
+
expect(MetricValueFormatter.hasDisplayableUnit(unit)).toBe(false);
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
test("'1' and 'ratio' become displayable on a fraction metric", () => {
|
|
462
|
+
expect(
|
|
463
|
+
MetricValueFormatter.hasDisplayableUnit("1", "system.cpu.utilization"),
|
|
464
|
+
).toBe(true);
|
|
465
|
+
expect(
|
|
466
|
+
MetricValueFormatter.hasDisplayableUnit("ratio", "pve_cpu_usage_ratio"),
|
|
467
|
+
).toBe(true);
|
|
468
|
+
expect(
|
|
469
|
+
MetricValueFormatter.hasDisplayableUnit(
|
|
470
|
+
"1",
|
|
471
|
+
"k8s.node.cpu.utilization",
|
|
472
|
+
),
|
|
473
|
+
).toBe(false);
|
|
474
|
+
});
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
describe("getReadableUnit", () => {
|
|
478
|
+
test("spells UCUM codes out", () => {
|
|
479
|
+
expect(MetricValueFormatter.getReadableUnit("By")).toBe("Bytes");
|
|
480
|
+
expect(MetricValueFormatter.getReadableUnit("ms")).toBe("Milliseconds");
|
|
481
|
+
expect(MetricValueFormatter.getReadableUnit("s")).toBe("Seconds");
|
|
482
|
+
expect(MetricValueFormatter.getReadableUnit("Cel")).toBe("Celsius");
|
|
483
|
+
expect(MetricValueFormatter.getReadableUnit("%")).toBe("Percent");
|
|
484
|
+
expect(MetricValueFormatter.getReadableUnit("By/s")).toBe(
|
|
485
|
+
"Bytes per Second",
|
|
486
|
+
);
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
test("echoes a unit it does not recognise", () => {
|
|
490
|
+
expect(MetricValueFormatter.getReadableUnit("cores")).toBe("cores");
|
|
491
|
+
expect(MetricValueFormatter.getReadableUnit("widgets")).toBe("widgets");
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
/*
|
|
495
|
+
* Returning null rather than "" is what lets the caller drop the whole
|
|
496
|
+
* "- Unit:" line instead of emitting the noise "- Unit: 1".
|
|
497
|
+
*/
|
|
498
|
+
test("returns null when there is no dimension to name", () => {
|
|
499
|
+
expect(MetricValueFormatter.getReadableUnit(undefined)).toBeNull();
|
|
500
|
+
expect(MetricValueFormatter.getReadableUnit(null)).toBeNull();
|
|
501
|
+
expect(MetricValueFormatter.getReadableUnit("")).toBeNull();
|
|
502
|
+
expect(MetricValueFormatter.getReadableUnit("1")).toBeNull();
|
|
503
|
+
expect(MetricValueFormatter.getReadableUnit("count")).toBeNull();
|
|
504
|
+
expect(MetricValueFormatter.getReadableUnit("{restarts}")).toBeNull();
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
test("names the percent a fraction metric really carries", () => {
|
|
508
|
+
expect(
|
|
509
|
+
MetricValueFormatter.getReadableUnit("1", "system.cpu.utilization"),
|
|
510
|
+
).toBe("Percent");
|
|
511
|
+
expect(
|
|
512
|
+
MetricValueFormatter.getReadableUnit("ratio", "pve_cpu_usage_ratio"),
|
|
513
|
+
).toBe("Percent");
|
|
514
|
+
expect(
|
|
515
|
+
MetricValueFormatter.getReadableUnit("1", "k8s.node.cpu.utilization"),
|
|
516
|
+
).toBeNull();
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
describe("agreement with the dashboard", () => {
|
|
521
|
+
/*
|
|
522
|
+
* The point of reusing ValueFormatter is that an email and the metric
|
|
523
|
+
* explorer say the same thing about the same sample. Where a ladder
|
|
524
|
+
* applies, the two must be byte-identical.
|
|
525
|
+
*/
|
|
526
|
+
test("matches ValueFormatter exactly for every ladder unit", () => {
|
|
527
|
+
const cases: Array<[number, string]> = [
|
|
528
|
+
[1073741824, "By"],
|
|
529
|
+
[1048576, "bytes"],
|
|
530
|
+
[512, "By"],
|
|
531
|
+
[1500, "ms"],
|
|
532
|
+
[3661, "seconds"],
|
|
533
|
+
[1500, "ns"],
|
|
534
|
+
[1500000, "By/s"],
|
|
535
|
+
[87.5, "%"],
|
|
536
|
+
[0, "bytes"],
|
|
537
|
+
];
|
|
538
|
+
|
|
539
|
+
for (const [value, unit] of cases) {
|
|
540
|
+
expect(MetricValueFormatter.format({ value: value, unit: unit })).toBe(
|
|
541
|
+
ValueFormatter.formatValue(value, unit),
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
});
|