@oneuptime/common 12.0.0 → 12.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/Models/DatabaseModels/Host.ts +12 -4
  2. package/Models/DatabaseModels/KubernetesContainer.ts +5 -4
  3. package/Models/DatabaseModels/KubernetesResource.ts +8 -7
  4. package/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.ts +12 -2
  5. package/Models/DatabaseModels/PodmanResource.ts +6 -6
  6. package/Models/DatabaseModels/ServiceLevelObjective.ts +91 -0
  7. package/Models/DatabaseModels/StatusPage.ts +95 -0
  8. package/Server/API/TeamMemberAPI.ts +85 -0
  9. package/Server/Infrastructure/Postgres/LocalMigrationGenerationDataSource.ts +12 -2
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.ts +84 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.ts +40 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.ts +131 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.ts +25 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
  15. package/Server/Middleware/ProjectAuthorization.ts +66 -4
  16. package/Server/Services/BillingService.ts +600 -51
  17. package/Server/Services/CephClusterService.ts +47 -5
  18. package/Server/Services/CephResourceService.ts +43 -4
  19. package/Server/Services/CloudResourceService.ts +47 -5
  20. package/Server/Services/DatabaseService.ts +87 -6
  21. package/Server/Services/DockerHostService.ts +47 -5
  22. package/Server/Services/DockerResourceService.ts +62 -8
  23. package/Server/Services/DockerSwarmClusterService.ts +48 -5
  24. package/Server/Services/DockerSwarmResourceService.ts +53 -4
  25. package/Server/Services/HostService.ts +48 -5
  26. package/Server/Services/IoTDeviceService.ts +1 -17
  27. package/Server/Services/IoTFleetService.ts +47 -5
  28. package/Server/Services/KubernetesClusterService.ts +48 -5
  29. package/Server/Services/KubernetesContainerService.ts +50 -9
  30. package/Server/Services/KubernetesResourceService.ts +53 -7
  31. package/Server/Services/LabelService.ts +106 -1
  32. package/Server/Services/LlmProviderService.ts +69 -9
  33. package/Server/Services/MonitorService.ts +57 -0
  34. package/Server/Services/PodmanHostService.ts +47 -5
  35. package/Server/Services/PodmanResourceService.ts +62 -8
  36. package/Server/Services/ProjectService.ts +165 -35
  37. package/Server/Services/ProxmoxClusterService.ts +47 -5
  38. package/Server/Services/ProxmoxResourceService.ts +43 -4
  39. package/Server/Services/RumApplicationService.ts +48 -5
  40. package/Server/Services/RunnerService.ts +1 -7
  41. package/Server/Services/ServerlessFunctionService.ts +48 -5
  42. package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +426 -0
  43. package/Server/Services/ServiceLevelObjectiveService.ts +39 -0
  44. package/Server/Services/StatusPageService.ts +95 -36
  45. package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +18 -1
  46. package/Server/Services/StatusPageSubscriberService.ts +3 -0
  47. package/Server/Types/Database/JSONColumnQuery.ts +621 -0
  48. package/Server/Types/Database/Permissions/PublicPermission.ts +9 -2
  49. package/Server/Types/Database/QueryHelper.ts +17 -32
  50. package/Server/Utils/Database/TruncateColumnValue.ts +84 -0
  51. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +12 -0
  52. package/Tests/App/Dashboard/RunnerInstallInstructions.test.tsx +119 -0
  53. package/Tests/App/Dashboard/RunnerStatus.test.tsx +274 -0
  54. package/Tests/App/Dashboard/UsersTableGroupsByPerson.test.tsx +448 -0
  55. package/Tests/Server/API/BaseAPIApiKeyAuth.test.ts +704 -0
  56. package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +303 -0
  57. package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +350 -0
  58. package/Tests/Server/Middleware/ProjectAuthorizationApiKeyHeader.test.ts +362 -0
  59. package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +687 -0
  60. package/Tests/Server/Services/BillingService.test.ts +78 -3
  61. package/Tests/Server/Services/BillingServiceChangePlanCancel.test.ts +645 -0
  62. package/Tests/Server/Services/BillingServiceTrialPlanChange.test.ts +1005 -0
  63. package/Tests/Server/Services/HookFreeWriteLengthClamp.test.ts +503 -0
  64. package/Tests/Server/Services/HostIpAddressesColumnWidth.test.ts +198 -0
  65. package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +510 -0
  66. package/Tests/Server/Services/InventoryResourceNameColumnWidth.test.ts +500 -0
  67. package/Tests/Server/Services/InventoryUpsertSchemaParity.test.ts +586 -0
  68. package/Tests/Server/Services/InventoryUpsertTruncationGuard.test.ts +1242 -0
  69. package/Tests/Server/Services/LlmProviderUsableByProject.test.ts +293 -0
  70. package/Tests/Server/Services/ProjectServiceChangePlan.test.ts +650 -0
  71. package/Tests/Server/Services/ProjectServiceExtendTrial.test.ts +1 -0
  72. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +387 -0
  73. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +770 -0
  74. package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +130 -0
  75. package/Tests/Server/Services/SloMonitorLabelRuleHooks.test.ts +352 -0
  76. package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +29 -13
  77. package/Tests/Server/TestingUtils/Services/BillingServiceHelper.ts +69 -8
  78. package/Tests/Server/Types/Database/JSONColumnQuery.test.ts +593 -0
  79. package/Tests/Server/Types/Database/Permissions/PublicPermission.test.ts +495 -0
  80. package/Tests/Server/Types/Database/QueryUtil.test.ts +113 -0
  81. package/Tests/Server/Utils/AI/ToolArgsExtractors.test.ts +141 -0
  82. package/Tests/Server/Utils/AI/Toolbox/WidgetBuilder.test.ts +205 -0
  83. package/Tests/Server/Utils/Database/TruncateColumnValue.test.ts +179 -0
  84. package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +897 -0
  85. package/Tests/Types/Events/Recurring.test.ts +157 -0
  86. package/Tests/Types/Runner/RunnerLiveStatus.test.ts +320 -0
  87. package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +285 -0
  88. package/Tests/UI/Components/Detail/EntityFields.test.tsx +166 -0
  89. package/Tests/UI/Components/ModelTable/ModelTableGroupsProjectUsers.test.tsx +375 -0
  90. package/Tests/UI/Components/ModelTable/useCustomFieldColumns.test.tsx +196 -0
  91. package/Tests/UI/Utils/ProjectUsersModelAPI.test.ts +739 -0
  92. package/Tests/UI/Utils/TeamMembersByUser.test.ts +633 -0
  93. package/Tests/Utils/StatusPage/ReportPeriod.test.ts +218 -0
  94. package/Tests/Utils/Telemetry/HostIpAddresses.test.ts +291 -0
  95. package/Types/CustomField/CustomFieldDefinition.ts +24 -0
  96. package/Types/Date.ts +112 -0
  97. package/Types/Events/Recurring.ts +99 -1
  98. package/Types/Runbook/RunbookStep.ts +15 -0
  99. package/Types/Runner/RunnerLiveStatus.ts +114 -0
  100. package/Types/StatusPage/StatusPageReport.ts +11 -0
  101. package/Types/StatusPage/StatusPageReportPeriodType.ts +21 -0
  102. package/UI/Components/Detail/Detail.tsx +155 -0
  103. package/UI/Components/ModelTable/CustomFieldColumns.tsx +2 -6
  104. package/UI/Components/ModelTable/useCustomFieldColumns.ts +21 -2
  105. package/UI/Utils/ModelAPI/ProjectUsersModelAPI.ts +305 -0
  106. package/UI/Utils/TeamMembersByUser.ts +302 -0
  107. package/Utils/StatusPage/ReportPeriod.ts +236 -0
  108. package/Utils/Telemetry/HostIpAddresses.ts +98 -0
  109. package/build/dist/Models/DatabaseModels/Host.js +12 -4
  110. package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
  111. package/build/dist/Models/DatabaseModels/KubernetesContainer.js +4 -4
  112. package/build/dist/Models/DatabaseModels/KubernetesContainer.js.map +1 -1
  113. package/build/dist/Models/DatabaseModels/KubernetesResource.js +7 -7
  114. package/build/dist/Models/DatabaseModels/KubernetesResource.js.map +1 -1
  115. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.js +16 -4
  116. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.js.map +1 -1
  117. package/build/dist/Models/DatabaseModels/PodmanResource.js +6 -6
  118. package/build/dist/Models/DatabaseModels/PodmanResource.js.map +1 -1
  119. package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js +87 -0
  120. package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js.map +1 -1
  121. package/build/dist/Models/DatabaseModels/StatusPage.js +98 -0
  122. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  123. package/build/dist/Server/API/TeamMemberAPI.js +57 -2
  124. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  125. package/build/dist/Server/Infrastructure/Postgres/LocalMigrationGenerationDataSource.js +11 -1
  126. package/build/dist/Server/Infrastructure/Postgres/LocalMigrationGenerationDataSource.js.map +1 -1
  127. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.js +45 -0
  128. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.js.map +1 -0
  129. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.js +31 -0
  130. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.js.map +1 -0
  131. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.js +90 -0
  132. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.js.map +1 -0
  133. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.js +14 -0
  134. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.js.map +1 -0
  135. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
  136. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  137. package/build/dist/Server/Middleware/ProjectAuthorization.js +59 -4
  138. package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
  139. package/build/dist/Server/Services/BillingService.js +466 -36
  140. package/build/dist/Server/Services/BillingService.js.map +1 -1
  141. package/build/dist/Server/Services/CephClusterService.js +42 -5
  142. package/build/dist/Server/Services/CephClusterService.js.map +1 -1
  143. package/build/dist/Server/Services/CephResourceService.js +28 -3
  144. package/build/dist/Server/Services/CephResourceService.js.map +1 -1
  145. package/build/dist/Server/Services/CloudResourceService.js +42 -5
  146. package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
  147. package/build/dist/Server/Services/DatabaseService.js +69 -2
  148. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  149. package/build/dist/Server/Services/DockerHostService.js +42 -5
  150. package/build/dist/Server/Services/DockerHostService.js.map +1 -1
  151. package/build/dist/Server/Services/DockerResourceService.js +34 -4
  152. package/build/dist/Server/Services/DockerResourceService.js.map +1 -1
  153. package/build/dist/Server/Services/DockerSwarmClusterService.js +43 -5
  154. package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
  155. package/build/dist/Server/Services/DockerSwarmResourceService.js +28 -3
  156. package/build/dist/Server/Services/DockerSwarmResourceService.js.map +1 -1
  157. package/build/dist/Server/Services/HostService.js +43 -5
  158. package/build/dist/Server/Services/HostService.js.map +1 -1
  159. package/build/dist/Server/Services/IoTDeviceService.js +1 -8
  160. package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
  161. package/build/dist/Server/Services/IoTFleetService.js +42 -5
  162. package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
  163. package/build/dist/Server/Services/KubernetesClusterService.js +43 -5
  164. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  165. package/build/dist/Server/Services/KubernetesContainerService.js +29 -3
  166. package/build/dist/Server/Services/KubernetesContainerService.js.map +1 -1
  167. package/build/dist/Server/Services/KubernetesResourceService.js +32 -4
  168. package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -1
  169. package/build/dist/Server/Services/LabelService.js +92 -0
  170. package/build/dist/Server/Services/LabelService.js.map +1 -1
  171. package/build/dist/Server/Services/LlmProviderService.js +61 -8
  172. package/build/dist/Server/Services/LlmProviderService.js.map +1 -1
  173. package/build/dist/Server/Services/MonitorService.js +47 -0
  174. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  175. package/build/dist/Server/Services/PodmanHostService.js +42 -5
  176. package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
  177. package/build/dist/Server/Services/PodmanResourceService.js +34 -4
  178. package/build/dist/Server/Services/PodmanResourceService.js.map +1 -1
  179. package/build/dist/Server/Services/ProjectService.js +135 -21
  180. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  181. package/build/dist/Server/Services/ProxmoxClusterService.js +42 -5
  182. package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
  183. package/build/dist/Server/Services/ProxmoxResourceService.js +28 -3
  184. package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
  185. package/build/dist/Server/Services/RumApplicationService.js +43 -5
  186. package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
  187. package/build/dist/Server/Services/RunnerService.js +1 -6
  188. package/build/dist/Server/Services/RunnerService.js.map +1 -1
  189. package/build/dist/Server/Services/ServerlessFunctionService.js +43 -5
  190. package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
  191. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +347 -0
  192. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -0
  193. package/build/dist/Server/Services/ServiceLevelObjectiveService.js +33 -1
  194. package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
  195. package/build/dist/Server/Services/StatusPageService.js +69 -46
  196. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  197. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +17 -1
  198. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
  199. package/build/dist/Server/Services/StatusPageSubscriberService.js +3 -0
  200. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  201. package/build/dist/Server/Types/Database/JSONColumnQuery.js +402 -0
  202. package/build/dist/Server/Types/Database/JSONColumnQuery.js.map +1 -0
  203. package/build/dist/Server/Types/Database/Permissions/PublicPermission.js +9 -2
  204. package/build/dist/Server/Types/Database/Permissions/PublicPermission.js.map +1 -1
  205. package/build/dist/Server/Types/Database/QueryHelper.js +17 -27
  206. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  207. package/build/dist/Server/Utils/Database/TruncateColumnValue.js +32 -0
  208. package/build/dist/Server/Utils/Database/TruncateColumnValue.js.map +1 -0
  209. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +11 -0
  210. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
  211. package/build/dist/Types/CustomField/CustomFieldDefinition.js +2 -0
  212. package/build/dist/Types/CustomField/CustomFieldDefinition.js.map +1 -0
  213. package/build/dist/Types/Date.js +68 -0
  214. package/build/dist/Types/Date.js.map +1 -1
  215. package/build/dist/Types/Events/Recurring.js +63 -0
  216. package/build/dist/Types/Events/Recurring.js.map +1 -1
  217. package/build/dist/Types/Runbook/RunbookStep.js.map +1 -1
  218. package/build/dist/Types/Runner/RunnerLiveStatus.js +69 -0
  219. package/build/dist/Types/Runner/RunnerLiveStatus.js.map +1 -0
  220. package/build/dist/Types/StatusPage/StatusPageReportPeriodType.js +22 -0
  221. package/build/dist/Types/StatusPage/StatusPageReportPeriodType.js.map +1 -0
  222. package/build/dist/UI/Components/Detail/Detail.js +97 -0
  223. package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
  224. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
  225. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +18 -2
  226. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -1
  227. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js +213 -0
  228. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js.map +1 -0
  229. package/build/dist/UI/Utils/TeamMembersByUser.js +195 -0
  230. package/build/dist/UI/Utils/TeamMembersByUser.js.map +1 -0
  231. package/build/dist/Utils/StatusPage/ReportPeriod.js +131 -0
  232. package/build/dist/Utils/StatusPage/ReportPeriod.js.map +1 -0
  233. package/build/dist/Utils/Telemetry/HostIpAddresses.js +82 -0
  234. package/build/dist/Utils/Telemetry/HostIpAddresses.js.map +1 -0
  235. package/jest.config.json +2 -0
  236. package/package.json +1 -1
  237. package/tsconfig.json +12 -1
@@ -0,0 +1,897 @@
1
+ import CompareCriteria from "../../../../../Server/Utils/Monitor/Criteria/CompareCriteria";
2
+ import {
3
+ CheckOn,
4
+ CriteriaFilter,
5
+ EvaluateOverTimeType,
6
+ FilterType,
7
+ } from "../../../../../Types/Monitor/CriteriaFilter";
8
+
9
+ /*
10
+ * CompareCriteria is the pure comparison engine that decides whether a
11
+ * monitored value breaches a criterion and, if so, builds the human-readable
12
+ * root-cause message. It is exercised indirectly by every monitor-criteria
13
+ * evaluator, but had no direct unit test — so the AnyValue/AllValues array
14
+ * semantics, the number/string/boolean coercion rules, and the message
15
+ * formatting are pinned down here.
16
+ */
17
+
18
+ /**
19
+ * Build a minimal CriteriaFilter for the numeric/string comparison helpers.
20
+ */
21
+ function makeFilter(overrides: Partial<CriteriaFilter>): CriteriaFilter {
22
+ return {
23
+ checkOn: CheckOn.ResponseTime,
24
+ filterType: FilterType.GreaterThan,
25
+ value: undefined,
26
+ ...overrides,
27
+ };
28
+ }
29
+
30
+ describe("CompareCriteria", () => {
31
+ describe("greaterThan", () => {
32
+ test("compares a scalar against the threshold", () => {
33
+ expect(CompareCriteria.greaterThan({ value: 10, threshold: 5 })).toBe(
34
+ true,
35
+ );
36
+ expect(CompareCriteria.greaterThan({ value: 5, threshold: 5 })).toBe(
37
+ false,
38
+ );
39
+ expect(CompareCriteria.greaterThan({ value: 1, threshold: 5 })).toBe(
40
+ false,
41
+ );
42
+ });
43
+
44
+ test("AnyValue passes when at least one array element exceeds the threshold", () => {
45
+ expect(
46
+ CompareCriteria.greaterThan({
47
+ value: [1, 2, 9],
48
+ threshold: 5,
49
+ evaluationType: EvaluateOverTimeType.AnyValue,
50
+ }),
51
+ ).toBe(true);
52
+
53
+ expect(
54
+ CompareCriteria.greaterThan({
55
+ value: [1, 2, 3],
56
+ threshold: 5,
57
+ evaluationType: EvaluateOverTimeType.AnyValue,
58
+ }),
59
+ ).toBe(false);
60
+ });
61
+
62
+ test("AllValues (and an unspecified evaluation type) require every element to exceed the threshold", () => {
63
+ expect(
64
+ CompareCriteria.greaterThan({
65
+ value: [6, 7, 8],
66
+ threshold: 5,
67
+ evaluationType: EvaluateOverTimeType.AllValues,
68
+ }),
69
+ ).toBe(true);
70
+
71
+ expect(
72
+ CompareCriteria.greaterThan({
73
+ value: [6, 7, 4],
74
+ threshold: 5,
75
+ evaluationType: EvaluateOverTimeType.AllValues,
76
+ }),
77
+ ).toBe(false);
78
+
79
+ // No evaluationType provided defaults to the "every" branch.
80
+ expect(
81
+ CompareCriteria.greaterThan({ value: [6, 7, 8], threshold: 5 }),
82
+ ).toBe(true);
83
+ expect(
84
+ CompareCriteria.greaterThan({ value: [6, 7, 4], threshold: 5 }),
85
+ ).toBe(false);
86
+ });
87
+ });
88
+
89
+ describe("lessThan", () => {
90
+ test("scalar comparison", () => {
91
+ expect(CompareCriteria.lessThan({ value: 3, threshold: 5 })).toBe(true);
92
+ expect(CompareCriteria.lessThan({ value: 5, threshold: 5 })).toBe(false);
93
+ });
94
+
95
+ test("AnyValue vs AllValues array semantics", () => {
96
+ expect(
97
+ CompareCriteria.lessThan({
98
+ value: [9, 9, 1],
99
+ threshold: 5,
100
+ evaluationType: EvaluateOverTimeType.AnyValue,
101
+ }),
102
+ ).toBe(true);
103
+ expect(
104
+ CompareCriteria.lessThan({
105
+ value: [9, 9, 1],
106
+ threshold: 5,
107
+ evaluationType: EvaluateOverTimeType.AllValues,
108
+ }),
109
+ ).toBe(false);
110
+ });
111
+ });
112
+
113
+ describe("greaterThanOrEqual / lessThanOrEqual", () => {
114
+ test("boundary values are inclusive", () => {
115
+ expect(
116
+ CompareCriteria.greaterThanOrEqual({ value: 5, threshold: 5 }),
117
+ ).toBe(true);
118
+ expect(CompareCriteria.lessThanOrEqual({ value: 5, threshold: 5 })).toBe(
119
+ true,
120
+ );
121
+ expect(
122
+ CompareCriteria.greaterThanOrEqual({ value: 4, threshold: 5 }),
123
+ ).toBe(false);
124
+ expect(CompareCriteria.lessThanOrEqual({ value: 6, threshold: 5 })).toBe(
125
+ false,
126
+ );
127
+ });
128
+
129
+ test("AnyValue arrays", () => {
130
+ expect(
131
+ CompareCriteria.greaterThanOrEqual({
132
+ value: [1, 5],
133
+ threshold: 5,
134
+ evaluationType: EvaluateOverTimeType.AnyValue,
135
+ }),
136
+ ).toBe(true);
137
+ expect(
138
+ CompareCriteria.lessThanOrEqual({
139
+ value: [5, 9],
140
+ threshold: 5,
141
+ evaluationType: EvaluateOverTimeType.AnyValue,
142
+ }),
143
+ ).toBe(true);
144
+ });
145
+ });
146
+
147
+ describe("equalTo / notEqualTo", () => {
148
+ test("scalar equality", () => {
149
+ expect(CompareCriteria.equalTo({ value: 5, threshold: 5 })).toBe(true);
150
+ expect(CompareCriteria.equalTo({ value: 4, threshold: 5 })).toBe(false);
151
+ expect(CompareCriteria.notEqualTo({ value: 4, threshold: 5 })).toBe(true);
152
+ expect(CompareCriteria.notEqualTo({ value: 5, threshold: 5 })).toBe(
153
+ false,
154
+ );
155
+ });
156
+
157
+ test("equalTo with AllValues requires every element to match", () => {
158
+ expect(
159
+ CompareCriteria.equalTo({
160
+ value: [5, 5, 5],
161
+ threshold: 5,
162
+ evaluationType: EvaluateOverTimeType.AllValues,
163
+ }),
164
+ ).toBe(true);
165
+ expect(
166
+ CompareCriteria.equalTo({
167
+ value: [5, 5, 6],
168
+ threshold: 5,
169
+ evaluationType: EvaluateOverTimeType.AllValues,
170
+ }),
171
+ ).toBe(false);
172
+ });
173
+
174
+ test("notEqualTo with AnyValue passes when any element differs", () => {
175
+ expect(
176
+ CompareCriteria.notEqualTo({
177
+ value: [5, 5, 6],
178
+ threshold: 5,
179
+ evaluationType: EvaluateOverTimeType.AnyValue,
180
+ }),
181
+ ).toBe(true);
182
+ });
183
+ });
184
+
185
+ describe("isTrue / isFalse", () => {
186
+ test("scalar booleans", () => {
187
+ expect(CompareCriteria.isTrue({ value: true })).toBe(true);
188
+ expect(CompareCriteria.isTrue({ value: false })).toBe(false);
189
+ expect(CompareCriteria.isFalse({ value: false })).toBe(true);
190
+ expect(CompareCriteria.isFalse({ value: true })).toBe(false);
191
+ });
192
+
193
+ test("array semantics for isTrue", () => {
194
+ expect(
195
+ CompareCriteria.isTrue({
196
+ value: [true, false],
197
+ evaluationType: EvaluateOverTimeType.AnyValue,
198
+ }),
199
+ ).toBe(true);
200
+ expect(
201
+ CompareCriteria.isTrue({
202
+ value: [true, false],
203
+ evaluationType: EvaluateOverTimeType.AllValues,
204
+ }),
205
+ ).toBe(false);
206
+ expect(
207
+ CompareCriteria.isTrue({
208
+ value: [true, true],
209
+ evaluationType: EvaluateOverTimeType.AllValues,
210
+ }),
211
+ ).toBe(true);
212
+ });
213
+
214
+ test("array semantics for isFalse", () => {
215
+ expect(
216
+ CompareCriteria.isFalse({
217
+ value: [false, true],
218
+ evaluationType: EvaluateOverTimeType.AnyValue,
219
+ }),
220
+ ).toBe(true);
221
+ expect(
222
+ CompareCriteria.isFalse({
223
+ value: [false, false],
224
+ evaluationType: EvaluateOverTimeType.AllValues,
225
+ }),
226
+ ).toBe(true);
227
+ });
228
+ });
229
+
230
+ describe("convertToNumber", () => {
231
+ test("passes numbers through unchanged", () => {
232
+ expect(CompareCriteria.convertToNumber(42)).toBe(42);
233
+ expect(CompareCriteria.convertToNumber(0)).toBe(0);
234
+ expect(CompareCriteria.convertToNumber(-3.5)).toBe(-3.5);
235
+ });
236
+
237
+ test("parses integer-ish strings (parseInt semantics)", () => {
238
+ expect(CompareCriteria.convertToNumber("42")).toBe(42);
239
+ // parseInt stops at the first non-numeric char.
240
+ expect(CompareCriteria.convertToNumber("42.9")).toBe(42);
241
+ expect(CompareCriteria.convertToNumber("10px")).toBe(10);
242
+ expect(CompareCriteria.convertToNumber(" 7 ")).toBe(7);
243
+ });
244
+
245
+ test("returns null for undefined", () => {
246
+ expect(CompareCriteria.convertToNumber(undefined)).toBeNull();
247
+ });
248
+
249
+ test("returns null (not NaN) for an unparseable string", () => {
250
+ /*
251
+ * Regression guard: callers only check `=== null`; a leaked NaN would
252
+ * make every downstream numeric comparison silently false.
253
+ */
254
+ expect(CompareCriteria.convertToNumber("abc")).toBeNull();
255
+ expect(CompareCriteria.convertToNumber("")).toBeNull();
256
+ });
257
+ });
258
+
259
+ describe("checkEqualToOrNotEqualTo", () => {
260
+ test("EqualTo returns a message only when the values match", () => {
261
+ const filter: CriteriaFilter = makeFilter({
262
+ checkOn: CheckOn.ResponseStatusCode,
263
+ filterType: FilterType.EqualTo,
264
+ });
265
+
266
+ expect(
267
+ CompareCriteria.checkEqualToOrNotEqualTo({
268
+ value: 200,
269
+ threshold: 200,
270
+ criteriaFilter: filter,
271
+ }),
272
+ ).toBe("Response Status Code is equal to 200.");
273
+
274
+ expect(
275
+ CompareCriteria.checkEqualToOrNotEqualTo({
276
+ value: 200,
277
+ threshold: 500,
278
+ criteriaFilter: filter,
279
+ }),
280
+ ).toBeNull();
281
+ });
282
+
283
+ test("uses strict equality, so number and string forms differ", () => {
284
+ const filter: CriteriaFilter = makeFilter({
285
+ filterType: FilterType.EqualTo,
286
+ });
287
+ expect(
288
+ CompareCriteria.checkEqualToOrNotEqualTo({
289
+ value: 200,
290
+ threshold: "200",
291
+ criteriaFilter: filter,
292
+ }),
293
+ ).toBeNull();
294
+ });
295
+
296
+ test("NotEqualTo returns a message only when the values differ", () => {
297
+ const filter: CriteriaFilter = makeFilter({
298
+ checkOn: CheckOn.ResponseStatusCode,
299
+ filterType: FilterType.NotEqualTo,
300
+ });
301
+
302
+ expect(
303
+ CompareCriteria.checkEqualToOrNotEqualTo({
304
+ value: 200,
305
+ threshold: 500,
306
+ criteriaFilter: filter,
307
+ }),
308
+ ).toBe("Response Status Code is not equal to 500.");
309
+
310
+ expect(
311
+ CompareCriteria.checkEqualToOrNotEqualTo({
312
+ value: 200,
313
+ threshold: 200,
314
+ criteriaFilter: filter,
315
+ }),
316
+ ).toBeNull();
317
+ });
318
+
319
+ test("returns null for an unrelated filter type", () => {
320
+ const filter: CriteriaFilter = makeFilter({
321
+ filterType: FilterType.GreaterThan,
322
+ });
323
+ expect(
324
+ CompareCriteria.checkEqualToOrNotEqualTo({
325
+ value: 1,
326
+ threshold: 1,
327
+ criteriaFilter: filter,
328
+ }),
329
+ ).toBeNull();
330
+ });
331
+ });
332
+
333
+ describe("compareEmptyAndNotEmpty", () => {
334
+ test("IsEmpty matches null and undefined only", () => {
335
+ const filter: CriteriaFilter = makeFilter({
336
+ checkOn: CheckOn.ResponseBody,
337
+ filterType: FilterType.IsEmpty,
338
+ });
339
+
340
+ expect(
341
+ CompareCriteria.compareEmptyAndNotEmpty({
342
+ value: null,
343
+ criteriaFilter: filter,
344
+ }),
345
+ ).toBe("Response Body is empty.");
346
+ expect(
347
+ CompareCriteria.compareEmptyAndNotEmpty({
348
+ value: undefined,
349
+ criteriaFilter: filter,
350
+ }),
351
+ ).toBe("Response Body is empty.");
352
+ expect(
353
+ CompareCriteria.compareEmptyAndNotEmpty({
354
+ value: "something",
355
+ criteriaFilter: filter,
356
+ }),
357
+ ).toBeNull();
358
+ });
359
+
360
+ test("IsNotEmpty reports the value and truncates very long values", () => {
361
+ const filter: CriteriaFilter = makeFilter({
362
+ checkOn: CheckOn.ResponseBody,
363
+ filterType: FilterType.IsNotEmpty,
364
+ });
365
+
366
+ expect(
367
+ CompareCriteria.compareEmptyAndNotEmpty({
368
+ value: "hello",
369
+ criteriaFilter: filter,
370
+ }),
371
+ ).toBe("Response Body is not empty. Value: hello");
372
+
373
+ const longValue: string = "a".repeat(600);
374
+ const message: string | null = CompareCriteria.compareEmptyAndNotEmpty({
375
+ value: longValue,
376
+ criteriaFilter: filter,
377
+ });
378
+ expect(message).toBe(
379
+ `Response Body is not empty. Value: ${"a".repeat(500)}...`,
380
+ );
381
+
382
+ expect(
383
+ CompareCriteria.compareEmptyAndNotEmpty({
384
+ value: null,
385
+ criteriaFilter: filter,
386
+ }),
387
+ ).toBeNull();
388
+ });
389
+ });
390
+
391
+ describe("compareCriteriaStrings", () => {
392
+ test("Contains / NotContains", () => {
393
+ const contains: CriteriaFilter = makeFilter({
394
+ checkOn: CheckOn.ResponseBody,
395
+ filterType: FilterType.Contains,
396
+ });
397
+ expect(
398
+ CompareCriteria.compareCriteriaStrings({
399
+ value: "the quick brown fox",
400
+ threshold: "quick",
401
+ criteriaFilter: contains,
402
+ }),
403
+ ).toContain("contains");
404
+ expect(
405
+ CompareCriteria.compareCriteriaStrings({
406
+ value: "the quick brown fox",
407
+ threshold: "slow",
408
+ criteriaFilter: contains,
409
+ }),
410
+ ).toBeNull();
411
+
412
+ const notContains: CriteriaFilter = makeFilter({
413
+ checkOn: CheckOn.ResponseBody,
414
+ filterType: FilterType.NotContains,
415
+ });
416
+ expect(
417
+ CompareCriteria.compareCriteriaStrings({
418
+ value: "the quick brown fox",
419
+ threshold: "slow",
420
+ criteriaFilter: notContains,
421
+ }),
422
+ ).toContain("does not contain");
423
+ expect(
424
+ CompareCriteria.compareCriteriaStrings({
425
+ value: "the quick brown fox",
426
+ threshold: "quick",
427
+ criteriaFilter: notContains,
428
+ }),
429
+ ).toBeNull();
430
+ });
431
+
432
+ test("StartsWith / EndsWith", () => {
433
+ const startsWith: CriteriaFilter = makeFilter({
434
+ checkOn: CheckOn.ResponseBody,
435
+ filterType: FilterType.StartsWith,
436
+ });
437
+ expect(
438
+ CompareCriteria.compareCriteriaStrings({
439
+ value: "error: boom",
440
+ threshold: "error",
441
+ criteriaFilter: startsWith,
442
+ }),
443
+ ).toContain("starts with");
444
+ expect(
445
+ CompareCriteria.compareCriteriaStrings({
446
+ value: "error: boom",
447
+ threshold: "boom",
448
+ criteriaFilter: startsWith,
449
+ }),
450
+ ).toBeNull();
451
+
452
+ const endsWith: CriteriaFilter = makeFilter({
453
+ checkOn: CheckOn.ResponseBody,
454
+ filterType: FilterType.EndsWith,
455
+ });
456
+ expect(
457
+ CompareCriteria.compareCriteriaStrings({
458
+ value: "error: boom",
459
+ threshold: "boom",
460
+ criteriaFilter: endsWith,
461
+ }),
462
+ ).toContain("ends with");
463
+ });
464
+
465
+ test("coerces non-string values before comparing", () => {
466
+ const contains: CriteriaFilter = makeFilter({
467
+ checkOn: CheckOn.ResponseStatusCode,
468
+ filterType: FilterType.Contains,
469
+ });
470
+ // 404 is coerced to "404" which includes "40".
471
+ expect(
472
+ CompareCriteria.compareCriteriaStrings({
473
+ value: 404 as unknown as string,
474
+ threshold: "40" as unknown as string,
475
+ criteriaFilter: contains,
476
+ }),
477
+ ).toContain("contains");
478
+ });
479
+
480
+ test("returns null when the value or threshold is missing", () => {
481
+ const contains: CriteriaFilter = makeFilter({
482
+ filterType: FilterType.Contains,
483
+ });
484
+ expect(
485
+ CompareCriteria.compareCriteriaStrings({
486
+ value: null as unknown as string,
487
+ threshold: "x",
488
+ criteriaFilter: contains,
489
+ }),
490
+ ).toBeNull();
491
+ expect(
492
+ CompareCriteria.compareCriteriaStrings({
493
+ value: "x",
494
+ threshold: null as unknown as string,
495
+ criteriaFilter: contains,
496
+ }),
497
+ ).toBeNull();
498
+ });
499
+ });
500
+
501
+ describe("compareCriteriaBoolean", () => {
502
+ test("True fires only for a true value", () => {
503
+ const filter: CriteriaFilter = makeFilter({
504
+ checkOn: CheckOn.IsOnline,
505
+ filterType: FilterType.True,
506
+ });
507
+ expect(
508
+ CompareCriteria.compareCriteriaBoolean({
509
+ value: true,
510
+ criteriaFilter: filter,
511
+ }),
512
+ ).toBe("Is Online is true.");
513
+ expect(
514
+ CompareCriteria.compareCriteriaBoolean({
515
+ value: false,
516
+ criteriaFilter: filter,
517
+ }),
518
+ ).toBeNull();
519
+ });
520
+
521
+ test("False fires only for a false value", () => {
522
+ const filter: CriteriaFilter = makeFilter({
523
+ checkOn: CheckOn.IsOnline,
524
+ filterType: FilterType.False,
525
+ });
526
+ expect(
527
+ CompareCriteria.compareCriteriaBoolean({
528
+ value: false,
529
+ criteriaFilter: filter,
530
+ }),
531
+ ).toBe("Is Online is false.");
532
+ expect(
533
+ CompareCriteria.compareCriteriaBoolean({
534
+ value: true,
535
+ criteriaFilter: filter,
536
+ }),
537
+ ).toBeNull();
538
+ });
539
+
540
+ test("null/undefined value never fires", () => {
541
+ const filter: CriteriaFilter = makeFilter({
542
+ checkOn: CheckOn.IsOnline,
543
+ filterType: FilterType.True,
544
+ });
545
+ expect(
546
+ CompareCriteria.compareCriteriaBoolean({
547
+ value: null as unknown as boolean,
548
+ criteriaFilter: filter,
549
+ }),
550
+ ).toBeNull();
551
+ });
552
+
553
+ test("honors AnyValue array evaluation", () => {
554
+ const filter: CriteriaFilter = makeFilter({
555
+ checkOn: CheckOn.IsOnline,
556
+ filterType: FilterType.True,
557
+ evaluateOverTimeOptions: {
558
+ timeValueInMinutes: 5,
559
+ evaluateOverTimeType: EvaluateOverTimeType.AnyValue,
560
+ },
561
+ });
562
+ expect(
563
+ CompareCriteria.compareCriteriaBoolean({
564
+ value: [false, true],
565
+ criteriaFilter: filter,
566
+ }),
567
+ ).not.toBeNull();
568
+ });
569
+ });
570
+
571
+ describe("compareCriteriaNumbers", () => {
572
+ test("GreaterThan fires and returns a descriptive message", () => {
573
+ const filter: CriteriaFilter = makeFilter({
574
+ checkOn: CheckOn.ResponseTime,
575
+ filterType: FilterType.GreaterThan,
576
+ });
577
+ expect(
578
+ CompareCriteria.compareCriteriaNumbers({
579
+ value: 100,
580
+ threshold: 50,
581
+ criteriaFilter: filter,
582
+ }),
583
+ ).toBe("Response Time (in ms) is 100 which is greater than 50.");
584
+ });
585
+
586
+ test("does not fire when the threshold is not breached", () => {
587
+ const filter: CriteriaFilter = makeFilter({
588
+ checkOn: CheckOn.ResponseTime,
589
+ filterType: FilterType.GreaterThan,
590
+ });
591
+ expect(
592
+ CompareCriteria.compareCriteriaNumbers({
593
+ value: 10,
594
+ threshold: 50,
595
+ criteriaFilter: filter,
596
+ }),
597
+ ).toBeNull();
598
+ });
599
+
600
+ test("each numeric filter type is wired to the right comparator", () => {
601
+ const cases: Array<{
602
+ filterType: FilterType;
603
+ value: number;
604
+ threshold: number;
605
+ fires: boolean;
606
+ }> = [
607
+ {
608
+ filterType: FilterType.LessThan,
609
+ value: 1,
610
+ threshold: 5,
611
+ fires: true,
612
+ },
613
+ {
614
+ filterType: FilterType.LessThan,
615
+ value: 9,
616
+ threshold: 5,
617
+ fires: false,
618
+ },
619
+ { filterType: FilterType.EqualTo, value: 5, threshold: 5, fires: true },
620
+ {
621
+ filterType: FilterType.NotEqualTo,
622
+ value: 4,
623
+ threshold: 5,
624
+ fires: true,
625
+ },
626
+ {
627
+ filterType: FilterType.GreaterThanOrEqualTo,
628
+ value: 5,
629
+ threshold: 5,
630
+ fires: true,
631
+ },
632
+ {
633
+ filterType: FilterType.LessThanOrEqualTo,
634
+ value: 5,
635
+ threshold: 5,
636
+ fires: true,
637
+ },
638
+ ];
639
+
640
+ for (const c of cases) {
641
+ const filter: CriteriaFilter = makeFilter({
642
+ checkOn: CheckOn.ResponseTime,
643
+ filterType: c.filterType,
644
+ });
645
+ const result: string | null = CompareCriteria.compareCriteriaNumbers({
646
+ value: c.value,
647
+ threshold: c.threshold,
648
+ criteriaFilter: filter,
649
+ });
650
+ if (c.fires) {
651
+ expect(result).not.toBeNull();
652
+ } else {
653
+ expect(result).toBeNull();
654
+ }
655
+ }
656
+ });
657
+
658
+ test("returns null when value or threshold is missing", () => {
659
+ const filter: CriteriaFilter = makeFilter({
660
+ filterType: FilterType.GreaterThan,
661
+ });
662
+ expect(
663
+ CompareCriteria.compareCriteriaNumbers({
664
+ value: null as unknown as number,
665
+ threshold: 5,
666
+ criteriaFilter: filter,
667
+ }),
668
+ ).toBeNull();
669
+ expect(
670
+ CompareCriteria.compareCriteriaNumbers({
671
+ value: 5,
672
+ threshold: null as unknown as number,
673
+ criteriaFilter: filter,
674
+ }),
675
+ ).toBeNull();
676
+ });
677
+ });
678
+
679
+ describe("getCompareMessage formatting", () => {
680
+ test("prefixes 'Any value of' / 'All values of' for over-time evaluation", () => {
681
+ const anyFilter: CriteriaFilter = makeFilter({
682
+ checkOn: CheckOn.ResponseTime,
683
+ filterType: FilterType.GreaterThan,
684
+ evaluateOverTimeOptions: {
685
+ timeValueInMinutes: undefined,
686
+ evaluateOverTimeType: EvaluateOverTimeType.AnyValue,
687
+ },
688
+ });
689
+ expect(
690
+ CompareCriteria.getCompareMessage({
691
+ values: [10, 20],
692
+ threshold: 5,
693
+ criteriaFilter: anyFilter,
694
+ }),
695
+ ).toBe(
696
+ "Any value of Response Time (in ms) is 10, 20 which is greater than 5.",
697
+ );
698
+
699
+ const allFilter: CriteriaFilter = makeFilter({
700
+ checkOn: CheckOn.ResponseTime,
701
+ filterType: FilterType.GreaterThan,
702
+ evaluateOverTimeOptions: {
703
+ timeValueInMinutes: undefined,
704
+ evaluateOverTimeType: EvaluateOverTimeType.AllValues,
705
+ },
706
+ });
707
+ expect(
708
+ CompareCriteria.getCompareMessage({
709
+ values: [10, 20],
710
+ threshold: 5,
711
+ criteriaFilter: allFilter,
712
+ }),
713
+ ).toBe(
714
+ "All values of Response Time (in ms) is 10, 20 which is greater than 5.",
715
+ );
716
+ });
717
+
718
+ test("summarizes more than five numeric samples as a range", () => {
719
+ const filter: CriteriaFilter = makeFilter({
720
+ checkOn: CheckOn.ResponseTime,
721
+ filterType: FilterType.GreaterThan,
722
+ });
723
+ expect(
724
+ CompareCriteria.getCompareMessage({
725
+ values: [1, 2, 3, 4, 5, 6],
726
+ threshold: 0,
727
+ criteriaFilter: filter,
728
+ }),
729
+ ).toBe(
730
+ "Response Time (in ms) is 6 samples between 1 and 6 which is greater than 0.",
731
+ );
732
+ });
733
+
734
+ test("truncates a long non-numeric value list", () => {
735
+ const filter: CriteriaFilter = makeFilter({
736
+ checkOn: CheckOn.IsOnline,
737
+ filterType: FilterType.GreaterThan,
738
+ });
739
+ // A boolean in the array makes it "not all numeric" -> truncated list.
740
+ const message: string = CompareCriteria.getCompareMessage({
741
+ values: [1, 2, 3, 4, 5, true] as Array<number | boolean>,
742
+ threshold: 0,
743
+ criteriaFilter: filter,
744
+ });
745
+ expect(message).toContain("1, 2, 3, 4, 5, … (6 values total)");
746
+ });
747
+
748
+ test("rounds non-integer values to two decimals", () => {
749
+ const filter: CriteriaFilter = makeFilter({
750
+ checkOn: CheckOn.ResponseTime,
751
+ filterType: FilterType.GreaterThan,
752
+ });
753
+ expect(
754
+ CompareCriteria.getCompareMessage({
755
+ values: 12.3456,
756
+ threshold: 1.9999,
757
+ criteriaFilter: filter,
758
+ }),
759
+ ).toBe("Response Time (in ms) is 12.35 which is greater than 2.");
760
+ });
761
+
762
+ test("appends a unit suffix to both the value and the threshold", () => {
763
+ const filter: CriteriaFilter = makeFilter({
764
+ checkOn: CheckOn.MetricValue,
765
+ filterType: FilterType.GreaterThan,
766
+ });
767
+ expect(
768
+ CompareCriteria.getCompareMessage({
769
+ values: 100,
770
+ threshold: 50,
771
+ criteriaFilter: filter,
772
+ unit: "ms",
773
+ }),
774
+ ).toBe("Metric Value is 100 ms which is greater than 50 ms.");
775
+ });
776
+
777
+ test("includes the disk path for disk usage checks", () => {
778
+ const withPath: CriteriaFilter = makeFilter({
779
+ checkOn: CheckOn.DiskUsagePercent,
780
+ filterType: FilterType.GreaterThan,
781
+ serverMonitorOptions: { diskPath: "/var" },
782
+ });
783
+ expect(
784
+ CompareCriteria.getCompareMessage({
785
+ values: 95,
786
+ threshold: 80,
787
+ criteriaFilter: withPath,
788
+ }),
789
+ ).toBe("Disk Usage (in %) on disk /var is 95 which is greater than 80.");
790
+
791
+ const withoutPath: CriteriaFilter = makeFilter({
792
+ checkOn: CheckOn.DiskUsagePercent,
793
+ filterType: FilterType.GreaterThan,
794
+ });
795
+ expect(
796
+ CompareCriteria.getCompareMessage({
797
+ values: 95,
798
+ threshold: 80,
799
+ criteriaFilter: withoutPath,
800
+ }),
801
+ ).toContain("on disk /");
802
+ });
803
+
804
+ test("mentions the evaluation window when evaluateOverTime is set", () => {
805
+ const filter: CriteriaFilter = makeFilter({
806
+ checkOn: CheckOn.CPUUsagePercent,
807
+ filterType: FilterType.GreaterThan,
808
+ evaluateOverTime: true,
809
+ evaluateOverTimeOptions: {
810
+ timeValueInMinutes: 5,
811
+ evaluateOverTimeType: EvaluateOverTimeType.Average,
812
+ },
813
+ });
814
+ expect(
815
+ CompareCriteria.getCompareMessage({
816
+ values: 90,
817
+ threshold: 70,
818
+ criteriaFilter: filter,
819
+ }),
820
+ ).toContain("over the last 5 minutes");
821
+ });
822
+
823
+ test("uses the metric display name only for MetricValue checks", () => {
824
+ const metricFilter: CriteriaFilter = makeFilter({
825
+ checkOn: CheckOn.MetricValue,
826
+ filterType: FilterType.GreaterThan,
827
+ });
828
+ expect(
829
+ CompareCriteria.getCompareMessage({
830
+ values: 100,
831
+ threshold: 50,
832
+ criteriaFilter: metricFilter,
833
+ metricDisplayName: "http.server.duration",
834
+ }),
835
+ ).toBe("http.server.duration is 100 which is greater than 50.");
836
+
837
+ // For a non-MetricValue check the display name is ignored.
838
+ const responseFilter: CriteriaFilter = makeFilter({
839
+ checkOn: CheckOn.ResponseTime,
840
+ filterType: FilterType.GreaterThan,
841
+ });
842
+ expect(
843
+ CompareCriteria.getCompareMessage({
844
+ values: 100,
845
+ threshold: 50,
846
+ criteriaFilter: responseFilter,
847
+ metricDisplayName: "http.server.duration",
848
+ }),
849
+ ).toBe("Response Time (in ms) is 100 which is greater than 50.");
850
+ });
851
+
852
+ test("True/False messages omit the 'which is' clause", () => {
853
+ const trueFilter: CriteriaFilter = makeFilter({
854
+ checkOn: CheckOn.IsOnline,
855
+ filterType: FilterType.True,
856
+ });
857
+ expect(
858
+ CompareCriteria.getCompareMessage({
859
+ values: true,
860
+ threshold: true,
861
+ criteriaFilter: trueFilter,
862
+ }),
863
+ ).toBe("Is Online is true.");
864
+ });
865
+
866
+ test("renders each comparison verb", () => {
867
+ const verbs: Array<{ filterType: FilterType; verb: string }> = [
868
+ { filterType: FilterType.GreaterThan, verb: "greater than" },
869
+ {
870
+ filterType: FilterType.GreaterThanOrEqualTo,
871
+ verb: "greater than or equal to",
872
+ },
873
+ { filterType: FilterType.LessThan, verb: "less than" },
874
+ {
875
+ filterType: FilterType.LessThanOrEqualTo,
876
+ verb: "less than or equal to",
877
+ },
878
+ { filterType: FilterType.EqualTo, verb: "equal to" },
879
+ { filterType: FilterType.NotEqualTo, verb: "not equal to" },
880
+ ];
881
+
882
+ for (const { filterType, verb } of verbs) {
883
+ const filter: CriteriaFilter = makeFilter({
884
+ checkOn: CheckOn.ResponseTime,
885
+ filterType,
886
+ });
887
+ expect(
888
+ CompareCriteria.getCompareMessage({
889
+ values: 100,
890
+ threshold: 50,
891
+ criteriaFilter: filter,
892
+ }),
893
+ ).toContain(verb);
894
+ }
895
+ });
896
+ });
897
+ });