defmodule Towerops.HoneybadgerNoticeFilterTest do
  use ExUnit.Case, async: true

  import Swoosh.TestAssertions

  alias Towerops.HoneybadgerNoticeFilter

  defp build_notice(attrs \\ %{}) do
    default_error = %{
      class: "RuntimeError",
      message: "something went wrong",
      backtrace: [
        %{file: "lib/towerops_web/live/dashboard_live.ex", number: 42, method: "mount/3"},
        %{file: "lib/phoenix_live_view/channel.ex", number: 100, method: "handle_info/2"}
      ],
      tags: [],
      fingerprint: ""
    }

    default_server = %{
      environment_name: :test,
      hostname: "test-host",
      project_root: "/app",
      revision: nil
    }

    %Honeybadger.Notice{
      error: Map.merge(default_error, Map.get(attrs, :error, %{})),
      server: Map.merge(default_server, Map.get(attrs, :server, %{})),
      request: Map.get(attrs, :request, %{}),
      notifier: %{name: "honeybadger-elixir", url: "", version: "0.0.0"},
      breadcrumbs: %{},
      correlation_context: %{}
    }
  end

  describe "filter/1" do
    test "returns the notice unchanged so Honeybadger still receives it" do
      notice = build_notice()

      result = HoneybadgerNoticeFilter.filter(notice)

      assert result == notice
    end

    test "sends an email with the error class and message" do
      notice = build_notice(%{error: %{class: "ArgumentError", message: "bad argument"}})

      HoneybadgerNoticeFilter.filter(notice)

      assert_email_sent(fn email ->
        assert email.to == [{"", "graham@mcintire.me"}]
        assert email.subject =~ "ArgumentError"
        assert email.subject =~ "bad argument"
      end)
    end

    test "email body contains the full stacktrace" do
      notice =
        build_notice(%{
          error: %{
            backtrace: [
              %{file: "lib/my_app/worker.ex", number: 10, method: "perform/1"},
              %{file: "lib/oban/queue/executor.ex", number: 55, method: "safe_call/1"}
            ]
          }
        })

      HoneybadgerNoticeFilter.filter(notice)

      assert_email_sent(fn email ->
        body = email.text_body
        assert body =~ "lib/my_app/worker.ex:10 in perform/1"
        assert body =~ "lib/oban/queue/executor.ex:55 in safe_call/1"
      end)
    end

    test "email body includes server environment info" do
      notice =
        build_notice(%{
          server: %{environment_name: :prod, hostname: "web-1.towerops.net"}
        })

      HoneybadgerNoticeFilter.filter(notice)

      assert_email_sent(fn email ->
        body = email.text_body
        assert body =~ "prod"
        assert body =~ "web-1.towerops.net"
      end)
    end

    test "email is sent from the configured mailer_from address" do
      notice = build_notice()

      HoneybadgerNoticeFilter.filter(notice)

      assert_email_sent(fn email ->
        assert email.from == {"Towerops", "hi@towerops.net"}
      end)
    end

    test "drops notice with port_died error message (no email, no report)" do
      notice =
        build_notice(%{
          error: %{
            class: "ErlangError",
            message: "Erlang error: {:port_died, :normal}"
          }
        })

      assert HoneybadgerNoticeFilter.filter(notice) == nil
      assert_no_email_sent()
    end

    test "drops notice with write_failed and epipe error message" do
      notice =
        build_notice(%{
          error: %{
            class: "ErlangError",
            message: "{:write_failed, :standard_io, :enoent, {:no_translation, :unicode, :latin1}} {:device, :epipe}"
          }
        })

      assert HoneybadgerNoticeFilter.filter(notice) == nil
      assert_no_email_sent()
    end

    test "does not drop write_failed without epipe" do
      notice =
        build_notice(%{
          error: %{
            class: "ErlangError",
            message: "{:write_failed, :standard_io, :enoent}"
          }
        })

      result = HoneybadgerNoticeFilter.filter(notice)

      assert result == notice
    end

    test "does not drop unrelated errors" do
      notice =
        build_notice(%{
          error: %{
            class: "RuntimeError",
            message: "something totally unrelated"
          }
        })

      result = HoneybadgerNoticeFilter.filter(notice)

      assert result == notice
    end
  end
end
