towerops/test/towerops_web/remote_ip_test.exs
Graham McIntire 7a3950d1ad chore: add jump_credo_checks ~> 0.4 with all 19 checks enabled
All checks set to exit_status: 0 initially to avoid blocking on
pre-existing violations. Remove exit_status overrides as each
check category is cleaned up incrementally.

Dependency added to dev/test only.
2026-06-12 13:45:59 -05:00

111 lines
3.5 KiB
Elixir

defmodule ToweropsWeb.RemoteIpTest do
use ToweropsWeb.ConnCase, async: true
alias ToweropsWeb.RemoteIp
describe "from_conn/1" do
test "extracts the first forwarded IP when from a trusted proxy (10.x)", %{conn: conn} do
conn =
conn
|> Map.put(:remote_ip, {10, 0, 0, 1})
|> put_req_header("x-forwarded-for", "203.0.113.10, 10.0.0.1")
assert RemoteIp.from_conn(conn) == "203.0.113.10"
end
test "extracts the first forwarded IP when from a trusted proxy (172.16-31.x)", %{conn: conn} do
conn =
conn
|> Map.put(:remote_ip, {172, 16, 0, 1})
|> put_req_header("x-forwarded-for", "198.51.100.1, 172.16.0.1")
assert RemoteIp.from_conn(conn) == "198.51.100.1"
end
test "extracts the first forwarded IP when from a trusted proxy (192.168.x)", %{conn: conn} do
conn =
conn
|> Map.put(:remote_ip, {192, 168, 1, 1})
|> put_req_header("x-forwarded-for", "198.51.100.1")
assert RemoteIp.from_conn(conn) == "198.51.100.1"
end
test "uses x-real-ip when x-forwarded-for is missing and from trusted proxy", %{conn: conn} do
conn =
conn
|> Map.put(:remote_ip, {10, 0, 0, 1})
|> put_req_header("x-real-ip", "198.51.100.1")
assert RemoteIp.from_conn(conn) == "198.51.100.1"
end
test "uses remote_ip directly when neither forwarded header is present and from trusted proxy",
%{conn: conn} do
conn = Map.put(conn, :remote_ip, {10, 0, 0, 1})
assert RemoteIp.from_conn(conn) == "10.0.0.1"
end
test "uses remote_ip directly when NOT from a trusted proxy", %{conn: conn} do
# 8.8.8.8 is a public IP, not a trusted proxy — x-forwarded-for is ignored
conn =
conn
|> Map.put(:remote_ip, {8, 8, 8, 8})
|> put_req_header("x-forwarded-for", "203.0.113.10")
assert RemoteIp.from_conn(conn) == "8.8.8.8"
end
test "trims whitespace from forwarded IP", %{conn: conn} do
conn =
conn
|> Map.put(:remote_ip, {10, 0, 0, 1})
|> put_req_header("x-forwarded-for", " 203.0.113.10 , 10.0.0.1")
assert RemoteIp.from_conn(conn) == "203.0.113.10"
end
end
describe "format/1" do
test "formats IPv4 tuple" do
assert RemoteIp.format({192, 168, 1, 1}) == "192.168.1.1"
assert RemoteIp.format({10, 0, 0, 1}) == "10.0.0.1"
assert RemoteIp.format({0, 0, 0, 0}) == "0.0.0.0"
end
test "formats IPv6 addresses in uppercase hex" do
assert RemoteIp.format({8193, 3512, 0, 0, 0, 0, 0, 1}) == "2001:DB8:0:0:0:0:0:1"
end
test "formats loopback IPv6" do
assert RemoteIp.format({0, 0, 0, 0, 0, 0, 0, 1}) == "0:0:0:0:0:0:0:1"
end
test "returns nil for invalid tuple" do
assert RemoteIp.format(:error) == nil
assert RemoteIp.format("not-a-tuple") == nil
assert RemoteIp.format(nil) == nil
end
end
describe "from_socket/1" do
test "returns formatted IP from peer_data in assigns" do
socket = %Phoenix.Socket{
assigns: %{peer_data: %{address: {192, 168, 1, 42}, port: 12_345}}
}
assert RemoteIp.from_socket(socket) == "192.168.1.42"
end
test "returns nil when peer_data is missing" do
socket = %Phoenix.Socket{assigns: %{}}
assert RemoteIp.from_socket(socket) == nil
end
test "returns nil when peer_data has no address tuple" do
socket = %Phoenix.Socket{assigns: %{peer_data: %{}}}
assert RemoteIp.from_socket(socket) == nil
end
end
end