114 lines
2.6 KiB
Go
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 nostr.Kind, 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: []nostr.Kind{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")
|
|
}
|
|
}
|
|
})
|
|
})
|
|
}
|