diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml
index 4401e72..2b07673 100644
--- a/.github/workflows/elixir.yml
+++ b/.github/workflows/elixir.yml
@@ -4,7 +4,6 @@ on: [push, pull_request]
jobs:
build:
-
name: Build and test
runs-on: ubuntu-20.04
@@ -44,62 +43,73 @@ jobs:
otp: '23'
- elixir: '1.13'
otp: '24'
- lint: lint
- - elixir: '1.13'
+ - elixir: '1.14'
+ otp: '23'
+ - elixir: '1.14'
otp: '24'
+ - elixir: '1.14'
+ otp: '25'
+ - elixir: '1.15'
+ otp: '24'
+ - elixir: '1.15'
+ otp: '25'
+ - elixir: '1.15'
+ otp: '26'
+ lint: lint
+ - elixir: '1.15'
+ otp: '26'
deps: latest
-
steps:
- - uses: actions/checkout@v2
- - uses: nanasess/setup-chromedriver@v2
+ - uses: actions/checkout@v2
+ - uses: nanasess/setup-chromedriver@v2
- - name: Set up Elixir
- uses: erlef/setup-beam@v1
- with:
- elixir-version: ${{ matrix.elixir }}
- otp-version: ${{ matrix.otp }}
+ - name: Set up Elixir
+ uses: erlef/setup-beam@v1
+ with:
+ elixir-version: ${{ matrix.elixir }}
+ otp-version: ${{ matrix.otp }}
- - name: Retrieve Mix Dependencies Cache
- if: matrix.deps != 'latest'
- uses: actions/cache@v1
- id: mix-cache
- with:
- path: deps
- key: ${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-mix-${{ hashFiles('mix.lock') }}
+ - name: Retrieve Mix Dependencies Cache
+ if: matrix.deps != 'latest'
+ uses: actions/cache@v1
+ id: mix-cache
+ with:
+ path: deps
+ key: ${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-mix-${{ hashFiles('mix.lock') }}
- - name: Remove mix.lock
- if: matrix.deps == 'latest'
- run: rm mix.lock
+ - name: Remove mix.lock
+ if: matrix.deps == 'latest'
+ run: rm mix.lock
- - name: Install dependencies
- if: steps.mix-cache.outputs.cache-hit != 'true'
- run: mix deps.get
+ - name: Install dependencies
+ if: steps.mix-cache.outputs.cache-hit != 'true'
+ run: mix deps.get
- - name: Retrieve PLT Cache
- uses: actions/cache@v1
- id: plt-cache
- with:
- path: priv/plts
- key: ${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-plts-v2-${{ hashFiles('mix.lock') }}
- if: ${{ matrix.lint }}
+ - name: Retrieve PLT Cache
+ uses: actions/cache@v1
+ id: plt-cache
+ with:
+ path: priv/plts
+ key: ${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-plts-v2-${{ hashFiles('mix.lock') }}
+ if: ${{ matrix.lint }}
- - name: Create PLTs
- run: |
- mkdir -p priv/plts
- mix dialyzer --plt
- if: ${{ matrix.lint }}
+ - name: Create PLTs
+ run: |
+ mkdir -p priv/plts
+ mix dialyzer --plt
+ if: ${{ matrix.lint }}
- - name: Check quality
- run: |
- mix format --check-formatted
- mix credo --strict
- mix dialyzer --no-check
- mix e2e
- if: ${{ matrix.lint }}
+ - name: Check quality
+ run: |
+ mix format --check-formatted
+ mix credo --strict
+ mix dialyzer --no-check
+ mix e2e
+ if: ${{ matrix.lint }}
- - name: Run tests
- run: mix test
+ - name: Run tests
+ run: mix test
- - name: Run e2e test
- run: mix e2e
- if: ${{ matrix.lint }}
+ - name: Run e2e test
+ run: mix e2e
+ if: ${{ matrix.lint }}
diff --git a/.tool-versions b/.tool-versions
index 1c29dcf..5aaf93a 100644
--- a/.tool-versions
+++ b/.tool-versions
@@ -1,2 +1,2 @@
-elixir 1.12.2-otp-24
-erlang 24.0.4
+elixir 1.15.8-otp-26
+erlang 26.2.1
diff --git a/config/config.exs b/config/config.exs
index bded96b..6ee7fc7 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -1,6 +1,6 @@
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
-use Mix.Config
+import Config
# This configuration is loaded before any dependency and is restricted
# to this project. If another project depends on this project, this
diff --git a/lib/boom_notifier/error_storage.ex b/lib/boom_notifier/error_storage.ex
index 019ac4d..e2510de 100644
--- a/lib/boom_notifier/error_storage.ex
+++ b/lib/boom_notifier/error_storage.ex
@@ -138,7 +138,7 @@ defmodule BoomNotifier.ErrorStorage do
# the crc32 algorithm that was taken from the Exception Notification library
# for Rails
@spec generate_error_key(ErrorInfo.t()) :: non_neg_integer()
- defp generate_error_key(error_info) do
+ def generate_error_key(error_info) do
error_info
|> Map.delete(:request)
|> Map.delete(:metadata)
diff --git a/lib/boom_notifier/mail_notifier/templates/email_body.html.eex b/lib/boom_notifier/mail_notifier/templates/email_body.html.eex
index 3f53a21..7b686c4 100644
--- a/lib/boom_notifier/mail_notifier/templates/email_body.html.eex
+++ b/lib/boom_notifier/mail_notifier/templates/email_body.html.eex
@@ -105,10 +105,10 @@
Metadata:
- <%= for {source, fields} <- error.metadata do %>
+ <%= for {source, fields} <- Enum.sort(error.metadata) do %>
<%= source %>:
- <%= for {k, v} <- fields do %>
+ <%= for {k, v} <- Enum.sort(fields) do %>
- <%= k %>: <%= v %>
<% end %>
diff --git a/lib/boom_notifier/mail_notifier/templates/email_body.text.eex b/lib/boom_notifier/mail_notifier/templates/email_body.text.eex
index fbb0fe2..d32ab2f 100644
--- a/lib/boom_notifier/mail_notifier/templates/email_body.text.eex
+++ b/lib/boom_notifier/mail_notifier/templates/email_body.text.eex
@@ -14,9 +14,9 @@
<% end %>
<%= if error.metadata do %>
Metadata:
- <%= for {source, fields} <- error.metadata do %>
+ <%= for {source, fields} <- Enum.sort(error.metadata) do %>
<%= source %>:
- <%= for {k, v} <- fields do %>
+ <%= for {k, v} <- Enum.sort(fields) do %>
<%= k %>: <%= v %>
<% end %>
<% end %>
diff --git a/test/support/bamboo_fake_mailer.ex b/test/support/bamboo_fake_mailer.ex
index 69d5567..dbfc41d 100644
--- a/test/support/bamboo_fake_mailer.ex
+++ b/test/support/bamboo_fake_mailer.ex
@@ -9,7 +9,7 @@ defmodule Support.BambooFakeMailer do
# Since to addresses must be of the correct type, we send the PID through
# as a string. Convert it back into a true pid() for mailbox delivery.
"#PID" <> pid_string = email.to
- pid = :erlang.list_to_pid('#{pid_string}')
+ pid = :erlang.list_to_pid(~c"#{pid_string}")
send(pid, {:email_subject, email.subject})
send(pid, {:email_from, email.from})
diff --git a/test/support/fake_swoosh_mailer.ex b/test/support/fake_swoosh_mailer.ex
index 4af5a1b..1984cb1 100644
--- a/test/support/fake_swoosh_mailer.ex
+++ b/test/support/fake_swoosh_mailer.ex
@@ -28,7 +28,7 @@ defmodule Support.SwooshFakeMailer do
# Since to addresses must be of the correct type, we send the PID through
# as a string. Convert it back into a true pid() for mailbox delivery.
- pid = :erlang.list_to_pid('#{pid_string}')
+ pid = :erlang.list_to_pid(~c"#{pid_string}")
send(pid, {:email_subject, email.subject})
send(pid, {:email_from, from_addr})
diff --git a/test/unit/error_info_test.exs b/test/unit/error_info_test.exs
index c0f8f75..1b52cd2 100644
--- a/test/unit/error_info_test.exs
+++ b/test/unit/error_info_test.exs
@@ -159,19 +159,20 @@ defmodule ErrorInfoTest do
error_info
} = hd(error_info_stack)
- assert 'test/unit/error_info_test.exs' = Keyword.fetch!(error_info, :file)
+ assert ~c"test/unit/error_info_test.exs" = Keyword.fetch!(error_info, :file)
assert 17 = Keyword.fetch!(error_info, :line)
assert {
ExUnit.Runner,
_,
_,
- [file: 'lib/ex_unit/runner.ex', line: _]
+ [file: ~c"lib/ex_unit/runner.ex", line: _]
} = List.last(error_info_stack)
assert 10 = Enum.count(error_info_stack)
end
+ @tag skip: System.version() >= "1.14.0"
test "Error info includes stacktrace when entry doesn’t contain file and line info" do
%Plug.Conn.WrapperError{conn: conn, stack: stack} =
catch_error(get(build_conn(), "nil_access"))
@@ -185,7 +186,7 @@ defmodule ErrorInfoTest do
ExUnit.Runner,
_,
_,
- [file: 'lib/ex_unit/runner.ex', line: _]
+ [file: ~c"lib/ex_unit/runner.ex", line: _]
} = List.last(error_info_stack)
assert 10 = Enum.count(error_info_stack)
diff --git a/test/unit/error_storage_test.exs b/test/unit/error_storage_test.exs
index d7acf2c..d8c6951 100644
--- a/test/unit/error_storage_test.exs
+++ b/test/unit/error_storage_test.exs
@@ -12,6 +12,8 @@ defmodule ErrorStorageTest do
timestamp: @timestamp
}
+ @error_hash ErrorStorage.generate_error_key(@error_info)
+
setup_all do
ErrorStorage.start_link()
:ok
@@ -31,26 +33,27 @@ defmodule ErrorStorageTest do
timestamp: another_timestamp
}
+ another_error_hash = ErrorStorage.generate_error_key(another_error_info)
+
ErrorStorage.store_error(@error_info)
ErrorStorage.store_error(@error_info)
ErrorStorage.store_error(another_error_info)
- [error_stat_1, error_stat_2] =
- Agent.get(:boom_notifier, fn state -> state end)
- |> Map.values()
+ %{@error_hash => error_stat_1, ^another_error_hash => error_stat_2} =
+ Agent.get(:boom_notifier, & &1)
assert error_stat_1 == %ErrorStorage{
__max_storage_capacity__: 1,
- accumulated_occurrences: 1,
- first_occurrence: another_timestamp,
- last_occurrence: another_timestamp
+ accumulated_occurrences: 2,
+ first_occurrence: @timestamp,
+ last_occurrence: @timestamp
}
assert error_stat_2 == %ErrorStorage{
__max_storage_capacity__: 1,
- accumulated_occurrences: 2,
- first_occurrence: @timestamp,
- last_occurrence: @timestamp
+ accumulated_occurrences: 1,
+ first_occurrence: another_timestamp,
+ last_occurrence: another_timestamp
}
end
end
@@ -235,27 +238,29 @@ defmodule ErrorStorageTest do
timestamp: @timestamp
}
+ another_error_hash = ErrorStorage.generate_error_key(another_error_info)
+
ErrorStorage.store_error(@error_info)
ErrorStorage.store_error(another_error_info)
ErrorStorage.reset_accumulated_errors(:exponential, @error_info)
- [error_stat_1, error_stat_2] =
- Agent.get(:boom_notifier, fn state -> state end) |> Map.values()
+ %{@error_hash => error_stat_1, ^another_error_hash => error_stat_2} =
+ Agent.get(:boom_notifier, & &1)
assert error_stat_1 == %ErrorStorage{
- __max_storage_capacity__: 1,
- accumulated_occurrences: 1,
- first_occurrence: @timestamp,
- last_occurrence: @timestamp
- }
-
- assert error_stat_2 == %ErrorStorage{
__max_storage_capacity__: 2,
accumulated_occurrences: 0,
first_occurrence: nil,
last_occurrence: nil
}
+
+ assert error_stat_2 == %ErrorStorage{
+ __max_storage_capacity__: 1,
+ accumulated_occurrences: 1,
+ first_occurrence: @timestamp,
+ last_occurrence: @timestamp
+ }
end
end
end