Files
parrhesia/test/parrhesia/api/sync_test.exs

220 lines
6.9 KiB
Elixir

defmodule Parrhesia.API.SyncTest do
use ExUnit.Case, async: false
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()
assert {:ok, stored_server} = Sync.put_server(valid_server(), manager: manager)
assert stored_server.id == "tribes-primary"
assert stored_server.mode == :req_stream
assert stored_server.auth.type == :nip42
assert stored_server.tls.mode == :required
assert stored_server.tls.hostname == "relay-a.example"
assert stored_server.runtime.state == :running
assert File.exists?(path)
assert {:ok, fetched_server} = Sync.get_server("tribes-primary", manager: manager)
assert fetched_server == stored_server
assert {:ok, [listed_server]} = Sync.list_servers(manager: manager)
assert listed_server.id == "tribes-primary"
monitor_ref = Process.monitor(pid)
assert :ok = GenServer.stop(pid)
assert_receive {:DOWN, ^monitor_ref, :process, ^pid, :normal}
assert {:ok, persisted_server} = wait_for_server(manager, "tribes-primary")
assert persisted_server.id == "tribes-primary"
assert persisted_server.tls.hostname == "relay-a.example"
assert persisted_server.runtime.state == :running
end
test "start_server stop_server and sync_now update runtime stats" do
{manager, _path, _pid} = start_sync_manager()
disabled_server = valid_server(%{"id" => "tribes-disabled", "enabled?" => false})
assert {:ok, stored_server} = Sync.put_server(disabled_server, manager: manager)
assert stored_server.runtime.state == :stopped
assert :ok = Sync.start_server("tribes-disabled", manager: manager)
assert {:ok, started_server} = Sync.get_server("tribes-disabled", manager: manager)
assert started_server.runtime.state == :running
assert :ok = Sync.sync_now("tribes-disabled", manager: manager)
assert {:ok, stats} = Sync.server_stats("tribes-disabled", manager: manager)
assert stats["server_id"] == "tribes-disabled"
assert stats["state"] == "running"
assert stats["query_runs"] == 1
assert is_binary(stats["last_sync_started_at"])
assert is_binary(stats["last_sync_completed_at"])
assert :ok = Sync.stop_server("tribes-disabled", manager: manager)
assert {:ok, stopped_server} = Sync.get_server("tribes-disabled", manager: manager)
assert stopped_server.runtime.state == :stopped
assert is_binary(stopped_server.runtime.last_disconnected_at)
assert {:ok, sync_stats} = Sync.sync_stats(manager: manager)
assert sync_stats["servers_total"] == 1
assert sync_stats["servers_enabled"] == 0
assert sync_stats["servers_running"] == 0
assert sync_stats["query_runs"] == 1
assert {:ok, sync_health} = Sync.sync_health(manager: manager)
assert sync_health == %{
"status" => "ok",
"servers_total" => 1,
"servers_connected" => 0,
"servers_failing" => []
}
end
test "put_server rejects invalid sync server shapes" do
{manager, _path, _pid} = start_sync_manager()
assert {:error, :invalid_url} =
Sync.put_server(Map.put(valid_server(), "url", "https://relay-a.example"),
manager: manager
)
assert {:error, :empty_filters} =
Sync.put_server(Map.put(valid_server(), "filters", []), manager: manager)
assert {:error, :invalid_tls_pins} =
Sync.put_server(
put_in(valid_server()["tls"]["pins"], []),
manager: manager
)
end
test "admin executes sync methods against an injected sync manager" do
{manager, _path, _pid} = start_sync_manager()
assert {:ok, created_server} =
Admin.execute("sync_put_server", valid_server(%{"id" => "tribes-admin"}),
manager: manager
)
assert created_server.id == "tribes-admin"
assert {:ok, listed_servers} = Admin.execute("sync_list_servers", %{}, manager: manager)
assert Enum.any?(listed_servers, &(&1.id == "tribes-admin"))
assert {:ok, %{"ok" => true}} =
Admin.execute("sync_sync_now", %{"id" => "tribes-admin"}, manager: manager)
assert {:ok, sync_stats} = Admin.stats(manager: manager)
assert sync_stats["sync"]["servers_total"] == 1
assert sync_stats["sync"]["query_runs"] == 1
assert {:ok, execute_stats} = Admin.execute("stats", %{}, manager: manager)
assert execute_stats["sync"]["servers_total"] == 1
assert {:ok, health} = Admin.health(manager: manager)
assert health["status"] == "ok"
assert health["sync"]["servers_total"] == 1
assert {:ok, execute_health} = Admin.execute("health", %{}, manager: manager)
assert execute_health["status"] == "ok"
assert execute_health["sync"]["servers_total"] == 1
end
defp start_sync_manager do
path = unique_sync_path()
manager = {:global, {:sync_manager, System.unique_integer([:positive, :monotonic])}}
pid = start_supervised!({Manager, name: manager, path: path, start_workers?: false})
{manager, path, pid}
end
defp valid_server(overrides \\ %{}) do
Map.merge(
%{
"id" => "tribes-primary",
"url" => "wss://relay-a.example/relay",
"enabled?" => true,
"auth_pubkey" => String.duplicate("a", 64),
"filters" => [
%{
"kinds" => [5000],
"#r" => ["tribes.accounts.user"]
}
],
"tls" => %{
"pins" => [
%{
"type" => "spki_sha256",
"value" => "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
}
]
},
"metadata" => %{"cluster" => "primary"}
},
overrides
)
end
defp unique_sync_path do
path =
Path.join(
System.tmp_dir!(),
"parrhesia_sync_#{System.unique_integer([:positive, :monotonic])}.json"
)
on_exit(fn ->
_ = File.rm(path)
end)
path
end
defp wait_for_server(manager, server_id, attempts \\ 10)
defp wait_for_server(_manager, _server_id, 0), do: :error
defp wait_for_server(manager, server_id, attempts) do
result =
try do
Sync.get_server(server_id, manager: manager)
catch
:exit, _reason -> {:error, :noproc}
end
case result do
{:ok, server} ->
{:ok, server}
:error ->
receive do
after
10 -> wait_for_server(manager, server_id, attempts - 1)
end
{:error, :noproc} ->
receive do
after
10 -> wait_for_server(manager, server_id, attempts - 1)
end
{:error, {:noproc, _details}} ->
receive do
after
10 -> wait_for_server(manager, server_id, attempts - 1)
end
end
end
end