towerops/test/support/fixtures/on_call_fixtures.ex
Graham McIntire 0cd2ed3567
feat: add on-call schedules and escalation policies
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
2026-03-11 12:32:54 -05:00

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