defmodule Aprsme.Swoosh.ResendAdapterTest do use ExUnit.Case, async: true alias Aprsme.Swoosh.ResendAdapter alias Swoosh.Email defp stub_config(plug_name) do [api_key: "re_test_key", plug: {Req.Test, plug_name}] end defp new_email(opts \\ []) do defaults = [ from: {"Sender Name", "sender@example.com"}, to: [{"Recipient", "recipient@example.com"}], subject: "Test Subject", html_body: "

Hello

", text_body: "Hello" ] Email.new(Keyword.merge(defaults, opts)) end describe "deliver/2" do test "posts to Resend API and returns id on success" do Req.Test.stub(__MODULE__, fn conn -> Req.Test.json(conn, %{"id" => "msg_abc123"}) end) email = new_email() assert {:ok, %{id: "msg_abc123"}} = ResendAdapter.deliver(email, stub_config(__MODULE__)) end test "returns error on API error response" do Req.Test.stub(__MODULE__, fn conn -> conn |> Plug.Conn.put_status(422) |> Req.Test.json(%{"name" => "validation_error", "message" => "Invalid email"}) end) email = new_email() assert {:error, {"validation_error", "Invalid email"}} = ResendAdapter.deliver(email, stub_config(__MODULE__)) end test "returns error on network failure" do Req.Test.stub(__MODULE__, fn conn -> Req.Test.transport_error(conn, :econnrefused) end) email = new_email() assert {:error, %Req.TransportError{reason: :econnrefused}} = ResendAdapter.deliver(email, stub_config(__MODULE__)) end test "formats named sender as 'Name '" do Req.Test.stub(__MODULE__, fn conn -> {:ok, body, conn} = Plug.Conn.read_body(conn) decoded = Jason.decode!(body) send(self(), {:request_body, decoded}) Req.Test.json(conn, %{"id" => "msg_1"}) end) email = new_email(from: {"My App", "noreply@example.com"}) ResendAdapter.deliver(email, stub_config(__MODULE__)) assert_received {:request_body, body} assert body["from"] == "My App " end test "formats bare email sender without name prefix" do Req.Test.stub(__MODULE__, fn conn -> {:ok, body, conn} = Plug.Conn.read_body(conn) decoded = Jason.decode!(body) send(self(), {:request_body, decoded}) Req.Test.json(conn, %{"id" => "msg_1"}) end) email = new_email(from: {"", "noreply@example.com"}) ResendAdapter.deliver(email, stub_config(__MODULE__)) assert_received {:request_body, body} assert body["from"] == "noreply@example.com" end test "formats list of recipients as list of email strings" do Req.Test.stub(__MODULE__, fn conn -> {:ok, body, conn} = Plug.Conn.read_body(conn) decoded = Jason.decode!(body) send(self(), {:request_body, decoded}) Req.Test.json(conn, %{"id" => "msg_1"}) end) email = new_email(to: [{"Alice", "alice@example.com"}, {"Bob", "bob@example.com"}]) ResendAdapter.deliver(email, stub_config(__MODULE__)) assert_received {:request_body, body} assert body["to"] == ["alice@example.com", "bob@example.com"] end test "omits nil fields from request body" do Req.Test.stub(__MODULE__, fn conn -> {:ok, body, conn} = Plug.Conn.read_body(conn) decoded = Jason.decode!(body) send(self(), {:request_body, decoded}) Req.Test.json(conn, %{"id" => "msg_1"}) end) email = new_email() ResendAdapter.deliver(email, stub_config(__MODULE__)) assert_received {:request_body, body} refute Map.has_key?(body, "cc") refute Map.has_key?(body, "bcc") refute Map.has_key?(body, "reply_to") end end describe "validate_config/1" do test "returns :ok when api_key is present" do assert :ok = ResendAdapter.validate_config(api_key: "re_abc") end test "returns error when api_key is missing" do assert {:error, _reason} = ResendAdapter.validate_config([]) end end end