defmodule MicrowavepropWeb.Plugs.RemoteIpTest do use ExUnit.Case, async: true import Plug.Conn import Plug.Test alias MicrowavepropWeb.Plugs.RemoteIp defp call_plug(conn) do RemoteIp.call(conn, RemoteIp.init([])) end describe "call/2" do test "extracts IP from cf-connecting-ip header" do conn = :get |> conn("/") |> put_req_header("cf-connecting-ip", "1.2.3.4") |> call_plug() assert conn.remote_ip == {1, 2, 3, 4} end test "falls back to x-forwarded-for when no cf-connecting-ip" do conn = :get |> conn("/") |> put_req_header("x-forwarded-for", "5.6.7.8") |> call_plug() assert conn.remote_ip == {5, 6, 7, 8} end test "prefers cf-connecting-ip over x-forwarded-for" do conn = :get |> conn("/") |> put_req_header("cf-connecting-ip", "1.2.3.4") |> put_req_header("x-forwarded-for", "5.6.7.8") |> call_plug() assert conn.remote_ip == {1, 2, 3, 4} end test "takes first IP from comma-separated x-forwarded-for" do conn = :get |> conn("/") |> put_req_header("x-forwarded-for", "10.0.0.1, 10.0.0.2, 10.0.0.3") |> call_plug() assert conn.remote_ip == {10, 0, 0, 1} end test "preserves original remote_ip when no headers present" do original_ip = {127, 0, 0, 1} conn = :get |> conn("/") |> Map.put(:remote_ip, original_ip) |> call_plug() assert conn.remote_ip == original_ip end test "handles IPv6 addresses" do conn = :get |> conn("/") |> put_req_header("cf-connecting-ip", "::1") |> call_plug() assert conn.remote_ip == {0, 0, 0, 0, 0, 0, 0, 1} end test "ignores malformed IP addresses" do original_ip = {127, 0, 0, 1} conn = :get |> conn("/") |> Map.put(:remote_ip, original_ip) |> put_req_header("cf-connecting-ip", "not-an-ip") |> call_plug() assert conn.remote_ip == original_ip end end end