425 lines
12 KiB
Elixir
425 lines
12 KiB
Elixir
defmodule ToweropsWeb.Api.V1.ChecksControllerTest do
|
|
use ToweropsWeb.ConnCase, async: true
|
|
|
|
import Towerops.AccountsFixtures
|
|
import Towerops.OrganizationsFixtures
|
|
|
|
alias Towerops.ApiTokens
|
|
alias Towerops.Monitoring
|
|
|
|
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,
|
|
raw_token: raw_token
|
|
}
|
|
end
|
|
|
|
defp create_check(organization_id, attrs \\ %{}) do
|
|
defaults = %{
|
|
name: "Test Check",
|
|
check_type: "http",
|
|
organization_id: organization_id,
|
|
config: %{"url" => "https://example.com"}
|
|
}
|
|
|
|
{:ok, check} = Monitoring.create_check(Map.merge(defaults, attrs))
|
|
check
|
|
end
|
|
|
|
describe "index/2" do
|
|
test "lists service checks for authenticated organization", %{
|
|
conn: conn,
|
|
organization: organization
|
|
} do
|
|
check1 =
|
|
create_check(organization.id, %{name: "HTTP Check", check_type: "http", config: %{"url" => "https://example.com"}})
|
|
|
|
check2 =
|
|
create_check(organization.id, %{
|
|
name: "TCP Check",
|
|
check_type: "tcp",
|
|
config: %{"host" => "10.0.0.1", "port" => 80}
|
|
})
|
|
|
|
conn = get(conn, ~p"/api/v1/checks")
|
|
|
|
assert %{"checks" => checks} = json_response(conn, 200)
|
|
assert length(checks) == 2
|
|
|
|
check_ids = Enum.map(checks, & &1["id"])
|
|
assert check1.id in check_ids
|
|
assert check2.id in check_ids
|
|
|
|
check_json = List.first(checks)
|
|
assert Map.has_key?(check_json, "id")
|
|
assert Map.has_key?(check_json, "name")
|
|
assert Map.has_key?(check_json, "check_type")
|
|
assert Map.has_key?(check_json, "config")
|
|
assert Map.has_key?(check_json, "enabled")
|
|
assert Map.has_key?(check_json, "inserted_at")
|
|
end
|
|
|
|
test "excludes SNMP checks from listing", %{conn: conn, organization: organization} do
|
|
_http =
|
|
create_check(organization.id, %{name: "HTTP Check", check_type: "http", config: %{"url" => "https://example.com"}})
|
|
|
|
# Create an SNMP check — should not appear
|
|
{:ok, _snmp} =
|
|
Monitoring.create_check(%{
|
|
name: "SNMP Sensor",
|
|
check_type: "snmp_sensor",
|
|
organization_id: organization.id,
|
|
config: %{"oid" => "1.3.6.1.2.1.1.3.0"}
|
|
})
|
|
|
|
conn = get(conn, ~p"/api/v1/checks")
|
|
|
|
assert %{"checks" => checks} = json_response(conn, 200)
|
|
assert length(checks) == 1
|
|
assert List.first(checks)["check_type"] == "http"
|
|
end
|
|
|
|
test "filters by check_type query param", %{conn: conn, organization: organization} do
|
|
_http =
|
|
create_check(organization.id, %{name: "HTTP Check", check_type: "http", config: %{"url" => "https://example.com"}})
|
|
|
|
_tcp =
|
|
create_check(organization.id, %{
|
|
name: "TCP Check",
|
|
check_type: "tcp",
|
|
config: %{"host" => "10.0.0.1", "port" => 80}
|
|
})
|
|
|
|
conn = get(conn, ~p"/api/v1/checks?check_type=tcp")
|
|
|
|
assert %{"checks" => checks} = json_response(conn, 200)
|
|
assert length(checks) == 1
|
|
assert List.first(checks)["check_type"] == "tcp"
|
|
end
|
|
|
|
test "returns empty list when organization has no checks", %{conn: conn} do
|
|
conn = get(conn, ~p"/api/v1/checks")
|
|
|
|
assert %{"checks" => []} = json_response(conn, 200)
|
|
end
|
|
|
|
test "does not return checks from other organizations", %{conn: conn} do
|
|
other_user = user_fixture()
|
|
other_org = organization_fixture(other_user.id)
|
|
_other_check = create_check(other_org.id)
|
|
|
|
conn = get(conn, ~p"/api/v1/checks")
|
|
|
|
assert %{"checks" => checks} = json_response(conn, 200)
|
|
assert Enum.empty?(checks)
|
|
end
|
|
end
|
|
|
|
describe "create/2" do
|
|
test "creates HTTP check with valid attributes", %{conn: conn, organization: organization} do
|
|
params = %{
|
|
"check" => %{
|
|
"name" => "Web Health",
|
|
"check_type" => "http",
|
|
"config" => %{
|
|
"url" => "https://example.com/health",
|
|
"expected_status" => 200
|
|
}
|
|
}
|
|
}
|
|
|
|
conn = post(conn, ~p"/api/v1/checks", params)
|
|
|
|
assert %{
|
|
"id" => id,
|
|
"name" => "Web Health",
|
|
"check_type" => "http",
|
|
"enabled" => true
|
|
} = json_response(conn, 201)
|
|
|
|
assert is_binary(id) and byte_size(id) > 0
|
|
|
|
check = Monitoring.get_check!(id)
|
|
assert check.organization_id == organization.id
|
|
end
|
|
|
|
test "creates TCP check with valid attributes", %{conn: conn} do
|
|
params = %{
|
|
"check" => %{
|
|
"name" => "MySQL Port",
|
|
"check_type" => "tcp",
|
|
"config" => %{
|
|
"host" => "10.0.0.5",
|
|
"port" => 3306
|
|
}
|
|
}
|
|
}
|
|
|
|
conn = post(conn, ~p"/api/v1/checks", params)
|
|
|
|
assert %{
|
|
"id" => _id,
|
|
"name" => "MySQL Port",
|
|
"check_type" => "tcp"
|
|
} = json_response(conn, 201)
|
|
end
|
|
|
|
test "creates DNS check with valid attributes", %{conn: conn} do
|
|
params = %{
|
|
"check" => %{
|
|
"name" => "DNS Resolution",
|
|
"check_type" => "dns",
|
|
"config" => %{
|
|
"hostname" => "google.com",
|
|
"dns_server" => "8.8.8.8",
|
|
"record_type" => "A"
|
|
}
|
|
}
|
|
}
|
|
|
|
conn = post(conn, ~p"/api/v1/checks", params)
|
|
|
|
assert %{
|
|
"id" => _id,
|
|
"name" => "DNS Resolution",
|
|
"check_type" => "dns"
|
|
} = json_response(conn, 201)
|
|
end
|
|
|
|
test "creates ping check with valid attributes", %{conn: conn} do
|
|
params = %{
|
|
"check" => %{
|
|
"name" => "Gateway Ping",
|
|
"check_type" => "ping",
|
|
"config" => %{
|
|
"host" => "10.0.0.1",
|
|
"count" => 3
|
|
}
|
|
}
|
|
}
|
|
|
|
conn = post(conn, ~p"/api/v1/checks", params)
|
|
|
|
assert %{
|
|
"id" => _id,
|
|
"name" => "Gateway Ping",
|
|
"check_type" => "ping"
|
|
} = json_response(conn, 201)
|
|
end
|
|
|
|
test "returns 422 when config is invalid for check type", %{conn: conn} do
|
|
params = %{
|
|
"check" => %{
|
|
"name" => "Bad HTTP Check",
|
|
"check_type" => "http",
|
|
"config" => %{}
|
|
}
|
|
}
|
|
|
|
conn = post(conn, ~p"/api/v1/checks", params)
|
|
|
|
assert %{"errors" => _errors} = json_response(conn, 422)
|
|
end
|
|
|
|
test "returns 422 when name is missing", %{conn: conn} do
|
|
params = %{
|
|
"check" => %{
|
|
"check_type" => "http",
|
|
"config" => %{"url" => "https://example.com"}
|
|
}
|
|
}
|
|
|
|
conn = post(conn, ~p"/api/v1/checks", params)
|
|
|
|
assert %{"errors" => errors} = json_response(conn, 422)
|
|
assert Map.has_key?(errors, "name")
|
|
end
|
|
|
|
test "returns 400 when check parameter is missing", %{conn: conn} do
|
|
conn = post(conn, ~p"/api/v1/checks", %{})
|
|
|
|
assert %{"error" => "Missing 'check' parameter"} = json_response(conn, 400)
|
|
end
|
|
|
|
test "rejects SNMP check types", %{conn: conn} do
|
|
params = %{
|
|
"check" => %{
|
|
"name" => "SNMP Check",
|
|
"check_type" => "snmp_sensor",
|
|
"config" => %{"oid" => "1.3.6.1.2.1.1.3.0"}
|
|
}
|
|
}
|
|
|
|
conn = post(conn, ~p"/api/v1/checks", params)
|
|
|
|
assert %{"error" => _} = json_response(conn, 400)
|
|
end
|
|
end
|
|
|
|
describe "show/2" do
|
|
test "returns check when it belongs to organization", %{
|
|
conn: conn,
|
|
organization: organization
|
|
} do
|
|
check = create_check(organization.id, %{name: "Show Me"})
|
|
|
|
conn = get(conn, ~p"/api/v1/checks/#{check.id}")
|
|
|
|
assert %{
|
|
"id" => id,
|
|
"name" => "Show Me",
|
|
"check_type" => "http"
|
|
} = json_response(conn, 200)
|
|
|
|
assert id == check.id
|
|
end
|
|
|
|
test "returns 404 when check does not exist", %{conn: conn} do
|
|
conn = get(conn, ~p"/api/v1/checks/#{Ecto.UUID.generate()}")
|
|
|
|
assert %{"error" => "Check not found"} = json_response(conn, 404)
|
|
end
|
|
|
|
test "returns 404 when check belongs to different organization", %{conn: conn} do
|
|
other_user = user_fixture()
|
|
other_org = organization_fixture(other_user.id)
|
|
other_check = create_check(other_org.id)
|
|
|
|
conn = get(conn, ~p"/api/v1/checks/#{other_check.id}")
|
|
|
|
assert %{"error" => "Check not found"} = json_response(conn, 404)
|
|
end
|
|
end
|
|
|
|
describe "update/2" do
|
|
test "updates check with valid attributes", %{conn: conn, organization: organization} do
|
|
check = create_check(organization.id, %{name: "Original"})
|
|
|
|
params = %{
|
|
"check" => %{
|
|
"name" => "Updated",
|
|
"interval_seconds" => 120
|
|
}
|
|
}
|
|
|
|
conn = patch(conn, ~p"/api/v1/checks/#{check.id}", params)
|
|
|
|
assert %{
|
|
"id" => id,
|
|
"name" => "Updated",
|
|
"interval_seconds" => 120
|
|
} = json_response(conn, 200)
|
|
|
|
assert id == check.id
|
|
end
|
|
|
|
test "returns 404 when check does not exist", %{conn: conn} do
|
|
params = %{
|
|
"check" => %{
|
|
"name" => "Updated"
|
|
}
|
|
}
|
|
|
|
conn = patch(conn, ~p"/api/v1/checks/#{Ecto.UUID.generate()}", params)
|
|
|
|
assert %{"error" => "Check not found"} = json_response(conn, 404)
|
|
end
|
|
|
|
test "returns 404 when check belongs to different organization", %{conn: conn} do
|
|
other_user = user_fixture()
|
|
other_org = organization_fixture(other_user.id)
|
|
other_check = create_check(other_org.id)
|
|
|
|
params = %{"check" => %{"name" => "Hacked"}}
|
|
|
|
conn = patch(conn, ~p"/api/v1/checks/#{other_check.id}", params)
|
|
|
|
assert %{"error" => "Check not found"} = json_response(conn, 404)
|
|
end
|
|
|
|
test "returns 422 with invalid params", %{conn: conn, organization: organization} do
|
|
check = create_check(organization.id)
|
|
|
|
params = %{"check" => %{"name" => ""}}
|
|
|
|
conn = patch(conn, ~p"/api/v1/checks/#{check.id}", params)
|
|
|
|
assert %{"errors" => errors} = json_response(conn, 422)
|
|
assert Map.has_key?(errors, "name")
|
|
end
|
|
|
|
test "returns 400 when check parameter is missing", %{conn: conn, organization: organization} do
|
|
check = create_check(organization.id)
|
|
|
|
conn = patch(conn, ~p"/api/v1/checks/#{check.id}", %{})
|
|
|
|
assert %{"error" => "Missing 'check' parameter"} = json_response(conn, 400)
|
|
end
|
|
end
|
|
|
|
describe "delete/2" do
|
|
test "deletes check successfully", %{conn: conn, organization: organization} do
|
|
check = create_check(organization.id, %{name: "Doomed"})
|
|
|
|
conn = delete(conn, ~p"/api/v1/checks/#{check.id}")
|
|
|
|
assert %{"success" => true} = json_response(conn, 200)
|
|
|
|
assert Monitoring.get_check(check.id) == nil
|
|
end
|
|
|
|
test "returns 404 when check does not exist", %{conn: conn} do
|
|
conn = delete(conn, ~p"/api/v1/checks/#{Ecto.UUID.generate()}")
|
|
|
|
assert %{"error" => "Check not found"} = json_response(conn, 404)
|
|
end
|
|
|
|
test "returns 404 when check belongs to different organization", %{conn: conn} do
|
|
other_user = user_fixture()
|
|
other_org = organization_fixture(other_user.id)
|
|
other_check = create_check(other_org.id)
|
|
|
|
conn = delete(conn, ~p"/api/v1/checks/#{other_check.id}")
|
|
|
|
assert %{"error" => "Check 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/checks")
|
|
|
|
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/checks")
|
|
|
|
assert %{"error" => _message} = json_response(conn, 401)
|
|
end
|
|
end
|
|
end
|