@oneuptime/common 11.3.26 → 11.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/AIRun.ts +50 -0
- package/Models/DatabaseModels/IncidentEpisodeRoleMember.ts +1 -1
- package/Models/DatabaseModels/IncidentMember.ts +1 -1
- package/Models/DatabaseModels/IncomingCallPolicy.ts +13 -0
- package/Models/DatabaseModels/Index.ts +3 -0
- package/Models/DatabaseModels/MigrationFailure.ts +170 -0
- package/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.ts +51 -2
- package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +44 -0
- package/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.ts +6 -0
- package/Models/DatabaseModels/OnCallDutyPolicyScheduleLayerUser.ts +6 -0
- package/Models/DatabaseModels/OnCallDutyPolicyTimeLog.ts +1 -1
- package/Models/DatabaseModels/OnCallDutyPolicyUserOverride.ts +2 -2
- package/Models/DatabaseModels/Project.ts +60 -0
- package/Server/API/AIInvestigationAPI.ts +148 -0
- package/Server/API/AlertAPI.ts +1 -1
- package/Server/API/IncidentAPI.ts +2 -2
- package/Server/API/IncidentEpisodeAPI.ts +1 -1
- package/Server/API/LlmProviderAPI.ts +1 -0
- package/Server/API/ScheduledMaintenanceAPI.ts +1 -1
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783470000000-AddIncomingCallPolicyRoutingPhoneNumberUnique.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783510935686-FixNotNullForeignKeysOnDelete.ts +81 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783515836148-MigrationName.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783523076215-AddMigrationFailureTable.ts +35 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783598145111-AddEnableAutomaticIncidentInvestigationToProject.ts +19 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783615884106-AddInvestigationSubjectToAIRun.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783632447975-AddEnableAutomaticAlertInvestigationToProject.ts +19 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +14 -0
- package/Server/Infrastructure/PostgresDatabase.ts +11 -0
- package/Server/Services/AIService.ts +74 -2
- package/Server/Services/AlertService.ts +25 -0
- package/Server/Services/AnalyticsDatabaseService.ts +10 -1
- package/Server/Services/DatabaseService.ts +10 -1
- package/Server/Services/IncidentService.ts +26 -1
- package/Server/Services/IncidentStateTimelineService.ts +19 -0
- package/Server/Services/IncomingCallPolicyEscalationRuleService.ts +75 -2
- package/Server/Services/IncomingCallPolicyService.ts +42 -1
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/MigrationFailureService.ts +9 -0
- package/Server/Services/MonitorProbeService.ts +33 -7
- package/Server/Services/MonitorService.ts +19 -7
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +65 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +185 -41
- package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +23 -15
- package/Server/Services/OnCallDutyPolicyExecutionLogService.ts +39 -0
- package/Server/Services/OnCallDutyPolicyScheduleLayerService.ts +148 -0
- package/Server/Services/OnCallDutyPolicyScheduleLayerUserService.ts +11 -2
- package/Server/Services/OnCallDutyPolicyScheduleService.ts +502 -26
- package/Server/Services/OnCallDutyPolicyTimeLogService.ts +49 -7
- package/Server/Services/OnCallDutyPolicyUserOverrideService.ts +218 -4
- package/Server/Services/ProjectCallSMSConfigService.ts +82 -1
- package/Server/Services/TeamMemberService.ts +6 -0
- package/Server/Services/UserNotificationRuleService.ts +13 -42
- package/Server/Services/UserOnCallLogService.ts +90 -0
- package/Server/Services/UserOnCallLogTimelineService.ts +43 -1
- package/Server/Types/Database/QueryUtil.ts +57 -0
- package/Server/Utils/AI/AlertAIContextBuilder.ts +6 -1
- package/Server/Utils/AI/Chat/ChatAgentRunner.ts +14 -2
- package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +119 -11
- package/Server/Utils/AI/IncidentAIContextBuilder.ts +18 -3
- package/Server/Utils/AI/IncidentEpisodeAIContextBuilder.ts +6 -1
- package/Server/Utils/AI/ScheduledMaintenanceAIContextBuilder.ts +12 -2
- package/Server/Utils/AI/Sentinel/AlertInvestigationRunner.ts +141 -0
- package/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.ts +178 -0
- package/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.ts +105 -0
- package/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.ts +405 -0
- package/Server/Utils/AI/Sentinel/SentinelMemory.ts +167 -0
- package/Server/Utils/AI/Toolbox/IncidentTools.ts +183 -1
- package/Server/Utils/AI/Toolbox/Index.ts +15 -1
- package/Server/Utils/AI/Toolbox/RecentChangesTools.ts +208 -0
- package/Server/Utils/AI/Toolbox/SentinelActionTools.ts +577 -0
- package/Server/Utils/AI/Toolbox/Serializer.ts +42 -0
- package/Server/Utils/Database/MigrationFailureLog.ts +240 -0
- package/Server/Utils/IncomingCallPhoneNumber.ts +41 -0
- package/Server/Utils/LLM/LLMService.ts +110 -11
- package/Server/Utils/Telemetry/IoTSnapshotScan.ts +12 -9
- package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +12 -9
- package/Tests/Server/API/BaseAPI.test.ts +1 -0
- package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +0 -8
- package/Tests/Server/Services/OnCallDutyPolicyScheduleLayerUserReorder.test.ts +233 -0
- package/Tests/Server/Services/OnCallDutyPolicyTimeLogServiceScoping.test.ts +242 -0
- package/Tests/Server/Services/OnCallDutyPolicyUserOverrideEdit.test.ts +198 -0
- package/Tests/Server/Services/UserOnCallLogClaimNotificationRule.test.ts +495 -0
- package/Tests/Server/Types/Database/QueryUtil.test.ts +74 -0
- package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +78 -0
- package/Tests/Server/Utils/AI/ToolResultSerializer.test.ts +68 -0
- package/Tests/Types/DateExhaustiveTimezone.test.ts +700 -0
- package/Tests/Types/DateTimezoneWallClock.test.ts +99 -0
- package/Tests/Types/Events/Recurring.test.ts +22 -9
- package/Tests/Types/OnCallDutyPolicy/LayerUtilAuditFixes.test.ts +291 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilAuditFixesRound2.test.ts +398 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilDSTWeekendGap.test.ts +114 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilDSTWeeklyDayShift.test.ts +76 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilDailyMutation.test.ts +128 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilDailyProbe.test.ts +203 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilDiffFuzz.test.ts +322 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilEdgeCases.test.ts +280 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveCurrentUser.test.ts +863 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveDaily.test.ts +900 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveMultiLayer.test.ts +1104 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveRotation.test.ts +957 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveWeekly.test.ts +1095 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilInvariants.test.ts +232 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeAudit.test.ts +227 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilMonthYearAudit.test.ts +255 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilMultiLayerAudit.test.ts +388 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilMultiLayerEdge.test.ts +300 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilOvernightDSTAudit.test.ts +158 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilRestrictedGapFix.test.ts +253 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilRestrictedGapRepro.test.ts +157 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilRotationFixes.test.ts +414 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilTimezone.test.ts +243 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilWeekendGapRepro.test.ts +112 -0
- package/Tests/Types/OnCallDutyPolicy/OverrideLensAudit.test.ts +117 -0
- package/Tests/Types/OnCallDutyPolicy/RestrictionTimes.test.ts +8 -6
- package/Tests/Types/OnCallDutyPolicy/RestrictionTimesDefaultDayAudit.test.ts +112 -0
- package/Tests/Types/OnCallDutyPolicy/RestrictionTimesExhaustive.test.ts +821 -0
- package/Tests/Types/OnCallDutyPolicy/RestrictionTimesMutationInvariant.test.ts +886 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleShiftUtil.test.ts +260 -0
- package/Tests/Types/OnCallDutyPolicy/UserOverrideUtil.test.ts +255 -0
- package/Tests/Types/OnCallDutyPolicy/UserOverrideUtilExhaustive.test.ts +1126 -0
- package/Types/Date.ts +246 -15
- package/Types/OnCallDutyPolicy/Layer.ts +790 -260
- package/Types/OnCallDutyPolicy/RestrictionTimes.ts +39 -13
- package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +257 -0
- package/Types/OnCallDutyPolicy/UserOverrideUtil.ts +21 -5
- package/UI/Components/Calendar/Calendar.tsx +8 -2
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +5 -5
- package/UI/Components/Events/RecurringFieldElement.tsx +16 -0
- package/UI/Components/ModelFormModal/ModelFormModal.tsx +8 -0
- package/UI/Components/RadioButtons/BasicRadioButtons.tsx +1 -1
- package/build/dist/Models/DatabaseModels/AIRun.js +52 -0
- package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeRoleMember.js +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeRoleMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentMember.js +1 -1
- package/build/dist/Models/DatabaseModels/IncidentMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncomingCallPolicy.js +10 -0
- package/build/dist/Models/DatabaseModels/IncomingCallPolicy.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MigrationFailure.js +196 -0
- package/build/dist/Models/DatabaseModels/MigrationFailure.js.map +1 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js +52 -2
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +45 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.js +6 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayerUser.js +6 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayerUser.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyTimeLog.js +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyTimeLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyUserOverride.js +2 -2
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyUserOverride.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +62 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +107 -0
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -0
- package/build/dist/Server/API/AlertAPI.js +1 -1
- package/build/dist/Server/API/IncidentAPI.js +2 -2
- package/build/dist/Server/API/IncidentEpisodeAPI.js +1 -1
- package/build/dist/Server/API/LlmProviderAPI.js +1 -0
- package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
- package/build/dist/Server/API/ScheduledMaintenanceAPI.js +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783470000000-AddIncomingCallPolicyRoutingPhoneNumberUnique.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783470000000-AddIncomingCallPolicyRoutingPhoneNumberUnique.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783510935686-FixNotNullForeignKeysOnDelete.js +38 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783510935686-FixNotNullForeignKeysOnDelete.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783515836148-MigrationName.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783515836148-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783523076215-AddMigrationFailureTable.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783523076215-AddMigrationFailureTable.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783598145111-AddEnableAutomaticIncidentInvestigationToProject.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783598145111-AddEnableAutomaticIncidentInvestigationToProject.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783615884106-AddInvestigationSubjectToAIRun.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783615884106-AddInvestigationSubjectToAIRun.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783632447975-AddEnableAutomaticAlertInvestigationToProject.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783632447975-AddEnableAutomaticAlertInvestigationToProject.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/PostgresDatabase.js +9 -0
- package/build/dist/Server/Infrastructure/PostgresDatabase.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +53 -2
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +24 -0
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +10 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +10 -1
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +25 -1
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncomingCallPolicyEscalationRuleService.js +55 -2
- package/build/dist/Server/Services/IncomingCallPolicyEscalationRuleService.js.map +1 -1
- package/build/dist/Server/Services/IncomingCallPolicyService.js +40 -0
- package/build/dist/Server/Services/IncomingCallPolicyService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/MigrationFailureService.js +9 -0
- package/build/dist/Server/Services/MigrationFailureService.js.map +1 -0
- package/build/dist/Server/Services/MonitorProbeService.js +33 -7
- package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +12 -5
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +81 -22
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +134 -31
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +21 -13
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js +36 -0
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js +121 -0
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js +11 -2
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +409 -36
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyTimeLogService.js +52 -8
- package/build/dist/Server/Services/OnCallDutyPolicyTimeLogService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js +170 -3
- package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js.map +1 -1
- package/build/dist/Server/Services/ProjectCallSMSConfigService.js +72 -0
- package/build/dist/Server/Services/ProjectCallSMSConfigService.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +6 -0
- package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +12 -30
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserOnCallLogService.js +75 -0
- package/build/dist/Server/Services/UserOnCallLogService.js.map +1 -1
- package/build/dist/Server/Services/UserOnCallLogTimelineService.js +31 -1
- package/build/dist/Server/Services/UserOnCallLogTimelineService.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryUtil.js +45 -0
- package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
- package/build/dist/Server/Utils/AI/AlertAIContextBuilder.js +6 -1
- package/build/dist/Server/Utils/AI/AlertAIContextBuilder.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +13 -2
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +59 -11
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
- package/build/dist/Server/Utils/AI/IncidentAIContextBuilder.js +18 -3
- package/build/dist/Server/Utils/AI/IncidentAIContextBuilder.js.map +1 -1
- package/build/dist/Server/Utils/AI/IncidentEpisodeAIContextBuilder.js +6 -1
- package/build/dist/Server/Utils/AI/IncidentEpisodeAIContextBuilder.js.map +1 -1
- package/build/dist/Server/Utils/AI/ScheduledMaintenanceAIContextBuilder.js +12 -2
- package/build/dist/Server/Utils/AI/ScheduledMaintenanceAIContextBuilder.js.map +1 -1
- package/build/dist/Server/Utils/AI/Sentinel/AlertInvestigationRunner.js +119 -0
- package/build/dist/Server/Utils/AI/Sentinel/AlertInvestigationRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.js +147 -0
- package/build/dist/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.js +93 -0
- package/build/dist/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.js +311 -0
- package/build/dist/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.js.map +1 -0
- package/build/dist/Server/Utils/AI/Sentinel/SentinelMemory.js +143 -0
- package/build/dist/Server/Utils/AI/Sentinel/SentinelMemory.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js +152 -1
- package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/Index.js +10 -1
- package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/RecentChangesTools.js +165 -0
- package/build/dist/Server/Utils/AI/Toolbox/RecentChangesTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/SentinelActionTools.js +451 -0
- package/build/dist/Server/Utils/AI/Toolbox/SentinelActionTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/Serializer.js +40 -0
- package/build/dist/Server/Utils/AI/Toolbox/Serializer.js.map +1 -1
- package/build/dist/Server/Utils/Database/MigrationFailureLog.js +174 -0
- package/build/dist/Server/Utils/Database/MigrationFailureLog.js.map +1 -0
- package/build/dist/Server/Utils/IncomingCallPhoneNumber.js +30 -0
- package/build/dist/Server/Utils/IncomingCallPhoneNumber.js.map +1 -0
- package/build/dist/Server/Utils/LLM/LLMService.js +81 -5
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/IoTSnapshotScan.js +11 -8
- package/build/dist/Server/Utils/Telemetry/IoTSnapshotScan.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +11 -8
- package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -1
- package/build/dist/Types/Date.js +185 -15
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/Layer.js +602 -171
- package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/RestrictionTimes.js +27 -13
- package/build/dist/Types/OnCallDutyPolicy/RestrictionTimes.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +159 -0
- package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -0
- package/build/dist/Types/OnCallDutyPolicy/UserOverrideUtil.js +20 -5
- package/build/dist/Types/OnCallDutyPolicy/UserOverrideUtil.js.map +1 -1
- package/build/dist/UI/Components/Calendar/Calendar.js +2 -2
- package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +2 -2
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Events/RecurringFieldElement.js +13 -0
- package/build/dist/UI/Components/Events/RecurringFieldElement.js.map +1 -1
- package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
- package/build/dist/UI/Components/RadioButtons/BasicRadioButtons.js +1 -1
- package/build/dist/UI/Components/RadioButtons/BasicRadioButtons.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the timezone wall-clock helpers backing the on-call restriction
|
|
3
|
+
* time-picker fix (audit F1) and the summary formatting fix (audit F10):
|
|
4
|
+
* - getInstantFromLocalWallClockInTimezone
|
|
5
|
+
* - getLocalDateFromWallClockInTimezone (inverse)
|
|
6
|
+
* - getHourAndMinuteInTimezoneString
|
|
7
|
+
*
|
|
8
|
+
* Assertions are expressed in wall-clock via moment-timezone so they hold
|
|
9
|
+
* regardless of the process TZ the suite runs under.
|
|
10
|
+
*/
|
|
11
|
+
import OneUptimeDate from "../../Types/Date";
|
|
12
|
+
import moment from "moment-timezone";
|
|
13
|
+
|
|
14
|
+
const NY: string = "America/New_York";
|
|
15
|
+
const KOLKATA: string = "Asia/Kolkata";
|
|
16
|
+
|
|
17
|
+
describe("OneUptimeDate timezone wall-clock helpers", () => {
|
|
18
|
+
describe("getInstantFromLocalWallClockInTimezone", () => {
|
|
19
|
+
it("plants the local wall-clock components as the same wall-clock in the target zone", () => {
|
|
20
|
+
// A local Date reading 09:00 on 2025-01-06 in the process zone.
|
|
21
|
+
const local: Date = new Date(2025, 0, 6, 9, 0, 0);
|
|
22
|
+
const instant: Date =
|
|
23
|
+
OneUptimeDate.getInstantFromLocalWallClockInTimezone(local, NY);
|
|
24
|
+
// The resulting instant must read 09:00 in New York.
|
|
25
|
+
expect(moment.tz(instant, NY).format("HH:mm")).toBe("09:00");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("produces different absolute instants for the same wall-clock in different zones", () => {
|
|
29
|
+
const local: Date = new Date(2025, 5, 15, 9, 0, 0);
|
|
30
|
+
const inNY: Date = OneUptimeDate.getInstantFromLocalWallClockInTimezone(
|
|
31
|
+
local,
|
|
32
|
+
NY,
|
|
33
|
+
);
|
|
34
|
+
const inKolkata: Date =
|
|
35
|
+
OneUptimeDate.getInstantFromLocalWallClockInTimezone(local, KOLKATA);
|
|
36
|
+
// 09:00 NY and 09:00 Kolkata are different moments.
|
|
37
|
+
expect(inNY.getTime()).not.toBe(inKolkata.getTime());
|
|
38
|
+
expect(moment.tz(inNY, NY).format("HH:mm")).toBe("09:00");
|
|
39
|
+
expect(moment.tz(inKolkata, KOLKATA).format("HH:mm")).toBe("09:00");
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe("getLocalDateFromWallClockInTimezone", () => {
|
|
44
|
+
it("returns a local Date whose local wall-clock equals the target-zone wall-clock", () => {
|
|
45
|
+
// An instant that is 09:00 in New York.
|
|
46
|
+
const instant: Date = moment.tz("2025-01-06 09:00", NY).toDate();
|
|
47
|
+
const local: Date = OneUptimeDate.getLocalDateFromWallClockInTimezone(
|
|
48
|
+
instant,
|
|
49
|
+
NY,
|
|
50
|
+
);
|
|
51
|
+
// Its LOCAL hour/minute should read 09:00.
|
|
52
|
+
expect(local.getHours()).toBe(9);
|
|
53
|
+
expect(local.getMinutes()).toBe(0);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe("round-trip", () => {
|
|
58
|
+
it("local -> tz-instant -> local preserves the wall-clock (incl. across a DST week)", () => {
|
|
59
|
+
for (const wall of [
|
|
60
|
+
new Date(2025, 0, 6, 9, 0, 0), // winter
|
|
61
|
+
new Date(2025, 6, 6, 14, 30, 0), // summer
|
|
62
|
+
new Date(2026, 2, 8, 1, 30, 0), // US spring-forward day
|
|
63
|
+
]) {
|
|
64
|
+
const instant: Date =
|
|
65
|
+
OneUptimeDate.getInstantFromLocalWallClockInTimezone(wall, NY);
|
|
66
|
+
const back: Date = OneUptimeDate.getLocalDateFromWallClockInTimezone(
|
|
67
|
+
instant,
|
|
68
|
+
NY,
|
|
69
|
+
);
|
|
70
|
+
expect(back.getHours()).toBe(wall.getHours());
|
|
71
|
+
expect(back.getMinutes()).toBe(wall.getMinutes());
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe("getHourAndMinuteInTimezoneString", () => {
|
|
77
|
+
it("formats an instant in the given timezone (NY noon reads 09:00 in LA, differs from NY)", () => {
|
|
78
|
+
const nyNoon: Date = moment.tz("2025-01-06 12:00", NY).toDate();
|
|
79
|
+
const inNY: string = OneUptimeDate.getHourAndMinuteInTimezoneString(
|
|
80
|
+
nyNoon,
|
|
81
|
+
NY,
|
|
82
|
+
);
|
|
83
|
+
const inKolkata: string = OneUptimeDate.getHourAndMinuteInTimezoneString(
|
|
84
|
+
nyNoon,
|
|
85
|
+
KOLKATA,
|
|
86
|
+
);
|
|
87
|
+
expect(inNY).not.toBe(inKolkata);
|
|
88
|
+
// Kolkata is +5:30, so the minute component is :30.
|
|
89
|
+
expect(inKolkata).toContain("30");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("falls back to local wall-clock when no timezone is given", () => {
|
|
93
|
+
const d: Date = new Date(2025, 0, 6, 8, 15, 0);
|
|
94
|
+
expect(OneUptimeDate.getHourAndMinuteInTimezoneString(d, undefined)).toBe(
|
|
95
|
+
OneUptimeDate.getLocalHourAndMinuteFromDate(d),
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -273,15 +273,28 @@ describe("Recurring", () => {
|
|
|
273
273
|
});
|
|
274
274
|
|
|
275
275
|
test("leaves a start date equal to now unchanged (zero intervals to add)", () => {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
276
|
+
/*
|
|
277
|
+
* getNextDate() reads the clock internally via getCurrentDate(). Pin it so
|
|
278
|
+
* the start date and the internal "now" resolve to the exact same instant;
|
|
279
|
+
* otherwise a sub-millisecond gap between the two clock reads makes diff a
|
|
280
|
+
* tiny positive number and Math.ceil() rounds it up to a full extra day.
|
|
281
|
+
*/
|
|
282
|
+
const now: Date = OneUptimeDate.fromString("2023-01-01T00:00:00.000Z");
|
|
283
|
+
const getCurrentDateSpy: jest.SpyInstance = jest
|
|
284
|
+
.spyOn(OneUptimeDate, "getCurrentDate")
|
|
285
|
+
.mockReturnValue(now);
|
|
286
|
+
|
|
287
|
+
try {
|
|
288
|
+
const next: Date = Recurring.getNextDate(
|
|
289
|
+
now,
|
|
290
|
+
makeRecurring(EventInterval.Day, 1),
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
// The <= branch is entered, diff is 0, so Math.ceil(0) === 0 intervals are added.
|
|
294
|
+
expect(next.getTime()).toBe(now.getTime());
|
|
295
|
+
} finally {
|
|
296
|
+
getCurrentDateSpy.mockRestore();
|
|
297
|
+
}
|
|
285
298
|
});
|
|
286
299
|
|
|
287
300
|
test("throws BadDataException for an unknown interval type when the start date is in the past", () => {
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import LayerUtil, { LayerProps } from "../../../Types/OnCallDutyPolicy/Layer";
|
|
2
|
+
import CalendarEvent from "../../../Types/Calendar/CalendarEvent";
|
|
3
|
+
import RestrictionTimes, {
|
|
4
|
+
RestrictionType,
|
|
5
|
+
WeeklyResctriction,
|
|
6
|
+
} from "../../../Types/OnCallDutyPolicy/RestrictionTimes";
|
|
7
|
+
import Recurring from "../../../Types/Events/Recurring";
|
|
8
|
+
import OneUptimeDate from "../../../Types/Date";
|
|
9
|
+
import User from "../../../Models/DatabaseModels/User";
|
|
10
|
+
import EventInterval from "../../../Types/Events/EventInterval";
|
|
11
|
+
import DayOfWeek from "../../../Types/Day/DayOfWeek";
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
* Regression tests for the on-call audit fixes:
|
|
15
|
+
* 1. Daily/weekly restriction dropped coverage for every day after the first
|
|
16
|
+
* in a multi-day rotation period (getEventsByDailyRestriction case 2 used
|
|
17
|
+
* to terminate instead of advancing).
|
|
18
|
+
* 2. Weekly wrap-around restriction emitted phantom all-day windows when the
|
|
19
|
+
* resolution window did not start on the ISO-week boundary.
|
|
20
|
+
* 3. Multi-layer merge silently deleted a lower-priority (fallback) layer's
|
|
21
|
+
* coverage after two adjacent higher-priority windows.
|
|
22
|
+
*
|
|
23
|
+
* These assert observable on-call coverage (who is on call at a given instant),
|
|
24
|
+
* which is what actually matters operationally.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
function user(id: string): User {
|
|
28
|
+
return {
|
|
29
|
+
id: {
|
|
30
|
+
toString: () => {
|
|
31
|
+
return id;
|
|
32
|
+
},
|
|
33
|
+
} as any,
|
|
34
|
+
} as User;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function rotation(t: EventInterval, c: number): Recurring {
|
|
38
|
+
return Recurring.fromJSON({
|
|
39
|
+
_type: "Recurring",
|
|
40
|
+
value: {
|
|
41
|
+
intervalType: t,
|
|
42
|
+
intervalCount: { _type: "PositiveNumber", value: c },
|
|
43
|
+
},
|
|
44
|
+
} as any);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function noRestriction(): RestrictionTimes {
|
|
48
|
+
const r: RestrictionTimes = new RestrictionTimes();
|
|
49
|
+
r.restictionType = RestrictionType.None;
|
|
50
|
+
r.dayRestrictionTimes = null;
|
|
51
|
+
return r;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function dailyRestriction(
|
|
55
|
+
startHour: number,
|
|
56
|
+
endHour: number,
|
|
57
|
+
): RestrictionTimes {
|
|
58
|
+
const r: RestrictionTimes = new RestrictionTimes();
|
|
59
|
+
r.restictionType = RestrictionType.Daily;
|
|
60
|
+
r.dayRestrictionTimes = {
|
|
61
|
+
startTime: OneUptimeDate.getDateWithCustomTime({
|
|
62
|
+
hours: startHour,
|
|
63
|
+
minutes: 0,
|
|
64
|
+
seconds: 0,
|
|
65
|
+
}),
|
|
66
|
+
endTime: OneUptimeDate.getDateWithCustomTime({
|
|
67
|
+
hours: endHour,
|
|
68
|
+
minutes: 0,
|
|
69
|
+
seconds: 0,
|
|
70
|
+
}),
|
|
71
|
+
};
|
|
72
|
+
return r;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Which user (event title) covers instant t? (start <= t < end). null if none.
|
|
76
|
+
function coveringUser(events: Array<CalendarEvent>, t: Date): string | null {
|
|
77
|
+
for (const e of events) {
|
|
78
|
+
if (e.start.getTime() <= t.getTime() && e.end.getTime() > t.getTime()) {
|
|
79
|
+
return e.title;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Seconds in [rangeStart, rangeEnd] not covered by any event (tolerating 1s seams).
|
|
86
|
+
function uncoveredSeconds(
|
|
87
|
+
events: Array<CalendarEvent>,
|
|
88
|
+
rangeStart: Date,
|
|
89
|
+
rangeEnd: Date,
|
|
90
|
+
): number {
|
|
91
|
+
const sorted: Array<CalendarEvent> = [...events].sort(
|
|
92
|
+
(a: CalendarEvent, b: CalendarEvent) => {
|
|
93
|
+
return a.start.getTime() - b.start.getTime();
|
|
94
|
+
},
|
|
95
|
+
);
|
|
96
|
+
let uncovered: number = 0;
|
|
97
|
+
let cursor: number = rangeStart.getTime();
|
|
98
|
+
for (const e of sorted) {
|
|
99
|
+
if (e.start.getTime() > cursor + 1000) {
|
|
100
|
+
uncovered += e.start.getTime() - cursor;
|
|
101
|
+
}
|
|
102
|
+
cursor = Math.max(cursor, e.end.getTime());
|
|
103
|
+
}
|
|
104
|
+
if (rangeEnd.getTime() > cursor + 1000) {
|
|
105
|
+
uncovered += rangeEnd.getTime() - cursor;
|
|
106
|
+
}
|
|
107
|
+
return Math.round(uncovered / 1000);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
describe("LayerUtil audit fixes", () => {
|
|
111
|
+
describe("Fix 1: daily restriction keeps coverage every day of a multi-day rotation", () => {
|
|
112
|
+
test("weekly rotation + daily 09:00-17:00 + handoff at/after 09:00", () => {
|
|
113
|
+
const layerUtil: LayerUtil = new LayerUtil();
|
|
114
|
+
const layerStart: Date = new Date(2026, 0, 5, 0, 0, 0); // Mon Jan 5 2026
|
|
115
|
+
const events: Array<CalendarEvent> = layerUtil.getEvents({
|
|
116
|
+
users: [user("A"), user("B")],
|
|
117
|
+
startDateTimeOfLayer: layerStart,
|
|
118
|
+
restrictionTimes: dailyRestriction(9, 17),
|
|
119
|
+
handOffTime: new Date(2026, 0, 5, 10, 0, 0), // 10:00 (after restriction start)
|
|
120
|
+
rotation: rotation(EventInterval.Week, 1),
|
|
121
|
+
calendarStartDate: layerStart,
|
|
122
|
+
calendarEndDate: new Date(2026, 0, 19, 0, 0, 0),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
/*
|
|
126
|
+
* Week 1 (B is on call after the 10:00 handoff on Jan 5): every day Jan 6-11
|
|
127
|
+
* must be covered by B during the 09-17 window (the bug left these empty).
|
|
128
|
+
*/
|
|
129
|
+
for (let day: number = 6; day <= 11; day++) {
|
|
130
|
+
expect(coveringUser(events, new Date(2026, 0, day, 11, 0, 0))).toBe(
|
|
131
|
+
"B",
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
// Week 2 (A): every day Jan 13-18 covered by A during the window.
|
|
135
|
+
for (let day: number = 13; day <= 18; day++) {
|
|
136
|
+
expect(coveringUser(events, new Date(2026, 0, day, 11, 0, 0))).toBe(
|
|
137
|
+
"A",
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
// Outside the restriction window: nobody on call.
|
|
141
|
+
expect(coveringUser(events, new Date(2026, 0, 7, 3, 0, 0))).toBeNull();
|
|
142
|
+
expect(coveringUser(events, new Date(2026, 0, 7, 20, 0, 0))).toBeNull();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("Day x2 rotation + daily 09:00-17:00 + noon handoff covers in-between days", () => {
|
|
146
|
+
const layerUtil: LayerUtil = new LayerUtil();
|
|
147
|
+
const layerStart: Date = new Date(2026, 0, 5, 0, 0, 0);
|
|
148
|
+
const events: Array<CalendarEvent> = layerUtil.getEvents({
|
|
149
|
+
users: [user("A"), user("B")],
|
|
150
|
+
startDateTimeOfLayer: layerStart,
|
|
151
|
+
restrictionTimes: dailyRestriction(9, 17),
|
|
152
|
+
handOffTime: new Date(2026, 0, 5, 12, 0, 0),
|
|
153
|
+
rotation: rotation(EventInterval.Day, 2),
|
|
154
|
+
calendarStartDate: layerStart,
|
|
155
|
+
calendarEndDate: new Date(2026, 0, 13, 0, 0, 0),
|
|
156
|
+
});
|
|
157
|
+
// Jan 6 is the "second day" of B's 2-day period and used to be uncovered.
|
|
158
|
+
expect(coveringUser(events, new Date(2026, 0, 6, 11, 0, 0))).toBe("B");
|
|
159
|
+
// Jan 8 is the second day of A's next 2-day period.
|
|
160
|
+
expect(coveringUser(events, new Date(2026, 0, 8, 11, 0, 0))).toBe("A");
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe("Fix 2: weekly wrap-around restriction has no phantom coverage", () => {
|
|
165
|
+
const weekend: WeeklyResctriction = {
|
|
166
|
+
startDay: DayOfWeek.Friday,
|
|
167
|
+
endDay: DayOfWeek.Monday,
|
|
168
|
+
startTime: new Date(2026, 0, 2, 20, 0, 0), // Fri 20:00
|
|
169
|
+
endTime: new Date(2026, 0, 5, 8, 0, 0), // Mon 08:00
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
function weekendRestriction(): RestrictionTimes {
|
|
173
|
+
const r: RestrictionTimes = new RestrictionTimes();
|
|
174
|
+
r.restictionType = RestrictionType.Weekly;
|
|
175
|
+
r.weeklyRestrictionTimes = [weekend];
|
|
176
|
+
return r;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
test("resolved mid-week (Wed): excluded weekdays have NO coverage", () => {
|
|
180
|
+
const layerUtil: LayerUtil = new LayerUtil();
|
|
181
|
+
const events: Array<CalendarEvent> = layerUtil.getEvents({
|
|
182
|
+
users: [user("A")],
|
|
183
|
+
startDateTimeOfLayer: new Date(2026, 0, 1, 0, 0, 0),
|
|
184
|
+
restrictionTimes: weekendRestriction(),
|
|
185
|
+
handOffTime: new Date(2026, 0, 1, 0, 0, 0),
|
|
186
|
+
rotation: rotation(EventInterval.Week, 1),
|
|
187
|
+
calendarStartDate: new Date(2026, 0, 7, 0, 0, 0), // Wed Jan 7 (NOT week-aligned)
|
|
188
|
+
calendarEndDate: new Date(2026, 0, 13, 0, 0, 0),
|
|
189
|
+
});
|
|
190
|
+
// Weekday daytime is OUTSIDE the Fri20:00->Mon08:00 window: nobody on call.
|
|
191
|
+
expect(coveringUser(events, new Date(2026, 0, 7, 14, 30, 0))).toBeNull(); // Wed
|
|
192
|
+
expect(coveringUser(events, new Date(2026, 0, 8, 10, 0, 0))).toBeNull(); // Thu
|
|
193
|
+
expect(coveringUser(events, new Date(2026, 0, 9, 12, 0, 0))).toBeNull(); // Fri noon
|
|
194
|
+
// Inside the weekend window: A is on call.
|
|
195
|
+
expect(coveringUser(events, new Date(2026, 0, 9, 22, 0, 0))).toBe("A"); // Fri 22:00
|
|
196
|
+
expect(coveringUser(events, new Date(2026, 0, 10, 12, 0, 0))).toBe("A"); // Sat
|
|
197
|
+
expect(coveringUser(events, new Date(2026, 0, 11, 12, 0, 0))).toBe("A"); // Sun
|
|
198
|
+
expect(coveringUser(events, new Date(2026, 0, 12, 7, 0, 0))).toBe("A"); // Mon 07:00
|
|
199
|
+
expect(coveringUser(events, new Date(2026, 0, 12, 9, 0, 0))).toBeNull(); // Mon 09:00 (after handoff)
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("live 1-second window mid-week returns nobody during excluded hours", () => {
|
|
203
|
+
const layerUtil: LayerUtil = new LayerUtil();
|
|
204
|
+
const resolveAt: (now: Date) => string | null = (
|
|
205
|
+
now: Date,
|
|
206
|
+
): string | null => {
|
|
207
|
+
const events: Array<CalendarEvent> = layerUtil.getMultiLayerEvents(
|
|
208
|
+
{
|
|
209
|
+
layers: [
|
|
210
|
+
{
|
|
211
|
+
users: [user("A")],
|
|
212
|
+
startDateTimeOfLayer: new Date(2026, 0, 1, 0, 0, 0),
|
|
213
|
+
restrictionTimes: weekendRestriction(),
|
|
214
|
+
handOffTime: new Date(2026, 0, 1, 0, 0, 0),
|
|
215
|
+
rotation: rotation(EventInterval.Week, 1),
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
calendarStartDate: now,
|
|
219
|
+
calendarEndDate: OneUptimeDate.addRemoveSeconds(now, 1),
|
|
220
|
+
},
|
|
221
|
+
{ getNumberOfEvents: 1 },
|
|
222
|
+
);
|
|
223
|
+
return events[0]?.title ?? null;
|
|
224
|
+
};
|
|
225
|
+
expect(resolveAt(new Date(2026, 0, 7, 14, 30, 0))).toBeNull(); // Wed excluded
|
|
226
|
+
expect(resolveAt(new Date(2026, 0, 8, 10, 0, 0))).toBeNull(); // Thu excluded
|
|
227
|
+
expect(resolveAt(new Date(2026, 0, 10, 22, 0, 0))).toBe("A"); // Sat included
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test("resolved on the week boundary (Mon) still correct (no regression)", () => {
|
|
231
|
+
const layerUtil: LayerUtil = new LayerUtil();
|
|
232
|
+
const events: Array<CalendarEvent> = layerUtil.getEvents({
|
|
233
|
+
users: [user("A")],
|
|
234
|
+
startDateTimeOfLayer: new Date(2026, 0, 1, 0, 0, 0),
|
|
235
|
+
restrictionTimes: weekendRestriction(),
|
|
236
|
+
handOffTime: new Date(2026, 0, 1, 0, 0, 0),
|
|
237
|
+
rotation: rotation(EventInterval.Week, 1),
|
|
238
|
+
calendarStartDate: new Date(2026, 0, 5, 0, 0, 0), // Mon Jan 5
|
|
239
|
+
calendarEndDate: new Date(2026, 0, 13, 0, 0, 0),
|
|
240
|
+
});
|
|
241
|
+
expect(coveringUser(events, new Date(2026, 0, 5, 7, 0, 0))).toBe("A"); // Mon 07:00 (tail of prior weekend)
|
|
242
|
+
expect(coveringUser(events, new Date(2026, 0, 7, 12, 0, 0))).toBeNull(); // Wed excluded
|
|
243
|
+
expect(coveringUser(events, new Date(2026, 0, 10, 12, 0, 0))).toBe("A"); // Sat included
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
describe("Fix 3: multi-layer fallback coverage survives adjacent higher-priority windows", () => {
|
|
248
|
+
test("24/7 fallback under a primary that emits two back-to-back windows", () => {
|
|
249
|
+
const layerUtil: LayerUtil = new LayerUtil();
|
|
250
|
+
const calStart: Date = new Date(2026, 2, 2, 0, 0, 0);
|
|
251
|
+
const calEnd: Date = new Date(2026, 2, 4, 0, 0, 0);
|
|
252
|
+
|
|
253
|
+
/*
|
|
254
|
+
* Primary: A/B hourly rotation restricted to 10:00-12:00, handoff at 11:00
|
|
255
|
+
* -> emits two adjacent windows per day: A[10:00-11:00], B[11:00:01-12:00].
|
|
256
|
+
*/
|
|
257
|
+
const primary: LayerProps = {
|
|
258
|
+
users: [user("A"), user("B")],
|
|
259
|
+
startDateTimeOfLayer: calStart,
|
|
260
|
+
restrictionTimes: dailyRestriction(10, 12),
|
|
261
|
+
handOffTime: new Date(2026, 2, 2, 11, 0, 0),
|
|
262
|
+
rotation: rotation(EventInterval.Hour, 1),
|
|
263
|
+
};
|
|
264
|
+
// Fallback: C on call 24/7.
|
|
265
|
+
const fallback: LayerProps = {
|
|
266
|
+
users: [user("C")],
|
|
267
|
+
startDateTimeOfLayer: calStart,
|
|
268
|
+
restrictionTimes: noRestriction(),
|
|
269
|
+
handOffTime: new Date(2026, 2, 2, 0, 0, 0),
|
|
270
|
+
rotation: rotation(EventInterval.Week, 1),
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const events: Array<CalendarEvent> = layerUtil.getMultiLayerEvents({
|
|
274
|
+
layers: [primary, fallback],
|
|
275
|
+
calendarStartDate: calStart,
|
|
276
|
+
calendarEndDate: calEnd,
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// No uncovered time anywhere in the window (fallback fills every gap).
|
|
280
|
+
expect(uncoveredSeconds(events, calStart, calEnd)).toBe(0);
|
|
281
|
+
|
|
282
|
+
// Primary owns 10:00-12:00; fallback C owns everything else.
|
|
283
|
+
expect(coveringUser(events, new Date(2026, 2, 2, 10, 30, 0))).toBe("A");
|
|
284
|
+
expect(coveringUser(events, new Date(2026, 2, 2, 11, 30, 0))).toBe("B");
|
|
285
|
+
expect(coveringUser(events, new Date(2026, 2, 2, 15, 0, 0))).toBe("C"); // afternoon
|
|
286
|
+
expect(coveringUser(events, new Date(2026, 2, 3, 3, 0, 0))).toBe("C"); // overnight
|
|
287
|
+
expect(coveringUser(events, new Date(2026, 2, 3, 8, 0, 0))).toBe("C"); // next morning
|
|
288
|
+
expect(coveringUser(events, new Date(2026, 2, 3, 10, 30, 0))).toBe("A"); // next day primary
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
});
|