Inline error tracker notification code

As we don't need the Discord functionality which brings quite a lot of
extra libraries/code
This commit is contained in:
Claudio Ortolina
2026-03-04 17:19:14 +00:00
parent ed1dd2b440
commit ad9bcd8481
8 changed files with 433 additions and 12 deletions
+154
View File
@@ -0,0 +1,154 @@
defmodule MusicLibrary.ErrorNotifierTest do
use ExUnit.Case, async: false
import Swoosh.TestAssertions
alias MusicLibrary.ErrorNotifier
@config [
from_email: "test@example.com",
to_email: "admin@example.com",
mailer: MusicLibrary.Mailer,
base_url: "https://example.com"
]
defp occurrence(attrs \\ %{}) do
Map.merge(
%{
error_id: 42,
reason: "** (RuntimeError) something went wrong",
context: %{"request.path" => "/test", "live_view.view" => "TestLive"},
stacktrace: %{
lines: [
%{
module: "MyApp.Controller",
function: "index/2",
file: "lib/my_app/controller.ex",
line: 10
},
%{
module: "Phoenix.Router",
function: "call/2",
file: "lib/phoenix/router.ex",
line: 50
}
]
}
},
attrs
)
end
setup :set_swoosh_global
setup do
previous = Application.get_env(:music_library, ErrorNotifier)
case Process.whereis(ErrorNotifier) do
nil -> :ok
pid -> GenServer.stop(pid)
end
on_exit(fn ->
case Process.whereis(ErrorNotifier) do
nil -> :ok
pid -> GenServer.stop(pid)
end
Application.put_env(:music_library, ErrorNotifier, previous || [])
end)
:ok
end
describe "init/1" do
test "returns :ignore when unconfigured" do
Application.put_env(:music_library, ErrorNotifier, [])
assert :ignore = ErrorNotifier.init(:unused)
end
end
describe "Email.send/2" do
test "builds and delivers an email with expected content" do
Application.put_env(:music_library, ErrorNotifier, @config)
assert {:ok, :sent} = ErrorNotifier.Email.send(occurrence(), "New Error! (something)")
assert_email_sent(fn email ->
assert email.subject =~ "[MusicLibrary] Error:"
assert email.subject =~ "controller.ex"
assert [{"", "admin@example.com"}] = email.to
assert {"MusicLibrary", "test@example.com"} = email.from
body = email.html_body
assert body =~ "New Error!"
assert body =~ "something went wrong"
assert body =~ "MyApp.Controller.index/2"
assert body =~ "https://example.com/dev/errors/42"
assert body =~ "/test"
assert body =~ "TestLive"
true
end)
end
test "handles occurrence without stacktrace lines" do
Application.put_env(:music_library, ErrorNotifier, @config)
occ = occurrence(%{stacktrace: %{lines: []}})
assert {:ok, :sent} = ErrorNotifier.Email.send(occ, "Error")
assert_email_sent(fn email ->
assert email.subject =~ "unknown"
assert not (email.html_body =~ "Stack Trace:")
end)
end
end
describe "throttling" do
test "throttles repeated errors within the window" do
Application.put_env(
:music_library,
ErrorNotifier,
Keyword.put(@config, :throttle_seconds, 60)
)
{:ok, pid} = ErrorNotifier.start_link([])
:telemetry.execute([:error_tracker, :error, :new], %{}, %{
error: %{id: 1},
occurrence: occurrence(%{error_id: 1})
})
Process.sleep(50)
assert_email_sent()
# Second notification for same error: throttled
:telemetry.execute([:error_tracker, :occurrence, :new], %{}, %{
occurrence: occurrence(%{error_id: 1})
})
Process.sleep(50)
refute_email_sent()
GenServer.stop(pid)
end
end
describe "telemetry integration" do
test "sends email on :error_tracker :error :new event" do
Application.put_env(:music_library, ErrorNotifier, @config)
{:ok, pid} = ErrorNotifier.start_link([])
:telemetry.execute([:error_tracker, :error, :new], %{}, %{
error: %{id: 99},
occurrence: occurrence(%{error_id: 99})
})
Process.sleep(50)
assert_email_sent(subject: ~r/MusicLibrary/)
GenServer.stop(pid)
end
end
end