towerops/test/towerops_web/plugs/remote_ip_logger_test.exs

81 lines
2.5 KiB
Elixir

defmodule ToweropsWeb.Plugs.RemoteIpLoggerTest do
use ToweropsWeb.ConnCase, async: true
alias ToweropsWeb.Plugs.RemoteIpLogger
describe "init/1" do
test "returns options unchanged" do
assert RemoteIpLogger.init([]) == []
assert RemoteIpLogger.init(some: :option) == [some: :option]
end
end
describe "call/2" do
test "extracts IP from X-Forwarded-For header" do
build_conn()
|> put_req_header("x-forwarded-for", "203.0.113.195")
|> RemoteIpLogger.call([])
assert Logger.metadata()[:remote_ip] == "203.0.113.195"
end
test "extracts first IP from comma-separated X-Forwarded-For" do
build_conn()
|> put_req_header("x-forwarded-for", "203.0.113.195, 10.0.0.1, 192.168.1.1")
|> RemoteIpLogger.call([])
assert Logger.metadata()[:remote_ip] == "203.0.113.195"
end
test "trims whitespace from X-Forwarded-For IP" do
build_conn()
|> put_req_header("x-forwarded-for", " 203.0.113.195 , 10.0.0.1")
|> RemoteIpLogger.call([])
assert Logger.metadata()[:remote_ip] == "203.0.113.195"
end
test "falls back to X-Real-IP when X-Forwarded-For is not present" do
build_conn()
|> put_req_header("x-real-ip", "198.51.100.42")
|> RemoteIpLogger.call([])
assert Logger.metadata()[:remote_ip] == "198.51.100.42"
end
test "formats IPv4 from conn.remote_ip when no proxy headers present" do
# ConnCase builds a conn with remote_ip {127, 0, 0, 1}
RemoteIpLogger.call(build_conn(), [])
assert Logger.metadata()[:remote_ip] == "127.0.0.1"
end
test "formats IPv6 from conn.remote_ip" do
build_conn()
|> Map.put(:remote_ip, {8193, 3512, 0, 0, 0, 0, 0, 1})
|> RemoteIpLogger.call([])
# IPv6 formatted as hex segments separated by colons (uppercase)
assert Logger.metadata()[:remote_ip] == "2001:DB8:0:0:0:0:0:1"
end
test "prefers X-Forwarded-For over X-Real-IP" do
build_conn()
|> put_req_header("x-forwarded-for", "203.0.113.195")
|> put_req_header("x-real-ip", "198.51.100.42")
|> RemoteIpLogger.call([])
assert Logger.metadata()[:remote_ip] == "203.0.113.195"
end
test "returns conn unchanged" do
original_conn = build_conn()
result_conn = RemoteIpLogger.call(original_conn, [])
# Conn should pass through unchanged (only metadata is modified)
assert result_conn.adapter == original_conn.adapter
assert result_conn.method == original_conn.method
assert result_conn.path_info == original_conn.path_info
end
end
end