Implements a Buzz channel backed by a Nostr relay: - kind:9 events scoped by "h" tag, "p" tags for mentions - NIP-42 (kind:22242) auth before subscription - optional threaded replies via "e" tag - registered as config.ChannelBuzz with BuzzSettings
127 lines
3.1 KiB
Go
127 lines
3.1 KiB
Go
package buzz
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/nbd-wtf/go-nostr"
|
|
"github.com/nbd-wtf/go-nostr/nip19"
|
|
)
|
|
|
|
func TestNormalizeSecretKeyAcceptsHex(t *testing.T) {
|
|
sk := nostr.GeneratePrivateKey()
|
|
|
|
got, err := normalizeSecretKey(sk)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if got != sk {
|
|
t.Fatalf("expected %s, got %s", sk, got)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeSecretKeyAcceptsNsec(t *testing.T) {
|
|
sk := nostr.GeneratePrivateKey()
|
|
nsec, err := nip19.EncodePrivateKey(sk)
|
|
if err != nil {
|
|
t.Fatalf("failed to encode nsec: %v", err)
|
|
}
|
|
|
|
got, err := normalizeSecretKey(nsec)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if got != sk {
|
|
t.Fatalf("expected nsec to decode to %s, got %s", sk, got)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeSecretKeyTrimsWhitespace(t *testing.T) {
|
|
sk := nostr.GeneratePrivateKey()
|
|
|
|
got, err := normalizeSecretKey(" " + sk + "\n")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if got != sk {
|
|
t.Fatalf("expected %s, got %s", sk, got)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeSecretKeyRejectsBadInput(t *testing.T) {
|
|
cases := map[string]string{
|
|
"empty": "",
|
|
"whitespace": " ",
|
|
"short hex": "abcdef",
|
|
"bad nsec": "nsec1notarealkey",
|
|
"npub prefix": "npub1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
|
|
}
|
|
|
|
for name, input := range cases {
|
|
t.Run(name, func(t *testing.T) {
|
|
if _, err := normalizeSecretKey(input); err == nil {
|
|
t.Fatalf("expected an error for %q", input)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFirstTagValue(t *testing.T) {
|
|
tags := nostr.Tags{
|
|
nostr.Tag{"p", "pubkey-one"},
|
|
nostr.Tag{"h", "channel-abc"},
|
|
nostr.Tag{"h", "channel-second"},
|
|
nostr.Tag{"malformed"},
|
|
}
|
|
|
|
if got := firstTagValue(tags, "h"); got != "channel-abc" {
|
|
t.Fatalf("expected first h tag, got %q", got)
|
|
}
|
|
if got := firstTagValue(tags, "e"); got != "" {
|
|
t.Fatalf("expected empty string for missing tag, got %q", got)
|
|
}
|
|
// A tag with no value must not panic or be treated as present.
|
|
if got := firstTagValue(tags, "malformed"); got != "" {
|
|
t.Fatalf("expected empty string for valueless tag, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestHasPTag(t *testing.T) {
|
|
const self = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
|
|
|
tags := nostr.Tags{
|
|
nostr.Tag{"h", "channel-abc"},
|
|
nostr.Tag{"p", strings.ToUpper(self)},
|
|
}
|
|
if !hasPTag(tags, self) {
|
|
t.Fatal("expected p tag match to be case-insensitive")
|
|
}
|
|
|
|
other := nostr.Tags{
|
|
nostr.Tag{"h", "channel-abc"},
|
|
nostr.Tag{"p", "ffff"},
|
|
}
|
|
if hasPTag(other, self) {
|
|
t.Fatal("expected no match for a different pubkey")
|
|
}
|
|
|
|
// An "h" tag carrying our pubkey must not count as a mention.
|
|
spoof := nostr.Tags{nostr.Tag{"h", self}}
|
|
if hasPTag(spoof, self) {
|
|
t.Fatal("expected only p tags to count as mentions")
|
|
}
|
|
}
|
|
|
|
func TestShortPubkey(t *testing.T) {
|
|
const full = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
|
|
|
got := shortPubkey(full)
|
|
if !strings.HasPrefix(got, "01234567") || !strings.HasSuffix(got, "cdef") {
|
|
t.Fatalf("unexpected short form: %s", got)
|
|
}
|
|
|
|
// Short inputs are returned unchanged rather than sliced out of range.
|
|
if got := shortPubkey("abc"); got != "abc" {
|
|
t.Fatalf("expected short input unchanged, got %s", got)
|
|
}
|
|
}
|