Built-in PagerDuty-equivalent system for on-call scheduling and alert escalation. Users can now manage schedules, rotation layers, overrides, and escalation policies directly in the app alongside PagerDuty. - On-call schedules with rotation layers (daily/weekly/custom), member management, and temporary overrides - Escalation policies with ordered rules, timeout-based escalation, and user/schedule targets - Automatic escalation via Oban worker with configurable repeat count - Email notifications via Swoosh for on-call alerts - Resolver computes who's on-call from layer stacking and overrides - AlertNotificationWorker integration: starts escalation alongside PagerDuty, acknowledges/resolves incidents on alert state changes - Device and organization schemas support escalation_policy_id - Escalation policy picker on device form - Schedules nav item with tabbed index (schedules + escalation policies) - Full CRUD UI for schedules, layers, members, overrides, rules, targets - 62 LiveView tests, 56 context/schema/resolver/escalation tests - 26 E2E Playwright tests for smoke and critical path coverage
110 lines
2.7 KiB
Elixir
110 lines
2.7 KiB
Elixir
defmodule Towerops.OnCallFixtures do
|
|
@moduledoc """
|
|
Test helpers for creating on-call schedule and escalation policy entities.
|
|
"""
|
|
|
|
alias Towerops.OnCall.EscalationPolicy
|
|
alias Towerops.OnCall.EscalationRule
|
|
alias Towerops.OnCall.EscalationTarget
|
|
alias Towerops.OnCall.Layer
|
|
alias Towerops.OnCall.LayerMember
|
|
alias Towerops.OnCall.Override
|
|
alias Towerops.OnCall.Schedule
|
|
alias Towerops.Repo
|
|
|
|
def schedule_fixture(organization_id, attrs \\ %{}) do
|
|
attrs =
|
|
Enum.into(attrs, %{
|
|
name: "Schedule #{System.unique_integer([:positive])}",
|
|
timezone: "America/Chicago",
|
|
organization_id: organization_id
|
|
})
|
|
|
|
%Schedule{}
|
|
|> Schedule.changeset(attrs)
|
|
|> Repo.insert!()
|
|
end
|
|
|
|
def layer_fixture(schedule_id, attrs \\ %{}) do
|
|
attrs =
|
|
Enum.into(attrs, %{
|
|
name: "Layer #{System.unique_integer([:positive])}",
|
|
position: 1,
|
|
rotation_type: "weekly",
|
|
rotation_interval: 1,
|
|
handoff_time: ~T[09:00:00],
|
|
handoff_day: 1,
|
|
start_date: ~U[2026-01-01 09:00:00Z],
|
|
schedule_id: schedule_id
|
|
})
|
|
|
|
%Layer{}
|
|
|> Layer.changeset(attrs)
|
|
|> Repo.insert!()
|
|
end
|
|
|
|
def layer_member_fixture(layer_id, user_id, attrs \\ %{}) do
|
|
attrs =
|
|
Enum.into(attrs, %{
|
|
position: 0,
|
|
layer_id: layer_id,
|
|
user_id: user_id
|
|
})
|
|
|
|
%LayerMember{}
|
|
|> LayerMember.changeset(attrs)
|
|
|> Repo.insert!()
|
|
end
|
|
|
|
def override_fixture(schedule_id, user_id, attrs \\ %{}) do
|
|
attrs =
|
|
Enum.into(attrs, %{
|
|
start_time: ~U[2026-03-15 09:00:00Z],
|
|
end_time: ~U[2026-03-16 09:00:00Z],
|
|
schedule_id: schedule_id,
|
|
user_id: user_id
|
|
})
|
|
|
|
%Override{}
|
|
|> Override.changeset(attrs)
|
|
|> Repo.insert!()
|
|
end
|
|
|
|
def escalation_policy_fixture(organization_id, attrs \\ %{}) do
|
|
attrs =
|
|
Enum.into(attrs, %{
|
|
name: "Policy #{System.unique_integer([:positive])}",
|
|
repeat_count: 3,
|
|
organization_id: organization_id
|
|
})
|
|
|
|
%EscalationPolicy{}
|
|
|> EscalationPolicy.changeset(attrs)
|
|
|> Repo.insert!()
|
|
end
|
|
|
|
def escalation_rule_fixture(escalation_policy_id, attrs \\ %{}) do
|
|
attrs =
|
|
Enum.into(attrs, %{
|
|
position: 0,
|
|
timeout_minutes: 30,
|
|
escalation_policy_id: escalation_policy_id
|
|
})
|
|
|
|
%EscalationRule{}
|
|
|> EscalationRule.changeset(attrs)
|
|
|> Repo.insert!()
|
|
end
|
|
|
|
def escalation_target_fixture(escalation_rule_id, attrs \\ %{}) do
|
|
attrs =
|
|
Enum.into(attrs, %{
|
|
target_type: "user",
|
|
escalation_rule_id: escalation_rule_id
|
|
})
|
|
|
|
%EscalationTarget{}
|
|
|> EscalationTarget.changeset(attrs)
|
|
|> Repo.insert!()
|
|
end
|
|
end
|