prop/test/support/data_case.ex
Graham McIntire 167ad996d7
Some checks failed
Build and Push / Build and Push Docker Image (push) Has been cancelled
fix: suppress NARR stub noise and sandbox-cleanup Holder shutdown logs
Add default NarrClient 404 stub to DataCase so incidental inline
NarrFetchWorker execution doesn't log 500 errors in unrelated tests.
Add DBConnection.Holder to the sandbox-cleanup noise suppression list
— same benign 'owner exited' category as the already-suppressed
Postgrex.Protocol and DBConnection.Connection.
2026-08-04 17:50:24 -05:00

80 lines
2.1 KiB
Elixir

defmodule Microwaveprop.DataCase do
@moduledoc """
Test case template for tests that need database access.
"""
use ExUnit.CaseTemplate
alias Ecto.Adapters.SQL.Sandbox
alias Microwaveprop.Propagation.ScoreCache
alias Microwaveprop.Weather.GridCache
using do
quote do
import Ecto
import Ecto.Changeset
import Ecto.Query
import Microwaveprop.DataCase
alias Microwaveprop.Repo
end
end
setup tags do
_owner = Microwaveprop.DataCase.setup_sandbox(tags)
Microwaveprop.DataCase.reset_test_state()
Req.Test.set_req_test_from_context(tags)
:ok
end
@doc """
Reset state that persists across tests within a single ExUnit run.
The sandbox isolates the database, but ETS-backed caches
(GridCache, ScoreCache) and on-disk score files survive across
test boundaries because the cache GenServers and filesystem live
in the app's BEAM process tree, not per-test. A single reset
here avoids per-file setup duplication.
"""
@spec reset_test_state :: :ok
def reset_test_state do
dir = Application.get_env(:microwaveprop, :propagation_scores_dir)
if is_binary(dir), do: File.rm_rf(dir)
GridCache.clear()
ScoreCache.clear()
Req.Test.stub(Microwaveprop.Weather.NexradClient, fn conn ->
Plug.Conn.send_resp(conn, 404, "not found")
end)
Req.Test.stub(Microwaveprop.Weather.HrrrClient, fn conn ->
Plug.Conn.send_resp(conn, 404, "not found")
end)
Req.Test.stub(Microwaveprop.Weather.NarrClient, fn conn ->
Plug.Conn.send_resp(conn, 404, "not found")
end)
:ok
end
@spec setup_sandbox(map()) :: pid()
def setup_sandbox(tags) do
shared? = not tags[:async]
pid = Sandbox.start_owner!(Microwaveprop.Repo, shared: shared?)
on_exit(fn -> Sandbox.stop_owner(pid) end)
pid
end
@spec errors_on(Ecto.Changeset.t()) :: map()
def errors_on(changeset) do
Ecto.Changeset.traverse_errors(changeset, fn {message, opts} ->
Regex.replace(~r"%{(\w+)}", message, fn _, key ->
opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string()
end)
end)
end
end