Implement remaining Postgres storage adapters and contracts

This commit is contained in:
2026-03-13 20:46:50 +01:00
parent 693786615f
commit 336b192492
7 changed files with 905 additions and 49 deletions

View File

@@ -0,0 +1,130 @@
defmodule Parrhesia.Storage.Adapters.Postgres.AdapterContractTest do
use ExUnit.Case, async: false
alias Ecto.Adapters.SQL.Sandbox
alias Parrhesia.Repo
alias Parrhesia.Storage.Adapters.Postgres.Admin
alias Parrhesia.Storage.Adapters.Postgres.Groups
alias Parrhesia.Storage.Adapters.Postgres.Moderation
setup_all do
start_supervised!(Repo)
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)
assert {:ok, false} = Moderation.pubkey_banned?(%{}, pubkey)
assert :ok = Moderation.ban_pubkey(%{}, pubkey)
assert :ok = Moderation.ban_pubkey(%{}, pubkey)
assert {:ok, true} = Moderation.pubkey_banned?(%{}, pubkey)
assert :ok = Moderation.unban_pubkey(%{}, pubkey)
assert {:ok, false} = Moderation.pubkey_banned?(%{}, pubkey)
assert {:ok, false} = Moderation.pubkey_allowed?(%{}, pubkey)
assert :ok = Moderation.allow_pubkey(%{}, pubkey)
assert {:ok, true} = Moderation.pubkey_allowed?(%{}, pubkey)
assert :ok = Moderation.disallow_pubkey(%{}, pubkey)
assert {:ok, false} = Moderation.pubkey_allowed?(%{}, pubkey)
assert {:ok, false} = Moderation.event_banned?(%{}, event_id)
assert :ok = Moderation.ban_event(%{}, event_id)
assert {:ok, true} = Moderation.event_banned?(%{}, event_id)
assert :ok = Moderation.unban_event(%{}, event_id)
assert {:ok, false} = Moderation.event_banned?(%{}, event_id)
assert {:ok, false} = Moderation.ip_blocked?(%{}, "127.0.0.1")
assert :ok = Moderation.block_ip(%{}, "127.0.0.1")
assert {:ok, true} = Moderation.ip_blocked?(%{}, "127.0.0.1")
assert :ok = Moderation.unblock_ip(%{}, "127.0.0.1")
assert {:ok, false} = Moderation.ip_blocked?(%{}, "127.0.0.1")
end
test "groups adapter upserts and lists memberships and roles" do
group_id = "group-alpha"
member_pubkey = String.duplicate("c", 64)
assert {:ok, membership} =
Groups.put_membership(%{}, %{
group_id: group_id,
pubkey: member_pubkey,
role: "member",
metadata: %{"joined_via" => "invite"}
})
assert membership.group_id == group_id
assert membership.pubkey == member_pubkey
assert membership.role == "member"
assert {:ok, fetched_membership} = Groups.get_membership(%{}, group_id, member_pubkey)
assert fetched_membership.metadata == %{"joined_via" => "invite"}
assert {:ok, updated_membership} =
Groups.put_membership(%{}, %{
"group_id" => group_id,
"pubkey" => member_pubkey,
"role" => "admin",
"metadata" => %{"joined_via" => "promoted"}
})
assert updated_membership.role == "admin"
assert {:ok, memberships} = Groups.list_memberships(%{}, group_id)
assert Enum.map(memberships, &{&1.pubkey, &1.role}) == [{member_pubkey, "admin"}]
assert {:ok, role} =
Groups.put_role(%{}, %{
group_id: group_id,
pubkey: member_pubkey,
role: "moderator",
metadata: %{"scope" => "global"}
})
assert role.role == "moderator"
assert {:ok, roles} = Groups.list_roles(%{}, group_id, member_pubkey)
assert Enum.map(roles, & &1.role) == ["moderator"]
assert :ok = Groups.delete_role(%{}, group_id, member_pubkey, "moderator")
assert {:ok, []} = Groups.list_roles(%{}, group_id, member_pubkey)
assert :ok = Groups.delete_membership(%{}, group_id, member_pubkey)
assert {:ok, nil} = Groups.get_membership(%{}, group_id, member_pubkey)
end
test "admin adapter appends and filters audit logs" do
actor_pubkey = String.duplicate("d", 64)
assert :ok =
Admin.append_audit_log(%{}, %{
method: "ban_pubkey",
actor_pubkey: actor_pubkey,
params: %{"pubkey" => String.duplicate("e", 64)},
result: %{"ok" => true}
})
assert :ok =
Admin.append_audit_log(%{}, %{
method: "stats",
params: %{"window" => "24h"}
})
assert {:ok, logs} = Admin.list_audit_logs(%{}, limit: 10)
assert length(logs) == 2
assert {:ok, actor_logs} = Admin.list_audit_logs(%{}, actor_pubkey: actor_pubkey)
assert Enum.map(actor_logs, & &1.method) == ["ban_pubkey"]
assert {:ok, stats_logs} = Admin.list_audit_logs(%{}, method: :stats)
assert Enum.map(stats_logs, & &1.method) == ["stats"]
assert {:error, {:unsupported_method, "status"}} = Admin.execute(%{}, :status, %{})
end
end

View File

@@ -152,6 +152,71 @@ defmodule Parrhesia.Storage.Adapters.Postgres.EventsQueryCountTest do
assert {:ok, 2} = Events.count(%{}, filters, now: now)
end
test "replaceable events expose only the current winner" do
author = String.duplicate("a", 64)
older =
persist_event(%{
"pubkey" => author,
"created_at" => 1_700_000_300,
"kind" => 0,
"content" => "profile-v1"
})
newer =
persist_event(%{
"pubkey" => author,
"created_at" => 1_700_000_301,
"kind" => 0,
"content" => "profile-v2"
})
assert {:ok, [result]} = Events.query(%{}, [%{"authors" => [author], "kinds" => [0]}], [])
assert result["id"] == newer["id"]
assert {:ok, nil} = Events.get_event(%{}, older["id"])
assert {:ok, persisted_newer} = Events.get_event(%{}, newer["id"])
assert persisted_newer["id"] == newer["id"]
assert {:ok, 1} = Events.count(%{}, [%{"ids" => [older["id"], newer["id"]]}], [])
end
test "addressable events tie-break by lexical id for identical timestamps" do
author = String.duplicate("b", 64)
first =
persist_event(%{
"pubkey" => author,
"created_at" => 1_700_000_400,
"kind" => 30_023,
"tags" => [["d", "topic"]],
"content" => "version-a"
})
second =
persist_event(%{
"pubkey" => author,
"created_at" => 1_700_000_400,
"kind" => 30_023,
"tags" => [["d", "topic"]],
"content" => "version-b"
})
winner_id = Enum.min([first["id"], second["id"]])
loser_id = Enum.max([first["id"], second["id"]])
assert {:ok, [result]} =
Events.query(
%{},
[%{"authors" => [author], "kinds" => [30_023], "#d" => ["topic"]}],
[]
)
assert result["id"] == winner_id
assert {:ok, nil} = Events.get_event(%{}, loser_id)
assert {:ok, 1} = Events.count(%{}, [%{"ids" => [first["id"], second["id"]]}], [])
end
defp persist_event(overrides) do
event = build_event(overrides)
assert {:ok, _persisted} = Events.put_event(%{}, event)