test: GraphQL.Resolvers.EscalationPolicy + MobileController list_alerts formatting

This commit is contained in:
Graham McIntire 2026-05-08 13:00:56 -05:00
parent f8af326deb
commit 91b0cac164
2 changed files with 215 additions and 1 deletions

View file

@ -5,6 +5,7 @@ defmodule ToweropsWeb.Api.MobileControllerTest do
alias Towerops.MobileSessions
alias Towerops.Organizations
alias Towerops.Snmp.Device
setup do
user = user_fixture()
@ -188,7 +189,7 @@ defmodule ToweropsWeb.Api.MobileControllerTest do
})
{:ok, _snmp} =
Towerops.Repo.insert(%Towerops.Snmp.Device{
Towerops.Repo.insert(%Device{
device_id: device.id,
sys_uptime: (5 * 3600 + 30 * 60) * 100,
sys_descr: "test"
@ -208,4 +209,35 @@ defmodule ToweropsWeb.Api.MobileControllerTest do
assert body["uptime"] =~ "h"
end
end
describe "list_alerts — formatting" do
test "returns formatted alert with status mapping", %{conn: conn, organization: org} do
{:ok, site} = Towerops.Sites.create_site(%{name: "Alert Site", organization_id: org.id})
{:ok, device} =
Towerops.Devices.create_device(%{
name: "Alert-Dev",
ip_address: "10.20.30.40",
site_id: site.id,
organization_id: org.id
})
{:ok, _alert} =
Towerops.Alerts.create_alert(%{
device_id: device.id,
alert_type: "device_down",
triggered_at: ~U[2026-01-01 00:00:00Z],
message: "down"
})
response = get(conn, ~p"/api/v1/mobile/organizations/#{org.id}/alerts")
%{"alerts" => alerts} = json_response(response, 200)
[a] = alerts
assert a["status"] == "active"
assert a["message"] == "down"
assert a["equipment_name"] == "Alert-Dev"
assert a["device_id"] == device.id
end
end
end

View file

@ -0,0 +1,182 @@
defmodule ToweropsWeb.GraphQL.Resolvers.EscalationPolicyTest do
use Towerops.DataCase, async: true
import Towerops.AccountsFixtures
import Towerops.OnCallFixtures
import Towerops.OrganizationsFixtures
alias ToweropsWeb.GraphQL.Resolvers.EscalationPolicy, as: Resolver
setup do
user = user_fixture()
org = organization_fixture(user.id)
other_user = user_fixture()
other_org = organization_fixture(other_user.id)
%{user: user, org: org, other_org: other_org, ctx: %{organization_id: org.id}}
end
describe "list/3" do
test "returns policies for the org", %{org: org, ctx: ctx} do
_p = escalation_policy_fixture(org.id, %{name: "P1"})
assert {:ok, [policy]} = Resolver.list(nil, %{}, %{context: ctx})
assert policy.name == "P1"
end
test "without org context returns auth error" do
assert {:error, _} = Resolver.list(nil, %{}, %{})
end
end
describe "get/3" do
test "returns the policy preloaded with rules + targets", %{org: org, ctx: ctx} do
policy = escalation_policy_fixture(org.id)
assert {:ok, found} = Resolver.get(nil, %{id: policy.id}, %{context: ctx})
assert found.id == policy.id
end
test "returns error for cross-org id", %{other_org: other_org, ctx: ctx} do
foreign = escalation_policy_fixture(other_org.id)
assert {:error, "Escalation policy not found"} =
Resolver.get(nil, %{id: foreign.id}, %{context: ctx})
end
test "without org context returns auth error" do
assert {:error, _} = Resolver.get(nil, %{id: Ecto.UUID.generate()}, %{})
end
end
describe "create/3" do
test "creates a policy under the resolved org", %{ctx: ctx} do
assert {:ok, policy} =
Resolver.create(nil, %{input: %{name: "Created"}}, %{context: ctx})
assert policy.name == "Created"
assert policy.organization_id == ctx.organization_id
end
test "returns formatted errors on invalid input", %{ctx: ctx} do
assert {:error, errors} = Resolver.create(nil, %{input: %{name: ""}}, %{context: ctx})
assert is_list(errors) or is_binary(errors) or is_map(errors)
end
test "without org context returns auth error" do
assert {:error, _} = Resolver.create(nil, %{input: %{name: "x"}}, %{})
end
end
describe "update/3 and delete/3" do
test "update + delete a policy round-trip", %{org: org, ctx: ctx} do
policy = escalation_policy_fixture(org.id, %{name: "Original"})
assert {:ok, updated} =
Resolver.update(nil, %{id: policy.id, input: %{name: "Renamed"}}, %{context: ctx})
assert updated.name == "Renamed"
assert {:ok, %{success: true}} = Resolver.delete(nil, %{id: policy.id}, %{context: ctx})
end
test "update without org context returns auth error" do
assert {:error, _} =
Resolver.update(nil, %{id: Ecto.UUID.generate(), input: %{name: "x"}}, %{})
end
test "delete without org context returns auth error" do
assert {:error, _} = Resolver.delete(nil, %{id: Ecto.UUID.generate()}, %{})
end
test "delete returns 'not found' for cross-org id",
%{other_org: other_org, ctx: ctx} do
foreign = escalation_policy_fixture(other_org.id)
assert {:error, _} = Resolver.delete(nil, %{id: foreign.id}, %{context: ctx})
end
end
describe "rule + target mutations" do
setup %{org: org} do
policy = escalation_policy_fixture(org.id)
rule = escalation_rule_fixture(policy.id, %{position: 0, timeout_minutes: 5})
%{policy: policy, rule: rule}
end
test "create_rule attaches a rule to the policy",
%{policy: policy, ctx: ctx} do
assert {:ok, _rule} =
Resolver.create_rule(
nil,
%{
escalation_policy_id: policy.id,
input: %{position: 1, timeout_minutes: 5}
},
%{context: ctx}
)
end
test "create_rule without context returns auth error" do
assert {:error, _} =
Resolver.create_rule(
nil,
%{escalation_policy_id: Ecto.UUID.generate(), input: %{}},
%{}
)
end
test "update_rule and delete_rule round-trip",
%{rule: rule, ctx: ctx} do
assert {:ok, _} =
Resolver.update_rule(nil, %{id: rule.id, input: %{timeout_minutes: 10}}, %{
context: ctx
})
assert {:ok, %{success: true}} = Resolver.delete_rule(nil, %{id: rule.id}, %{context: ctx})
end
test "update_rule + delete_rule auth errors without context" do
assert {:error, _} =
Resolver.update_rule(nil, %{id: Ecto.UUID.generate(), input: %{}}, %{})
assert {:error, _} = Resolver.delete_rule(nil, %{id: Ecto.UUID.generate()}, %{})
end
test "create_target attaches a target to a rule",
%{rule: rule, user: user, ctx: ctx} do
assert {:ok, _target} =
Resolver.create_target(
nil,
%{
escalation_rule_id: rule.id,
input: %{target_type: "user", user_id: user.id}
},
%{context: ctx}
)
end
test "create_target auth error without context" do
assert {:error, _} =
Resolver.create_target(
nil,
%{escalation_rule_id: Ecto.UUID.generate(), input: %{}},
%{}
)
end
test "delete_target removes the target",
%{rule: rule, user: user, ctx: ctx} do
target = escalation_target_fixture(rule.id, %{target_type: "user", user_id: user.id})
assert {:ok, %{success: true}} =
Resolver.delete_target(nil, %{id: target.id}, %{context: ctx})
end
test "delete_target on missing id returns 'not found'", %{ctx: ctx} do
assert {:error, _} =
Resolver.delete_target(nil, %{id: Ecto.UUID.generate()}, %{context: ctx})
end
test "delete_target auth error without context" do
assert {:error, _} = Resolver.delete_target(nil, %{id: Ecto.UUID.generate()}, %{})
end
end
end