182 lines
6.1 KiB
Elixir
182 lines
6.1 KiB
Elixir
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 Enum.any?([is_list(errors), is_binary(errors), 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
|