bring in khatru and eventstore.

This commit is contained in:
fiatjaf
2025-04-15 08:49:28 -03:00
parent 8466a9757b
commit 76032dc089
170 changed files with 15018 additions and 42 deletions

191
eventstore/mmm/fuzz_test.go Normal file
View File

@@ -0,0 +1,191 @@
package mmm
import (
"context"
"fmt"
"math/rand/v2"
"os"
"slices"
"testing"
"github.com/nbd-wtf/go-nostr"
"github.com/rs/zerolog"
"github.com/stretchr/testify/require"
)
func FuzzTest(f *testing.F) {
f.Add(0, uint(84), uint(10), uint(5))
f.Fuzz(func(t *testing.T, seed int, nlayers, nevents, ndeletes uint) {
nlayers = nlayers%23 + 1
nevents = nevents%10000 + 1
ndeletes = ndeletes % nevents
// create a temporary directory for the test
tmpDir, err := os.MkdirTemp("", "mmm-test-*")
require.NoError(t, err)
defer os.RemoveAll(tmpDir)
logger := zerolog.Nop()
rnd := rand.New(rand.NewPCG(uint64(seed), 0))
// initialize MMM
mmm := &MultiMmapManager{
Dir: tmpDir,
Logger: &logger,
}
err = mmm.Init()
require.NoError(t, err)
defer mmm.Close()
for i := range nlayers {
name := string([]byte{97 + byte(i)})
err = mmm.EnsureLayer(name, &IndexingLayer{
MaxLimit: 1000,
})
require.NoError(t, err, "layer %s/%d", name, i)
}
// create test events
ctx := context.Background()
sk := "945e01e37662430162121b804d3645a86d97df9d256917d86735d0eb219393eb"
storedIds := make([]string, nevents)
nTags := make(map[string]int)
storedByLayer := make(map[string][]string)
// create n events with random combinations of tags
for i := 0; i < int(nevents); i++ {
tags := nostr.Tags{}
// randomly add 1-nlayers tags
numTags := 1 + (i % int(nlayers))
usedTags := make(map[string]bool)
for j := 0; j < numTags; j++ {
tag := string([]byte{97 + byte(i%int(nlayers))})
if !usedTags[tag] {
tags = append(tags, nostr.Tag{"t", tag})
usedTags[tag] = true
}
}
evt := &nostr.Event{
CreatedAt: nostr.Timestamp(i),
Kind: i, // hack to query by serial id
Tags: tags,
Content: fmt.Sprintf("test content %d", i),
}
evt.Sign(sk)
for _, layer := range mmm.layers {
if evt.Tags.FindWithValue("t", layer.name) != nil {
err := layer.SaveEvent(ctx, evt)
require.NoError(t, err)
storedByLayer[layer.name] = append(storedByLayer[layer.name], evt.ID)
}
}
storedIds = append(storedIds, evt.ID)
nTags[evt.ID] = len(evt.Tags)
}
// verify each layer has the correct events
for _, layer := range mmm.layers {
results, err := layer.QueryEvents(ctx, nostr.Filter{})
require.NoError(t, err)
count := 0
for evt := range results {
require.True(t, evt.Tags.ContainsAny("t", []string{layer.name}))
count++
}
require.Equal(t, count, len(storedByLayer[layer.name]))
}
// randomly select n events to delete from random layers
deleted := make(map[string][]*IndexingLayer)
for range ndeletes {
id := storedIds[rnd.Int()%len(storedIds)]
layer := mmm.layers[rnd.Int()%len(mmm.layers)]
evt, layers := mmm.GetByID(id)
if slices.Contains(deleted[id], layer) {
// already deleted from this layer
require.NotContains(t, layers, layer)
} else if evt != nil && evt.Tags.FindWithValue("t", layer.name) != nil {
require.Contains(t, layers, layer)
// delete now
layer.DeleteEvent(ctx, evt)
deleted[id] = append(deleted[id], layer)
} else {
// was never saved to this in the first place
require.NotContains(t, layers, layer)
}
}
for id, deletedlayers := range deleted {
evt, foundlayers := mmm.GetByID(id)
for _, layer := range deletedlayers {
require.NotContains(t, foundlayers, layer)
}
for _, layer := range foundlayers {
require.NotNil(t, evt.Tags.FindWithValue("t", layer.name))
}
if nTags[id] == len(deletedlayers) && evt != nil {
deletedlayersnames := make([]string, len(deletedlayers))
for i, layer := range deletedlayers {
deletedlayersnames[i] = layer.name
}
t.Fatalf("id %s has %d tags %v, should have been deleted from %v, but wasn't: %s",
id, nTags[id], evt.Tags, deletedlayersnames, evt)
} else if nTags[id] > len(deletedlayers) {
t.Fatalf("id %s should still be available as it had %d tags and was only deleted from %v, but isn't",
id, nTags[id], deletedlayers)
}
if evt != nil {
for _, layer := range mmm.layers {
// verify event still accessible from other layers
if slices.Contains(foundlayers, layer) {
ch, err := layer.QueryEvents(ctx, nostr.Filter{Kinds: []int{evt.Kind}}) // hack
require.NoError(t, err)
fetched := <-ch
require.NotNil(t, fetched)
} else {
// and not accessible from this layer we just deleted
ch, err := layer.QueryEvents(ctx, nostr.Filter{Kinds: []int{evt.Kind}}) // hack
require.NoError(t, err)
fetched := <-ch
require.Nil(t, fetched)
}
}
}
}
// now delete a layer and events that only exist in that layer should vanish
layer := mmm.layers[rnd.Int()%len(mmm.layers)]
ch, err := layer.QueryEvents(ctx, nostr.Filter{})
require.NoError(t, err)
eventsThatShouldVanish := make([]string, 0, nevents/2)
for evt := range ch {
if len(evt.Tags) == 1+len(deleted[evt.ID]) {
eventsThatShouldVanish = append(eventsThatShouldVanish, evt.ID)
}
}
err = mmm.DropLayer(layer.name)
require.NoError(t, err)
for _, id := range eventsThatShouldVanish {
v, ils := mmm.GetByID(id)
require.Nil(t, v)
require.Empty(t, ils)
}
})
}