Files
nostrlib/khatru/relay_fuzz_test.go
2025-04-18 11:29:31 -03:00

114 lines
2.6 KiB
Go

package khatru
import (
"context"
"math"
"math/rand/v2"
"net/http/httptest"
"testing"
"time"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/eventstore/lmdb"
"github.com/stretchr/testify/require"
)
func FuzzReplaceableEvents(f *testing.F) {
f.Add(uint(1), uint(2))
f.Fuzz(func(t *testing.T, seed uint, nevents uint) {
if nevents == 0 {
return
}
relay := NewRelay()
store := &lmdb.LMDBBackend{Path: "/tmp/fuzz"}
store.Init()
relay.UseEventstore(store)
defer store.Close()
// start test server
server := httptest.NewServer(relay)
defer server.Close()
// create test keys
sk1 := nostr.Generate()
pk1 := nostr.GetPublicKey(sk1)
// helper to create signed events
createEvent := func(sk nostr.SecretKey, kind uint16, content string, tags nostr.Tags) nostr.Event {
pk := nostr.GetPublicKey(sk)
evt := nostr.Event{
PubKey: pk,
CreatedAt: nostr.Now(),
Kind: kind,
Tags: tags,
Content: content,
}
evt.Sign(sk)
return evt
}
url := "ws" + server.URL[4:]
client1, err := nostr.RelayConnect(context.Background(), url, nostr.RelayOptions{})
if err != nil {
t.Skip("failed to connect client1")
}
defer client1.Close()
client2, err := nostr.RelayConnect(context.Background(), url, nostr.RelayOptions{})
if err != nil {
t.Skip("failed to connect client2")
}
defer client2.Close()
t.Run("replaceable events", func(t *testing.T) {
ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
defer cancel()
rnd := rand.New(rand.NewPCG(uint64(seed), 0))
newest := nostr.Timestamp(0)
for range nevents {
evt := createEvent(sk1, 0, `{"name":"blblbl"}`, nil)
evt.CreatedAt = nostr.Timestamp(rnd.Int64() % math.MaxUint32)
evt.Sign(sk1)
err = client1.Publish(ctx, evt)
if err != nil {
t.Fatalf("failed to publish event: %v", err)
}
if evt.CreatedAt > newest {
newest = evt.CreatedAt
}
}
// query to verify only the newest event exists
sub, err := client2.Subscribe(ctx, nostr.Filter{
Authors: []nostr.PubKey{pk1},
Kinds: []uint16{0},
}, nostr.SubscriptionOptions{})
if err != nil {
t.Fatalf("failed to subscribe: %v", err)
}
defer sub.Unsub()
// should only get one event back (the newest one)
var receivedEvents []nostr.Event
for {
select {
case evt := <-sub.Events:
receivedEvents = append(receivedEvents, evt)
case <-sub.EndOfStoredEvents:
require.Len(t, receivedEvents, 1)
require.Equal(t, newest, receivedEvents[0].CreatedAt)
return
case <-ctx.Done():
t.Fatal("timeout waiting for events")
}
}
})
})
}