towerops/test/towerops_web/controllers/api/v1/alerts_controller_test.exs

254 lines
8 KiB
Elixir

defmodule ToweropsWeb.Api.V1.AlertsControllerTest do
use ToweropsWeb.ConnCase, async: true
import Towerops.AccountsFixtures
import Towerops.DevicesFixtures
import Towerops.OrganizationsFixtures
alias Towerops.Alerts
alias Towerops.ApiTokens
setup do
user = user_fixture()
organization = organization_fixture(user.id)
{:ok, {_token, raw_token}} =
ApiTokens.create_api_token(%{
organization_id: organization.id,
user_id: user.id,
name: "Test Token"
})
conn =
build_conn()
|> put_req_header("authorization", "Bearer #{raw_token}")
|> put_req_header("accept", "application/json")
%{
conn: conn,
user: user,
organization: organization
}
end
defp create_alert(device, attrs \\ %{}) do
default_attrs = %{
device_id: device.id,
alert_type: "ping_down",
message: "Device unreachable",
triggered_at: Towerops.Time.now()
}
{:ok, alert} = Alerts.create_alert(Map.merge(default_attrs, attrs))
alert
end
describe "index/2" do
test "lists alerts for the authenticated organization", %{
conn: conn,
organization: organization
} do
device = device_fixture(%{organization_id: organization.id})
alert1 = create_alert(device)
alert2 = create_alert(device, %{alert_type: "device_down", message: "SNMP timeout"})
conn = get(conn, ~p"/api/v1/alerts")
assert %{"data" => alerts} = json_response(conn, 200)
assert length(alerts) == 2
alert_ids = Enum.map(alerts, & &1["id"])
assert alert1.id in alert_ids
assert alert2.id in alert_ids
alert_json = List.first(alerts)
assert Map.has_key?(alert_json, "id")
assert Map.has_key?(alert_json, "alert_type")
assert Map.has_key?(alert_json, "message")
assert Map.has_key?(alert_json, "triggered_at")
assert Map.has_key?(alert_json, "acknowledged_at")
assert Map.has_key?(alert_json, "resolved_at")
assert Map.has_key?(alert_json, "device_id")
assert Map.has_key?(alert_json, "device_name")
assert Map.has_key?(alert_json, "acknowledged_by_email")
assert Map.has_key?(alert_json, "gaiia_impact")
assert Map.has_key?(alert_json, "inserted_at")
end
test "returns empty list when organization has no alerts", %{conn: conn} do
conn = get(conn, ~p"/api/v1/alerts")
assert %{"data" => []} = json_response(conn, 200)
end
test "filters by status param for open alerts", %{
conn: conn,
organization: organization
} do
device = device_fixture(%{organization_id: organization.id})
_open_alert = create_alert(device)
resolved_alert = create_alert(device, %{alert_type: "device_down", message: "Resolved one"})
Alerts.resolve_alert(resolved_alert)
conn = get(conn, ~p"/api/v1/alerts?status=active")
assert %{"data" => alerts} = json_response(conn, 200)
assert length(alerts) == 1
[returned] = alerts
assert is_nil(returned["resolved_at"])
end
test "filters by device_id param", %{
conn: conn,
organization: organization
} do
device1 = device_fixture(%{organization_id: organization.id})
device2 = device_fixture(%{organization_id: organization.id})
_alert1 = create_alert(device1)
_alert2 = create_alert(device2)
conn = get(conn, ~p"/api/v1/alerts?device_id=#{device1.id}")
assert %{"data" => alerts} = json_response(conn, 200)
assert length(alerts) == 1
assert List.first(alerts)["device_id"] == device1.id
end
test "does not return alerts from other organizations", %{conn: conn} do
other_user = user_fixture()
other_org = organization_fixture(other_user.id)
other_device = device_fixture(%{organization_id: other_org.id})
_other_alert = create_alert(other_device)
conn = get(conn, ~p"/api/v1/alerts")
assert %{"data" => []} = json_response(conn, 200)
end
end
describe "show/2" do
test "returns alert details when it belongs to the organization", %{
conn: conn,
organization: organization
} do
device = device_fixture(%{organization_id: organization.id})
alert = create_alert(device)
conn = get(conn, ~p"/api/v1/alerts/#{alert.id}")
assert %{"data" => alert_json} = json_response(conn, 200)
assert alert_json["id"] == alert.id
assert alert_json["alert_type"] == "ping_down"
assert alert_json["message"] == "Device unreachable"
assert alert_json["device_id"] == device.id
assert alert_json["device_name"] == device.name
end
test "returns 404 when alert does not exist", %{conn: conn} do
conn = get(conn, ~p"/api/v1/alerts/#{Ecto.UUID.generate()}")
assert %{"error" => "Alert not found"} = json_response(conn, 404)
end
test "returns 404 when alert belongs to a different organization", %{conn: conn} do
other_user = user_fixture()
other_org = organization_fixture(other_user.id)
other_device = device_fixture(%{organization_id: other_org.id})
other_alert = create_alert(other_device)
conn = get(conn, ~p"/api/v1/alerts/#{other_alert.id}")
assert %{"error" => "Alert not found"} = json_response(conn, 404)
end
end
describe "acknowledge/2" do
test "sets acknowledged_at on the alert", %{
conn: conn,
organization: organization
} do
device = device_fixture(%{organization_id: organization.id})
alert = create_alert(device)
conn = post(conn, ~p"/api/v1/alerts/#{alert.id}/acknowledge")
assert %{"data" => alert_json} = json_response(conn, 200)
assert alert_json["id"] == alert.id
assert alert_json["acknowledged_at"]
end
test "returns 404 when alert belongs to a different organization", %{conn: conn} do
other_user = user_fixture()
other_org = organization_fixture(other_user.id)
other_device = device_fixture(%{organization_id: other_org.id})
other_alert = create_alert(other_device)
conn = post(conn, ~p"/api/v1/alerts/#{other_alert.id}/acknowledge")
assert %{"error" => "Alert not found"} = json_response(conn, 404)
end
test "returns 404 when alert does not exist", %{conn: conn} do
conn = post(conn, ~p"/api/v1/alerts/#{Ecto.UUID.generate()}/acknowledge")
assert %{"error" => "Alert not found"} = json_response(conn, 404)
end
end
describe "resolve/2" do
test "sets resolved_at on the alert", %{
conn: conn,
organization: organization
} do
device = device_fixture(%{organization_id: organization.id})
alert = create_alert(device)
conn = post(conn, ~p"/api/v1/alerts/#{alert.id}/resolve")
assert %{"data" => alert_json} = json_response(conn, 200)
assert alert_json["id"] == alert.id
assert alert_json["resolved_at"]
end
test "returns 404 when alert belongs to a different organization", %{conn: conn} do
other_user = user_fixture()
other_org = organization_fixture(other_user.id)
other_device = device_fixture(%{organization_id: other_org.id})
other_alert = create_alert(other_device)
conn = post(conn, ~p"/api/v1/alerts/#{other_alert.id}/resolve")
assert %{"error" => "Alert not found"} = json_response(conn, 404)
end
test "returns 404 when alert does not exist", %{conn: conn} do
conn = post(conn, ~p"/api/v1/alerts/#{Ecto.UUID.generate()}/resolve")
assert %{"error" => "Alert not found"} = json_response(conn, 404)
end
end
describe "authentication" do
test "returns 401 without authorization header" do
conn =
build_conn()
|> put_req_header("accept", "application/json")
|> get(~p"/api/v1/alerts")
assert %{"error" => _message} = json_response(conn, 401)
end
test "returns 401 with invalid token" do
conn =
build_conn()
|> put_req_header("authorization", "Bearer towerops_invalid_token")
|> put_req_header("accept", "application/json")
|> get(~p"/api/v1/alerts")
assert %{"error" => _message} = json_response(conn, 401)
end
end
end