Files
parrhesia/test/parrhesia/protocol_test.exs

116 lines
4.5 KiB
Elixir

defmodule Parrhesia.ProtocolTest do
use ExUnit.Case, async: true
alias Parrhesia.Protocol
alias Parrhesia.Protocol.EventValidator
test "decodes EVENT frame shape" do
payload = JSON.encode!(["EVENT", valid_event()])
assert {:ok, {:event, event}} = Protocol.decode_client(payload)
assert event["kind"] == 1
assert event["content"] == "hello"
end
test "decodes valid REQ, COUNT and CLOSE frames" do
req_payload = JSON.encode!(["REQ", "sub-1", %{"authors" => [String.duplicate("a", 64)]}])
count_payload = JSON.encode!(["COUNT", "sub-1", %{"kinds" => [1]}, %{"hll" => true}])
close_payload = JSON.encode!(["CLOSE", "sub-1"])
assert {:ok, {:req, "sub-1", [%{"authors" => [_author]}]}} =
Protocol.decode_client(req_payload)
assert {:ok, {:count, "sub-1", [%{"kinds" => [1]}], %{"hll" => true}}} =
Protocol.decode_client(count_payload)
assert {:ok, {:close, "sub-1"}} = Protocol.decode_client(close_payload)
end
test "rejects invalid subscription ids" do
empty_sub_payload = JSON.encode!(["REQ", "", %{"kinds" => [1]}])
long_sub_payload = JSON.encode!(["CLOSE", String.duplicate("x", 65)])
assert {:error, :invalid_subscription_id} = Protocol.decode_client(empty_sub_payload)
assert {:error, :invalid_subscription_id} = Protocol.decode_client(long_sub_payload)
end
test "decodes AUTH and NEG frames" do
auth_event = valid_event() |> Map.put("kind", 22_242) |> Map.put("content", "")
auth_event = Map.put(auth_event, "id", EventValidator.compute_id(auth_event))
assert {:ok, {:auth, ^auth_event}} =
Protocol.decode_client(JSON.encode!(["AUTH", auth_event]))
assert {:ok, {:neg_open, "sub-neg", %{"kinds" => [1]}, <<0x61>>}} =
Protocol.decode_client(
JSON.encode!(["NEG-OPEN", "sub-neg", %{"kinds" => [1]}, "61"])
)
assert {:ok, {:neg_msg, "sub-neg", <<0x61, 0x00>>}} =
Protocol.decode_client(JSON.encode!(["NEG-MSG", "sub-neg", "6100"]))
assert {:ok, {:neg_close, "sub-neg"}} =
Protocol.decode_client(JSON.encode!(["NEG-CLOSE", "sub-neg"]))
end
test "returns decode errors for malformed messages" do
assert {:error, :invalid_json} = Protocol.decode_client("not-json")
assert {:error, :invalid_filters} = Protocol.decode_client(JSON.encode!(["REQ", "sub-1"]))
assert {:error, :invalid_count} = Protocol.decode_client(JSON.encode!(["COUNT", "sub-1"]))
assert {:error, :invalid_event} =
Protocol.decode_client(JSON.encode!(["EVENT", "not-a-map"]))
end
test "validates strict NIP-01 event fields" do
event = valid_event()
assert :ok = Protocol.validate_event(event)
future_event = Map.put(event, "created_at", System.system_time(:second) + 3_600)
future_event = Map.put(future_event, "id", EventValidator.compute_id(future_event))
assert {:error, "invalid: event creation date is too far off from the current time"} =
Protocol.validate_event(future_event)
mismatched_id_event = Map.put(event, "id", String.duplicate("f", 64))
assert {:error, "invalid: event id does not match serialized event"} =
Protocol.validate_event(mismatched_id_event)
bad_sig_event = Map.put(event, "sig", "abc")
assert {:error, "invalid: sig must be 64-byte lowercase hex"} =
Protocol.validate_event(bad_sig_event)
end
test "encodes relay messages" do
frame = Protocol.encode_relay({:closed, "sub-1", "error: subscription closed"})
assert JSON.decode!(frame) == ["CLOSED", "sub-1", "error: subscription closed"]
auth_frame = Protocol.encode_relay({:auth, "challenge"})
assert JSON.decode!(auth_frame) == ["AUTH", "challenge"]
count_frame = Protocol.encode_relay({:count, "sub-1", %{"count" => 1}})
assert JSON.decode!(count_frame) == ["COUNT", "sub-1", %{"count" => 1}]
neg_message_frame = Protocol.encode_relay({:neg_msg, "sub-neg", "61"})
assert JSON.decode!(neg_message_frame) == ["NEG-MSG", "sub-neg", "61"]
neg_error_frame = Protocol.encode_relay({:neg_err, "sub-neg", "closed: too slow"})
assert JSON.decode!(neg_error_frame) == ["NEG-ERR", "sub-neg", "closed: too slow"]
end
defp valid_event do
base_event = %{
"pubkey" => String.duplicate("1", 64),
"created_at" => System.system_time(:second),
"kind" => 1,
"tags" => [["p", String.duplicate("2", 64)]],
"content" => "hello",
"sig" => String.duplicate("3", 128)
}
Map.put(base_event, "id", EventValidator.compute_id(base_event))
end
end