Promoted pure presentation and utility helpers from `defp` to `def @doc false` across ~20 LiveViews, Oban workers, and sync modules so they're reachable from unit tests. Refactored several `cond` blocks into idiomatic function heads with guards. Added ~250 new test cases in new files under test/towerops and test/towerops_web, including DB-backed tests for CnMaestro.Sync and AlertNotificationWorker, and removed dead LiveView tab components and CapacityLive (no callers anywhere in lib/test). Configured mix.exs test_coverage.ignore_modules to exclude vendored third-party code (SnmpKit, protobuf-generated Towerops.Agent.*, Absinthe GraphQL types, Phoenix HTML modules, Inspect protocol impls) from coverage calculations — these are not our project code. Coverage: 66.93% → 70.09%. Full suite: 10,127 tests, 0 failures.
186 lines
5.8 KiB
Elixir
186 lines
5.8 KiB
Elixir
defmodule Towerops.Sonar.ClientTest do
|
|
use ExUnit.Case, async: true
|
|
|
|
alias Towerops.Sonar.Client
|
|
|
|
@url "https://sonar.example.com"
|
|
@token "test-token"
|
|
|
|
describe "query/4" do
|
|
test "returns {:ok, data} on 200 success" do
|
|
Req.Test.stub(Client, fn conn ->
|
|
assert conn.request_path == "/api/graphql"
|
|
assert Plug.Conn.get_req_header(conn, "authorization") == ["Bearer #{@token}"]
|
|
|
|
{:ok, body, conn} = Plug.Conn.read_body(conn)
|
|
assert Jason.decode!(body)["query"] =~ "accounts"
|
|
|
|
Req.Test.json(conn, %{"data" => %{"accounts" => %{"entities" => []}}})
|
|
end)
|
|
|
|
assert {:ok, data} = Client.query(@url, @token, "{ accounts { entities { id } } }")
|
|
assert data == %{"accounts" => %{"entities" => []}}
|
|
end
|
|
|
|
test "returns :unauthorized on 401" do
|
|
Req.Test.stub(Client, fn conn ->
|
|
conn |> Plug.Conn.put_status(401) |> Req.Test.json(%{})
|
|
end)
|
|
|
|
assert {:error, :unauthorized} = Client.query(@url, @token, "{ q }")
|
|
end
|
|
|
|
test "returns :forbidden on 403" do
|
|
Req.Test.stub(Client, fn conn ->
|
|
conn |> Plug.Conn.put_status(403) |> Req.Test.json(%{})
|
|
end)
|
|
|
|
assert {:error, :forbidden} = Client.query(@url, @token, "{ q }")
|
|
end
|
|
|
|
test "returns {:rate_limited, seconds} on 429 with retry-after" do
|
|
Req.Test.stub(Client, fn conn ->
|
|
conn
|
|
|> Plug.Conn.put_resp_header("retry-after", "120")
|
|
|> Plug.Conn.put_status(429)
|
|
|> Req.Test.json(%{})
|
|
end)
|
|
|
|
assert {:error, {:rate_limited, 120}} = Client.query(@url, @token, "{ q }")
|
|
end
|
|
|
|
test "returns {:rate_limited, 60} on 429 with no retry-after" do
|
|
Req.Test.stub(Client, fn conn ->
|
|
conn |> Plug.Conn.put_status(429) |> Req.Test.json(%{})
|
|
end)
|
|
|
|
assert {:error, {:rate_limited, 60}} = Client.query(@url, @token, "{ q }")
|
|
end
|
|
|
|
test "returns {:rate_limited, 60} on 429 with non-integer retry-after" do
|
|
Req.Test.stub(Client, fn conn ->
|
|
conn
|
|
|> Plug.Conn.put_resp_header("retry-after", "invalid")
|
|
|> Plug.Conn.put_status(429)
|
|
|> Req.Test.json(%{})
|
|
end)
|
|
|
|
assert {:error, {:rate_limited, 60}} = Client.query(@url, @token, "{ q }")
|
|
end
|
|
|
|
test "returns graphql_errors on 200 with errors field" do
|
|
Req.Test.stub(Client, fn conn ->
|
|
Req.Test.json(conn, %{"errors" => [%{"message" => "schema error"}]})
|
|
end)
|
|
|
|
assert {:error, {:graphql_errors, [%{"message" => "schema error"}]}} =
|
|
Client.query(@url, @token, "{ bad }")
|
|
end
|
|
|
|
test "returns graphql_errors on 400 with errors field" do
|
|
Req.Test.stub(Client, fn conn ->
|
|
conn
|
|
|> Plug.Conn.put_status(400)
|
|
|> Req.Test.json(%{"errors" => [%{"message" => "bad query"}]})
|
|
end)
|
|
|
|
assert {:error, {:graphql_errors, _}} = Client.query(@url, @token, "{ bad }")
|
|
end
|
|
|
|
test "returns {:unexpected_status, status} on other non-2xx" do
|
|
Req.Test.stub(Client, fn conn ->
|
|
conn |> Plug.Conn.put_status(502) |> Req.Test.json(%{})
|
|
end)
|
|
|
|
assert {:error, {:unexpected_status, 502}} = Client.query(@url, @token, "{ q }")
|
|
end
|
|
|
|
test "trims trailing slash from instance url" do
|
|
Req.Test.stub(Client, fn conn ->
|
|
assert conn.request_path == "/api/graphql"
|
|
Req.Test.json(conn, %{"data" => %{}})
|
|
end)
|
|
|
|
assert {:ok, _} = Client.query("#{@url}/", @token, "{ q }")
|
|
end
|
|
|
|
test "sends variables in request body" do
|
|
Req.Test.stub(Client, fn conn ->
|
|
{:ok, body, conn} = Plug.Conn.read_body(conn)
|
|
payload = Jason.decode!(body)
|
|
assert payload["variables"] == %{"page" => 2, "limit" => 50}
|
|
Req.Test.json(conn, %{"data" => %{}})
|
|
end)
|
|
|
|
assert {:ok, _} = Client.query(@url, @token, "{ q }", %{"page" => 2, "limit" => 50})
|
|
end
|
|
end
|
|
|
|
describe "test_connection/2" do
|
|
test "returns ok when query succeeds" do
|
|
Req.Test.stub(Client, fn conn ->
|
|
Req.Test.json(conn, %{"data" => %{"accounts" => %{"entities" => []}}})
|
|
end)
|
|
|
|
assert {:ok, %{}} = Client.test_connection(@url, @token)
|
|
end
|
|
|
|
test "returns error when query fails" do
|
|
Req.Test.stub(Client, fn conn ->
|
|
conn |> Plug.Conn.put_status(401) |> Req.Test.json(%{})
|
|
end)
|
|
|
|
assert {:error, :unauthorized} = Client.test_connection(@url, @token)
|
|
end
|
|
end
|
|
|
|
describe "list_accounts/2 with pagination" do
|
|
test "aggregates entities across pages" do
|
|
{:ok, agent} = Agent.start_link(fn -> 1 end)
|
|
|
|
Req.Test.stub(Client, fn conn ->
|
|
page = Agent.get_and_update(agent, fn p -> {p, p + 1} end)
|
|
|
|
{:ok, body, conn} = Plug.Conn.read_body(conn)
|
|
payload = Jason.decode!(body)
|
|
assert payload["variables"]["page"] == page
|
|
|
|
Req.Test.json(conn, %{
|
|
"data" => %{
|
|
"accounts" => %{
|
|
"entities" => [%{"id" => "acct-#{page}"}],
|
|
"page_info" => %{"page" => page, "total_pages" => 2}
|
|
}
|
|
}
|
|
})
|
|
end)
|
|
|
|
assert {:ok, entities} = Client.list_accounts(@url, @token)
|
|
assert length(entities) == 2
|
|
assert Enum.map(entities, & &1["id"]) == ["acct-1", "acct-2"]
|
|
end
|
|
|
|
test "stops at single page when total_pages is 1" do
|
|
Req.Test.stub(Client, fn conn ->
|
|
Req.Test.json(conn, %{
|
|
"data" => %{
|
|
"accounts" => %{
|
|
"entities" => [%{"id" => "a"}, %{"id" => "b"}],
|
|
"page_info" => %{"page" => 1, "total_pages" => 1}
|
|
}
|
|
}
|
|
})
|
|
end)
|
|
|
|
assert {:ok, [%{"id" => "a"}, %{"id" => "b"}]} = Client.list_accounts(@url, @token)
|
|
end
|
|
|
|
test "propagates errors" do
|
|
Req.Test.stub(Client, fn conn ->
|
|
conn |> Plug.Conn.put_status(401) |> Req.Test.json(%{})
|
|
end)
|
|
|
|
assert {:error, :unauthorized} = Client.list_accounts(@url, @token)
|
|
end
|
|
end
|
|
end
|