254 lines
8 KiB
Elixir
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
|