Refactor test runtime ownership
Some checks failed
CI / Test (OTP 27.2 / Elixir 1.18.2) (push) Failing after 0s
CI / Test (OTP 28.4 / Elixir 1.19.4 + Marmot E2E) (push) Failing after 0s

This commit is contained in:
2026-03-17 12:06:32 +01:00
parent 35c8d50db0
commit f4d94c9fcb
37 changed files with 142 additions and 247 deletions

View File

@@ -5,19 +5,6 @@ defmodule Parrhesia.Application do
@impl true
def start(_type, _args) do
children = [
Parrhesia.Telemetry,
Parrhesia.Config,
Parrhesia.Storage.Supervisor,
Parrhesia.Subscriptions.Supervisor,
Parrhesia.Auth.Supervisor,
Parrhesia.Sync.Supervisor,
Parrhesia.Policy.Supervisor,
Parrhesia.Web.Endpoint,
Parrhesia.Tasks.Supervisor
]
opts = [strategy: :one_for_one, name: Parrhesia.Supervisor]
Supervisor.start_link(children, opts)
Parrhesia.Runtime.start_link(name: Parrhesia.Supervisor)
end
end

29
lib/parrhesia/runtime.ex Normal file
View File

@@ -0,0 +1,29 @@
defmodule Parrhesia.Runtime do
@moduledoc false
use Supervisor
def start_link(opts \\ []) do
name = Keyword.get(opts, :name, Parrhesia.Supervisor)
Supervisor.start_link(__MODULE__, opts, name: name)
end
@impl true
def init(_opts) do
Supervisor.init(children(), strategy: :one_for_one)
end
def children do
[
Parrhesia.Telemetry,
Parrhesia.Config,
Parrhesia.Storage.Supervisor,
Parrhesia.Subscriptions.Supervisor,
Parrhesia.Auth.Supervisor,
Parrhesia.Sync.Supervisor,
Parrhesia.Policy.Supervisor,
Parrhesia.Web.Endpoint,
Parrhesia.Tasks.Supervisor
]
end
end

View File

@@ -6,6 +6,7 @@ defmodule Parrhesia.MixProject do
app: :parrhesia,
version: "0.5.0",
elixir: "~> 1.18",
elixirc_paths: elixirc_paths(Mix.env()),
start_permanent: Mix.env() == :prod,
deps: deps(),
aliases: aliases()
@@ -20,6 +21,9 @@ defmodule Parrhesia.MixProject do
]
end
defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_env), do: ["lib"]
def cli do
[preferred_envs: [precommit: :test, bench: :test]]
end

View File

@@ -1,14 +1,10 @@
defmodule Parrhesia.API.ACLTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false, sandbox: true
alias Ecto.Adapters.SQL.Sandbox
alias Parrhesia.API.ACL
alias Parrhesia.API.RequestContext
alias Parrhesia.Repo
setup do
:ok = Sandbox.checkout(Repo)
previous_acl = Application.get_env(:parrhesia, :acl, [])
Application.put_env(

View File

@@ -1,16 +1,9 @@
defmodule Parrhesia.API.EventsTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false, sandbox: true
alias Ecto.Adapters.SQL.Sandbox
alias Parrhesia.API.Events
alias Parrhesia.API.RequestContext
alias Parrhesia.Protocol.EventValidator
alias Parrhesia.Repo
setup do
:ok = Sandbox.checkout(Repo)
:ok
end
test "publish stores valid events through the shared API" do
event = valid_event()

View File

@@ -1,5 +1,5 @@
defmodule Parrhesia.API.IdentityTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false
alias Parrhesia.API.Auth
alias Parrhesia.API.Identity

View File

@@ -1,19 +1,10 @@
defmodule Parrhesia.API.StreamTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false, sandbox: true
alias Ecto.Adapters.SQL.Sandbox
alias Parrhesia.API.Events
alias Parrhesia.API.RequestContext
alias Parrhesia.API.Stream
alias Parrhesia.Protocol.EventValidator
alias Parrhesia.Repo
setup do
ensure_repo_started()
ensure_stream_runtime_started()
:ok = Sandbox.checkout(Repo)
:ok
end
test "subscribe streams catch-up events followed by eose" do
event = valid_event()
@@ -79,24 +70,4 @@ defmodule Parrhesia.API.StreamTest do
defp recalculate_event_id(event) do
Map.put(event, "id", EventValidator.compute_id(event))
end
defp ensure_stream_runtime_started do
if is_nil(Process.whereis(Parrhesia.API.Stream.Supervisor)) do
case start_supervised({Parrhesia.Subscriptions.Supervisor, []}) do
{:ok, _pid} -> :ok
{:error, {:already_started, _pid}} -> :ok
end
else
:ok
end
end
defp ensure_repo_started do
if is_nil(Process.whereis(Repo)) do
_ = start_supervised(Repo)
:ok
else
:ok
end
end
end

View File

@@ -1,16 +1,9 @@
defmodule Parrhesia.API.SyncTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false, sandbox: true
alias Ecto.Adapters.SQL.Sandbox
alias Parrhesia.API.Admin
alias Parrhesia.API.Sync
alias Parrhesia.API.Sync.Manager
alias Parrhesia.Repo
setup do
:ok = Sandbox.checkout(Repo)
:ok
end
test "put_server stores normalized config and persists it across restart" do
{manager, path, pid} = start_sync_manager()

View File

@@ -1,5 +1,5 @@
defmodule Parrhesia.ApplicationTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false
test "starts the core supervision tree" do
assert is_pid(Process.whereis(Parrhesia.Supervisor))

View File

@@ -1,5 +1,5 @@
defmodule Parrhesia.E2E.NakCliTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false
@moduletag :nak_e2e

View File

@@ -1,5 +1,5 @@
defmodule Parrhesia.Fanout.MultiNodeTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false
alias Parrhesia.Fanout.MultiNode
alias Parrhesia.Subscriptions.Index

View File

@@ -1,17 +1,13 @@
defmodule Parrhesia.FaultInjectionGroupFlowTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false, sandbox: true
alias Ecto.Adapters.SQL.Sandbox
alias Parrhesia.Protocol.EventValidator
alias Parrhesia.Repo
alias Parrhesia.Storage
alias Parrhesia.TestSupport.FailingEvents
alias Parrhesia.TestSupport.PermissiveModeration
alias Parrhesia.Web.Connection
setup do
:ok = Sandbox.checkout(Repo)
previous_storage = Application.get_env(:parrhesia, :storage, [])
Application.put_env(

View File

@@ -1,5 +1,5 @@
defmodule Parrhesia.FaultInjectionTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false
alias Parrhesia.Protocol.EventValidator
alias Parrhesia.Web.Connection

View File

@@ -1,16 +1,9 @@
defmodule Parrhesia.Groups.FlowTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false, sandbox: true
alias Ecto.Adapters.SQL.Sandbox
alias Parrhesia.Groups.Flow
alias Parrhesia.Repo
alias Parrhesia.Storage
setup do
:ok = Sandbox.checkout(Repo)
:ok
end
test "handles membership request kinds by upserting group memberships" do
event = %{
"kind" => 8_000,

View File

@@ -1,7 +1,6 @@
defmodule Parrhesia.Negentropy.SessionsTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false, sandbox: true
alias Ecto.Adapters.SQL.Sandbox
alias Parrhesia.Negentropy.Engine
alias Parrhesia.Negentropy.Message
alias Parrhesia.Negentropy.Sessions
@@ -9,20 +8,6 @@ defmodule Parrhesia.Negentropy.SessionsTest do
alias Parrhesia.Repo
alias Parrhesia.Storage.Adapters.Postgres.Events
setup_all do
if is_nil(Process.whereis(Repo)) do
start_supervised!(Repo)
end
Sandbox.mode(Repo, :manual)
:ok
end
setup do
:ok = Sandbox.checkout(Repo)
:ok
end
test "opens, responds, advances and closes sessions" do
server = start_supervised!({Sessions, name: nil})
Sandbox.allow(Repo, self(), server)

View File

@@ -1,14 +1,10 @@
defmodule Parrhesia.Performance.LoadSoakTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false, sandbox: true
alias Ecto.Adapters.SQL.Sandbox
alias Parrhesia.Repo
alias Parrhesia.Web.Connection
@tag :performance
test "fanout enqueue/drain stays within relaxed p95 budget" do
:ok = Sandbox.checkout(Repo)
{:ok, state} = Connection.init(subscription_index: nil, max_outbound_queue: 10_000)
req_payload = JSON.encode!(["REQ", "sub-load", %{"kinds" => [1]}])

View File

@@ -1,5 +1,5 @@
defmodule Parrhesia.Policy.EventPolicyTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false
alias Parrhesia.Policy.EventPolicy

View File

@@ -1,5 +1,5 @@
defmodule Parrhesia.Protocol.EventValidatorSignatureTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false
alias Parrhesia.Protocol.EventValidator

View File

@@ -1,5 +1,5 @@
defmodule Parrhesia.Storage.Adapters.Memory.AdapterTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false
alias Parrhesia.Storage.Adapters.Memory.ACL
alias Parrhesia.Storage.Adapters.Memory.Admin

View File

@@ -1,26 +1,11 @@
defmodule Parrhesia.Storage.Adapters.Postgres.AdapterContractTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false, sandbox: true
alias Ecto.Adapters.SQL.Sandbox
alias Parrhesia.Repo
alias Parrhesia.Storage.Adapters.Postgres.ACL
alias Parrhesia.Storage.Adapters.Postgres.Admin
alias Parrhesia.Storage.Adapters.Postgres.Groups
alias Parrhesia.Storage.Adapters.Postgres.Moderation
setup_all do
if is_nil(Process.whereis(Repo)) do
start_supervised!(Repo)
end
Sandbox.mode(Repo, :manual)
:ok
end
setup do
:ok = Sandbox.checkout(Repo)
end
test "moderation adapter persists pubkey/event/ip block state" do
pubkey = String.duplicate("a", 64)
event_id = String.duplicate("b", 64)

View File

@@ -1,16 +1,9 @@
defmodule Parrhesia.Storage.Adapters.Postgres.EventsLifecycleTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false, sandbox: true
alias Ecto.Adapters.SQL.Sandbox
alias Parrhesia.Protocol.EventValidator
alias Parrhesia.Repo
alias Parrhesia.Storage.Adapters.Postgres.Events
setup do
:ok = Sandbox.checkout(Repo)
:ok
end
test "event tags round-trip without truncation" do
tagged_event =
event(%{

View File

@@ -1,24 +1,9 @@
defmodule Parrhesia.Storage.Adapters.Postgres.EventsQueryCountTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false, sandbox: true
alias Ecto.Adapters.SQL.Sandbox
alias Parrhesia.Protocol.EventValidator
alias Parrhesia.Repo
alias Parrhesia.Storage.Adapters.Postgres.Events
setup_all do
if is_nil(Process.whereis(Repo)) do
start_supervised!(Repo)
end
Sandbox.mode(Repo, :manual)
:ok
end
setup do
:ok = Sandbox.checkout(Repo)
end
test "query/3 translates NIP filters including tag filters" do
author = String.duplicate("a", 64)
other_author = String.duplicate("b", 64)

View File

@@ -1,5 +1,5 @@
defmodule Parrhesia.Storage.Adapters.Postgres.EventsTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false
alias Parrhesia.Protocol.EventValidator
alias Parrhesia.Storage.Adapters.Postgres.Events

View File

@@ -1,22 +1,11 @@
defmodule Parrhesia.Storage.Adapters.Postgres.QueryPlanRegressionTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false, sandbox: true
alias Ecto.Adapters.SQL.Sandbox
alias Parrhesia.Protocol.EventValidator
alias Parrhesia.Repo
alias Parrhesia.Storage.Adapters.Postgres.Events
setup_all do
if is_nil(Process.whereis(Repo)) do
start_supervised!(Repo)
end
Sandbox.mode(Repo, :manual)
:ok
end
setup do
:ok = Sandbox.checkout(Repo)
:ok = Repo.query!("SET enable_seqscan TO off") |> then(fn _ -> :ok end)
end

View File

@@ -1,15 +1,8 @@
defmodule Parrhesia.Storage.PartitionsTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false, sandbox: true
alias Ecto.Adapters.SQL.Sandbox
alias Parrhesia.Repo
alias Parrhesia.Storage.Partitions
setup do
:ok = Sandbox.checkout(Repo)
:ok
end
test "list_partitions returns partition tables" do
partitions = Partitions.list_partitions()
assert is_list(partitions)

View File

@@ -1,5 +1,5 @@
defmodule Parrhesia.StorageTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false
alias Parrhesia.Storage

View File

@@ -1,29 +1,16 @@
defmodule Parrhesia.Sync.WorkerTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false, sandbox: :shared
alias Ecto.Adapters.SQL.Sandbox
alias Parrhesia.API.ACL
alias Parrhesia.API.Events
alias Parrhesia.API.Identity
alias Parrhesia.API.RequestContext
alias Parrhesia.API.Sync
alias Parrhesia.Protocol.EventValidator
alias Parrhesia.Repo
alias Parrhesia.Sync.Supervisor
alias Parrhesia.TestSupport.SyncFakeRelay.Plug
alias Parrhesia.TestSupport.SyncFakeRelay.Server
setup do
:ok = Sandbox.checkout(Repo)
Sandbox.mode(Repo, {:shared, self()})
on_exit(fn ->
Sandbox.mode(Repo, :manual)
end)
:ok
end
test "req_stream worker verifies remote identity, authenticates, syncs catch-up, streams live, and sync_now reruns catch-up" do
{:ok, %{pubkey: local_pubkey}} = Identity.ensure()
remote_pubkey = String.duplicate("b", 64)

View File

@@ -1,5 +1,5 @@
defmodule Parrhesia.Tasks.ExpirationWorkerTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false
alias Parrhesia.Tasks.ExpirationWorker
alias Parrhesia.TestSupport.ExpirationStubEvents

View File

@@ -1,5 +1,5 @@
defmodule Parrhesia.Tasks.PartitionRetentionWorkerTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false
alias Parrhesia.Tasks.PartitionRetentionWorker
alias Parrhesia.TestSupport.PartitionRetentionStubPartitions

View File

@@ -1,17 +1,10 @@
defmodule Parrhesia.Web.ConformanceTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false, sandbox: true
alias Ecto.Adapters.SQL.Sandbox
alias Parrhesia.Protocol.EventValidator
alias Parrhesia.Repo
alias Parrhesia.Storage
alias Parrhesia.Web.Connection
setup do
:ok = Sandbox.checkout(Repo)
:ok
end
test "REQ -> EOSE emitted once and CLOSE emits CLOSED" do
{:ok, state} = Connection.init(subscription_index: nil)

View File

@@ -1,23 +1,14 @@
defmodule Parrhesia.Web.ConnectionTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false, sandbox: true
alias Ecto.Adapters.SQL.Sandbox
alias Parrhesia.API.ACL
alias Parrhesia.API.Events
alias Parrhesia.API.RequestContext
alias Parrhesia.Negentropy.Engine
alias Parrhesia.Negentropy.Message
alias Parrhesia.Protocol.EventValidator
alias Parrhesia.Repo
alias Parrhesia.Web.Connection
setup do
ensure_repo_started()
ensure_stream_runtime_started()
:ok = Sandbox.checkout(Repo)
:ok
end
test "REQ registers subscription, streams initial events and replies with EOSE" do
state = connection_state()
@@ -876,26 +867,6 @@ defmodule Parrhesia.Web.ConnectionTest do
state
end
defp ensure_stream_runtime_started do
if is_nil(Process.whereis(Parrhesia.API.Stream.Supervisor)) do
case start_supervised({Parrhesia.Subscriptions.Supervisor, []}) do
{:ok, _pid} -> :ok
{:error, {:already_started, _pid}} -> :ok
end
else
:ok
end
end
defp ensure_repo_started do
if is_nil(Process.whereis(Repo)) do
_ = start_supervised(Repo)
:ok
else
:ok
end
end
defp listener(overrides) do
base = %{
id: :test,

View File

@@ -1,9 +1,7 @@
defmodule Parrhesia.Web.ProxyIpE2ETest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false, sandbox: :shared
alias __MODULE__.TestClient
alias Ecto.Adapters.SQL.Sandbox
alias Parrhesia.Repo
setup_all do
{:ok, _apps} = Application.ensure_all_started(:websockex)
@@ -11,14 +9,10 @@ defmodule Parrhesia.Web.ProxyIpE2ETest do
end
setup do
:ok = Sandbox.checkout(Repo)
Sandbox.mode(Repo, {:shared, self()})
previous_trusted_proxies = Application.get_env(:parrhesia, :trusted_proxies, [])
on_exit(fn ->
Application.put_env(:parrhesia, :trusted_proxies, previous_trusted_proxies)
Sandbox.mode(Repo, :manual)
end)
{:ok, port: free_port()}

View File

@@ -1,21 +1,14 @@
defmodule Parrhesia.Web.RouterTest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false, sandbox: true
import Plug.Conn
import Plug.Test
alias Ecto.Adapters.SQL.Sandbox
alias Parrhesia.API.Sync
alias Parrhesia.Protocol.EventValidator
alias Parrhesia.Repo
alias Parrhesia.Web.Listener
alias Parrhesia.Web.Router
setup do
:ok = Sandbox.checkout(Repo)
:ok
end
test "GET /health returns ok" do
conn = conn(:get, "/health") |> Router.call([])

View File

@@ -1,16 +1,11 @@
defmodule Parrhesia.Web.TLSE2ETest do
use ExUnit.Case, async: false
use Parrhesia.IntegrationCase, async: false, sandbox: :shared
alias Ecto.Adapters.SQL.Sandbox
alias Parrhesia.Repo
alias Parrhesia.Sync.Transport.WebSockexClient
alias Parrhesia.TestSupport.TLSCerts
alias Parrhesia.Web.Endpoint
setup do
:ok = Sandbox.checkout(Repo)
Sandbox.mode(Repo, {:shared, self()})
tmp_dir =
Path.join(
System.tmp_dir!(),
@@ -20,7 +15,6 @@ defmodule Parrhesia.Web.TLSE2ETest do
File.mkdir_p!(tmp_dir)
on_exit(fn ->
Sandbox.mode(Repo, :manual)
_ = File.rm_rf(tmp_dir)
end)

View File

@@ -0,0 +1,43 @@
defmodule Parrhesia.IntegrationCase do
@moduledoc false
use ExUnit.CaseTemplate
alias Ecto.Adapters.SQL.Sandbox
alias ExUnit.Case
alias Parrhesia.Repo
alias Parrhesia.TestSupport.Runtime
using opts do
quote bind_quoted: [opts: opts] do
use Case, async: Keyword.get(opts, :async, false)
alias Ecto.Adapters.SQL.Sandbox
alias Parrhesia.Repo
@moduletag parrhesia_sandbox: Keyword.get(opts, :sandbox, false)
end
end
setup tags do
Runtime.ensure_started!()
case tags[:parrhesia_sandbox] do
false ->
:ok
true ->
:ok = Sandbox.checkout(Repo)
:shared ->
:ok = Sandbox.checkout(Repo)
Sandbox.mode(Repo, {:shared, self()})
on_exit(fn ->
Sandbox.mode(Repo, :manual)
end)
end
:ok
end
end

34
test/support/runtime.ex Normal file
View File

@@ -0,0 +1,34 @@
defmodule Parrhesia.TestSupport.Runtime do
@moduledoc false
@required_processes [
Parrhesia.Supervisor,
Parrhesia.Config,
Parrhesia.Repo,
Parrhesia.Subscriptions.Supervisor,
Parrhesia.Web.Endpoint
]
def ensure_started! do
if healthy?() do
:ok
else
restart!()
end
end
defp healthy? do
Enum.all?(@required_processes, &is_pid(Process.whereis(&1)))
end
defp restart! do
case Application.stop(:parrhesia) do
:ok -> :ok
{:error, {:not_started, :parrhesia}} -> :ok
{:error, {:not_started, _app}} -> :ok
end
{:ok, _apps} = Application.ensure_all_started(:parrhesia)
:ok
end
end

View File

@@ -5,6 +5,4 @@ exclude_tags =
[:nak_e2e]
end
{:ok, _apps} = Application.ensure_all_started(:parrhesia)
ExUnit.start(exclude: exclude_tags)