- DeviceCache.pattern_matches?: replace a 3-branch `cond` with a two-clause `case` on `String.contains?(pattern, "?")`. The old `*` branch was dead code (it did the same exact-match compare as the fallback), so it's removed. - Telemetry.DatabaseMetrics.collect_postgres_metrics: dispatch on environment atom via multi-clause private functions instead of `if Application.get_env(...) == :test`. New tests: - DeviceCache wildcard/exact lookup paths exercised via a seeded cache. - MobileUserSocket connect/3 for x_headers/peer_data/empty info and rate-limit deny. - DatabaseMetrics pool-metrics emission captured via :telemetry handler and the :test-env short-circuit.
99 lines
3.7 KiB
Elixir
99 lines
3.7 KiB
Elixir
defmodule AprsmeWeb.MobileUserSocketTest do
|
|
# The rate limiter uses a process-wide ETS table; we're careful to use unique
|
|
# IPs so tests don't interfere with each other, but we still mark async:false
|
|
# to avoid clashing with anything else that flips the :aprsme, :env flag.
|
|
use ExUnit.Case, async: false
|
|
|
|
alias AprsmeWeb.MobileUserSocket
|
|
|
|
defp unique_ip, do: "203.0.113." <> to_string(System.unique_integer([:positive]))
|
|
|
|
setup do
|
|
original_env = Application.get_env(:aprsme, :env)
|
|
on_exit(fn -> Application.put_env(:aprsme, :env, original_env) end)
|
|
:ok
|
|
end
|
|
|
|
describe "connect/3 in :test env" do
|
|
test "always succeeds regardless of rate-limit key" do
|
|
Application.put_env(:aprsme, :env, :test)
|
|
socket = %Phoenix.Socket{assigns: %{}}
|
|
|
|
connect_info = %{x_headers: [{"cf-connecting-ip", "198.51.100.1"}]}
|
|
|
|
assert {:ok, result} = MobileUserSocket.connect(%{}, socket, connect_info)
|
|
assert result.assigns.peer_ip == "198.51.100.1"
|
|
end
|
|
|
|
test "extracts IP from x-forwarded-for, splitting on comma" do
|
|
Application.put_env(:aprsme, :env, :test)
|
|
socket = %Phoenix.Socket{assigns: %{}}
|
|
|
|
connect_info = %{
|
|
x_headers: [{"x-forwarded-for", "198.51.100.5, 10.0.0.1"}]
|
|
}
|
|
|
|
assert {:ok, result} = MobileUserSocket.connect(%{}, socket, connect_info)
|
|
assert result.assigns.peer_ip == "198.51.100.5"
|
|
end
|
|
|
|
test "extracts IP from x-real-ip" do
|
|
Application.put_env(:aprsme, :env, :test)
|
|
socket = %Phoenix.Socket{assigns: %{}}
|
|
connect_info = %{x_headers: [{"x-real-ip", "198.51.100.7"}]}
|
|
assert {:ok, result} = MobileUserSocket.connect(%{}, socket, connect_info)
|
|
assert result.assigns.peer_ip == "198.51.100.7"
|
|
end
|
|
|
|
test "falls back to 'unknown' when x_headers list contains no known IP header" do
|
|
Application.put_env(:aprsme, :env, :test)
|
|
socket = %Phoenix.Socket{assigns: %{}}
|
|
connect_info = %{x_headers: [{"user-agent", "curl"}]}
|
|
assert {:ok, result} = MobileUserSocket.connect(%{}, socket, connect_info)
|
|
assert result.assigns.peer_ip == "unknown"
|
|
end
|
|
|
|
test "uses peer_data when no x_headers" do
|
|
Application.put_env(:aprsme, :env, :test)
|
|
socket = %Phoenix.Socket{assigns: %{}}
|
|
connect_info = %{peer_data: %{address: {127, 0, 0, 1}}}
|
|
assert {:ok, result} = MobileUserSocket.connect(%{}, socket, connect_info)
|
|
assert result.assigns.peer_ip == "127.0.0.1"
|
|
end
|
|
|
|
test "falls back to 'unknown' for empty connect_info" do
|
|
Application.put_env(:aprsme, :env, :test)
|
|
socket = %Phoenix.Socket{assigns: %{}}
|
|
assert {:ok, result} = MobileUserSocket.connect(%{}, socket, %{})
|
|
assert result.assigns.peer_ip == "unknown"
|
|
end
|
|
end
|
|
|
|
describe "connect/3 in non-test env with rate limits" do
|
|
test "allows first connection from an IP" do
|
|
Application.put_env(:aprsme, :env, :prod)
|
|
socket = %Phoenix.Socket{assigns: %{}}
|
|
ip = unique_ip()
|
|
connect_info = %{x_headers: [{"cf-connecting-ip", ip}]}
|
|
|
|
assert {:ok, _} = MobileUserSocket.connect(%{}, socket, connect_info)
|
|
end
|
|
|
|
test "denies once the per-IP limit is exceeded" do
|
|
Application.put_env(:aprsme, :env, :prod)
|
|
socket = %Phoenix.Socket{assigns: %{}}
|
|
ip = unique_ip()
|
|
connect_info = %{x_headers: [{"cf-connecting-ip", ip}]}
|
|
|
|
# The configured limit is 30 in-module; burn through it, then one more.
|
|
for _ <- 1..30, do: MobileUserSocket.connect(%{}, socket, connect_info)
|
|
assert :error == MobileUserSocket.connect(%{}, socket, connect_info)
|
|
end
|
|
end
|
|
|
|
describe "id/1" do
|
|
test "always returns nil (anonymous socket)" do
|
|
assert MobileUserSocket.id(%Phoenix.Socket{assigns: %{}}) == nil
|
|
end
|
|
end
|
|
end
|