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