@oneuptime/common 14.0.8 → 14.0.9
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/Index.ts +2 -0
- package/Models/DatabaseModels/KubernetesAiAgent.ts +364 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +3 -3
- package/Models/DatabaseModels/Project.ts +55 -5
- package/Models/DatabaseModels/ProxmoxResource.ts +49 -1
- package/Models/DatabaseModels/RunnerJob.ts +75 -0
- package/Models/DatabaseModels/TeamComplianceSetting.ts +143 -2
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +17 -0
- package/Server/API/AIChatAPI.ts +6 -3
- package/Server/API/AIInvestigationAPI.ts +17 -10
- package/Server/API/AutoRemediationAPI.ts +207 -25
- package/Server/API/KubernetesClusterAiAccessAPI.ts +623 -21
- package/Server/API/MicrosoftTeamsAPI.ts +93 -14
- package/Server/API/ProxmoxResourceAPI.ts +244 -14
- package/Server/Infrastructure/GlobalCache.ts +32 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.ts +99 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.ts +137 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.ts +42 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Middleware/TelemetryIngest.ts +41 -18
- package/Server/Services/AIService.ts +70 -1
- package/Server/Services/AlertService.ts +352 -11
- package/Server/Services/AlertSeverityService.ts +65 -15
- package/Server/Services/AutoRemediationRuleService.ts +1 -1
- package/Server/Services/IncidentService.ts +280 -104
- package/Server/Services/IncidentSeverityService.ts +66 -15
- package/Server/Services/KubernetesAiAgentJobService.ts +185 -0
- package/Server/Services/KubernetesAiAgentService.ts +1634 -0
- package/Server/Services/KubernetesClusterAiAccessService.ts +1118 -451
- package/Server/Services/KubernetesClusterService.ts +129 -21
- package/Server/Services/ProjectService.ts +32 -0
- package/Server/Services/ProxmoxResourceService.ts +322 -5
- package/Server/Services/RunnerJobService.ts +241 -104
- package/Server/Services/RunnerService.ts +1 -1
- package/Server/Services/ScheduledMaintenanceService.ts +737 -1
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +414 -56
- package/Server/Services/TeamComplianceSettingService.ts +1125 -9
- package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +67 -9
- package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +39 -10
- package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +14 -3
- package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +37 -15
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +67 -24
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +48 -5
- package/Server/Utils/AI/SRE/IncidentPostmortemRunner.ts +40 -8
- package/Server/Utils/AI/SRE/InvestigationEligibility.ts +13 -2
- package/Server/Utils/AI/SRE/InvestigationGrader.ts +1 -1
- package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +6 -1
- package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +133 -7
- package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +58 -4
- package/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.ts +447 -0
- package/Server/Utils/Telemetry/ProxmoxNativePush.ts +903 -0
- package/Server/Utils/Telemetry/TelemetryEntity.ts +87 -27
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +684 -47
- package/Tests/App/AdminDashboard/RowActionPlacementOverrides.test.tsx +278 -0
- package/Tests/App/Dashboard/AIChatChartWidgetZoom.test.tsx +738 -0
- package/Tests/App/Dashboard/AIFeaturesSettingsPointers.test.tsx +147 -0
- package/Tests/App/Dashboard/AIInsightsKubernetesClusterPage.test.tsx +1523 -0
- package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +463 -3
- package/Tests/App/Dashboard/AffectedResourcesPickerDropdownPlacement.test.tsx +958 -0
- package/Tests/App/Dashboard/AffectedResourcesPickerFocusLeave.test.tsx +595 -0
- package/Tests/App/Dashboard/AskAiAvailability.test.ts +20 -3
- package/Tests/App/Dashboard/AskAiDisabled.test.tsx +8 -3
- package/Tests/App/Dashboard/CephRateChartReload.test.tsx +1456 -0
- package/Tests/App/Dashboard/CephTimeRangeZoom.test.tsx +1229 -0
- package/Tests/App/Dashboard/ChartCardTimeRangeZoom.test.tsx +151 -0
- package/Tests/App/Dashboard/ChartCardZoomAffordances.test.tsx +257 -0
- package/Tests/App/Dashboard/ChartRefetchFrame.test.tsx +141 -0
- package/Tests/App/Dashboard/ChartZoomHarness.tsx +249 -0
- package/Tests/App/Dashboard/ChartZoomStandIn.tsx +131 -0
- package/Tests/App/Dashboard/CloudServerlessSlowLoads.test.tsx +628 -0
- package/Tests/App/Dashboard/CloudServerlessTimeRangeZoom.test.tsx +607 -0
- package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +91 -19
- package/Tests/App/Dashboard/CompanionMetricsTabZoom.test.tsx +632 -0
- package/Tests/App/Dashboard/ContainerChartZoomStandIn.tsx +195 -0
- package/Tests/App/Dashboard/ContainerHostOverviewSlowLoads.test.tsx +542 -0
- package/Tests/App/Dashboard/ContainerHostOverviewTimeRangeZoom.test.tsx +848 -0
- package/Tests/App/Dashboard/ContainerMetricsTimeRangeZoom.test.tsx +561 -0
- package/Tests/App/Dashboard/DashboardBoardWideZoomShell.test.tsx +1147 -0
- package/Tests/App/Dashboard/DashboardEditPermissions.test.tsx +3 -2
- package/Tests/App/Dashboard/DashboardHistogramWidgetDragCursor.test.tsx +399 -0
- package/Tests/App/Dashboard/DashboardHistogramWidgetZoom.test.tsx +985 -0
- package/Tests/App/Dashboard/DashboardHistogramWidgetZoomHint.test.tsx +803 -0
- package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +1 -0
- package/Tests/App/Dashboard/DashboardSettingsPreviewZoom.test.tsx +679 -0
- package/Tests/App/Dashboard/DashboardSloAndDataSourceChartZoom.test.tsx +881 -0
- package/Tests/App/Dashboard/DashboardTraceStatusWidgets.test.tsx +878 -0
- package/Tests/App/Dashboard/DatabaseMetricChartModalTimeRangeZoom.test.tsx +442 -0
- package/Tests/App/Dashboard/DatabaseOverviewTimeRangeZoom.test.tsx +831 -0
- package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +43 -0
- package/Tests/App/Dashboard/DockerSwarmInsightsTimeRangeZoom.test.tsx +524 -0
- package/Tests/App/Dashboard/DockerSwarmRateChartRefresh.test.tsx +259 -0
- package/Tests/App/Dashboard/EdgeDetailPanelHistory.test.tsx +763 -0
- package/Tests/App/Dashboard/EdgeDetailPanelTimeRangeZoom.test.tsx +547 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsCases.tsx +565 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsKathmandu.test.tsx +73 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsKolkata.test.tsx +74 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsNewYork.test.tsx +72 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsUtc.test.tsx +72 -0
- package/Tests/App/Dashboard/EmbeddedMetricCardRefresh.test.tsx +310 -0
- package/Tests/App/Dashboard/EmbeddedMetricCardTimeRangeZoom.test.tsx +536 -0
- package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +3 -1
- package/Tests/App/Dashboard/EscalationRuleRowActions.test.tsx +660 -0
- package/Tests/App/Dashboard/EventNotes.test.tsx +2 -2
- package/Tests/App/Dashboard/EventOverviewPages.test.tsx +587 -13
- package/Tests/App/Dashboard/EventOverviewSnapshotExplorerZoom.test.tsx +1139 -0
- package/Tests/App/Dashboard/EventOverviewSnapshotZoomAcrossTabs.test.tsx +1057 -0
- package/Tests/App/Dashboard/EventOverviewTelemetrySnapshotZoom.test.tsx +757 -0
- package/Tests/App/Dashboard/ExceptionOccurrenceTrendZoom.test.tsx +675 -0
- package/Tests/App/Dashboard/ExceptionOccurrencesZoomSurvivesViewSwitch.test.tsx +405 -0
- package/Tests/App/Dashboard/ExceptionSignalPages.test.tsx +67 -3
- package/Tests/App/Dashboard/ExceptionTablesSpanStatus.test.tsx +515 -0
- package/Tests/App/Dashboard/ExplorerOwnZoomRegression.test.tsx +739 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisCases.tsx +461 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisKathmandu.test.tsx +25 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisKolkata.test.tsx +25 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisNewYork.test.tsx +25 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisUtc.test.tsx +24 -0
- package/Tests/App/Dashboard/FlowTopTalkersZoomRefetch.test.tsx +722 -0
- package/Tests/App/Dashboard/FlowViewportGuard.test.tsx +2526 -0
- package/Tests/App/Dashboard/HostPagesSlowLoads.test.tsx +872 -0
- package/Tests/App/Dashboard/HostPagesTimeRangeZoom.test.tsx +1208 -0
- package/Tests/App/Dashboard/IncidentRootCauseTimeRangeZoom.test.tsx +358 -0
- package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +100 -9
- package/Tests/App/Dashboard/InfrastructureMetricsTabTimeRangeZoom.test.tsx +485 -0
- package/Tests/App/Dashboard/InvestigationDrawerCompanionZoom.test.tsx +655 -0
- package/Tests/App/Dashboard/InvestigationDrawerZoom.test.tsx +830 -0
- package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +338 -2
- package/Tests/App/Dashboard/InvoicesPayInvoice.test.tsx +454 -16
- package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +799 -819
- package/Tests/App/Dashboard/KubernetesClusterAiAgentOverviewCard.test.tsx +367 -0
- package/Tests/App/Dashboard/KubernetesClusterAiAgentStatus.test.ts +1202 -0
- package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +106 -0
- package/Tests/App/Dashboard/KubernetesClusterAiNavigation.test.tsx +354 -0
- package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +1884 -2054
- package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +478 -179
- package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +50 -0
- package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +2 -1
- package/Tests/App/Dashboard/KubernetesClusterOverviewZoom.test.tsx +1217 -0
- package/Tests/App/Dashboard/KubernetesClusterPagesZoom.test.tsx +731 -0
- package/Tests/App/Dashboard/KubernetesCostsLoadRecovery.test.tsx +869 -0
- package/Tests/App/Dashboard/KubernetesCostsZoom.test.tsx +1214 -0
- package/Tests/App/Dashboard/KubernetesCostsZoomExactHours.test.tsx +692 -0
- package/Tests/App/Dashboard/KubernetesNetworkThroughputReload.test.tsx +902 -0
- package/Tests/App/Dashboard/KubernetesNetworkZoomBucketEdges.test.tsx +571 -0
- package/Tests/App/Dashboard/KubernetesOverviewZoomBucketEdges.test.tsx +697 -0
- package/Tests/App/Dashboard/KubernetesResourceMetricsTabZoom.test.tsx +633 -0
- package/Tests/App/Dashboard/LogsExplorerTimeRangeZoom.test.tsx +470 -0
- package/Tests/App/Dashboard/LogsInsightsTimeRangeZoom.test.tsx +948 -0
- package/Tests/App/Dashboard/LogsViewerPinnedWindowZoom.test.tsx +486 -0
- package/Tests/App/Dashboard/MetricChartsBucketInspectorDoubleClick.test.tsx +1001 -0
- package/Tests/App/Dashboard/MetricExplorerInvestigationAutoRefresh.test.tsx +514 -0
- package/Tests/App/Dashboard/MetricExplorerSavedViewZoomRoundTrip.test.tsx +581 -0
- package/Tests/App/Dashboard/MetricExplorerTimeRangeZoom.test.tsx +1162 -0
- package/Tests/App/Dashboard/MetricMonitorPreviewZoomLabel.test.ts +72 -0
- package/Tests/App/Dashboard/MetricRowSparklineTimeRangeZoom.test.tsx +204 -0
- package/Tests/App/Dashboard/MetricViewFirstResultLands.test.tsx +408 -0
- package/Tests/App/Dashboard/MetricViewOwnZoomResetPlacement.test.tsx +952 -0
- package/Tests/App/Dashboard/MetricViewTimeRange.test.ts +139 -0
- package/Tests/App/Dashboard/MetricViewTimeRangeZoom.test.tsx +556 -0
- package/Tests/App/Dashboard/MonitorMetricsChartWrappersZoom.test.tsx +552 -0
- package/Tests/App/Dashboard/MonitorMetricsTabsTimeRangeZoom.test.tsx +740 -0
- package/Tests/App/Dashboard/MonitorOverviewChartsTimeRangeZoom.test.tsx +735 -0
- package/Tests/App/Dashboard/MonitorStepPreviewsTimeRangeZoom.test.tsx +835 -0
- package/Tests/App/Dashboard/NetworkDeviceTimeRangeZoom.test.tsx +1115 -0
- package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +186 -17
- package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +155 -46
- package/Tests/App/Dashboard/PasskeySettings.test.tsx +87 -2
- package/Tests/App/Dashboard/ProjectAISettingsPages.test.tsx +884 -0
- package/Tests/App/Dashboard/ProxmoxNodeRemove.test.tsx +621 -0
- package/Tests/App/Dashboard/ProxmoxOverviewSlowLoads.test.tsx +584 -0
- package/Tests/App/Dashboard/ProxmoxPagesTimeRangeZoom.test.tsx +823 -0
- package/Tests/App/Dashboard/ProxmoxRateChartReload.test.tsx +1253 -0
- package/Tests/App/Dashboard/PublicDashboardBoardWideZoom.test.tsx +605 -0
- package/Tests/App/Dashboard/RealRowsLineChart.tsx +250 -0
- package/Tests/App/Dashboard/RechartsHostsPlotCursor.test.tsx +513 -0
- package/Tests/App/Dashboard/RowActionPlacementOverrides.test.tsx +865 -0
- package/Tests/App/Dashboard/RumOverviewTimeRangeZoom.test.tsx +877 -0
- package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +20 -4
- package/Tests/App/Dashboard/RunnerSupersededByAiAgentNote.test.tsx +296 -0
- package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +146 -36
- package/Tests/App/Dashboard/ServiceDatabaseSlowLoads.test.tsx +1166 -0
- package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +1243 -104
- package/Tests/App/Dashboard/ServiceOverviewTimeRangeZoom.test.tsx +792 -0
- package/Tests/App/Dashboard/SloBudgetBurnDownZoom.test.tsx +771 -0
- package/Tests/App/Dashboard/SloBudgetBurnDownZoomAffordances.test.tsx +380 -0
- package/Tests/App/Dashboard/SloEventMetricsBarChartZoom.test.tsx +369 -0
- package/Tests/App/Dashboard/SloHistoryChartsTimeRangeZoom.test.tsx +694 -0
- package/Tests/App/Dashboard/SloHistoryChartsZoomAffordances.test.tsx +497 -0
- package/Tests/App/Dashboard/SloMetricsTimeRangeZoom.test.tsx +541 -0
- package/Tests/App/Dashboard/SlowLoadHarness.ts +123 -0
- package/Tests/App/Dashboard/SnapshotExplorerPrimaryZoom.test.tsx +1194 -0
- package/Tests/App/Dashboard/SnapshotViewerPinnedWindow.test.tsx +620 -0
- package/Tests/App/Dashboard/TelemetrySnapshotPanelTimeRangeZoom.test.tsx +765 -0
- package/Tests/App/Dashboard/TelemetrySnapshotZoom.test.tsx +516 -0
- package/Tests/App/Dashboard/TelemetrySnapshotZoomAcrossTabs.test.tsx +827 -0
- package/Tests/App/Dashboard/TimeRangeZoomPageHarness.tsx +463 -0
- package/Tests/App/Dashboard/TraceChartEditorStatusSplit.test.tsx +543 -0
- package/Tests/App/Dashboard/TraceStatusPresentation.test.tsx +967 -0
- package/Tests/App/Dashboard/TraceTimelineMinimapZoom.test.tsx +315 -0
- package/Tests/App/Dashboard/TracesAnalyticsStatusSplit.test.tsx +778 -0
- package/Tests/App/Dashboard/TracesAnalyticsViewZoom.test.tsx +349 -0
- package/Tests/App/Dashboard/TracesExplorerTimeRangeZoom.test.tsx +551 -0
- package/Tests/App/Dashboard/TracesStatusChartAndFacet.test.tsx +687 -0
- package/Tests/App/Dashboard/TracesViewerStaleResponses.test.tsx +458 -0
- package/Tests/App/Dashboard/UptimeStripsExcludedFromZoom.test.tsx +406 -0
- package/Tests/App/Dashboard/VMwareOverviewSlowLoads.test.tsx +535 -0
- package/Tests/App/Dashboard/VMwarePagesTimeRangeZoom.test.tsx +606 -0
- package/Tests/App/Dashboard/ValueWidgetSparklineStaleHover.test.tsx +1292 -0
- package/Tests/App/Dashboard/ValueWidgetSparklineZoom.test.tsx +765 -0
- package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +98 -0
- package/Tests/Models/DatabaseModels/KubernetesAiAgentModel.test.ts +295 -0
- package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +347 -0
- package/Tests/Server/API/AIChatRouteAuthorization.test.ts +58 -0
- package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +1 -1
- package/Tests/Server/API/AIInvestigationAPI.test.ts +123 -45
- package/Tests/Server/API/AutoRemediationApproveClusterRound.test.ts +1329 -0
- package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +25 -2
- package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -0
- package/Tests/Server/API/KubernetesClusterAiAccessAPIAgent.test.ts +903 -0
- package/Tests/Server/API/MicrosoftTeamsChatsRefreshRoute.test.ts +667 -0
- package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +294 -6
- package/Tests/Server/API/ProxmoxResourceAPIRemoveNode.test.ts +1932 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +195 -0
- package/Tests/Server/Infrastructure/Postgres/AddKubernetesAiAgentAndAiDefaultsMigration.test.ts +623 -0
- package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +87 -10
- package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleScopeMigration.test.ts +622 -0
- package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +61 -4
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +98 -2
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +105 -2
- package/Tests/Server/Services/AIServiceBalanceBlocker.test.ts +282 -0
- package/Tests/Server/Services/AlertMonitorClearWritesNull.test.ts +409 -0
- package/Tests/Server/Services/AlertMonitorEditFeed.test.ts +474 -0
- package/Tests/Server/Services/AlertMonitorEditGuard.test.ts +610 -0
- package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +2 -0
- package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +2 -1
- package/Tests/Server/Services/IncidentAffectedMonitorsEdit.test.ts +1360 -0
- package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +3 -1
- package/Tests/Server/Services/IncidentSeverityDeletePausesComplianceRules.test.ts +488 -0
- package/Tests/Server/Services/KubernetesAiAgentJobService.test.ts +329 -0
- package/Tests/Server/Services/KubernetesAiAgentServiceHelpers.test.ts +618 -0
- package/Tests/Server/Services/KubernetesAiAgentServiceLifecycle.test.ts +1444 -0
- package/Tests/Server/Services/KubernetesAiAgentServiceRegister.test.ts +1666 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +140 -111
- package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +267 -105
- package/Tests/Server/Services/KubernetesClusterAiAccessLegacySuperseded.test.ts +440 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +567 -411
- package/Tests/Server/Services/KubernetesClusterAiAccessTarget.test.ts +1168 -0
- package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +410 -12
- package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +55 -0
- package/Tests/Server/Services/ProjectServiceNewProjectAiDefaults.test.ts +288 -0
- package/Tests/Server/Services/ProxmoxResourcePostgres.test.ts +5633 -0
- package/Tests/Server/Services/ProxmoxResourceService.test.ts +6458 -5
- package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +48 -15
- package/Tests/Server/Services/RunnerJobEnqueueKubectlAiAgent.test.ts +620 -0
- package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +170 -3
- package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +72 -0
- package/Tests/Server/Services/RunnerServiceAgentName.test.ts +6 -4
- package/Tests/Server/Services/ScheduledMaintenanceAttachmentEdit.test.ts +2113 -0
- package/Tests/Server/Services/ScheduledMaintenanceMonitorRelease.test.ts +972 -0
- package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +2237 -0
- package/Tests/Server/Services/TeamComplianceSettingService.test.ts +3988 -138
- package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +107 -1
- package/Tests/Server/Utils/AI/IncidentPostmortemRunner.test.ts +351 -0
- package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +2 -0
- package/Tests/Server/Utils/AI/InvestigationEligibility.test.ts +310 -0
- package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +70 -0
- package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +58 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +125 -1
- package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +252 -3
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerProjectGates.test.ts +240 -0
- package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +2 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +2 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +2 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +2 -0
- package/Tests/Server/Utils/Telemetry/HostAssetAttributesFromOtlp.test.ts +68 -0
- package/Tests/Server/Utils/Telemetry/InventoryMirrorPostgres.test.ts +370 -0
- package/Tests/Server/Utils/Telemetry/NetworkDeviceInventoryAttributes.test.ts +522 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.test.ts +155 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.test.ts +8631 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxNativePush.test.ts +2852 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +367 -1
- package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +45 -3
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +25 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNames.test.ts +3119 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNamesPostgres.test.ts +1721 -0
- package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +581 -113
- package/Tests/Types/Kubernetes/KubernetesAiAgentAccessContract.test.ts +510 -0
- package/Tests/Types/Team/ComplianceRule.test.ts +1029 -0
- package/Tests/Types/Team/TeamComplianceStatus.test.ts +176 -0
- package/Tests/UI/Components/ActionButton/RowActions.test.tsx +641 -0
- package/Tests/UI/Components/ActionButton/RowActionsAccessibility.test.tsx +414 -0
- package/Tests/UI/Components/ActionButton/SplitActionButtons.test.ts +565 -0
- package/Tests/UI/Components/Charts/BarChartDragToSelect.test.tsx +386 -0
- package/Tests/UI/Components/Charts/ChartDoubleClickResetWithoutDblclick.test.tsx +1025 -0
- package/Tests/UI/Components/Charts/ChartGroupPageZoomHint.test.tsx +196 -0
- package/Tests/UI/Components/Charts/ChartPlaceholdersTakeDoubleClick.test.tsx +1861 -0
- package/Tests/UI/Components/Charts/ChartPlotClicksWhileResetArmed.test.tsx +382 -0
- package/Tests/UI/Components/Charts/ChartRangeSelection.test.tsx +704 -0
- package/Tests/UI/Components/Charts/ChartRangeSelectionDoubleClickReset.test.tsx +1481 -0
- package/Tests/UI/Components/Charts/ChartRangeSelectionRealRecharts.test.tsx +918 -0
- package/Tests/UI/Components/Charts/ChartRowBucketStart.test.ts +350 -0
- package/Tests/UI/Components/Charts/DoubleClickReset.test.tsx +996 -0
- package/Tests/UI/Components/Charts/HistogramDoubleClickResetRealRecharts.test.tsx +965 -0
- package/Tests/UI/Components/Charts/HistogramDriftThenRefetch.test.tsx +277 -0
- package/Tests/UI/Components/Charts/HistogramRangeSelectionDataChange.test.tsx +1757 -0
- package/Tests/UI/Components/Charts/HistogramRangeSelectionRealRecharts.test.tsx +560 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/ChartPageTimeRangeZoom.test.tsx +513 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.test.tsx +845 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.test.tsx +119 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHintTailwind.test.tsx +274 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.test.ts +433 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.test.tsx +863 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/ZoomDocsPromises.test.tsx +1147 -0
- package/Tests/UI/Components/DisabledButtonTooltip.test.tsx +14 -3
- package/Tests/UI/Components/EntityDropdownEscape.test.tsx +454 -0
- package/Tests/UI/Components/EntityDropdownFocusLeave.test.tsx +420 -0
- package/Tests/UI/Components/Forms/FormFieldDropdownDisabled.test.tsx +233 -0
- package/Tests/UI/Components/Input.test.tsx +80 -1
- package/Tests/UI/Components/LogsAnalyticsViewZoom.test.tsx +947 -0
- package/Tests/UI/Components/LogsHistogramDragTooltip.test.tsx +17 -5
- package/Tests/UI/Components/LogsViewerAnalyticsZoom.test.tsx +501 -0
- package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +1 -1
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +6 -4
- package/Tests/UI/Components/ModelTable/BaseModelTablePermissionGating.test.tsx +60 -8
- package/Tests/UI/Components/ModelTable/BaseModelTableRowActions.test.tsx +476 -0
- package/Tests/UI/Components/ModelTable/ModelTableWrapContent.test.tsx +7 -5
- package/Tests/UI/Components/MoreMenuPortal.test.tsx +623 -0
- package/Tests/UI/Components/StatusPage/ResourceGridActions.test.tsx +538 -0
- package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +14 -4
- package/Tests/UI/Components/TableCellWrapping.test.tsx +3 -3
- package/Tests/UI/Components/TableRowActions.test.tsx +472 -0
- package/Tests/UI/Components/TelemetryHistogramDragTooltip.test.tsx +1 -1
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +1 -1
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerZoomScope.test.tsx +470 -0
- package/Tests/UI/Components/TelemetryViewer/UseViewerTimeRangeZoom.test.tsx +571 -0
- package/Tests/UI/Components/TelemetryViewer/ViewerDoubleClickResetUnderDataChange.test.tsx +928 -0
- package/Tests/UI/Components/TelemetryViewer/ViewerFollowsEnclosingZoom.test.tsx +1569 -0
- package/Tests/UI/Components/Toggle.test.tsx +66 -0
- package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardRefreshNames.test.tsx +1023 -0
- package/Tests/UI/Rum/ChunkLoader.test.ts +201 -0
- package/Tests/UI/Rum/FidelityNotices.test.ts +54 -0
- package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +72 -0
- package/Tests/UI/Rum/ReplayFrameCapture.test.ts +1009 -9
- package/Tests/UI/Rum/ReplayRecordedAssets.test.ts +1069 -0
- package/Tests/UI/Rum/ReplayStage.test.tsx +913 -2
- package/Tests/UI/Rum/SessionReplayTable.test.tsx +443 -8
- package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +439 -9
- package/Tests/UI/Rum/UserFlowTablesRowActions.test.tsx +501 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +47 -19
- package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +2 -0
- package/Tests/Utils/AiRemediation/KubernetesAiAgentPolicyCopyParity.test.ts +396 -0
- package/Tests/Utils/Dashboard/DashboardTimeRangeZoom.test.ts +104 -1
- package/Types/AI/AIRunType.ts +9 -4
- package/Types/AI/InvestigationNotStartedReason.ts +6 -0
- package/Types/AutoRemediation/AiRemediationCommandPlan.ts +14 -16
- package/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.ts +28 -0
- package/Types/Dashboard/DashboardComponents/DashboardTraceChartComponent.ts +4 -2
- package/Types/Kubernetes/KubernetesClusterAiAccess.ts +263 -32
- package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +20 -16
- package/Types/Kubernetes/KubernetesClusterAiInsights.ts +81 -0
- package/Types/Monitor/ProxmoxAlertTemplates.ts +4 -4
- package/Types/Team/ComplianceNotificationChannel.ts +22 -0
- package/Types/Team/ComplianceRule.ts +434 -0
- package/Types/Team/ComplianceRuleType.ts +21 -0
- package/Types/Team/TeamComplianceStatus.ts +81 -0
- package/Types/Telemetry/TelemetryIngestSurface.ts +28 -0
- package/UI/Components/ActionButton/ActionButtonSchema.ts +21 -0
- package/UI/Components/ActionButton/RowActions.tsx +238 -0
- package/UI/Components/ActionButton/SplitActionButtons.ts +173 -0
- package/UI/Components/Charts/Area/AreaChart.tsx +32 -2
- package/UI/Components/Charts/Bar/BarChart.tsx +40 -3
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +37 -11
- package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +80 -243
- package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +122 -31
- package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +95 -244
- package/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.ts +214 -0
- package/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.ts +32 -0
- package/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.ts +599 -0
- package/UI/Components/Charts/Line/LineChart.tsx +32 -2
- package/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.tsx +70 -0
- package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.tsx +226 -0
- package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.tsx +73 -0
- package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.ts +163 -0
- package/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.ts +215 -0
- package/UI/Components/Charts/Utils/DataPoint.ts +20 -8
- package/UI/Components/Charts/Utils/XAxis.ts +75 -0
- package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +493 -59
- package/UI/Components/EntityDropdown/EntityDropdown.tsx +107 -11
- package/UI/Components/Forms/Fields/FormField.tsx +10 -0
- package/UI/Components/Input/Input.tsx +90 -57
- package/UI/Components/List/ListRow.tsx +8 -74
- package/UI/Components/LogsViewer/LogsViewer.tsx +40 -11
- package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +22 -6
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +209 -6
- package/UI/Components/LogsViewer/components/LogsHistogram.tsx +127 -84
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +3 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +12 -1
- package/UI/Components/MoreMenu/MoreMenu.tsx +365 -30
- package/UI/Components/MoreMenu/MoreMenuItem.tsx +70 -17
- package/UI/Components/OrderedStatesList/Item.tsx +7 -72
- package/UI/Components/StatusPage/ResourceGrid.tsx +126 -77
- package/UI/Components/Table/TableBody.tsx +8 -2
- package/UI/Components/Table/TableRow.tsx +20 -140
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +41 -10
- package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +47 -9
- package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +25 -6
- package/UI/Components/TelemetryViewer/types.ts +2 -0
- package/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.ts +448 -0
- package/UI/Components/Toggle/Toggle.tsx +26 -0
- package/UI/Components/Tooltip/Tooltip.tsx +8 -1
- package/UI/Styles/Theme.css +14 -2
- package/Utils/AiRemediation/CommandPolicy.ts +1 -1
- package/Utils/AiRemediation/KubectlPolicy.ts +1 -1
- package/Utils/Dashboard/DashboardTimeRangeZoom.ts +46 -2
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js +385 -0
- package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js.map +1 -0
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +3 -3
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +56 -5
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxResource.js +51 -1
- package/build/dist/Models/DatabaseModels/ProxmoxResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RunnerJob.js +74 -0
- package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +134 -3
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +6 -1
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +17 -10
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/AutoRemediationAPI.js +144 -24
- package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
- package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +436 -22
- package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +69 -13
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/ProxmoxResourceAPI.js +187 -14
- package/build/dist/Server/API/ProxmoxResourceAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +27 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js +50 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js +84 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js +31 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +34 -19
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +59 -1
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +248 -13
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AlertSeverityService.js +51 -15
- package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleService.js +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +199 -72
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentSeverityService.js +51 -15
- package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesAiAgentJobService.js +158 -0
- package/build/dist/Server/Services/KubernetesAiAgentJobService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesAiAgentService.js +1145 -0
- package/build/dist/Server/Services/KubernetesAiAgentService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +842 -371
- package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +86 -17
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +22 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxResourceService.js +258 -3
- package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
- package/build/dist/Server/Services/RunnerJobService.js +178 -92
- package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
- package/build/dist/Server/Services/RunnerService.js +1 -1
- package/build/dist/Server/Services/RunnerService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +535 -3
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +316 -46
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/TeamComplianceSettingService.js +833 -7
- package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +52 -9
- package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +13 -9
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +7 -3
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +29 -15
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +56 -20
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +39 -5
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js +30 -3
- package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js +13 -2
- package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +1 -1
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +4 -2
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +111 -6
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +40 -4
- package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js +296 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js +699 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +77 -19
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +505 -42
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/AI/AIRunType.js +9 -4
- package/build/dist/Types/AI/AIRunType.js.map +1 -1
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +10 -16
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js +28 -0
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js.map +1 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +72 -16
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +20 -16
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js +17 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js.map +1 -0
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +4 -4
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js.map +1 -1
- package/build/dist/Types/Team/ComplianceNotificationChannel.js +23 -0
- package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -0
- package/build/dist/Types/Team/ComplianceRule.js +304 -0
- package/build/dist/Types/Team/ComplianceRule.js.map +1 -0
- package/build/dist/Types/Team/ComplianceRuleType.js +21 -0
- package/build/dist/Types/Team/ComplianceRuleType.js.map +1 -1
- package/build/dist/Types/Team/TeamComplianceStatus.js +2 -0
- package/build/dist/Types/Team/TeamComplianceStatus.js.map +1 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +25 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
- package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js +20 -1
- package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js.map +1 -1
- package/build/dist/UI/Components/ActionButton/RowActions.js +113 -0
- package/build/dist/UI/Components/ActionButton/RowActions.js.map +1 -0
- package/build/dist/UI/Components/ActionButton/SplitActionButtons.js +71 -0
- package/build/dist/UI/Components/ActionButton/SplitActionButtons.js.map +1 -0
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +16 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +16 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +27 -5
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +61 -194
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +84 -26
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +75 -196
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js +133 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js +26 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js.map +1 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js +371 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js.map +1 -0
- package/build/dist/UI/Components/Charts/Line/LineChart.js +16 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js +35 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js +107 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js +41 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js +117 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js +121 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js.map +1 -0
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js +15 -7
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/XAxis.js +70 -0
- package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +305 -38
- package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -1
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +89 -14
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +11 -2
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +69 -54
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/List/ListRow.js +4 -39
- package/build/dist/UI/Components/List/ListRow.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +31 -8
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +10 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +139 -21
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +30 -8
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.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/ModelTable/BaseModelTable.js +10 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js +253 -14
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +43 -13
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
- package/build/dist/UI/Components/OrderedStatesList/Item.js +2 -38
- package/build/dist/UI/Components/OrderedStatesList/Item.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js +53 -15
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
- package/build/dist/UI/Components/Table/TableBody.js +6 -2
- package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +12 -75
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +32 -7
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +27 -9
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +11 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js +272 -0
- package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js.map +1 -0
- package/build/dist/UI/Components/Toggle/Toggle.js +14 -1
- package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js +1 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
- package/build/dist/Utils/AiRemediation/CommandPolicy.js +1 -1
- package/build/dist/Utils/AiRemediation/CommandPolicy.js.map +1 -1
- package/build/dist/Utils/AiRemediation/KubectlPolicy.js +1 -1
- package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -1
- package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js +27 -1
- package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,1166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @timezone UTC
|
|
3
|
+
*/
|
|
4
|
+
import "@testing-library/jest-dom";
|
|
5
|
+
import {
|
|
6
|
+
afterEach,
|
|
7
|
+
beforeEach,
|
|
8
|
+
describe,
|
|
9
|
+
expect,
|
|
10
|
+
jest,
|
|
11
|
+
test,
|
|
12
|
+
} from "@jest/globals";
|
|
13
|
+
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
|
|
14
|
+
import * as React from "react";
|
|
15
|
+
import { MemoryRouter } from "react-router-dom";
|
|
16
|
+
import getJestMockFunction, { MockFunction } from "../../MockType";
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
* Issue #4105 follow-up on the Service (APM) and Database overviews when
|
|
20
|
+
* their telemetry is slower than the 30-second auto-refresh. The Service
|
|
21
|
+
* overview's metrics effect was keyed on the model object, the Database
|
|
22
|
+
* overview's telemetry effect on the row and its endpoint list, and every
|
|
23
|
+
* tick replaced them (the tick reloads the model). The effect's staleness
|
|
24
|
+
* guard then dropped the load still running for the same window and
|
|
25
|
+
* started an identical one. When every load outlasted the interval, none
|
|
26
|
+
* ever landed: the tiles loading for good, the charts skeletons, nothing
|
|
27
|
+
* to zoom - and a zoom on a slow service or database never painted.
|
|
28
|
+
*
|
|
29
|
+
* Each page is rendered for real (ResourceOverview, ChartCard, the
|
|
30
|
+
* Database sections, the hero's AutoRefreshControl and picker, the
|
|
31
|
+
* auto-refresh timer) with the model answered at once and each telemetry
|
|
32
|
+
* query parked until a test answers it:
|
|
33
|
+
*
|
|
34
|
+
* - a tick while a load runs leaves it alone (the model still reloads);
|
|
35
|
+
* the load lands and fills the page, however many ticks it outlasts,
|
|
36
|
+
* whichever of the page's queries is the slow one;
|
|
37
|
+
* - once it has landed, the next tick loads the slid window, once;
|
|
38
|
+
* - a zoom, its reset, the picker and Refresh still replace a running
|
|
39
|
+
* load, and the newest window wins whichever answer lands first;
|
|
40
|
+
* - a reload that changes what scopes the telemetry (the service's
|
|
41
|
+
* language, the database's endpoints or pods) still replaces a running
|
|
42
|
+
* load; one that only re-stamps when a pod was last seen does not;
|
|
43
|
+
* - a failed load settles, auto-refresh Off still lands, and a database
|
|
44
|
+
* with no scope yet never queries at all.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
const NOW: Date = new Date("2026-09-28T12:00:00.000Z");
|
|
48
|
+
const DAY_MS: number = 24 * 60 * 60 * 1000;
|
|
49
|
+
const MIB: number = 1024 * 1024;
|
|
50
|
+
|
|
51
|
+
const PROJECT_ID: string = "21075038-d9f1-4d3a-b878-f64ae861f7be";
|
|
52
|
+
const MODEL_ID: string = "42b6aaae-7558-42fe-999b-395bbfc79d63";
|
|
53
|
+
const CHECKOUT_SERVICE_ID: string = "3c0e7b2a-1111-4111-8111-000000000001";
|
|
54
|
+
const ORDERS_DB: string = "orders-db.example.com:5432";
|
|
55
|
+
const ORDERS_DB_REPLICA: string = "orders-db-replica.example.com:5432";
|
|
56
|
+
// The member keys of the database's pods.
|
|
57
|
+
const ORDERS_DB_POD: string = "c281e9f63ebf20d9";
|
|
58
|
+
const ORDERS_DB_SECOND_POD: string = "4f1e2d3c4b5a6978";
|
|
59
|
+
|
|
60
|
+
// A time on the test's day, e.g. at("11:36") or at("12:00:30").
|
|
61
|
+
function at(time: string): Date {
|
|
62
|
+
const withSeconds: string = time.length === 5 ? `${time}:00` : time;
|
|
63
|
+
return new Date(`2026-09-28T${withSeconds}.000Z`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// The same moment a day earlier: where "Past 1 Day" starts.
|
|
67
|
+
function dayBefore(time: Date): Date {
|
|
68
|
+
return new Date(time.getTime() - DAY_MS);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const getItemMock: MockFunction = getJestMockFunction();
|
|
72
|
+
const getListMock: MockFunction = getJestMockFunction();
|
|
73
|
+
// Every telemetry query either page asks, as (fetcher, request).
|
|
74
|
+
const telemetryMock: MockFunction = getJestMockFunction();
|
|
75
|
+
|
|
76
|
+
// ProjectUtil's answer, which a row with no projectId falls back to.
|
|
77
|
+
let mockCurrentProjectId: string | null = PROJECT_ID;
|
|
78
|
+
|
|
79
|
+
jest.mock("react-i18next", () => {
|
|
80
|
+
return {
|
|
81
|
+
useTranslation: () => {
|
|
82
|
+
return {
|
|
83
|
+
t: (key: string, options?: { defaultValue?: string }): string => {
|
|
84
|
+
return options?.defaultValue ?? key;
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
|
|
92
|
+
return {
|
|
93
|
+
__esModule: true,
|
|
94
|
+
default: {
|
|
95
|
+
getItem: (...args: Array<unknown>): unknown => {
|
|
96
|
+
return getItemMock(...args);
|
|
97
|
+
},
|
|
98
|
+
getList: (...args: Array<unknown>): unknown => {
|
|
99
|
+
return getListMock(...args);
|
|
100
|
+
},
|
|
101
|
+
getCommonHeaders: (): Record<string, string> => {
|
|
102
|
+
return {};
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
jest.mock("../../../UI/Utils/API/API", () => {
|
|
109
|
+
return {
|
|
110
|
+
__esModule: true,
|
|
111
|
+
default: {
|
|
112
|
+
getFriendlyMessage: (): string => {
|
|
113
|
+
return "failed";
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
jest.mock("../../../UI/Utils/Project", () => {
|
|
120
|
+
return {
|
|
121
|
+
__esModule: true,
|
|
122
|
+
default: {
|
|
123
|
+
getCurrentProjectId: (): unknown => {
|
|
124
|
+
if (!mockCurrentProjectId) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
const { default: ObjectIDType } = jest.requireActual(
|
|
128
|
+
"../../../Types/ObjectID",
|
|
129
|
+
) as { default: new (id: string) => unknown };
|
|
130
|
+
return new ObjectIDType(mockCurrentProjectId);
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
jest.mock("../../../UI/Utils/Navigation", () => {
|
|
137
|
+
return {
|
|
138
|
+
__esModule: true,
|
|
139
|
+
default: {
|
|
140
|
+
getLastParamAsObjectID: (): unknown => {
|
|
141
|
+
const { default: ObjectIDType } = jest.requireActual(
|
|
142
|
+
"../../../Types/ObjectID",
|
|
143
|
+
) as { default: new (id: string) => unknown };
|
|
144
|
+
return new ObjectIDType("42b6aaae-7558-42fe-999b-395bbfc79d63");
|
|
145
|
+
},
|
|
146
|
+
navigate: (): void => {},
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
jest.mock("../../../UI/Components/Charts/Line/LineChart", () => {
|
|
152
|
+
const harness: { StandInLineChart: unknown } = jest.requireActual(
|
|
153
|
+
"./TimeRangeZoomPageHarness",
|
|
154
|
+
) as { StandInLineChart: unknown };
|
|
155
|
+
return { __esModule: true, default: harness.StandInLineChart };
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// The Service overview's span, runtime and log / exception queries.
|
|
159
|
+
jest.mock(
|
|
160
|
+
"../../../../App/FeatureSet/Dashboard/src/Components/TelemetryResource/telemetryMetrics",
|
|
161
|
+
() => {
|
|
162
|
+
const format: Record<string, unknown> = jest.requireActual(
|
|
163
|
+
"../../../../App/FeatureSet/Dashboard/src/Components/TelemetryResource/telemetryFormat",
|
|
164
|
+
) as Record<string, unknown>;
|
|
165
|
+
return {
|
|
166
|
+
__esModule: true,
|
|
167
|
+
...format,
|
|
168
|
+
fetchSpanMetrics: (request: unknown): unknown => {
|
|
169
|
+
return telemetryMock("span metrics", request);
|
|
170
|
+
},
|
|
171
|
+
fetchLogAndExceptionSignals: (request: unknown): unknown => {
|
|
172
|
+
return telemetryMock("log and exception signals", request);
|
|
173
|
+
},
|
|
174
|
+
fetchMetricSeries: (request: unknown): unknown => {
|
|
175
|
+
return mockRuntimeMetricSeries(request);
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
},
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
// The Database overview's query, caller, engine and pod queries.
|
|
182
|
+
jest.mock(
|
|
183
|
+
"../../../../App/FeatureSet/Dashboard/src/Pages/Database/Utils/DatabaseServerTelemetryQueries",
|
|
184
|
+
() => {
|
|
185
|
+
const actual: Record<string, unknown> = jest.requireActual(
|
|
186
|
+
"../../../../App/FeatureSet/Dashboard/src/Pages/Database/Utils/DatabaseServerTelemetryQueries",
|
|
187
|
+
) as Record<string, unknown>;
|
|
188
|
+
return {
|
|
189
|
+
...actual,
|
|
190
|
+
__esModule: true,
|
|
191
|
+
fetchDatabaseQueryMetrics: (request: unknown): unknown => {
|
|
192
|
+
return telemetryMock("query metrics", request);
|
|
193
|
+
},
|
|
194
|
+
fetchDatabaseCallingServices: (request: unknown): unknown => {
|
|
195
|
+
return telemetryMock("calling services", request);
|
|
196
|
+
},
|
|
197
|
+
fetchDatabaseEngineMetrics: (request: unknown): unknown => {
|
|
198
|
+
return telemetryMock("engine metrics", request);
|
|
199
|
+
},
|
|
200
|
+
fetchDatabaseMetricSeries: (request: unknown): unknown => {
|
|
201
|
+
return mockPodMetricSeries(request);
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
},
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
// Not range-based (ongoing incidents, alerts, maintenance).
|
|
208
|
+
jest.mock(
|
|
209
|
+
"../../../../App/FeatureSet/Dashboard/src/Components/ResourceActivity/ResourceActivityCards",
|
|
210
|
+
() => {
|
|
211
|
+
return {
|
|
212
|
+
__esModule: true,
|
|
213
|
+
default: (): null => {
|
|
214
|
+
return null;
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
},
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
// The Service details card below the overview loads its own row.
|
|
221
|
+
jest.mock("../../../UI/Components/ModelDetail/CardModelDetail", () => {
|
|
222
|
+
return {
|
|
223
|
+
__esModule: true,
|
|
224
|
+
default: (): null => {
|
|
225
|
+
return null;
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
import ServiceView from "../../../../App/FeatureSet/Dashboard/src/Pages/Service/View/Index";
|
|
231
|
+
import DatabaseServerOverview from "../../../../App/FeatureSet/Dashboard/src/Pages/Database/View/Overview";
|
|
232
|
+
import PageComponentProps from "../../../../App/FeatureSet/Dashboard/src/Pages/PageComponentProps";
|
|
233
|
+
import {
|
|
234
|
+
DatabaseCallingServices,
|
|
235
|
+
DatabaseEngineMetricResult,
|
|
236
|
+
DatabaseQueryMetrics,
|
|
237
|
+
DatabaseTimePoint,
|
|
238
|
+
} from "../../../../App/FeatureSet/Dashboard/src/Pages/Database/Utils/DatabaseServerTelemetryQueries";
|
|
239
|
+
import {
|
|
240
|
+
DATABASE_RUNTIME_METRICS,
|
|
241
|
+
DatabaseRuntimePlatform,
|
|
242
|
+
} from "../../../../App/FeatureSet/Dashboard/src/Pages/Database/Utils/DatabaseServerPresentation";
|
|
243
|
+
import { LogAndExceptionSignals } from "../../../../App/FeatureSet/Dashboard/src/Components/TelemetryResource/telemetryMetrics";
|
|
244
|
+
import DatabaseServer from "../../../Models/DatabaseModels/DatabaseServer";
|
|
245
|
+
import DatabaseServerEndpoint from "../../../Models/DatabaseModels/DatabaseServerEndpoint";
|
|
246
|
+
import Service from "../../../Models/DatabaseModels/Service";
|
|
247
|
+
import { DatabaseServerMetricDefinition } from "../../../Types/DatabaseServer/DatabaseServerMetricCatalog";
|
|
248
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
249
|
+
import TimeRange from "../../../Types/Time/TimeRange";
|
|
250
|
+
import {
|
|
251
|
+
AUTO_REFRESH_MS,
|
|
252
|
+
advance,
|
|
253
|
+
Backlog,
|
|
254
|
+
expectRefreshSettled,
|
|
255
|
+
refreshButton,
|
|
256
|
+
} from "./SlowLoadHarness";
|
|
257
|
+
import {
|
|
258
|
+
chartWindows,
|
|
259
|
+
customRangeLabel,
|
|
260
|
+
doubleClick,
|
|
261
|
+
dragAcross,
|
|
262
|
+
flush,
|
|
263
|
+
pickerLabel,
|
|
264
|
+
pickPreset,
|
|
265
|
+
presetLabel,
|
|
266
|
+
resetZoomButtons,
|
|
267
|
+
windowOf,
|
|
268
|
+
zoomCharts,
|
|
269
|
+
} from "./TimeRangeZoomPageHarness";
|
|
270
|
+
|
|
271
|
+
const PAGE_PROPS: PageComponentProps = {} as PageComponentProps;
|
|
272
|
+
const PAST_HOUR: string = presetLabel(TimeRange.PAST_ONE_HOUR);
|
|
273
|
+
const PAST_DAY: string = presetLabel(TimeRange.PAST_ONE_DAY);
|
|
274
|
+
|
|
275
|
+
type Fetcher =
|
|
276
|
+
| "span metrics"
|
|
277
|
+
| "runtime metrics"
|
|
278
|
+
| "log and exception signals"
|
|
279
|
+
| "query metrics"
|
|
280
|
+
| "calling services"
|
|
281
|
+
| "engine metrics"
|
|
282
|
+
| "pod CPU"
|
|
283
|
+
| "pod memory";
|
|
284
|
+
|
|
285
|
+
// What the fetchers are asked; each takes a window, some a little more.
|
|
286
|
+
interface TelemetryRequest {
|
|
287
|
+
start: Date;
|
|
288
|
+
end: Date;
|
|
289
|
+
// The runtime probe: which metric.
|
|
290
|
+
name?: string | undefined;
|
|
291
|
+
// The database's pod charts: which metric.
|
|
292
|
+
metricName?: string | undefined;
|
|
293
|
+
// The database's engine metrics: its engine's catalog.
|
|
294
|
+
metrics?: Array<DatabaseServerMetricDefinition> | undefined;
|
|
295
|
+
// Every database query: the entity keys it is scoped by.
|
|
296
|
+
keys?: ReadonlyArray<string> | undefined;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// A telemetry query, parked until the test answers it.
|
|
300
|
+
interface SlowCall {
|
|
301
|
+
fetcher: Fetcher;
|
|
302
|
+
start: Date;
|
|
303
|
+
end: Date;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const backlog: Backlog<SlowCall> = new Backlog<SlowCall>();
|
|
307
|
+
|
|
308
|
+
const SERVICE_FETCHERS: Array<Fetcher> = [
|
|
309
|
+
"span metrics",
|
|
310
|
+
"runtime metrics",
|
|
311
|
+
"log and exception signals",
|
|
312
|
+
];
|
|
313
|
+
const DATABASE_FETCHERS: Array<Fetcher> = [
|
|
314
|
+
"query metrics",
|
|
315
|
+
"calling services",
|
|
316
|
+
"engine metrics",
|
|
317
|
+
"pod CPU",
|
|
318
|
+
"pod memory",
|
|
319
|
+
];
|
|
320
|
+
const ALL_FETCHERS: Array<Fetcher> = [
|
|
321
|
+
...SERVICE_FETCHERS,
|
|
322
|
+
...DATABASE_FETCHERS,
|
|
323
|
+
];
|
|
324
|
+
|
|
325
|
+
// The fetchers whose queries wait for the test; the rest answer at once.
|
|
326
|
+
let slowFetchers: ReadonlyArray<Fetcher> = ALL_FETCHERS;
|
|
327
|
+
|
|
328
|
+
// What the next model fetch answers; a test may change it before a tick.
|
|
329
|
+
let serviceName: string = "checkout";
|
|
330
|
+
let serviceLanguage: string = "nodejs";
|
|
331
|
+
let databaseName: string = "orders-db";
|
|
332
|
+
let databaseEndpoints: Array<string> = [ORDERS_DB];
|
|
333
|
+
let databaseProjectId: string | null = PROJECT_ID;
|
|
334
|
+
// Its pods' member keys, each with when discovery last saw it.
|
|
335
|
+
let databaseMembers: Record<string, string> = {};
|
|
336
|
+
|
|
337
|
+
/*
|
|
338
|
+
* The runtime metric each language's service emits, and its level. The
|
|
339
|
+
* probe asks for every candidate of the service's language one after
|
|
340
|
+
* another; the rest come back empty at once, as for a metric never sent.
|
|
341
|
+
*/
|
|
342
|
+
const RUNTIME_METRIC_LEVELS: Record<string, number> = {
|
|
343
|
+
"nodejs.eventloop.utilization": 40,
|
|
344
|
+
"go.goroutine.count": 120,
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
function mockRuntimeMetricSeries(request: unknown): Promise<unknown> {
|
|
348
|
+
const name: string = (request as TelemetryRequest).name || "";
|
|
349
|
+
|
|
350
|
+
if (RUNTIME_METRIC_LEVELS[name] === undefined) {
|
|
351
|
+
return Promise.resolve([]);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return telemetryMock("runtime metrics", request) as Promise<unknown>;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// The database's two pod charts, told apart by the metric they ask for.
|
|
358
|
+
function mockPodMetricSeries(request: unknown): Promise<unknown> {
|
|
359
|
+
const cpuMetric: string =
|
|
360
|
+
DATABASE_RUNTIME_METRICS[DatabaseRuntimePlatform.Kubernetes].cpu.metricName;
|
|
361
|
+
const fetcher: Fetcher =
|
|
362
|
+
(request as TelemetryRequest).metricName === cpuMetric
|
|
363
|
+
? "pod CPU"
|
|
364
|
+
: "pod memory";
|
|
365
|
+
|
|
366
|
+
return telemetryMock(fetcher, request) as Promise<unknown>;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// One bucket a minute from 11:00 to 11:59: those inside [start, end].
|
|
370
|
+
function minutesWithin(start: Date, end: Date): Array<Date> {
|
|
371
|
+
const minutes: Array<Date> = [];
|
|
372
|
+
|
|
373
|
+
for (let minute: number = 0; minute < 60; minute++) {
|
|
374
|
+
const time: Date = new Date(Date.UTC(2026, 8, 28, 11, minute));
|
|
375
|
+
|
|
376
|
+
if (time.getTime() >= start.getTime() && time.getTime() <= end.getTime()) {
|
|
377
|
+
minutes.push(time);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return minutes;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function seriesOf(minutes: Array<Date>, y: number): Array<DatabaseTimePoint> {
|
|
385
|
+
return minutes.map((x: Date): DatabaseTimePoint => {
|
|
386
|
+
return { x: x, y: y };
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/*
|
|
391
|
+
* 10 requests (or queries) a minute, 1 of them failed, a 250 ms p95. Over
|
|
392
|
+
* 11:00-12:00 that is 600; over 11:00:30-12:00:30 (from 11:01) 590; over
|
|
393
|
+
* 11:02-12:02 580; over 11:20-11:30 (11 buckets) 110.
|
|
394
|
+
*/
|
|
395
|
+
function requestsOver(minutes: Array<Date>): DatabaseQueryMetrics {
|
|
396
|
+
const total: number = 10 * minutes.length;
|
|
397
|
+
|
|
398
|
+
return {
|
|
399
|
+
total: total,
|
|
400
|
+
errors: minutes.length,
|
|
401
|
+
errorRatePercent: total > 0 ? 10 : null,
|
|
402
|
+
p95DurationMs: total > 0 ? 250 : null,
|
|
403
|
+
countSeries: seriesOf(minutes, 10),
|
|
404
|
+
errorSeries: seriesOf(minutes, 1),
|
|
405
|
+
p95Series: seriesOf(minutes, 250),
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function signalsOver(minutes: Array<Date>): LogAndExceptionSignals {
|
|
410
|
+
return {
|
|
411
|
+
logs: {
|
|
412
|
+
total: 20 * minutes.length,
|
|
413
|
+
errorCount: 2 * minutes.length,
|
|
414
|
+
countSeries: seriesOf(minutes, 20),
|
|
415
|
+
errorSeries: seriesOf(minutes, 2),
|
|
416
|
+
failed: false,
|
|
417
|
+
},
|
|
418
|
+
exceptions: {
|
|
419
|
+
total: 3 * minutes.length,
|
|
420
|
+
unhandledCount: minutes.length,
|
|
421
|
+
unhandledSeries: seriesOf(minutes, 1),
|
|
422
|
+
handledSeries: seriesOf(minutes, 2),
|
|
423
|
+
failed: false,
|
|
424
|
+
},
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function callersOver(minutes: Array<Date>): DatabaseCallingServices {
|
|
429
|
+
return {
|
|
430
|
+
services: [
|
|
431
|
+
{
|
|
432
|
+
serviceId: CHECKOUT_SERVICE_ID,
|
|
433
|
+
calls: 10 * minutes.length,
|
|
434
|
+
errors: minutes.length,
|
|
435
|
+
errorRatePercent: 10,
|
|
436
|
+
p95DurationMs: 250,
|
|
437
|
+
},
|
|
438
|
+
],
|
|
439
|
+
total: 1,
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// The first two engine metrics have points to chart; the rest only tiles.
|
|
444
|
+
function engineOver(
|
|
445
|
+
minutes: Array<Date>,
|
|
446
|
+
request: TelemetryRequest,
|
|
447
|
+
): Array<DatabaseEngineMetricResult> {
|
|
448
|
+
return (request.metrics || []).map(
|
|
449
|
+
(
|
|
450
|
+
definition: DatabaseServerMetricDefinition,
|
|
451
|
+
index: number,
|
|
452
|
+
): DatabaseEngineMetricResult => {
|
|
453
|
+
const series: Array<DatabaseTimePoint> =
|
|
454
|
+
index < 2 ? seriesOf(minutes, 12 + index) : [];
|
|
455
|
+
return {
|
|
456
|
+
definition: definition,
|
|
457
|
+
series: series,
|
|
458
|
+
value: series.length > 0 ? 12 + index : null,
|
|
459
|
+
};
|
|
460
|
+
},
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Each fetcher's answer for the minutes of the window it was asked for.
|
|
465
|
+
const ANSWERS: Record<
|
|
466
|
+
Fetcher,
|
|
467
|
+
(minutes: Array<Date>, request: TelemetryRequest) => unknown
|
|
468
|
+
> = {
|
|
469
|
+
"span metrics": requestsOver,
|
|
470
|
+
"runtime metrics": (
|
|
471
|
+
minutes: Array<Date>,
|
|
472
|
+
request: TelemetryRequest,
|
|
473
|
+
): unknown => {
|
|
474
|
+
return seriesOf(minutes, RUNTIME_METRIC_LEVELS[request.name || ""] ?? 0);
|
|
475
|
+
},
|
|
476
|
+
"log and exception signals": signalsOver,
|
|
477
|
+
"query metrics": requestsOver,
|
|
478
|
+
"calling services": callersOver,
|
|
479
|
+
"engine metrics": engineOver,
|
|
480
|
+
"pod CPU": (minutes: Array<Date>): unknown => {
|
|
481
|
+
return seriesOf(minutes, 0.5);
|
|
482
|
+
},
|
|
483
|
+
"pod memory": (minutes: Array<Date>): unknown => {
|
|
484
|
+
return seriesOf(minutes, 512 * MIB);
|
|
485
|
+
},
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
// A fresh object per fetch, as the API returns; seen a minute ago.
|
|
489
|
+
function serviceModel(): Service {
|
|
490
|
+
const service: Service = new Service();
|
|
491
|
+
service.name = serviceName;
|
|
492
|
+
service.telemetrySdkLanguage = serviceLanguage;
|
|
493
|
+
service.lastSeenAt = new Date(Date.now() - 60_000);
|
|
494
|
+
return service;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// A Kubernetes-hosted PostgreSQL with its engine metrics connected.
|
|
498
|
+
function databaseRow(): DatabaseServer {
|
|
499
|
+
const row: DatabaseServer = new DatabaseServer();
|
|
500
|
+
row.id = new ObjectID(MODEL_ID);
|
|
501
|
+
if (databaseProjectId) {
|
|
502
|
+
row.projectId = new ObjectID(databaseProjectId);
|
|
503
|
+
}
|
|
504
|
+
row.name = databaseName;
|
|
505
|
+
row.dbSystem = "postgresql";
|
|
506
|
+
row.serverAddress = "orders-db.example.com";
|
|
507
|
+
row.serverPort = 5432;
|
|
508
|
+
row.discoverySource = "client-spans";
|
|
509
|
+
row.otelCollectorStatus = "connected";
|
|
510
|
+
row.collectorLastSeenAt = new Date(Date.now() - 60_000);
|
|
511
|
+
row.lastSeenAt = new Date(Date.now() - 60_000);
|
|
512
|
+
row.kubernetesClusterId = new ObjectID(
|
|
513
|
+
"7b2c1d4e-0000-4000-8000-00000000c1a5",
|
|
514
|
+
);
|
|
515
|
+
row.kubernetesNamespace = "shop";
|
|
516
|
+
row.workloadKind = "StatefulSet";
|
|
517
|
+
row.workloadName = "orders-db";
|
|
518
|
+
row.memberEntityKeys = { ...databaseMembers };
|
|
519
|
+
return row;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
interface OverviewCase {
|
|
523
|
+
Page: React.FunctionComponent<PageComponentProps>;
|
|
524
|
+
// The page's telemetry queries: one load asks each of them once.
|
|
525
|
+
fetchers: Array<Fetcher>;
|
|
526
|
+
// The tile counting requests (Service) or queries (Database).
|
|
527
|
+
countTile: string;
|
|
528
|
+
// The charts a landed load draws.
|
|
529
|
+
charts: number;
|
|
530
|
+
// Where the page remembers the reader's auto-refresh interval.
|
|
531
|
+
storageKey: string;
|
|
532
|
+
// Renames the model for its next fetch.
|
|
533
|
+
rename: (name: string) => void;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const SERVICE_OVERVIEW: OverviewCase = {
|
|
537
|
+
Page: ServiceView,
|
|
538
|
+
fetchers: SERVICE_FETCHERS,
|
|
539
|
+
countTile: "Requests",
|
|
540
|
+
// Requests, Latency (p95), Logs, Exceptions and Event loop utilization.
|
|
541
|
+
charts: 5,
|
|
542
|
+
storageKey: "service-overview-auto-refresh-interval",
|
|
543
|
+
rename: (name: string): void => {
|
|
544
|
+
serviceName = name;
|
|
545
|
+
},
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
const DATABASE_OVERVIEW: OverviewCase = {
|
|
549
|
+
Page: DatabaseServerOverview,
|
|
550
|
+
fetchers: DATABASE_FETCHERS,
|
|
551
|
+
countTile: "Queries",
|
|
552
|
+
// Queries, p95, the pods' CPU and memory, and two engine metrics.
|
|
553
|
+
charts: 6,
|
|
554
|
+
storageKey: "database-overview-auto-refresh-interval",
|
|
555
|
+
rename: (name: string): void => {
|
|
556
|
+
databaseName = name;
|
|
557
|
+
},
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
const OVERVIEWS: Array<[string, OverviewCase]> = [
|
|
561
|
+
["Service overview", SERVICE_OVERVIEW],
|
|
562
|
+
["Database overview", DATABASE_OVERVIEW],
|
|
563
|
+
];
|
|
564
|
+
|
|
565
|
+
function endsAt(end: Date): (call: SlowCall) => boolean {
|
|
566
|
+
return (call: SlowCall): boolean => {
|
|
567
|
+
return call.end.getTime() === end.getTime();
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function same<T>(value: T, count: number): Array<T> {
|
|
572
|
+
return Array.from({ length: count }, (): T => {
|
|
573
|
+
return value;
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function tileOf(title: string): HTMLElement {
|
|
578
|
+
// The first "About <title>" is the tile's; a chart card may share it.
|
|
579
|
+
return screen
|
|
580
|
+
.getAllByRole("button", { name: `About ${title}` })[0]!
|
|
581
|
+
.closest(".rounded-xl") as HTMLElement;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// The figure a tile shows; null while it loads.
|
|
585
|
+
function tileValue(title: string): string | null {
|
|
586
|
+
const value: Element | null = tileOf(title).querySelector(".text-2xl");
|
|
587
|
+
return value ? (value.textContent || "").trim() : null;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// No load has landed yet: the count tile loads, and no chart is drawn.
|
|
591
|
+
function expectNothingPainted(overview: OverviewCase): void {
|
|
592
|
+
expect(tileOf(overview.countTile)).toHaveAttribute("aria-busy", "true");
|
|
593
|
+
expect(tileValue(overview.countTile)).toBeNull();
|
|
594
|
+
expect(zoomCharts()).toHaveLength(0);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// The newest load has landed: its count on the tile, every chart over it.
|
|
598
|
+
function expectPainted(
|
|
599
|
+
overview: OverviewCase,
|
|
600
|
+
count: string,
|
|
601
|
+
window: [string, string],
|
|
602
|
+
): void {
|
|
603
|
+
expect(tileOf(overview.countTile)).toHaveAttribute("aria-busy", "false");
|
|
604
|
+
expect(tileValue(overview.countTile)).toBe(count);
|
|
605
|
+
expect(chartWindows()).toEqual(same(window, overview.charts));
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// The newest load still runs: the count tile loads.
|
|
609
|
+
function expectLoading(overview: OverviewCase): void {
|
|
610
|
+
expect(tileOf(overview.countTile)).toHaveAttribute("aria-busy", "true");
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// The telemetry requests one fetcher was asked, oldest first.
|
|
614
|
+
function requestsTo(fetcher: Fetcher): Array<TelemetryRequest> {
|
|
615
|
+
return telemetryMock.mock.calls
|
|
616
|
+
.filter((call: Array<unknown>): boolean => {
|
|
617
|
+
return call[0] === fetcher;
|
|
618
|
+
})
|
|
619
|
+
.map((call: Array<unknown>): TelemetryRequest => {
|
|
620
|
+
return call[1] as TelemetryRequest;
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
function modelLoads(): number {
|
|
625
|
+
return getItemMock.mock.calls.length;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
async function mount(overview: OverviewCase): Promise<void> {
|
|
629
|
+
render(
|
|
630
|
+
<MemoryRouter>
|
|
631
|
+
<overview.Page {...PAGE_PROPS} />
|
|
632
|
+
</MemoryRouter>,
|
|
633
|
+
);
|
|
634
|
+
await flush();
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// Renders the page with its first load answered at once.
|
|
638
|
+
async function mountPainted(overview: OverviewCase): Promise<void> {
|
|
639
|
+
slowFetchers = [];
|
|
640
|
+
await mount(overview);
|
|
641
|
+
expectPainted(overview, "600", windowOf(at("11:00"), NOW));
|
|
642
|
+
slowFetchers = ALL_FETCHERS;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
beforeEach(() => {
|
|
646
|
+
jest.useFakeTimers();
|
|
647
|
+
jest.setSystemTime(NOW);
|
|
648
|
+
window.localStorage.clear();
|
|
649
|
+
getItemMock.mockReset();
|
|
650
|
+
getListMock.mockReset();
|
|
651
|
+
telemetryMock.mockReset();
|
|
652
|
+
backlog.clear();
|
|
653
|
+
slowFetchers = ALL_FETCHERS;
|
|
654
|
+
serviceName = "checkout";
|
|
655
|
+
serviceLanguage = "nodejs";
|
|
656
|
+
databaseName = "orders-db";
|
|
657
|
+
databaseEndpoints = [ORDERS_DB];
|
|
658
|
+
databaseProjectId = PROJECT_ID;
|
|
659
|
+
databaseMembers = { [ORDERS_DB_POD]: "2026-09-28T11:00:00.000Z" };
|
|
660
|
+
mockCurrentProjectId = PROJECT_ID;
|
|
661
|
+
|
|
662
|
+
getItemMock.mockImplementation((request: unknown): Promise<unknown> => {
|
|
663
|
+
const modelType: unknown = (request as { modelType: unknown }).modelType;
|
|
664
|
+
return Promise.resolve(
|
|
665
|
+
modelType === DatabaseServer ? databaseRow() : serviceModel(),
|
|
666
|
+
);
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
getListMock.mockImplementation((request: unknown): Promise<unknown> => {
|
|
670
|
+
const modelType: unknown = (request as { modelType: unknown }).modelType;
|
|
671
|
+
|
|
672
|
+
if (modelType === DatabaseServerEndpoint) {
|
|
673
|
+
const rows: Array<DatabaseServerEndpoint> = databaseEndpoints.map(
|
|
674
|
+
(value: string): DatabaseServerEndpoint => {
|
|
675
|
+
const row: DatabaseServerEndpoint = new DatabaseServerEndpoint();
|
|
676
|
+
row.endpoint = value;
|
|
677
|
+
return row;
|
|
678
|
+
},
|
|
679
|
+
);
|
|
680
|
+
return Promise.resolve({ data: rows, count: rows.length });
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// The calling services' names.
|
|
684
|
+
const checkout: Service = new Service();
|
|
685
|
+
checkout._id = CHECKOUT_SERVICE_ID;
|
|
686
|
+
checkout.name = "checkout";
|
|
687
|
+
return Promise.resolve({ data: [checkout], count: 1 });
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
telemetryMock.mockImplementation(
|
|
691
|
+
(fetcher: unknown, request: unknown): Promise<unknown> => {
|
|
692
|
+
const which: Fetcher = fetcher as Fetcher;
|
|
693
|
+
const call: TelemetryRequest = request as TelemetryRequest;
|
|
694
|
+
// Worked out when asked, so it holds the rows of the window asked for.
|
|
695
|
+
const answer: unknown = ANSWERS[which](
|
|
696
|
+
minutesWithin(call.start, call.end),
|
|
697
|
+
call,
|
|
698
|
+
);
|
|
699
|
+
|
|
700
|
+
if (slowFetchers.includes(which)) {
|
|
701
|
+
return backlog.park(
|
|
702
|
+
{ fetcher: which, start: call.start, end: call.end },
|
|
703
|
+
answer,
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
return Promise.resolve(answer);
|
|
708
|
+
},
|
|
709
|
+
);
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
afterEach(() => {
|
|
713
|
+
cleanup();
|
|
714
|
+
jest.useRealTimers();
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
describe.each(OVERVIEWS)(
|
|
718
|
+
"%s: telemetry that outlasts the auto-refresh",
|
|
719
|
+
(_name: string, overview: OverviewCase) => {
|
|
720
|
+
const perLoad: number = overview.fetchers.length;
|
|
721
|
+
|
|
722
|
+
test("a tick while the load runs leaves it alone; it lands, fills the tiles and draws every chart", async () => {
|
|
723
|
+
await mount(overview);
|
|
724
|
+
|
|
725
|
+
expect(backlog.calls()).toHaveLength(perLoad);
|
|
726
|
+
expectNothingPainted(overview);
|
|
727
|
+
const loadsBefore: number = modelLoads();
|
|
728
|
+
|
|
729
|
+
await advance(AUTO_REFRESH_MS);
|
|
730
|
+
|
|
731
|
+
// The model reloaded; the telemetry load was left to run.
|
|
732
|
+
expect(modelLoads()).toBe(loadsBefore + 1);
|
|
733
|
+
expect(backlog.calls()).toHaveLength(perLoad);
|
|
734
|
+
expectRefreshSettled();
|
|
735
|
+
|
|
736
|
+
await advance(5_000);
|
|
737
|
+
await backlog.release(endsAt(NOW));
|
|
738
|
+
|
|
739
|
+
expectPainted(overview, "600", windowOf(at("11:00"), NOW));
|
|
740
|
+
expectRefreshSettled();
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
test("the model still reloads on every tick while the load runs, and the hero shows what it brings", async () => {
|
|
744
|
+
await mount(overview);
|
|
745
|
+
const loadsBefore: number = modelLoads();
|
|
746
|
+
overview.rename("payments");
|
|
747
|
+
|
|
748
|
+
await advance(AUTO_REFRESH_MS);
|
|
749
|
+
await advance(AUTO_REFRESH_MS);
|
|
750
|
+
|
|
751
|
+
expect(modelLoads()).toBe(loadsBefore + 2);
|
|
752
|
+
expect(
|
|
753
|
+
screen.getByRole("heading", { level: 1, name: "payments" }),
|
|
754
|
+
).toBeInTheDocument();
|
|
755
|
+
// All the while, one telemetry load: the first.
|
|
756
|
+
expect(backlog.calls()).toHaveLength(perLoad);
|
|
757
|
+
expect(backlog.calls().every(endsAt(NOW))).toBe(true);
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
test("a load that outlasts three ticks still lands and paints", async () => {
|
|
761
|
+
await mount(overview);
|
|
762
|
+
|
|
763
|
+
await advance(3 * AUTO_REFRESH_MS + 5_000);
|
|
764
|
+
|
|
765
|
+
expect(backlog.calls()).toHaveLength(perLoad);
|
|
766
|
+
expect(backlog.calls().every(endsAt(NOW))).toBe(true);
|
|
767
|
+
expectNothingPainted(overview);
|
|
768
|
+
|
|
769
|
+
await backlog.release(endsAt(NOW));
|
|
770
|
+
|
|
771
|
+
expectPainted(overview, "600", windowOf(at("11:00"), NOW));
|
|
772
|
+
expectRefreshSettled();
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
test("after a load that outlasted three ticks lands, the next tick loads the slid hour exactly once, and the tick after waits for it", async () => {
|
|
776
|
+
await mount(overview);
|
|
777
|
+
|
|
778
|
+
await advance(3 * AUTO_REFRESH_MS + 5_000);
|
|
779
|
+
expect(backlog.calls()).toHaveLength(perLoad);
|
|
780
|
+
await backlog.release(endsAt(NOW));
|
|
781
|
+
expect(tileValue(overview.countTile)).toBe("600");
|
|
782
|
+
|
|
783
|
+
// 12:02:00: one tick, one load, for the hour up to it.
|
|
784
|
+
await advance(AUTO_REFRESH_MS - 5_000);
|
|
785
|
+
expect(backlog.calls()).toHaveLength(perLoad);
|
|
786
|
+
expect(backlog.calls().every(endsAt(at("12:02")))).toBe(true);
|
|
787
|
+
|
|
788
|
+
// 12:02:30: still running, so this tick leaves it alone.
|
|
789
|
+
await advance(AUTO_REFRESH_MS);
|
|
790
|
+
expect(backlog.calls()).toHaveLength(perLoad);
|
|
791
|
+
|
|
792
|
+
await backlog.release(endsAt(at("12:02")));
|
|
793
|
+
expectPainted(overview, "580", windowOf(at("11:02"), at("12:02")));
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
test("each tick once the load has landed loads the slid hour", async () => {
|
|
797
|
+
await mountPainted(overview);
|
|
798
|
+
|
|
799
|
+
await advance(AUTO_REFRESH_MS);
|
|
800
|
+
|
|
801
|
+
expect(backlog.calls()).toHaveLength(perLoad);
|
|
802
|
+
expect(backlog.calls().every(endsAt(at("12:00:30")))).toBe(true);
|
|
803
|
+
await backlog.release(endsAt(at("12:00:30")));
|
|
804
|
+
expectPainted(overview, "590", windowOf(at("11:00:30"), at("12:00:30")));
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
test("a drag during a slow auto-refresh load wins when its answer lands first", async () => {
|
|
808
|
+
await mountPainted(overview);
|
|
809
|
+
|
|
810
|
+
await advance(AUTO_REFRESH_MS);
|
|
811
|
+
await dragAcross(zoomCharts()[0]!, at("11:20"), at("11:30"));
|
|
812
|
+
expect(backlog.calls()).toHaveLength(2 * perLoad);
|
|
813
|
+
|
|
814
|
+
await backlog.release(endsAt(at("11:30")));
|
|
815
|
+
await backlog.release(endsAt(at("12:00:30")));
|
|
816
|
+
|
|
817
|
+
expectPainted(overview, "110", windowOf(at("11:20"), at("11:30")));
|
|
818
|
+
expect(pickerLabel()).toBe(customRangeLabel(at("11:20"), at("11:30")));
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
test("a drag during a slow auto-refresh load wins when the older answer lands first", async () => {
|
|
822
|
+
await mountPainted(overview);
|
|
823
|
+
|
|
824
|
+
await advance(AUTO_REFRESH_MS);
|
|
825
|
+
await dragAcross(zoomCharts()[1]!, at("11:20"), at("11:30"));
|
|
826
|
+
|
|
827
|
+
await backlog.release(endsAt(at("12:00:30")));
|
|
828
|
+
// The replaced load is dropped: the tiles still wait for the zoom's.
|
|
829
|
+
expectLoading(overview);
|
|
830
|
+
|
|
831
|
+
await backlog.release(endsAt(at("11:30")));
|
|
832
|
+
expectPainted(overview, "110", windowOf(at("11:20"), at("11:30")));
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
test("the replaced load landing does not let the next tick replace the zoom's load, which still lands", async () => {
|
|
836
|
+
await mountPainted(overview);
|
|
837
|
+
|
|
838
|
+
await advance(AUTO_REFRESH_MS);
|
|
839
|
+
await dragAcross(zoomCharts()[0]!, at("11:20"), at("11:30"));
|
|
840
|
+
await advance(5_000);
|
|
841
|
+
await backlog.release(endsAt(at("12:00:30")));
|
|
842
|
+
|
|
843
|
+
// 12:01:00: the zoom's load is still running; the tick leaves it.
|
|
844
|
+
await advance(AUTO_REFRESH_MS - 5_000);
|
|
845
|
+
expect(backlog.calls()).toHaveLength(perLoad);
|
|
846
|
+
expect(backlog.calls().every(endsAt(at("11:30")))).toBe(true);
|
|
847
|
+
|
|
848
|
+
await backlog.release(endsAt(at("11:30")));
|
|
849
|
+
expectPainted(overview, "110", windowOf(at("11:20"), at("11:30")));
|
|
850
|
+
expect(resetZoomButtons()).toHaveLength(1);
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
test("a double-click during the zoom's slow load puts the hour back, and that load wins", async () => {
|
|
854
|
+
await mountPainted(overview);
|
|
855
|
+
|
|
856
|
+
await dragAcross(zoomCharts()[0]!, at("11:20"), at("11:30"));
|
|
857
|
+
await advance(AUTO_REFRESH_MS);
|
|
858
|
+
// The tick left the zoom's load alone.
|
|
859
|
+
expect(backlog.calls()).toHaveLength(perLoad);
|
|
860
|
+
|
|
861
|
+
await doubleClick(zoomCharts()[1]!);
|
|
862
|
+
await backlog.release(endsAt(at("12:00:30")));
|
|
863
|
+
await backlog.release(endsAt(at("11:30")));
|
|
864
|
+
|
|
865
|
+
expectPainted(overview, "590", windowOf(at("11:00:30"), at("12:00:30")));
|
|
866
|
+
expect(pickerLabel()).toBe(PAST_HOUR);
|
|
867
|
+
expect(resetZoomButtons()).toHaveLength(0);
|
|
868
|
+
});
|
|
869
|
+
|
|
870
|
+
test("the picker during a slow auto-refresh load wins over it", async () => {
|
|
871
|
+
await mountPainted(overview);
|
|
872
|
+
|
|
873
|
+
await advance(AUTO_REFRESH_MS);
|
|
874
|
+
await advance(2_000);
|
|
875
|
+
await pickPreset(PAST_DAY);
|
|
876
|
+
expect(backlog.calls()).toHaveLength(2 * perLoad);
|
|
877
|
+
|
|
878
|
+
await backlog.release(endsAt(at("12:00:32")));
|
|
879
|
+
await backlog.release(endsAt(at("12:00:30")));
|
|
880
|
+
|
|
881
|
+
expectPainted(
|
|
882
|
+
overview,
|
|
883
|
+
"600",
|
|
884
|
+
windowOf(dayBefore(at("12:00:32")), at("12:00:32")),
|
|
885
|
+
);
|
|
886
|
+
expect(pickerLabel()).toBe(PAST_DAY);
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
test("a pick during the first slow load wins over it, and the next tick leaves the pick's load alone", async () => {
|
|
890
|
+
await mount(overview);
|
|
891
|
+
|
|
892
|
+
await advance(10_000);
|
|
893
|
+
await pickPreset(PAST_DAY);
|
|
894
|
+
expect(backlog.calls()).toHaveLength(2 * perLoad);
|
|
895
|
+
|
|
896
|
+
// 12:00:30: the pick's load is running; the tick leaves it.
|
|
897
|
+
await advance(20_000);
|
|
898
|
+
expect(backlog.calls()).toHaveLength(2 * perLoad);
|
|
899
|
+
|
|
900
|
+
await backlog.release(endsAt(at("12:00:10")));
|
|
901
|
+
await backlog.release(endsAt(NOW));
|
|
902
|
+
|
|
903
|
+
expectPainted(
|
|
904
|
+
overview,
|
|
905
|
+
"600",
|
|
906
|
+
windowOf(dayBefore(at("12:00:10")), at("12:00:10")),
|
|
907
|
+
);
|
|
908
|
+
expect(pickerLabel()).toBe(PAST_DAY);
|
|
909
|
+
});
|
|
910
|
+
|
|
911
|
+
test("Refresh during a slow load replaces it at once, and the newer answer wins", async () => {
|
|
912
|
+
await mount(overview);
|
|
913
|
+
await advance(10_000);
|
|
914
|
+
|
|
915
|
+
fireEvent.click(refreshButton());
|
|
916
|
+
await flush();
|
|
917
|
+
expect(backlog.calls()).toHaveLength(2 * perLoad);
|
|
918
|
+
// The model reloaded at once; Refresh is not left spinning.
|
|
919
|
+
expectRefreshSettled();
|
|
920
|
+
|
|
921
|
+
// The first load, for the hour up to 12:00:00, is not drawn...
|
|
922
|
+
await backlog.release(endsAt(NOW));
|
|
923
|
+
expectNothingPainted(overview);
|
|
924
|
+
|
|
925
|
+
// ...the Refresh's, for the hour up to 12:00:10, is.
|
|
926
|
+
await backlog.release(endsAt(at("12:00:10")));
|
|
927
|
+
expectPainted(overview, "590", windowOf(at("11:00:10"), at("12:00:10")));
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
test("Refresh pressed shortly before a tick: the tick leaves the Refresh's load alone, and it lands", async () => {
|
|
931
|
+
await mountPainted(overview);
|
|
932
|
+
|
|
933
|
+
await advance(25_000);
|
|
934
|
+
fireEvent.click(refreshButton());
|
|
935
|
+
await flush();
|
|
936
|
+
expect(backlog.calls()).toHaveLength(perLoad);
|
|
937
|
+
expect(backlog.calls().every(endsAt(at("12:00:25")))).toBe(true);
|
|
938
|
+
|
|
939
|
+
// 12:00:30: the tick reloads the model and leaves the telemetry alone...
|
|
940
|
+
await advance(5_000);
|
|
941
|
+
expect(backlog.calls()).toHaveLength(perLoad);
|
|
942
|
+
|
|
943
|
+
// ...whose answer lands at 12:00:35 and is drawn.
|
|
944
|
+
await advance(5_000);
|
|
945
|
+
await backlog.release(endsAt(at("12:00:25")));
|
|
946
|
+
expectPainted(overview, "590", windowOf(at("11:00:25"), at("12:00:25")));
|
|
947
|
+
expectRefreshSettled();
|
|
948
|
+
});
|
|
949
|
+
|
|
950
|
+
test("a drag shortly before a tick: the tick leaves the zoom's load alone, and it lands", async () => {
|
|
951
|
+
await mountPainted(overview);
|
|
952
|
+
|
|
953
|
+
await advance(25_000);
|
|
954
|
+
await dragAcross(zoomCharts()[0]!, at("11:20"), at("11:30"));
|
|
955
|
+
expect(backlog.calls()).toHaveLength(perLoad);
|
|
956
|
+
|
|
957
|
+
// 12:00:30: the tick would load the zoomed window again; it waits.
|
|
958
|
+
await advance(5_000);
|
|
959
|
+
expect(backlog.calls()).toHaveLength(perLoad);
|
|
960
|
+
|
|
961
|
+
await advance(5_000);
|
|
962
|
+
await backlog.release(endsAt(at("11:30")));
|
|
963
|
+
expectPainted(overview, "110", windowOf(at("11:20"), at("11:30")));
|
|
964
|
+
expect(pickerLabel()).toBe(customRangeLabel(at("11:20"), at("11:30")));
|
|
965
|
+
});
|
|
966
|
+
|
|
967
|
+
test("on a remembered 10-second interval, a load that outlasts two ticks still lands, and the ticks go on after it", async () => {
|
|
968
|
+
window.localStorage.setItem(overview.storageKey, "10s");
|
|
969
|
+
await mount(overview);
|
|
970
|
+
|
|
971
|
+
// The ticks at 12:00:10 and 12:00:20 leave the first load alone.
|
|
972
|
+
await advance(25_000);
|
|
973
|
+
expect(backlog.calls()).toHaveLength(perLoad);
|
|
974
|
+
|
|
975
|
+
await backlog.release(endsAt(NOW));
|
|
976
|
+
expectPainted(overview, "600", windowOf(at("11:00"), NOW));
|
|
977
|
+
|
|
978
|
+
// 12:00:30: nothing is running, so the tick loads the slid hour.
|
|
979
|
+
await advance(5_000);
|
|
980
|
+
expect(backlog.calls()).toHaveLength(perLoad);
|
|
981
|
+
expect(backlog.calls().every(endsAt(at("12:00:30")))).toBe(true);
|
|
982
|
+
});
|
|
983
|
+
|
|
984
|
+
test("a failed load settles too: the charts leave their skeletons, and the next tick loads again", async () => {
|
|
985
|
+
await mount(overview);
|
|
986
|
+
await advance(5_000);
|
|
987
|
+
|
|
988
|
+
await backlog.fail(endsAt(NOW), new Error("ClickHouse is down"));
|
|
989
|
+
|
|
990
|
+
expect(tileOf(overview.countTile)).toHaveAttribute("aria-busy", "false");
|
|
991
|
+
expect(tileValue(overview.countTile)).toBe("—");
|
|
992
|
+
expect(screen.getAllByText("No data in this time range")).toHaveLength(4);
|
|
993
|
+
|
|
994
|
+
// 12:00:30: nothing is running any more, so the tick loads again.
|
|
995
|
+
await advance(AUTO_REFRESH_MS - 5_000);
|
|
996
|
+
expect(backlog.calls()).toHaveLength(perLoad);
|
|
997
|
+
expect(backlog.calls().every(endsAt(at("12:00:30")))).toBe(true);
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
test("with auto-refresh off, a slow load lands as it always did", async () => {
|
|
1001
|
+
window.localStorage.setItem(overview.storageKey, "off");
|
|
1002
|
+
await mount(overview);
|
|
1003
|
+
const loadsBefore: number = modelLoads();
|
|
1004
|
+
|
|
1005
|
+
await advance(5 * AUTO_REFRESH_MS);
|
|
1006
|
+
expect(modelLoads()).toBe(loadsBefore);
|
|
1007
|
+
expect(backlog.calls()).toHaveLength(perLoad);
|
|
1008
|
+
|
|
1009
|
+
await backlog.release(endsAt(NOW));
|
|
1010
|
+
expectPainted(overview, "600", windowOf(at("11:00"), NOW));
|
|
1011
|
+
});
|
|
1012
|
+
},
|
|
1013
|
+
);
|
|
1014
|
+
|
|
1015
|
+
/*
|
|
1016
|
+
* One slow query holds the whole load: the page draws a load only once
|
|
1017
|
+
* every query in it has answered, so each must be left to run.
|
|
1018
|
+
*/
|
|
1019
|
+
const SLOW_QUERIES: Array<[string, Fetcher, OverviewCase]> = OVERVIEWS.flatMap(
|
|
1020
|
+
([name, overview]: [string, OverviewCase]): Array<
|
|
1021
|
+
[string, Fetcher, OverviewCase]
|
|
1022
|
+
> => {
|
|
1023
|
+
return overview.fetchers.map(
|
|
1024
|
+
(fetcher: Fetcher): [string, Fetcher, OverviewCase] => {
|
|
1025
|
+
return [name, fetcher, overview];
|
|
1026
|
+
},
|
|
1027
|
+
);
|
|
1028
|
+
},
|
|
1029
|
+
);
|
|
1030
|
+
|
|
1031
|
+
describe.each(SLOW_QUERIES)(
|
|
1032
|
+
"%s, only its %s slower than the auto-refresh",
|
|
1033
|
+
(_name: string, fetcher: Fetcher, overview: OverviewCase) => {
|
|
1034
|
+
test("the ticks leave the load alone, and it lands", async () => {
|
|
1035
|
+
slowFetchers = [fetcher];
|
|
1036
|
+
await mount(overview);
|
|
1037
|
+
|
|
1038
|
+
expect(backlog.calls()).toEqual([
|
|
1039
|
+
{ fetcher: fetcher, start: at("11:00"), end: NOW },
|
|
1040
|
+
]);
|
|
1041
|
+
|
|
1042
|
+
await advance(AUTO_REFRESH_MS);
|
|
1043
|
+
await advance(AUTO_REFRESH_MS);
|
|
1044
|
+
expect(backlog.calls()).toHaveLength(1);
|
|
1045
|
+
expectNothingPainted(overview);
|
|
1046
|
+
|
|
1047
|
+
await backlog.release(endsAt(NOW));
|
|
1048
|
+
expectPainted(overview, "600", windowOf(at("11:00"), NOW));
|
|
1049
|
+
});
|
|
1050
|
+
},
|
|
1051
|
+
);
|
|
1052
|
+
|
|
1053
|
+
describe("Service overview: what scopes its metrics", () => {
|
|
1054
|
+
test("a reload that finds another language replaces the running load at once, and the new runtime charts win", async () => {
|
|
1055
|
+
await mount(SERVICE_OVERVIEW);
|
|
1056
|
+
expect(backlog.calls()).toHaveLength(3);
|
|
1057
|
+
|
|
1058
|
+
serviceLanguage = "go";
|
|
1059
|
+
await advance(AUTO_REFRESH_MS);
|
|
1060
|
+
|
|
1061
|
+
// The Node.js load is dropped for a Go one, over the hour up to now.
|
|
1062
|
+
expect(backlog.calls()).toHaveLength(6);
|
|
1063
|
+
expect(
|
|
1064
|
+
requestsTo("runtime metrics").map((request: TelemetryRequest) => {
|
|
1065
|
+
return request.name;
|
|
1066
|
+
}),
|
|
1067
|
+
).toEqual(["nodejs.eventloop.utilization", "go.goroutine.count"]);
|
|
1068
|
+
|
|
1069
|
+
await backlog.release(endsAt(NOW));
|
|
1070
|
+
expectLoading(SERVICE_OVERVIEW);
|
|
1071
|
+
|
|
1072
|
+
await backlog.release(endsAt(at("12:00:30")));
|
|
1073
|
+
expectPainted(
|
|
1074
|
+
SERVICE_OVERVIEW,
|
|
1075
|
+
"590",
|
|
1076
|
+
windowOf(at("11:00:30"), at("12:00:30")),
|
|
1077
|
+
);
|
|
1078
|
+
expect(screen.getAllByText("Goroutines").length).toBeGreaterThan(0);
|
|
1079
|
+
expect(screen.queryByText("Event loop utilization")).toBeNull();
|
|
1080
|
+
});
|
|
1081
|
+
});
|
|
1082
|
+
|
|
1083
|
+
describe("Database overview: what scopes its telemetry", () => {
|
|
1084
|
+
test("a reload that brings a new endpoint replaces the running load at once, scoped by it too", async () => {
|
|
1085
|
+
await mount(DATABASE_OVERVIEW);
|
|
1086
|
+
expect(backlog.calls()).toHaveLength(5);
|
|
1087
|
+
const keysBefore: number = requestsTo("query metrics")[0]!.keys!.length;
|
|
1088
|
+
|
|
1089
|
+
databaseEndpoints = [ORDERS_DB, ORDERS_DB_REPLICA];
|
|
1090
|
+
await advance(AUTO_REFRESH_MS);
|
|
1091
|
+
|
|
1092
|
+
expect(backlog.calls()).toHaveLength(10);
|
|
1093
|
+
const queries: Array<TelemetryRequest> = requestsTo("query metrics");
|
|
1094
|
+
expect(queries).toHaveLength(2);
|
|
1095
|
+
expect(queries[1]!.keys).toHaveLength(keysBefore + 1);
|
|
1096
|
+
|
|
1097
|
+
await backlog.release(endsAt(NOW));
|
|
1098
|
+
expectLoading(DATABASE_OVERVIEW);
|
|
1099
|
+
|
|
1100
|
+
await backlog.release(endsAt(at("12:00:30")));
|
|
1101
|
+
expectPainted(
|
|
1102
|
+
DATABASE_OVERVIEW,
|
|
1103
|
+
"590",
|
|
1104
|
+
windowOf(at("11:00:30"), at("12:00:30")),
|
|
1105
|
+
);
|
|
1106
|
+
});
|
|
1107
|
+
|
|
1108
|
+
test("a reload that only re-stamps when its pod was last seen leaves the running load alone", async () => {
|
|
1109
|
+
await mount(DATABASE_OVERVIEW);
|
|
1110
|
+
expect(backlog.calls()).toHaveLength(5);
|
|
1111
|
+
|
|
1112
|
+
// Discovery saw the same pod again: a new time, the same member key.
|
|
1113
|
+
databaseMembers = { [ORDERS_DB_POD]: "2026-09-28T12:00:20.000Z" };
|
|
1114
|
+
await advance(AUTO_REFRESH_MS);
|
|
1115
|
+
|
|
1116
|
+
expect(backlog.calls()).toHaveLength(5);
|
|
1117
|
+
expect(backlog.calls().every(endsAt(NOW))).toBe(true);
|
|
1118
|
+
|
|
1119
|
+
await backlog.release(endsAt(NOW));
|
|
1120
|
+
expectPainted(DATABASE_OVERVIEW, "600", windowOf(at("11:00"), NOW));
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
test("a reload that finds another pod replaces the running load at once, and its pod charts cover both pods", async () => {
|
|
1124
|
+
await mount(DATABASE_OVERVIEW);
|
|
1125
|
+
expect(requestsTo("pod CPU")[0]!.keys).toEqual([ORDERS_DB_POD]);
|
|
1126
|
+
|
|
1127
|
+
databaseMembers = {
|
|
1128
|
+
[ORDERS_DB_POD]: "2026-09-28T12:00:20.000Z",
|
|
1129
|
+
[ORDERS_DB_SECOND_POD]: "2026-09-28T12:00:20.000Z",
|
|
1130
|
+
};
|
|
1131
|
+
await advance(AUTO_REFRESH_MS);
|
|
1132
|
+
|
|
1133
|
+
expect(backlog.calls()).toHaveLength(10);
|
|
1134
|
+
expect([...requestsTo("pod CPU")[1]!.keys!].sort()).toEqual(
|
|
1135
|
+
[ORDERS_DB_POD, ORDERS_DB_SECOND_POD].sort(),
|
|
1136
|
+
);
|
|
1137
|
+
|
|
1138
|
+
await backlog.release(endsAt(NOW));
|
|
1139
|
+
expectLoading(DATABASE_OVERVIEW);
|
|
1140
|
+
|
|
1141
|
+
await backlog.release(endsAt(at("12:00:30")));
|
|
1142
|
+
expectPainted(
|
|
1143
|
+
DATABASE_OVERVIEW,
|
|
1144
|
+
"590",
|
|
1145
|
+
windowOf(at("11:00:30"), at("12:00:30")),
|
|
1146
|
+
);
|
|
1147
|
+
});
|
|
1148
|
+
|
|
1149
|
+
test("a database with no scope yet never queries its telemetry, tick or Refresh", async () => {
|
|
1150
|
+
databaseProjectId = null;
|
|
1151
|
+
mockCurrentProjectId = null;
|
|
1152
|
+
await mount(DATABASE_OVERVIEW);
|
|
1153
|
+
|
|
1154
|
+
await advance(AUTO_REFRESH_MS);
|
|
1155
|
+
fireEvent.click(refreshButton());
|
|
1156
|
+
await flush();
|
|
1157
|
+
await advance(AUTO_REFRESH_MS);
|
|
1158
|
+
|
|
1159
|
+
expect(telemetryMock).not.toHaveBeenCalled();
|
|
1160
|
+
expect(tileOf("Queries")).toHaveAttribute("aria-busy", "false");
|
|
1161
|
+
expect(tileValue("Queries")).toBe("—");
|
|
1162
|
+
expect(
|
|
1163
|
+
screen.getByTestId("database-server-unscoped-banner"),
|
|
1164
|
+
).toHaveAttribute("data-variant", "unscoped");
|
|
1165
|
+
});
|
|
1166
|
+
});
|