towerops/test/towerops_web/graphql/resolvers/device_test.exs

221 lines
6.8 KiB
Elixir

defmodule ToweropsWeb.GraphQL.Resolvers.DeviceTest do
use Towerops.DataCase, async: true
import Towerops.AccountsFixtures
import Towerops.DevicesFixtures
import Towerops.OrganizationsFixtures
alias ToweropsWeb.GraphQL.Resolvers.Device, as: Resolver
setup do
user = user_fixture()
org = organization_fixture(user.id)
other_user = user_fixture()
other_org = organization_fixture(other_user.id)
{:ok, site} = Towerops.Sites.create_site(%{name: "S1", organization_id: org.id})
%{
user: user,
org: org,
other_org: other_org,
site: site,
ctx: %{organization_id: org.id}
}
end
describe "list/3" do
test "returns devices for the org", %{org: org, site: site, ctx: ctx} do
_ =
device_fixture(%{
name: "Listed",
organization_id: org.id,
site_id: site.id
})
assert {:ok, devices} = Resolver.list(nil, %{}, %{context: ctx})
assert Enum.any?(devices, &(&1.name == "Listed"))
end
test "without context returns auth error" do
assert {:error, _} = Resolver.list(nil, %{}, %{})
end
end
describe "get/3" do
test "returns device with site preloaded", %{org: org, site: site, ctx: ctx} do
device =
device_fixture(%{name: "Found", organization_id: org.id, site_id: site.id})
assert {:ok, found} = Resolver.get(nil, %{id: device.id}, %{context: ctx})
assert found.id == device.id
end
test "returns 'not found' for cross-org id", %{other_org: other_org, ctx: ctx} do
foreign = device_fixture(%{organization_id: other_org.id})
assert {:error, "Device not found"} =
Resolver.get(nil, %{id: foreign.id}, %{context: ctx})
end
test "without context returns auth error" do
assert {:error, _} = Resolver.get(nil, %{id: Ecto.UUID.generate()}, %{})
end
end
describe "create/3, update/3, delete/3" do
test "creates a device under the org", %{site: site, ctx: ctx} do
assert {:ok, device} =
Resolver.create(
nil,
%{
input: %{
name: "Created",
site_id: site.id,
ip_address: "10.10.10.10"
}
},
%{context: ctx}
)
assert device.name == "Created"
assert device.organization_id == ctx.organization_id
end
test "create 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 "create without context returns auth error" do
assert {:error, _} = Resolver.create(nil, %{input: %{name: "x"}}, %{})
end
test "update + delete round-trip", %{org: org, site: site, ctx: ctx} do
device =
device_fixture(%{name: "Old", organization_id: org.id, site_id: site.id})
assert {:ok, updated} =
Resolver.update(nil, %{id: device.id, input: %{name: "New"}}, %{
context: ctx
})
assert updated.name == "New"
assert {:ok, %{success: true}} =
Resolver.delete(nil, %{id: device.id}, %{context: ctx})
end
test "update on cross-org id returns 'not found'",
%{other_org: other_org, ctx: ctx} do
foreign = device_fixture(%{organization_id: other_org.id})
assert {:error, "Device not found"} =
Resolver.update(nil, %{id: foreign.id, input: %{name: "x"}}, %{
context: ctx
})
end
test "update / delete without context returns auth errors" do
assert {:error, _} =
Resolver.update(nil, %{id: Ecto.UUID.generate(), input: %{}}, %{})
assert {:error, _} = Resolver.delete(nil, %{id: Ecto.UUID.generate()}, %{})
end
end
describe "metrics/3" do
test "returns [] for a device with no checks", %{org: org, site: site, ctx: ctx} do
device =
device_fixture(%{name: "NoChecks", organization_id: org.id, site_id: site.id})
assert {:ok, []} =
Resolver.metrics(
nil,
%{device_id: device.id, time_range: "24h"},
%{context: ctx}
)
end
test "returns [] when device has a check but no results yet",
%{org: org, site: site, ctx: ctx} do
device =
device_fixture(%{name: "PendingMetric", organization_id: org.id, site_id: site.id})
{:ok, _check} =
Towerops.Monitoring.create_check(%{
name: "Inline Ping",
check_type: "ping",
device_id: device.id,
organization_id: org.id,
enabled: true,
interval_seconds: 60,
timeout_ms: 1000,
config: %{"host" => "127.0.0.1"}
})
assert {:ok, []} =
Resolver.metrics(
nil,
%{device_id: device.id, time_range: "24h"},
%{context: ctx}
)
end
test "metrics without context returns auth error" do
assert {:error, _} =
Resolver.metrics(nil, %{device_id: Ecto.UUID.generate()}, %{})
end
test "metrics on cross-org id returns 'not found'",
%{other_org: other_org, ctx: ctx} do
foreign = device_fixture(%{organization_id: other_org.id})
assert {:error, "Device not found"} =
Resolver.metrics(
nil,
%{device_id: foreign.id, time_range: "1h"},
%{context: ctx}
)
end
test "metrics falls back to 24h when time_range is unparseable",
%{org: org, site: site, ctx: ctx} do
device =
device_fixture(%{name: "Fallback", organization_id: org.id, site_id: site.id})
assert {:ok, _} =
Resolver.metrics(
nil,
%{device_id: device.id, time_range: "garbage"},
%{context: ctx}
)
end
end
describe "interfaces/3" do
test "returns [] when device has no snmp_device",
%{org: org, site: site, ctx: ctx} do
device =
device_fixture(%{name: "NoSnmp", organization_id: org.id, site_id: site.id})
assert {:ok, []} =
Resolver.interfaces(nil, %{device_id: device.id}, %{context: ctx})
end
test "interfaces returns 'not found' for cross-org id",
%{other_org: other_org, ctx: ctx} do
foreign = device_fixture(%{organization_id: other_org.id})
assert {:error, "Device not found"} =
Resolver.interfaces(nil, %{device_id: foreign.id}, %{context: ctx})
end
test "interfaces without context returns auth error" do
assert {:error, _} =
Resolver.interfaces(nil, %{device_id: Ecto.UUID.generate()}, %{})
end
end
end