save-event_test.go raw

   1  package database
   2  
   3  import (
   4  	"bufio"
   5  	"bytes"
   6  	"context"
   7  	"os"
   8  	"sort"
   9  	"testing"
  10  	"time"
  11  
  12  	"next.orly.dev/pkg/nostr/encoders/event"
  13  	"next.orly.dev/pkg/nostr/encoders/event/examples"
  14  	"next.orly.dev/pkg/nostr/encoders/hex"
  15  	"next.orly.dev/pkg/nostr/encoders/kind"
  16  	"next.orly.dev/pkg/nostr/encoders/tag"
  17  	"next.orly.dev/pkg/nostr/encoders/timestamp"
  18  	"next.orly.dev/pkg/nostr/interfaces/signer/p8k"
  19  	"next.orly.dev/pkg/lol/chk"
  20  	"next.orly.dev/pkg/lol/errorf"
  21  )
  22  
  23  // TestSaveEvents tests saving all events from examples.Cache to the database
  24  // to verify there are no errors during the saving process.
  25  func TestSaveEvents(t *testing.T) {
  26  	// Create a temporary directory for the database
  27  	tempDir, err := os.MkdirTemp("", "test-db-*")
  28  	if err != nil {
  29  		t.Fatalf("Failed to create temporary directory: %v", err)
  30  	}
  31  	defer os.RemoveAll(tempDir) // Clean up after the test
  32  
  33  	// Create a context and cancel function for the database
  34  	ctx, cancel := context.WithCancel(context.Background())
  35  	defer cancel()
  36  
  37  	// Initialize the database
  38  	db, err := New(ctx, cancel, tempDir, "info")
  39  	if err != nil {
  40  		t.Fatalf("Failed to create database: %v", err)
  41  	}
  42  	defer db.Close()
  43  
  44  	// Create a scanner to read events from examples.Cache
  45  	scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
  46  	scanner.Buffer(make([]byte, 0, 1_000_000_000), 1_000_000_000)
  47  
  48  	// Collect all events first
  49  	var events []*event.E
  50  	var original int
  51  	for scanner.Scan() {
  52  		chk.E(scanner.Err())
  53  		b := scanner.Bytes()
  54  		original += len(b)
  55  		ev := event.New()
  56  
  57  		// Unmarshal the event
  58  		if _, err = ev.Unmarshal(b); chk.E(err) {
  59  			t.Fatal(err)
  60  		}
  61  
  62  		events = append(events, ev)
  63  	}
  64  
  65  	// Sort events by timestamp to ensure addressable events are processed in order
  66  	sort.Slice(events, func(i, j int) bool {
  67  		return events[i].CreatedAt < events[j].CreatedAt
  68  	})
  69  
  70  	// Count the number of events processed
  71  	eventCount := 0
  72  	skippedCount := 0
  73  	var kc, vc int
  74  	now := time.Now()
  75  	// Process each event in chronological order
  76  	for _, ev := range events {
  77  		// Save the event to the database
  78  		var k, v int
  79  		if _, err = db.SaveEvent(ctx, ev); err != nil {
  80  			// Skip events that fail validation (e.g., kind 3 without p tags)
  81  			skippedCount++
  82  			continue
  83  		}
  84  		kc += k
  85  		vc += v
  86  		eventCount++
  87  	}
  88  	_ = skippedCount // Used for logging below
  89  
  90  	// Check for scanner errors
  91  	if err = scanner.Err(); err != nil {
  92  		t.Fatalf("Scanner error: %v", err)
  93  	}
  94  	dur := time.Since(now)
  95  	t.Logf(
  96  		"Successfully saved %d events %d bytes to the database, %d bytes keys, %d bytes values in %v (%v/ev; %f ev/s)",
  97  		eventCount,
  98  		original,
  99  		kc, vc,
 100  		dur,
 101  		dur/time.Duration(eventCount),
 102  		float64(time.Second)/float64(dur/time.Duration(eventCount)),
 103  	)
 104  }
 105  
 106  // TestDeletionEventWithETagRejection tests that a deletion event with an "e" tag is rejected.
 107  func TestDeletionEventWithETagRejection(t *testing.T) {
 108  	// Create a temporary directory for the database
 109  	tempDir, err := os.MkdirTemp("", "test-db-*")
 110  	if err != nil {
 111  		t.Fatalf("Failed to create temporary directory: %v", err)
 112  	}
 113  	defer os.RemoveAll(tempDir) // Clean up after the test
 114  
 115  	// Create a context and cancel function for the database
 116  	ctx, cancel := context.WithCancel(context.Background())
 117  	defer cancel()
 118  
 119  	// Initialize the database
 120  	db, err := New(ctx, cancel, tempDir, "info")
 121  	if err != nil {
 122  		t.Fatalf("Failed to create database: %v", err)
 123  	}
 124  	defer db.Close()
 125  
 126  	// Create a signer
 127  	sign := p8k.MustNew()
 128  	if err := sign.Generate(); chk.E(err) {
 129  		t.Fatal(err)
 130  	}
 131  
 132  	// Create a regular event
 133  	regularEvent := event.New()
 134  	regularEvent.Kind = kind.TextNote.K
 135  	regularEvent.Pubkey = sign.Pub()
 136  	regularEvent.CreatedAt = timestamp.Now().V - 3600 // 1 hour ago
 137  	regularEvent.Content = []byte("Regular event")
 138  	regularEvent.Tags = tag.NewS()
 139  	regularEvent.Sign(sign)
 140  
 141  	// Save the regular event
 142  	if _, err := db.SaveEvent(ctx, regularEvent); err != nil {
 143  		t.Fatalf("Failed to save regular event: %v", err)
 144  	}
 145  
 146  	// Create a deletion event with an "e" tag referencing the regular event
 147  	deletionEvent := event.New()
 148  	deletionEvent.Kind = kind.Deletion.K
 149  	deletionEvent.Pubkey = sign.Pub()
 150  	deletionEvent.CreatedAt = timestamp.Now().V // Current time
 151  	deletionEvent.Content = []byte("Deleting the regular event")
 152  	deletionEvent.Tags = tag.NewS()
 153  
 154  	// Add an e-tag referencing the regular event
 155  	*deletionEvent.Tags = append(
 156  		*deletionEvent.Tags,
 157  		tag.NewFromAny("e", hex.Enc(regularEvent.ID)),
 158  	)
 159  
 160  	deletionEvent.Sign(sign)
 161  
 162  	// Check if this is a deletion event with "e" tags
 163  	if deletionEvent.Kind == kind.Deletion.K && deletionEvent.Tags.GetFirst([]byte{'e'}) != nil {
 164  		// In this test, we want to reject deletion events with "e" tags
 165  		err = errorf.E("deletion events referencing other events with 'e' tag are not allowed")
 166  	} else {
 167  		// Try to save the deletion event
 168  		_, err = db.SaveEvent(ctx, deletionEvent)
 169  	}
 170  
 171  	if err == nil {
 172  		t.Fatal("Expected deletion event with e-tag to be rejected, but it was accepted")
 173  	}
 174  
 175  	// Verify the error message
 176  	expectedError := "deletion events referencing other events with 'e' tag are not allowed"
 177  	if err.Error() != expectedError {
 178  		t.Fatalf(
 179  			"Expected error message '%s', got '%s'", expectedError, err.Error(),
 180  		)
 181  	}
 182  }
 183  
 184  // TestSaveExistingEvent tests that attempting to save an event that already exists
 185  // returns an error.
 186  func TestSaveExistingEvent(t *testing.T) {
 187  	// Create a temporary directory for the database
 188  	tempDir, err := os.MkdirTemp("", "test-db-*")
 189  	if err != nil {
 190  		t.Fatalf("Failed to create temporary directory: %v", err)
 191  	}
 192  	defer os.RemoveAll(tempDir) // Clean up after the test
 193  
 194  	// Create a context and cancel function for the database
 195  	ctx, cancel := context.WithCancel(context.Background())
 196  	defer cancel()
 197  
 198  	// Initialize the database
 199  	db, err := New(ctx, cancel, tempDir, "info")
 200  	if err != nil {
 201  		t.Fatalf("Failed to create database: %v", err)
 202  	}
 203  	defer db.Close()
 204  
 205  	// Create a signer
 206  	sign := p8k.MustNew()
 207  	if err := sign.Generate(); chk.E(err) {
 208  		t.Fatal(err)
 209  	}
 210  
 211  	// Create an event
 212  	ev := event.New()
 213  	ev.Kind = kind.TextNote.K
 214  	ev.Pubkey = sign.Pub()
 215  	ev.CreatedAt = timestamp.Now().V
 216  	ev.Content = []byte("Test event")
 217  	ev.Tags = tag.NewS()
 218  	ev.Sign(sign)
 219  
 220  	// Save the event for the first time
 221  	if _, err := db.SaveEvent(ctx, ev); err != nil {
 222  		t.Fatalf("Failed to save event: %v", err)
 223  	}
 224  
 225  	// Try to save the same event again, it should be rejected
 226  	_, err = db.SaveEvent(ctx, ev)
 227  	if err == nil {
 228  		t.Fatal("Expected error when saving an existing event, but got nil")
 229  	}
 230  
 231  	// Verify the error message
 232  	expectedErrorPrefix := "blocked: event already exists: "
 233  	if !bytes.HasPrefix([]byte(err.Error()), []byte(expectedErrorPrefix)) {
 234  		t.Fatalf(
 235  			"Expected error message to start with '%s', got '%s'",
 236  			expectedErrorPrefix, err.Error(),
 237  		)
 238  	}
 239  }
 240