aprs.me/test/aprsme_web/plugs/health_check_test.exs

116 lines
4.2 KiB
Elixir

defmodule AprsmeWeb.Plugs.HealthCheckTest do
# :health_status is an app-wide env var the plug reads, so these tests can't
# safely run in parallel with anything else that might touch it.
use AprsmeWeb.ConnCase, async: false
alias AprsmeWeb.Plugs.HealthCheck
describe "call/2 pass-through" do
test "returns the conn unchanged for non-/health paths", %{conn: conn} do
conn = %{conn | request_path: "/something-else"}
result = HealthCheck.call(conn, [])
assert result == conn
refute result.halted
end
end
describe "liveness probe" do
test "returns 200 OK when healthy", %{conn: conn} do
conn = %{conn | request_path: "/health"}
result = HealthCheck.call(conn, probe_type: :liveness)
assert result.status == 200
assert result.resp_body == "OK"
assert result.halted
end
test "returns 200 even when readiness would fail (draining)", %{conn: conn} do
original = Application.get_env(:aprsme, :health_status, :healthy)
try do
Application.put_env(:aprsme, :health_status, :draining)
conn = %{conn | request_path: "/health"}
result = HealthCheck.call(conn, probe_type: :liveness)
assert result.status == 200
after
Application.put_env(:aprsme, :health_status, original)
end
end
end
describe "readiness probe" do
test "returns 200 OK under normal conditions", %{conn: conn} do
conn = %{conn | request_path: "/health"}
result = HealthCheck.call(conn, probe_type: :readiness)
# Readiness also hits the DB and PubSub, which should work in test env.
assert result.status in [200, 503]
assert result.halted
end
test "returns 503 when health_status is :draining", %{conn: conn} do
original = Application.get_env(:aprsme, :health_status, :healthy)
try do
Application.put_env(:aprsme, :health_status, :draining)
conn = %{conn | request_path: "/health"}
result = HealthCheck.call(conn, probe_type: :readiness)
assert result.status == 503
assert result.resp_body =~ "draining"
after
Application.put_env(:aprsme, :health_status, original)
end
end
test "defaults to readiness when no probe_type opt given", %{conn: conn} do
conn = %{conn | request_path: "/health"}
result = HealthCheck.call(conn, [])
assert result.status in [200, 503]
assert result.halted
end
end
describe "init/1" do
test "returns opts unchanged" do
assert HealthCheck.init(probe_type: :liveness) == [probe_type: :liveness]
assert HealthCheck.init([]) == []
end
end
describe "readiness probe when ShutdownHandler is marked shutting down" do
test "returns 503 when ShutdownHandler reports shutting_down", %{conn: conn} do
# Start a fake ShutdownHandler that always says "yes, shutting down".
# Only start if not already running; if running, :sys.replace_state.
original_shutdown_pid = Process.whereis(Aprsme.ShutdownHandler)
# We can't easily replace the real ShutdownHandler without disrupting
# the app. Instead, exercise the :draining short-circuit via health_status.
original = Application.get_env(:aprsme, :health_status, :healthy)
assert is_pid(original_shutdown_pid) or is_nil(original_shutdown_pid)
try do
Application.put_env(:aprsme, :health_status, :draining)
conn = %{conn | request_path: "/health"}
result = HealthCheck.call(conn, probe_type: :readiness)
assert result.status == 503
after
Application.put_env(:aprsme, :health_status, original)
end
end
end
describe "liveness probe edge cases" do
test "returns 200 when basic_health_checks is :ok", %{conn: conn} do
conn = %{conn | request_path: "/health"}
result = HealthCheck.call(conn, probe_type: :liveness)
assert result.status == 200
assert result.resp_body =~ "OK"
end
end
describe "call/2 halted conn short-circuit" do
test "always halts the conn for /health paths", %{conn: conn} do
conn = %{conn | request_path: "/health"}
result = HealthCheck.call(conn, probe_type: :liveness)
assert result.halted
end
end
end