Decompose handle-event.go into DDD domain services (v0.36.15)
Some checks failed
Go / build-and-release (push) Has been cancelled
Some checks failed
Go / build-and-release (push) Has been cancelled
Major refactoring of event handling into clean, testable domain services: - Add pkg/event/validation: JSON hex validation, signature verification, timestamp bounds, NIP-70 protected tag validation - Add pkg/event/authorization: Policy and ACL authorization decisions, auth challenge handling, access level determination - Add pkg/event/routing: Event router registry with ephemeral and delete handlers, kind-based dispatch - Add pkg/event/processing: Event persistence, delivery to subscribers, and post-save hooks (ACL reconfig, sync, relay groups) - Reduce handle-event.go from 783 to 296 lines (62% reduction) - Add comprehensive unit tests for all new domain services - Refactor database tests to use shared TestMain setup - Fix blossom URL test expectations (missing "/" separator) - Add go-memory-optimization skill and analysis documentation - Update DDD_ANALYSIS.md to reflect completed decomposition Files modified: - app/handle-event.go: Slim orchestrator using domain services - app/server.go: Service initialization and interface wrappers - app/handle-event-types.go: Shared types (OkHelper, result types) - pkg/event/validation/*: New validation service package - pkg/event/authorization/*: New authorization service package - pkg/event/routing/*: New routing service package - pkg/event/processing/*: New processing service package - pkg/database/*_test.go: Refactored to shared TestMain - pkg/blossom/http_test.go: Fixed URL format expectations 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,100 +3,28 @@ package database
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"git.mleku.dev/mleku/nostr/encoders/event"
|
||||
"git.mleku.dev/mleku/nostr/encoders/event/examples"
|
||||
"lol.mleku.dev/chk"
|
||||
)
|
||||
|
||||
// TestExport tests the Export function by:
|
||||
// 1. Creating a new database with events from examples.Cache
|
||||
// 2. Checking that all event IDs in the cache are found in the export
|
||||
// 3. Verifying this also works when only a few pubkeys are requested
|
||||
// 1. Using the shared database with events from examples.Cache
|
||||
// 2. Checking that events can be exported
|
||||
// 3. Verifying the exported events can be parsed
|
||||
func TestExport(t *testing.T) {
|
||||
// Create a temporary directory for the database
|
||||
tempDir, err := os.MkdirTemp("", "test-db-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temporary directory: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir) // Clean up after the test
|
||||
// Use shared database (skips in short mode)
|
||||
db, ctx := GetSharedDB(t)
|
||||
savedEvents := GetSharedEvents(t)
|
||||
|
||||
// Create a context and cancel function for the database
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
t.Logf("Shared database has %d events", len(savedEvents))
|
||||
|
||||
// Initialize the database
|
||||
db, err := New(ctx, cancel, tempDir, "info")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create database: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Create a scanner to read events from examples.Cache
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
|
||||
scanner.Buffer(make([]byte, 0, 1_000_000_000), 1_000_000_000)
|
||||
|
||||
var events []*event.E
|
||||
|
||||
// First, collect all events
|
||||
for scanner.Scan() {
|
||||
chk.E(scanner.Err())
|
||||
b := scanner.Bytes()
|
||||
ev := event.New()
|
||||
|
||||
// Unmarshal the event
|
||||
if _, err = ev.Unmarshal(b); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
events = append(events, ev)
|
||||
}
|
||||
|
||||
// Check for scanner errors
|
||||
if err = scanner.Err(); err != nil {
|
||||
t.Fatalf("Scanner error: %v", err)
|
||||
}
|
||||
|
||||
// Sort events by CreatedAt to ensure addressable events are processed in chronological order
|
||||
sort.Slice(events, func(i, j int) bool {
|
||||
return events[i].CreatedAt < events[j].CreatedAt
|
||||
})
|
||||
|
||||
// Maps to store event IDs and their associated pubkeys
|
||||
eventIDs := make(map[string]bool)
|
||||
pubkeyToEventIDs := make(map[string][]string)
|
||||
|
||||
// Process each event in chronological order
|
||||
skippedCount := 0
|
||||
for _, ev := range events {
|
||||
// Save the event to the database
|
||||
if _, err = db.SaveEvent(ctx, ev); err != nil {
|
||||
// Skip events that fail validation (e.g., kind 3 without p tags)
|
||||
// This can happen with real-world test data from examples.Cache
|
||||
skippedCount++
|
||||
continue
|
||||
}
|
||||
|
||||
// Store the event ID
|
||||
eventID := string(ev.ID)
|
||||
eventIDs[eventID] = true
|
||||
|
||||
// Store the event ID by pubkey
|
||||
pubkey := string(ev.Pubkey)
|
||||
pubkeyToEventIDs[pubkey] = append(pubkeyToEventIDs[pubkey], eventID)
|
||||
}
|
||||
|
||||
t.Logf("Saved %d events to the database (skipped %d invalid events)", len(eventIDs), skippedCount)
|
||||
|
||||
// Test 1: Export all events and verify all IDs are in the export
|
||||
// Test 1: Export all events and verify they can be parsed
|
||||
var exportBuffer bytes.Buffer
|
||||
db.Export(ctx, &exportBuffer)
|
||||
|
||||
// Parse the exported events and check that all IDs are present
|
||||
// Parse the exported events and count them
|
||||
exportedIDs := make(map[string]bool)
|
||||
exportScanner := bufio.NewScanner(&exportBuffer)
|
||||
exportScanner.Buffer(make([]byte, 0, 1_000_000_000), 1_000_000_000)
|
||||
@@ -104,26 +32,21 @@ func TestExport(t *testing.T) {
|
||||
for exportScanner.Scan() {
|
||||
b := exportScanner.Bytes()
|
||||
ev := event.New()
|
||||
if _, err = ev.Unmarshal(b); chk.E(err) {
|
||||
if _, err := ev.Unmarshal(b); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
exportedIDs[string(ev.ID)] = true
|
||||
exportCount++
|
||||
}
|
||||
// Check for scanner errors
|
||||
if err = exportScanner.Err(); err != nil {
|
||||
if err := exportScanner.Err(); err != nil {
|
||||
t.Fatalf("Scanner error: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("Found %d events in the export", exportCount)
|
||||
|
||||
// todo: this fails because some of the events replace earlier versions
|
||||
// // Check that all original event IDs are in the export
|
||||
// for id := range eventIDs {
|
||||
// if !exportedIDs[id] {
|
||||
// t.Errorf("Event ID %0x not found in export", id)
|
||||
// }
|
||||
// }
|
||||
|
||||
t.Logf("All %d event IDs found in export", len(eventIDs))
|
||||
// Verify we exported a reasonable number of events
|
||||
if exportCount == 0 {
|
||||
t.Fatal("Export returned no events")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,103 +1,26 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"git.mleku.dev/mleku/nostr/encoders/event"
|
||||
"git.mleku.dev/mleku/nostr/encoders/event/examples"
|
||||
"git.mleku.dev/mleku/nostr/encoders/filter"
|
||||
"git.mleku.dev/mleku/nostr/encoders/tag"
|
||||
"lol.mleku.dev/chk"
|
||||
"next.orly.dev/pkg/database/indexes/types"
|
||||
"next.orly.dev/pkg/utils"
|
||||
)
|
||||
|
||||
func TestFetchEventBySerial(t *testing.T) {
|
||||
// Create a temporary directory for the database
|
||||
tempDir, err := os.MkdirTemp("", "test-db-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temporary directory: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir) // Clean up after the test
|
||||
// Use shared database (skips in short mode)
|
||||
db, ctx := GetSharedDB(t)
|
||||
savedEvents := GetSharedEvents(t)
|
||||
|
||||
// Create a context and cancel function for the database
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Initialize the database
|
||||
db, err := New(ctx, cancel, tempDir, "info")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create database: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Create a scanner to read events from examples.Cache
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
|
||||
scanner.Buffer(make([]byte, 0, 1_000_000_000), 1_000_000_000)
|
||||
|
||||
var events []*event.E
|
||||
|
||||
// First, collect all events
|
||||
for scanner.Scan() {
|
||||
chk.E(scanner.Err())
|
||||
b := scanner.Bytes()
|
||||
ev := event.New()
|
||||
|
||||
// Unmarshal the event
|
||||
if _, err = ev.Unmarshal(b); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
events = append(events, ev)
|
||||
}
|
||||
|
||||
// Check for scanner errors
|
||||
if err = scanner.Err(); err != nil {
|
||||
t.Fatalf("Scanner error: %v", err)
|
||||
}
|
||||
|
||||
// Sort events by CreatedAt to ensure addressable events are processed in chronological order
|
||||
sort.Slice(events, func(i, j int) bool {
|
||||
return events[i].CreatedAt < events[j].CreatedAt
|
||||
})
|
||||
|
||||
// Count the number of events processed
|
||||
eventCount := 0
|
||||
skippedCount := 0
|
||||
var savedEvents []*event.E
|
||||
|
||||
// Process each event in chronological order
|
||||
for _, ev := range events {
|
||||
// Save the event to the database
|
||||
if _, err = db.SaveEvent(ctx, ev); err != nil {
|
||||
// Skip events that fail validation (e.g., kind 3 without p tags)
|
||||
// This can happen with real-world test data from examples.Cache
|
||||
skippedCount++
|
||||
continue
|
||||
}
|
||||
|
||||
savedEvents = append(savedEvents, ev)
|
||||
eventCount++
|
||||
}
|
||||
|
||||
t.Logf("Successfully saved %d events to the database (skipped %d invalid events)", eventCount, skippedCount)
|
||||
|
||||
// Instead of trying to find a valid serial directly, let's use QueryForIds
|
||||
// which is known to work from the other tests
|
||||
// Use the first successfully saved event (not original events which may include skipped ones)
|
||||
if len(savedEvents) < 4 {
|
||||
t.Fatalf("Need at least 4 saved events, got %d", len(savedEvents))
|
||||
}
|
||||
testEvent := savedEvents[3]
|
||||
|
||||
// Use QueryForIds to get the IdPkTs for this event
|
||||
var sers types.Uint40s
|
||||
sers, err = db.QueryForSerials(
|
||||
// Use QueryForIds to get the serial for this event
|
||||
sers, err := db.QueryForSerials(
|
||||
ctx, &filter.F{
|
||||
Ids: tag.NewFromBytesSlice(testEvent.ID),
|
||||
},
|
||||
@@ -108,7 +31,7 @@ func TestFetchEventBySerial(t *testing.T) {
|
||||
|
||||
// Verify we got exactly one result
|
||||
if len(sers) != 1 {
|
||||
t.Fatalf("Expected 1 IdPkTs, got %d", len(sers))
|
||||
t.Fatalf("Expected 1 serial, got %d", len(sers))
|
||||
}
|
||||
|
||||
// Fetch the event by serial
|
||||
|
||||
@@ -1,91 +1,18 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"git.mleku.dev/mleku/nostr/encoders/event"
|
||||
"git.mleku.dev/mleku/nostr/encoders/event/examples"
|
||||
"lol.mleku.dev/chk"
|
||||
)
|
||||
|
||||
func TestGetSerialById(t *testing.T) {
|
||||
// Create a temporary directory for the database
|
||||
tempDir, err := os.MkdirTemp("", "test-db-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temporary directory: %v", err)
|
||||
// Use shared database (skips in short mode)
|
||||
db, _ := GetSharedDB(t)
|
||||
savedEvents := GetSharedEvents(t)
|
||||
|
||||
if len(savedEvents) < 4 {
|
||||
t.Fatalf("Need at least 4 saved events, got %d", len(savedEvents))
|
||||
}
|
||||
defer os.RemoveAll(tempDir) // Clean up after the test
|
||||
|
||||
// Create a context and cancel function for the database
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Initialize the database
|
||||
db, err := New(ctx, cancel, tempDir, "info")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create database: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Create a scanner to read events from examples.Cache
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
|
||||
scanner.Buffer(make([]byte, 0, 1_000_000_000), 1_000_000_000)
|
||||
|
||||
// Collect all events first
|
||||
var allEvents []*event.E
|
||||
for scanner.Scan() {
|
||||
chk.E(scanner.Err())
|
||||
b := scanner.Bytes()
|
||||
ev := event.New()
|
||||
|
||||
// Unmarshal the event
|
||||
if _, err = ev.Unmarshal(b); chk.E(err) {
|
||||
ev.Free()
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
allEvents = append(allEvents, ev)
|
||||
}
|
||||
|
||||
// Check for scanner errors
|
||||
if err = scanner.Err(); err != nil {
|
||||
t.Fatalf("Scanner error: %v", err)
|
||||
}
|
||||
|
||||
// Sort events by timestamp to ensure addressable events are processed in chronological order
|
||||
sort.Slice(allEvents, func(i, j int) bool {
|
||||
return allEvents[i].CreatedAt < allEvents[j].CreatedAt
|
||||
})
|
||||
|
||||
// Now process the sorted events
|
||||
eventCount := 0
|
||||
skippedCount := 0
|
||||
var events []*event.E
|
||||
|
||||
for _, ev := range allEvents {
|
||||
// Save the event to the database
|
||||
if _, err = db.SaveEvent(ctx, ev); err != nil {
|
||||
// Skip events that fail validation (e.g., kind 3 without p tags)
|
||||
skippedCount++
|
||||
continue
|
||||
}
|
||||
|
||||
events = append(events, ev)
|
||||
eventCount++
|
||||
}
|
||||
|
||||
t.Logf("Successfully saved %d events to the database (skipped %d invalid events)", eventCount, skippedCount)
|
||||
|
||||
// Test GetSerialById with a known event ID
|
||||
if len(events) < 4 {
|
||||
t.Fatalf("Need at least 4 saved events, got %d", len(events))
|
||||
}
|
||||
testEvent := events[3]
|
||||
testEvent := savedEvents[3]
|
||||
|
||||
// Get the serial by ID
|
||||
serial, err := db.GetSerialById(testEvent.ID)
|
||||
|
||||
@@ -1,109 +1,28 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"git.mleku.dev/mleku/nostr/encoders/event"
|
||||
"git.mleku.dev/mleku/nostr/encoders/event/examples"
|
||||
"git.mleku.dev/mleku/nostr/encoders/filter"
|
||||
"git.mleku.dev/mleku/nostr/encoders/kind"
|
||||
"git.mleku.dev/mleku/nostr/encoders/tag"
|
||||
"git.mleku.dev/mleku/nostr/encoders/timestamp"
|
||||
"lol.mleku.dev/chk"
|
||||
"next.orly.dev/pkg/database/indexes/types"
|
||||
"next.orly.dev/pkg/utils"
|
||||
)
|
||||
|
||||
func TestGetSerialsByRange(t *testing.T) {
|
||||
// Create a temporary directory for the database
|
||||
tempDir, err := os.MkdirTemp("", "test-db-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temporary directory: %v", err)
|
||||
// Use shared database (skips in short mode)
|
||||
db, _ := GetSharedDB(t)
|
||||
savedEvents := GetSharedEvents(t)
|
||||
|
||||
if len(savedEvents) < 10 {
|
||||
t.Fatalf("Need at least 10 saved events, got %d", len(savedEvents))
|
||||
}
|
||||
defer os.RemoveAll(tempDir) // Clean up after the test
|
||||
|
||||
// Create a context and cancel function for the database
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Initialize the database
|
||||
db, err := New(ctx, cancel, tempDir, "info")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create database: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Create a scanner to read events from examples.Cache
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
|
||||
scanner.Buffer(make([]byte, 0, 1_000_000_000), 1_000_000_000)
|
||||
|
||||
var events []*event.E
|
||||
var eventSerials = make(map[string]*types.Uint40) // Map event ID (hex) to serial
|
||||
|
||||
// First, collect all events from examples.Cache
|
||||
for scanner.Scan() {
|
||||
chk.E(scanner.Err())
|
||||
b := scanner.Bytes()
|
||||
ev := event.New()
|
||||
|
||||
// Unmarshal the event
|
||||
if _, err = ev.Unmarshal(b); chk.E(err) {
|
||||
ev.Free()
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
events = append(events, ev)
|
||||
}
|
||||
|
||||
// Check for scanner errors
|
||||
if err = scanner.Err(); err != nil {
|
||||
t.Fatalf("Scanner error: %v", err)
|
||||
}
|
||||
|
||||
// Sort events by CreatedAt to ensure addressable events are processed in chronological order
|
||||
sort.Slice(events, func(i, j int) bool {
|
||||
return events[i].CreatedAt < events[j].CreatedAt
|
||||
})
|
||||
|
||||
// Count the number of events processed
|
||||
eventCount := 0
|
||||
skippedCount := 0
|
||||
|
||||
// Now process each event in chronological order
|
||||
for _, ev := range events {
|
||||
// Save the event to the database
|
||||
if _, err = db.SaveEvent(ctx, ev); err != nil {
|
||||
// Skip events that fail validation (e.g., kind 3 without p tags)
|
||||
skippedCount++
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the serial for this event
|
||||
serial, err := db.GetSerialById(ev.ID)
|
||||
if err != nil {
|
||||
t.Fatalf(
|
||||
"Failed to get serial for event #%d: %v", eventCount+1, err,
|
||||
)
|
||||
}
|
||||
|
||||
if serial != nil {
|
||||
eventSerials[string(ev.ID)] = serial
|
||||
}
|
||||
|
||||
eventCount++
|
||||
}
|
||||
|
||||
t.Logf("Successfully saved %d events to the database (skipped %d invalid events)", eventCount, skippedCount)
|
||||
|
||||
// Test GetSerialsByRange with a time range filter
|
||||
// Use the timestamp from the middle event as a reference
|
||||
middleIndex := len(events) / 2
|
||||
middleEvent := events[middleIndex]
|
||||
middleIndex := len(savedEvents) / 2
|
||||
middleEvent := savedEvents[middleIndex]
|
||||
|
||||
// Create a timestamp range that includes events before and after the middle event
|
||||
sinceTime := new(timestamp.T)
|
||||
@@ -202,7 +121,7 @@ func TestGetSerialsByRange(t *testing.T) {
|
||||
|
||||
// Test GetSerialsByRange with an author filter
|
||||
authorFilter := &filter.F{
|
||||
Authors: tag.NewFromBytesSlice(events[1].Pubkey),
|
||||
Authors: tag.NewFromBytesSlice(savedEvents[1].Pubkey),
|
||||
}
|
||||
|
||||
// Get the indexes from the filter
|
||||
@@ -235,10 +154,10 @@ func TestGetSerialsByRange(t *testing.T) {
|
||||
t.Fatalf("Failed to fetch event for serial %d: %v", i, err)
|
||||
}
|
||||
|
||||
if !utils.FastEqual(ev.Pubkey, events[1].Pubkey) {
|
||||
if !utils.FastEqual(ev.Pubkey, savedEvents[1].Pubkey) {
|
||||
t.Fatalf(
|
||||
"Event %d has incorrect author. Got %x, expected %x",
|
||||
i, ev.Pubkey, events[1].Pubkey,
|
||||
i, ev.Pubkey, savedEvents[1].Pubkey,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,6 +131,10 @@ func TestEventPubkeyGraph(t *testing.T) {
|
||||
eventSig := make([]byte, 64)
|
||||
eventSig[0] = 1
|
||||
|
||||
// Create a valid e-tag event ID (32 bytes = 64 hex chars)
|
||||
eTagEventID := make([]byte, 32)
|
||||
eTagEventID[0] = 0xAB
|
||||
|
||||
ev := &event.E{
|
||||
ID: eventID,
|
||||
Pubkey: authorPubkey,
|
||||
@@ -141,7 +145,7 @@ func TestEventPubkeyGraph(t *testing.T) {
|
||||
Tags: tag.NewS(
|
||||
tag.NewFromAny("p", hex.Enc(pTagPubkey1)),
|
||||
tag.NewFromAny("p", hex.Enc(pTagPubkey2)),
|
||||
tag.NewFromAny("e", "someeventid"),
|
||||
tag.NewFromAny("e", hex.Enc(eTagEventID)),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -2,17 +2,16 @@ package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"lol.mleku.dev/chk"
|
||||
"git.mleku.dev/mleku/nostr/interfaces/signer/p8k"
|
||||
"git.mleku.dev/mleku/nostr/encoders/event"
|
||||
"git.mleku.dev/mleku/nostr/encoders/filter"
|
||||
"git.mleku.dev/mleku/nostr/encoders/hex"
|
||||
"git.mleku.dev/mleku/nostr/encoders/kind"
|
||||
"git.mleku.dev/mleku/nostr/encoders/tag"
|
||||
"git.mleku.dev/mleku/nostr/encoders/timestamp"
|
||||
"git.mleku.dev/mleku/nostr/interfaces/signer/p8k"
|
||||
"lol.mleku.dev/chk"
|
||||
"next.orly.dev/pkg/utils"
|
||||
)
|
||||
|
||||
@@ -20,10 +19,9 @@ import (
|
||||
// replaceable events with the same pubkey, kind, and d-tag exist, only the newest one
|
||||
// is returned in query results.
|
||||
func TestMultipleParameterizedReplaceableEvents(t *testing.T) {
|
||||
db, _, ctx, cancel, tempDir := setupTestDB(t)
|
||||
defer os.RemoveAll(tempDir) // Clean up after the test
|
||||
defer cancel()
|
||||
defer db.Close()
|
||||
// Needs fresh database (modifies data)
|
||||
db, ctx, cleanup := setupFreshTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
sign := p8k.MustNew()
|
||||
if err := sign.Generate(); chk.E(err) {
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"git.mleku.dev/mleku/nostr/encoders/event"
|
||||
"git.mleku.dev/mleku/nostr/encoders/event/examples"
|
||||
"git.mleku.dev/mleku/nostr/encoders/filter"
|
||||
"git.mleku.dev/mleku/nostr/encoders/hex"
|
||||
"git.mleku.dev/mleku/nostr/encoders/kind"
|
||||
@@ -21,87 +17,44 @@ import (
|
||||
"next.orly.dev/pkg/utils"
|
||||
)
|
||||
|
||||
// setupTestDB creates a new test database and loads example events
|
||||
func setupTestDB(t *testing.T) (
|
||||
*D, []*event.E, context.Context, context.CancelFunc, string,
|
||||
) {
|
||||
// Create a temporary directory for the database
|
||||
// setupFreshTestDB creates a new isolated test database for tests that modify data.
|
||||
// Use this for tests that need to write/delete events.
|
||||
func setupFreshTestDB(t *testing.T) (*D, context.Context, func()) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test that requires fresh database in short mode")
|
||||
}
|
||||
|
||||
tempDir, err := os.MkdirTemp("", "test-db-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temporary directory: %v", err)
|
||||
}
|
||||
|
||||
// Create a context and cancel function for the database
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
// Initialize the database
|
||||
db, err := New(ctx, cancel, tempDir, "info")
|
||||
if err != nil {
|
||||
os.RemoveAll(tempDir)
|
||||
t.Fatalf("Failed to create database: %v", err)
|
||||
}
|
||||
|
||||
// Create a scanner to read events from examples.Cache
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
|
||||
scanner.Buffer(make([]byte, 0, 1_000_000_000), 1_000_000_000)
|
||||
|
||||
var events []*event.E
|
||||
|
||||
// First, collect all events from examples.Cache
|
||||
for scanner.Scan() {
|
||||
chk.E(scanner.Err())
|
||||
b := scanner.Bytes()
|
||||
ev := event.New()
|
||||
|
||||
// Unmarshal the event
|
||||
if _, err = ev.Unmarshal(b); chk.E(err) {
|
||||
ev.Free()
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
events = append(events, ev)
|
||||
cleanup := func() {
|
||||
db.Close()
|
||||
cancel()
|
||||
os.RemoveAll(tempDir)
|
||||
}
|
||||
|
||||
// Check for scanner errors
|
||||
if err = scanner.Err(); err != nil {
|
||||
t.Fatalf("Scanner error: %v", err)
|
||||
}
|
||||
|
||||
// Sort events by CreatedAt to ensure addressable events are processed in chronological order
|
||||
sort.Slice(events, func(i, j int) bool {
|
||||
return events[i].CreatedAt < events[j].CreatedAt
|
||||
})
|
||||
|
||||
// Count the number of events processed
|
||||
eventCount := 0
|
||||
skippedCount := 0
|
||||
var savedEvents []*event.E
|
||||
|
||||
// Now process each event in chronological order
|
||||
for _, ev := range events {
|
||||
// Save the event to the database
|
||||
if _, err = db.SaveEvent(ctx, ev); err != nil {
|
||||
// Skip events that fail validation (e.g., kind 3 without p tags)
|
||||
skippedCount++
|
||||
continue
|
||||
}
|
||||
|
||||
savedEvents = append(savedEvents, ev)
|
||||
eventCount++
|
||||
}
|
||||
|
||||
t.Logf("Successfully saved %d events to the database (skipped %d invalid events)", eventCount, skippedCount)
|
||||
|
||||
return db, savedEvents, ctx, cancel, tempDir
|
||||
return db, ctx, cleanup
|
||||
}
|
||||
|
||||
func TestQueryEventsByID(t *testing.T) {
|
||||
db, events, ctx, cancel, tempDir := setupTestDB(t)
|
||||
defer os.RemoveAll(tempDir) // Clean up after the test
|
||||
defer cancel()
|
||||
defer db.Close()
|
||||
// Use shared database (read-only test)
|
||||
db, ctx := GetSharedDB(t)
|
||||
events := GetSharedEvents(t)
|
||||
|
||||
// Test QueryEvents with an ID filter
|
||||
testEvent := events[3] // Using the same event as in other tests
|
||||
if len(events) < 4 {
|
||||
t.Fatalf("Need at least 4 saved events, got %d", len(events))
|
||||
}
|
||||
testEvent := events[3]
|
||||
|
||||
evs, err := db.QueryEvents(
|
||||
ctx, &filter.F{
|
||||
@@ -112,12 +65,10 @@ func TestQueryEventsByID(t *testing.T) {
|
||||
t.Fatalf("Failed to query events by ID: %v", err)
|
||||
}
|
||||
|
||||
// Verify we got exactly one event
|
||||
if len(evs) != 1 {
|
||||
t.Fatalf("Expected 1 event, got %d", len(evs))
|
||||
}
|
||||
|
||||
// Verify it's the correct event
|
||||
if !utils.FastEqual(evs[0].ID, testEvent.ID) {
|
||||
t.Fatalf(
|
||||
"Event ID doesn't match. Got %x, expected %x", evs[0].ID,
|
||||
@@ -127,12 +78,9 @@ func TestQueryEventsByID(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestQueryEventsByKind(t *testing.T) {
|
||||
db, _, ctx, cancel, tempDir := setupTestDB(t)
|
||||
defer os.RemoveAll(tempDir) // Clean up after the test
|
||||
defer cancel()
|
||||
defer db.Close()
|
||||
// Use shared database (read-only test)
|
||||
db, ctx := GetSharedDB(t)
|
||||
|
||||
// Test querying by kind
|
||||
testKind := kind.New(1) // Kind 1 is typically text notes
|
||||
kindFilter := kind.NewS(testKind)
|
||||
|
||||
@@ -146,12 +94,10 @@ func TestQueryEventsByKind(t *testing.T) {
|
||||
t.Fatalf("Failed to query events by kind: %v", err)
|
||||
}
|
||||
|
||||
// Verify we got results
|
||||
if len(evs) == 0 {
|
||||
t.Fatal("Expected events with kind 1, but got none")
|
||||
}
|
||||
|
||||
// Verify all events have the correct kind
|
||||
for i, ev := range evs {
|
||||
if ev.Kind != testKind.K {
|
||||
t.Fatalf(
|
||||
@@ -163,12 +109,14 @@ func TestQueryEventsByKind(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestQueryEventsByAuthor(t *testing.T) {
|
||||
db, events, ctx, cancel, tempDir := setupTestDB(t)
|
||||
defer os.RemoveAll(tempDir) // Clean up after the test
|
||||
defer cancel()
|
||||
defer db.Close()
|
||||
// Use shared database (read-only test)
|
||||
db, ctx := GetSharedDB(t)
|
||||
events := GetSharedEvents(t)
|
||||
|
||||
if len(events) < 2 {
|
||||
t.Fatalf("Need at least 2 saved events, got %d", len(events))
|
||||
}
|
||||
|
||||
// Test querying by author
|
||||
authorFilter := tag.NewFromBytesSlice(events[1].Pubkey)
|
||||
|
||||
evs, err := db.QueryEvents(
|
||||
@@ -180,12 +128,10 @@ func TestQueryEventsByAuthor(t *testing.T) {
|
||||
t.Fatalf("Failed to query events by author: %v", err)
|
||||
}
|
||||
|
||||
// Verify we got results
|
||||
if len(evs) == 0 {
|
||||
t.Fatal("Expected events from author, but got none")
|
||||
}
|
||||
|
||||
// Verify all events have the correct author
|
||||
for i, ev := range evs {
|
||||
if !utils.FastEqual(ev.Pubkey, events[1].Pubkey) {
|
||||
t.Fatalf(
|
||||
@@ -197,12 +143,16 @@ func TestQueryEventsByAuthor(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReplaceableEventsAndDeletion(t *testing.T) {
|
||||
db, events, ctx, cancel, tempDir := setupTestDB(t)
|
||||
defer os.RemoveAll(tempDir) // Clean up after the test
|
||||
defer cancel()
|
||||
defer db.Close()
|
||||
// Needs fresh database (modifies data)
|
||||
db, ctx, cleanup := setupFreshTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
// Seed with a few events for pubkey reference
|
||||
events := GetSharedEvents(t)
|
||||
if len(events) == 0 {
|
||||
t.Fatal("Need at least 1 event for pubkey reference")
|
||||
}
|
||||
|
||||
// Test querying for replaced events by ID
|
||||
sign := p8k.MustNew()
|
||||
if err := sign.Generate(); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
@@ -210,26 +160,26 @@ func TestReplaceableEventsAndDeletion(t *testing.T) {
|
||||
|
||||
// Create a replaceable event
|
||||
replaceableEvent := event.New()
|
||||
replaceableEvent.Kind = kind.ProfileMetadata.K // Kind 0 is replaceable
|
||||
replaceableEvent.Pubkey = events[0].Pubkey // Use the same pubkey as an existing event
|
||||
replaceableEvent.CreatedAt = timestamp.Now().V - 7200 // 2 hours ago
|
||||
replaceableEvent.Kind = kind.ProfileMetadata.K
|
||||
replaceableEvent.Pubkey = events[0].Pubkey
|
||||
replaceableEvent.CreatedAt = timestamp.Now().V - 7200
|
||||
replaceableEvent.Content = []byte("Original profile")
|
||||
replaceableEvent.Tags = tag.NewS()
|
||||
replaceableEvent.Sign(sign)
|
||||
// Save the replaceable event
|
||||
|
||||
if _, err := db.SaveEvent(ctx, replaceableEvent); err != nil {
|
||||
t.Errorf("Failed to save replaceable event: %v", err)
|
||||
}
|
||||
|
||||
// Create a newer version of the replaceable event
|
||||
// Create a newer version
|
||||
newerEvent := event.New()
|
||||
newerEvent.Kind = kind.ProfileMetadata.K // Same kind
|
||||
newerEvent.Pubkey = replaceableEvent.Pubkey // Same pubkey
|
||||
newerEvent.CreatedAt = timestamp.Now().V - 3600 // 1 hour ago (newer than the original)
|
||||
newerEvent.Kind = kind.ProfileMetadata.K
|
||||
newerEvent.Pubkey = replaceableEvent.Pubkey
|
||||
newerEvent.CreatedAt = timestamp.Now().V - 3600
|
||||
newerEvent.Content = []byte("Updated profile")
|
||||
newerEvent.Tags = tag.NewS()
|
||||
newerEvent.Sign(sign)
|
||||
// Save the newer event
|
||||
|
||||
if _, err := db.SaveEvent(ctx, newerEvent); err != nil {
|
||||
t.Errorf("Failed to save newer event: %v", err)
|
||||
}
|
||||
@@ -244,12 +194,10 @@ func TestReplaceableEventsAndDeletion(t *testing.T) {
|
||||
t.Errorf("Failed to query for replaced event by ID: %v", err)
|
||||
}
|
||||
|
||||
// Verify the original event is still found (it's kept but not returned in general queries)
|
||||
if len(evs) != 1 {
|
||||
t.Errorf("Expected 1 event when querying for replaced event by ID, got %d", len(evs))
|
||||
}
|
||||
|
||||
// Verify it's the original event
|
||||
if !utils.FastEqual(evs[0].ID, replaceableEvent.ID) {
|
||||
t.Errorf(
|
||||
"Event ID doesn't match when querying for replaced event. Got %x, expected %x",
|
||||
@@ -271,7 +219,6 @@ func TestReplaceableEventsAndDeletion(t *testing.T) {
|
||||
t.Errorf("Failed to query for replaceable events: %v", err)
|
||||
}
|
||||
|
||||
// Verify we got only one event (the latest one)
|
||||
if len(evs) != 1 {
|
||||
t.Errorf(
|
||||
"Expected 1 event when querying for replaceable events, got %d",
|
||||
@@ -279,7 +226,6 @@ func TestReplaceableEventsAndDeletion(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
// Verify it's the newer event
|
||||
if !utils.FastEqual(evs[0].ID, newerEvent.ID) {
|
||||
t.Fatalf(
|
||||
"Event ID doesn't match when querying for replaceable events. Got %x, expected %x",
|
||||
@@ -288,36 +234,23 @@ func TestReplaceableEventsAndDeletion(t *testing.T) {
|
||||
}
|
||||
|
||||
// Test deletion events
|
||||
// Create a deletion event that references the replaceable event
|
||||
deletionEvent := event.New()
|
||||
deletionEvent.Kind = kind.Deletion.K // Kind 5 is deletion
|
||||
deletionEvent.Pubkey = replaceableEvent.Pubkey // Same pubkey as the event being deleted
|
||||
deletionEvent.CreatedAt = timestamp.Now().V // Current time
|
||||
deletionEvent.Kind = kind.Deletion.K
|
||||
deletionEvent.Pubkey = replaceableEvent.Pubkey
|
||||
deletionEvent.CreatedAt = timestamp.Now().V
|
||||
deletionEvent.Content = []byte("Deleting the replaceable event")
|
||||
deletionEvent.Tags = tag.NewS()
|
||||
deletionEvent.Sign(sign)
|
||||
|
||||
// Add an e-tag referencing the replaceable event
|
||||
t.Logf("Replaceable event ID: %x", replaceableEvent.ID)
|
||||
*deletionEvent.Tags = append(
|
||||
*deletionEvent.Tags,
|
||||
tag.NewFromAny("e", hex.Enc(replaceableEvent.ID)),
|
||||
)
|
||||
|
||||
// Save the deletion event
|
||||
if _, err = db.SaveEvent(ctx, deletionEvent); err != nil {
|
||||
t.Fatalf("Failed to save deletion event: %v", err)
|
||||
}
|
||||
|
||||
// Debug: Check if the deletion event was saved
|
||||
t.Logf("Deletion event ID: %x", deletionEvent.ID)
|
||||
t.Logf("Deletion event pubkey: %x", deletionEvent.Pubkey)
|
||||
t.Logf("Deletion event kind: %d", deletionEvent.Kind)
|
||||
t.Logf("Deletion event tags count: %d", deletionEvent.Tags.Len())
|
||||
for i, tag := range *deletionEvent.Tags {
|
||||
t.Logf("Deletion event tag[%d]: %v", i, tag.T)
|
||||
}
|
||||
|
||||
// Query for all events of this kind and pubkey again
|
||||
evs, err = db.QueryEvents(
|
||||
ctx, &filter.F{
|
||||
@@ -331,7 +264,6 @@ func TestReplaceableEventsAndDeletion(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
// Verify we still get the newer event (deletion should only affect the original event)
|
||||
if len(evs) != 1 {
|
||||
t.Fatalf(
|
||||
"Expected 1 event when querying for replaceable events after deletion, got %d",
|
||||
@@ -339,7 +271,6 @@ func TestReplaceableEventsAndDeletion(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
// Verify it's still the newer event
|
||||
if !utils.FastEqual(evs[0].ID, newerEvent.ID) {
|
||||
t.Fatalf(
|
||||
"Event ID doesn't match after deletion. Got %x, expected %x",
|
||||
@@ -357,33 +288,20 @@ func TestReplaceableEventsAndDeletion(t *testing.T) {
|
||||
t.Errorf("Failed to query for deleted event by ID: %v", err)
|
||||
}
|
||||
|
||||
// Verify the original event is not found (it was deleted)
|
||||
if len(evs) != 0 {
|
||||
t.Errorf("Expected 0 events when querying for deleted event by ID, got %d", len(evs))
|
||||
}
|
||||
|
||||
// // Verify we still get the original event when querying by ID
|
||||
// if len(evs) != 1 {
|
||||
// t.Errorf(
|
||||
// "Expected 1 event when querying for deleted event by ID, got %d",
|
||||
// len(evs),
|
||||
// )
|
||||
// }
|
||||
|
||||
// // Verify it's the original event
|
||||
// if !utils.FastEqual(evs[0].ID, replaceableEvent.ID) {
|
||||
// t.Errorf(
|
||||
// "Event ID doesn't match when querying for deleted event by ID. Got %x, expected %x",
|
||||
// evs[0].ID, replaceableEvent.ID,
|
||||
// )
|
||||
// }
|
||||
}
|
||||
|
||||
func TestParameterizedReplaceableEventsAndDeletion(t *testing.T) {
|
||||
db, events, ctx, cancel, tempDir := setupTestDB(t)
|
||||
defer os.RemoveAll(tempDir) // Clean up after the test
|
||||
defer cancel()
|
||||
defer db.Close()
|
||||
// Needs fresh database (modifies data)
|
||||
db, ctx, cleanup := setupFreshTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
events := GetSharedEvents(t)
|
||||
if len(events) == 0 {
|
||||
t.Fatal("Need at least 1 event for pubkey reference")
|
||||
}
|
||||
|
||||
sign := p8k.MustNew()
|
||||
if err := sign.Generate(); chk.E(err) {
|
||||
@@ -392,31 +310,27 @@ func TestParameterizedReplaceableEventsAndDeletion(t *testing.T) {
|
||||
|
||||
// Create a parameterized replaceable event
|
||||
paramEvent := event.New()
|
||||
paramEvent.Kind = 30000 // Kind 30000+ is parameterized replaceable
|
||||
paramEvent.Pubkey = events[0].Pubkey // Use the same pubkey as an existing event
|
||||
paramEvent.CreatedAt = timestamp.Now().V - 7200 // 2 hours ago
|
||||
paramEvent.Kind = 30000
|
||||
paramEvent.Pubkey = events[0].Pubkey
|
||||
paramEvent.CreatedAt = timestamp.Now().V - 7200
|
||||
paramEvent.Content = []byte("Original parameterized event")
|
||||
paramEvent.Tags = tag.NewS()
|
||||
// Add a d-tag
|
||||
*paramEvent.Tags = append(
|
||||
*paramEvent.Tags, tag.NewFromAny([]byte{'d'}, []byte("test-d-tag")),
|
||||
)
|
||||
paramEvent.Sign(sign)
|
||||
|
||||
// Save the parameterized replaceable event
|
||||
if _, err := db.SaveEvent(ctx, paramEvent); err != nil {
|
||||
t.Fatalf("Failed to save parameterized replaceable event: %v", err)
|
||||
}
|
||||
|
||||
// Create a deletion event that references the parameterized replaceable event using an a-tag
|
||||
// Create a deletion event
|
||||
paramDeletionEvent := event.New()
|
||||
paramDeletionEvent.Kind = kind.Deletion.K // Kind 5 is deletion
|
||||
paramDeletionEvent.Pubkey = paramEvent.Pubkey // Same pubkey as the event being deleted
|
||||
paramDeletionEvent.CreatedAt = timestamp.Now().V // Current time
|
||||
paramDeletionEvent.Kind = kind.Deletion.K
|
||||
paramDeletionEvent.Pubkey = paramEvent.Pubkey
|
||||
paramDeletionEvent.CreatedAt = timestamp.Now().V
|
||||
paramDeletionEvent.Content = []byte("Deleting the parameterized replaceable event")
|
||||
paramDeletionEvent.Tags = tag.NewS()
|
||||
// Add an a-tag referencing the parameterized replaceable event
|
||||
// Format: kind:pubkey:d-tag
|
||||
aTagValue := fmt.Sprintf(
|
||||
"%d:%s:%s",
|
||||
paramEvent.Kind,
|
||||
@@ -429,47 +343,30 @@ func TestParameterizedReplaceableEventsAndDeletion(t *testing.T) {
|
||||
)
|
||||
paramDeletionEvent.Sign(sign)
|
||||
|
||||
// Save the parameterized deletion event
|
||||
if _, err := db.SaveEvent(ctx, paramDeletionEvent); err != nil {
|
||||
t.Fatalf("Failed to save parameterized deletion event: %v", err)
|
||||
}
|
||||
|
||||
// Query for all events of this kind and pubkey
|
||||
paramKindFilter := kind.NewS(kind.New(paramEvent.Kind))
|
||||
paramAuthorFilter := tag.NewFromBytesSlice(paramEvent.Pubkey)
|
||||
|
||||
// Print debug info about the a-tag
|
||||
fmt.Printf("Debug: a-tag value: %s\n", aTagValue)
|
||||
fmt.Printf(
|
||||
"Debug: kind: %d, pubkey: %s, d-tag: %s\n",
|
||||
paramEvent.Kind,
|
||||
hex.Enc(paramEvent.Pubkey),
|
||||
"test-d-tag",
|
||||
)
|
||||
|
||||
// Let's try a different approach - use an e-tag instead of an a-tag
|
||||
// Create another deletion event that references the parameterized replaceable event using an e-tag
|
||||
// Create deletion with e-tag too
|
||||
paramDeletionEvent2 := event.New()
|
||||
paramDeletionEvent2.Kind = kind.Deletion.K // Kind 5 is deletion
|
||||
paramDeletionEvent2.Pubkey = paramEvent.Pubkey // Same pubkey as the event being deleted
|
||||
paramDeletionEvent2.CreatedAt = timestamp.Now().V // Current time
|
||||
paramDeletionEvent2.Content = []byte("Deleting the parameterized replaceable event with e-tag")
|
||||
paramDeletionEvent2.Kind = kind.Deletion.K
|
||||
paramDeletionEvent2.Pubkey = paramEvent.Pubkey
|
||||
paramDeletionEvent2.CreatedAt = timestamp.Now().V
|
||||
paramDeletionEvent2.Content = []byte("Deleting with e-tag")
|
||||
paramDeletionEvent2.Tags = tag.NewS()
|
||||
// Add an e-tag referencing the parameterized replaceable event
|
||||
*paramDeletionEvent2.Tags = append(
|
||||
*paramDeletionEvent2.Tags,
|
||||
tag.NewFromAny("e", []byte(hex.Enc(paramEvent.ID))),
|
||||
)
|
||||
paramDeletionEvent2.Sign(sign)
|
||||
|
||||
// Save the parameterized deletion event with e-tag
|
||||
if _, err := db.SaveEvent(ctx, paramDeletionEvent2); err != nil {
|
||||
t.Fatalf(
|
||||
"Failed to save parameterized deletion event with e-tag: %v", err,
|
||||
)
|
||||
t.Fatalf("Failed to save deletion event with e-tag: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Debug: Added a second deletion event with e-tag referencing the event ID\n")
|
||||
// Query for all events of this kind and pubkey
|
||||
paramKindFilter := kind.NewS(kind.New(paramEvent.Kind))
|
||||
paramAuthorFilter := tag.NewFromBytesSlice(paramEvent.Pubkey)
|
||||
|
||||
evs, err := db.QueryEvents(
|
||||
ctx, &filter.F{
|
||||
@@ -478,71 +375,45 @@ func TestParameterizedReplaceableEventsAndDeletion(t *testing.T) {
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf(
|
||||
"Failed to query for parameterized replaceable events after deletion: %v",
|
||||
err,
|
||||
)
|
||||
t.Fatalf("Failed to query for parameterized events: %v", err)
|
||||
}
|
||||
|
||||
// Print debug info about the returned events
|
||||
fmt.Printf("Debug: Got %d events\n", len(evs))
|
||||
for i, ev := range evs {
|
||||
fmt.Printf(
|
||||
"Debug: Event %d: kind=%d, pubkey=%s\n",
|
||||
i, ev.Kind, hex.Enc(ev.Pubkey),
|
||||
)
|
||||
dTag := ev.Tags.GetFirst([]byte("d"))
|
||||
if dTag != nil && dTag.Len() > 1 {
|
||||
fmt.Printf("Debug: Event %d: d-tag=%s\n", i, dTag.Value())
|
||||
}
|
||||
}
|
||||
|
||||
// Verify we get no events (since the only one was deleted)
|
||||
if len(evs) != 0 {
|
||||
t.Fatalf(
|
||||
"Expected 0 events when querying for deleted parameterized replaceable events, got %d",
|
||||
len(evs),
|
||||
)
|
||||
t.Fatalf("Expected 0 events after deletion, got %d", len(evs))
|
||||
}
|
||||
|
||||
// Query for the parameterized event by ID
|
||||
// Query by ID
|
||||
evs, err = db.QueryEvents(
|
||||
ctx, &filter.F{
|
||||
Ids: tag.NewFromBytesSlice(paramEvent.ID),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf(
|
||||
"Failed to query for deleted parameterized event by ID: %v", err,
|
||||
)
|
||||
t.Fatalf("Failed to query for deleted event by ID: %v", err)
|
||||
}
|
||||
|
||||
// Verify the deleted event is not found when querying by ID
|
||||
if len(evs) != 0 {
|
||||
t.Fatalf(
|
||||
"Expected 0 events when querying for deleted parameterized event by ID, got %d",
|
||||
len(evs),
|
||||
)
|
||||
t.Fatalf("Expected 0 events when querying deleted event by ID, got %d", len(evs))
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryEventsByTimeRange(t *testing.T) {
|
||||
db, events, ctx, cancel, tempDir := setupTestDB(t)
|
||||
defer os.RemoveAll(tempDir) // Clean up after the test
|
||||
defer cancel()
|
||||
defer db.Close()
|
||||
// Use shared database (read-only test)
|
||||
db, ctx := GetSharedDB(t)
|
||||
events := GetSharedEvents(t)
|
||||
|
||||
if len(events) < 10 {
|
||||
t.Fatalf("Need at least 10 saved events, got %d", len(events))
|
||||
}
|
||||
|
||||
// Test querying by time range
|
||||
// Use the timestamp from the middle event as a reference
|
||||
middleIndex := len(events) / 2
|
||||
middleEvent := events[middleIndex]
|
||||
|
||||
// Create a timestamp range that includes events before and after the middle event
|
||||
sinceTime := new(timestamp.T)
|
||||
sinceTime.V = middleEvent.CreatedAt - 3600 // 1 hour before middle event
|
||||
sinceTime.V = middleEvent.CreatedAt - 3600
|
||||
|
||||
untilTime := new(timestamp.T)
|
||||
untilTime.V = middleEvent.CreatedAt + 3600 // 1 hour after middle event
|
||||
untilTime.V = middleEvent.CreatedAt + 3600
|
||||
|
||||
evs, err := db.QueryEvents(
|
||||
ctx, &filter.F{
|
||||
@@ -554,12 +425,10 @@ func TestQueryEventsByTimeRange(t *testing.T) {
|
||||
t.Fatalf("Failed to query events by time range: %v", err)
|
||||
}
|
||||
|
||||
// Verify we got results
|
||||
if len(evs) == 0 {
|
||||
t.Fatal("Expected events in time range, but got none")
|
||||
}
|
||||
|
||||
// Verify all events are within the time range
|
||||
for i, ev := range evs {
|
||||
if ev.CreatedAt < sinceTime.V || ev.CreatedAt > untilTime.V {
|
||||
t.Fatalf(
|
||||
@@ -571,16 +440,14 @@ func TestQueryEventsByTimeRange(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestQueryEventsByTag(t *testing.T) {
|
||||
db, events, ctx, cancel, tempDir := setupTestDB(t)
|
||||
defer os.RemoveAll(tempDir) // Clean up after the test
|
||||
defer cancel()
|
||||
defer db.Close()
|
||||
// Use shared database (read-only test)
|
||||
db, ctx := GetSharedDB(t)
|
||||
events := GetSharedEvents(t)
|
||||
|
||||
// Find an event with tags to use for testing
|
||||
// Find an event with tags
|
||||
var testTagEvent *event.E
|
||||
for _, ev := range events {
|
||||
if ev.Tags != nil && ev.Tags.Len() > 0 {
|
||||
// Find a tag with at least 2 elements and first element of length 1
|
||||
for _, tg := range *ev.Tags {
|
||||
if tg.Len() >= 2 && len(tg.Key()) == 1 {
|
||||
testTagEvent = ev
|
||||
@@ -598,7 +465,6 @@ func TestQueryEventsByTag(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
// Get the first tag with at least 2 elements and first element of length 1
|
||||
var testTag *tag.T
|
||||
for _, tg := range *testTagEvent.Tags {
|
||||
if tg.Len() >= 2 && len(tg.Key()) == 1 {
|
||||
@@ -607,7 +473,6 @@ func TestQueryEventsByTag(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Create a tags filter with the test tag
|
||||
tagsFilter := tag.NewS(testTag)
|
||||
|
||||
evs, err := db.QueryEvents(
|
||||
@@ -619,12 +484,10 @@ func TestQueryEventsByTag(t *testing.T) {
|
||||
t.Fatalf("Failed to query events by tag: %v", err)
|
||||
}
|
||||
|
||||
// Verify we got results
|
||||
if len(evs) == 0 {
|
||||
t.Fatal("Expected events with tag, but got none")
|
||||
}
|
||||
|
||||
// Verify all events have the tag
|
||||
for i, ev := range evs {
|
||||
var hasTag bool
|
||||
for _, tg := range *ev.Tags {
|
||||
|
||||
@@ -1,113 +1,20 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"git.mleku.dev/mleku/nostr/encoders/event"
|
||||
"git.mleku.dev/mleku/nostr/encoders/event/examples"
|
||||
"git.mleku.dev/mleku/nostr/encoders/filter"
|
||||
"git.mleku.dev/mleku/nostr/encoders/tag"
|
||||
"lol.mleku.dev/chk"
|
||||
"next.orly.dev/pkg/interfaces/store"
|
||||
"next.orly.dev/pkg/utils"
|
||||
)
|
||||
|
||||
func TestQueryForAuthorsTags(t *testing.T) {
|
||||
// Create a temporary directory for the database
|
||||
tempDir, err := os.MkdirTemp("", "test-db-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temporary directory: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir) // Clean up after the test
|
||||
|
||||
// Create a context and cancel function for the database
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Initialize the database
|
||||
db, err := New(ctx, cancel, tempDir, "info")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create database: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Create a scanner to read events from examples.Cache
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
|
||||
scanner.Buffer(make([]byte, 0, 1_000_000_000), 1_000_000_000)
|
||||
|
||||
// Count the number of events processed
|
||||
eventCount := 0
|
||||
|
||||
var events []*event.E
|
||||
|
||||
// First, collect all events from examples.Cache
|
||||
for scanner.Scan() {
|
||||
chk.E(scanner.Err())
|
||||
b := scanner.Bytes()
|
||||
ev := event.New()
|
||||
|
||||
// Unmarshal the event
|
||||
if _, err = ev.Unmarshal(b); chk.E(err) {
|
||||
ev.Free()
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
events = append(events, ev)
|
||||
}
|
||||
|
||||
// Check for scanner errors
|
||||
if err = scanner.Err(); err != nil {
|
||||
t.Fatalf("Scanner error: %v", err)
|
||||
}
|
||||
|
||||
// Sort events by CreatedAt to ensure addressable events are processed in chronological order
|
||||
sort.Slice(events, func(i, j int) bool {
|
||||
return events[i].CreatedAt < events[j].CreatedAt
|
||||
})
|
||||
|
||||
// Count the number of events processed
|
||||
eventCount = 0
|
||||
skippedCount := 0
|
||||
var savedEvents []*event.E
|
||||
|
||||
// Now process each event in chronological order
|
||||
for _, ev := range events {
|
||||
// Save the event to the database
|
||||
if _, err = db.SaveEvent(ctx, ev); err != nil {
|
||||
// Skip events that fail validation (e.g., kind 3 without p tags)
|
||||
skippedCount++
|
||||
continue
|
||||
}
|
||||
|
||||
savedEvents = append(savedEvents, ev)
|
||||
eventCount++
|
||||
}
|
||||
|
||||
t.Logf("Successfully saved %d events to the database (skipped %d invalid events)", eventCount, skippedCount)
|
||||
events = savedEvents // Use saved events for the rest of the test
|
||||
// Use shared database (read-only test)
|
||||
db, ctx := GetSharedDB(t)
|
||||
events := GetSharedEvents(t)
|
||||
|
||||
// Find an event with tags to use for testing
|
||||
var testEvent *event.E
|
||||
for _, ev := range events {
|
||||
if ev.Tags != nil && ev.Tags.Len() > 0 {
|
||||
// Find a tag with at least 2 elements and the first element of
|
||||
// length 1
|
||||
for _, tg := range *ev.Tags {
|
||||
if tg.Len() >= 2 && len(tg.Key()) == 1 {
|
||||
testEvent = ev
|
||||
break
|
||||
}
|
||||
}
|
||||
if testEvent != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
testEvent := findEventWithTag(events)
|
||||
|
||||
if testEvent == nil {
|
||||
t.Skip("No suitable event with tags found for testing")
|
||||
@@ -123,15 +30,13 @@ func TestQueryForAuthorsTags(t *testing.T) {
|
||||
}
|
||||
|
||||
// Test querying by author and tag
|
||||
var idTsPk []*store.IdPkTs
|
||||
|
||||
// Use the author from the test event
|
||||
authorFilter := tag.NewFromBytesSlice(testEvent.Pubkey)
|
||||
|
||||
// Create a tags filter with the test tag
|
||||
tagsFilter := tag.NewS(testTag)
|
||||
|
||||
idTsPk, err = db.QueryForIds(
|
||||
idTsPk, err := db.QueryForIds(
|
||||
ctx, &filter.F{
|
||||
Authors: authorFilter,
|
||||
Tags: tagsFilter,
|
||||
|
||||
@@ -1,95 +1,21 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"git.mleku.dev/mleku/nostr/encoders/event"
|
||||
"git.mleku.dev/mleku/nostr/encoders/event/examples"
|
||||
"git.mleku.dev/mleku/nostr/encoders/filter"
|
||||
"git.mleku.dev/mleku/nostr/encoders/timestamp"
|
||||
"lol.mleku.dev/chk"
|
||||
"next.orly.dev/pkg/interfaces/store"
|
||||
"next.orly.dev/pkg/utils"
|
||||
)
|
||||
|
||||
func TestQueryForCreatedAt(t *testing.T) {
|
||||
// Create a temporary directory for the database
|
||||
tempDir, err := os.MkdirTemp("", "test-db-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temporary directory: %v", err)
|
||||
// Use shared database (read-only test)
|
||||
db, ctx := GetSharedDB(t)
|
||||
events := GetSharedEvents(t)
|
||||
|
||||
if len(events) < 3 {
|
||||
t.Fatalf("Need at least 3 saved events, got %d", len(events))
|
||||
}
|
||||
defer os.RemoveAll(tempDir) // Clean up after the test
|
||||
|
||||
// Create a context and cancel function for the database
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Initialize the database
|
||||
db, err := New(ctx, cancel, tempDir, "info")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create database: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Create a scanner to read events from examples.Cache
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
|
||||
scanner.Buffer(make([]byte, 0, 1_000_000_000), 1_000_000_000)
|
||||
|
||||
// Count the number of events processed
|
||||
eventCount := 0
|
||||
|
||||
var events []*event.E
|
||||
|
||||
// First, collect all events from examples.Cache
|
||||
for scanner.Scan() {
|
||||
chk.E(scanner.Err())
|
||||
b := scanner.Bytes()
|
||||
ev := event.New()
|
||||
|
||||
// Unmarshal the event
|
||||
if _, err = ev.Unmarshal(b); chk.E(err) {
|
||||
ev.Free()
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
events = append(events, ev)
|
||||
}
|
||||
|
||||
// Check for scanner errors
|
||||
if err = scanner.Err(); err != nil {
|
||||
t.Fatalf("Scanner error: %v", err)
|
||||
}
|
||||
|
||||
// Sort events by CreatedAt to ensure addressable events are processed in chronological order
|
||||
sort.Slice(events, func(i, j int) bool {
|
||||
return events[i].CreatedAt < events[j].CreatedAt
|
||||
})
|
||||
|
||||
// Count the number of events processed
|
||||
eventCount = 0
|
||||
skippedCount := 0
|
||||
var savedEvents []*event.E
|
||||
|
||||
// Now process each event in chronological order
|
||||
for _, ev := range events {
|
||||
// Save the event to the database
|
||||
if _, err = db.SaveEvent(ctx, ev); err != nil {
|
||||
// Skip events that fail validation (e.g., kind 3 without p tags)
|
||||
skippedCount++
|
||||
continue
|
||||
}
|
||||
|
||||
savedEvents = append(savedEvents, ev)
|
||||
eventCount++
|
||||
}
|
||||
|
||||
t.Logf("Successfully saved %d events to the database (skipped %d invalid events)", eventCount, skippedCount)
|
||||
events = savedEvents // Use saved events for the rest of the test
|
||||
|
||||
// Find a timestamp range that should include some events
|
||||
// Use the timestamp from the middle event as a reference
|
||||
@@ -104,9 +30,7 @@ func TestQueryForCreatedAt(t *testing.T) {
|
||||
untilTime.V = middleEvent.CreatedAt + 3600 // 1 hour after middle event
|
||||
|
||||
// Test querying by created_at range
|
||||
var idTsPk []*store.IdPkTs
|
||||
|
||||
idTsPk, err = db.QueryForIds(
|
||||
idTsPk, err := db.QueryForIds(
|
||||
ctx, &filter.F{
|
||||
Since: sinceTime,
|
||||
Until: untilTime,
|
||||
|
||||
@@ -1,104 +1,33 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"git.mleku.dev/mleku/nostr/encoders/event"
|
||||
"git.mleku.dev/mleku/nostr/encoders/event/examples"
|
||||
"git.mleku.dev/mleku/nostr/encoders/filter"
|
||||
"git.mleku.dev/mleku/nostr/encoders/kind"
|
||||
"git.mleku.dev/mleku/nostr/encoders/tag"
|
||||
"git.mleku.dev/mleku/nostr/encoders/timestamp"
|
||||
"lol.mleku.dev/chk"
|
||||
"next.orly.dev/pkg/interfaces/store"
|
||||
"next.orly.dev/pkg/utils"
|
||||
)
|
||||
|
||||
func TestQueryForIds(t *testing.T) {
|
||||
// Create a temporary directory for the database
|
||||
tempDir, err := os.MkdirTemp("", "test-db-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temporary directory: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir) // Clean up after the test
|
||||
// Use shared database (read-only test)
|
||||
db, ctx := GetSharedDB(t)
|
||||
events := GetSharedEvents(t)
|
||||
|
||||
// Create a context and cancel function for the database
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Initialize the database
|
||||
db, err := New(ctx, cancel, tempDir, "info")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create database: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Create a scanner to read events from examples.Cache
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
|
||||
scanner.Buffer(make([]byte, 0, 1_000_000_000), 1_000_000_000)
|
||||
|
||||
// Count the number of events processed
|
||||
eventCount := 0
|
||||
|
||||
var events []*event.E
|
||||
|
||||
// First, collect all events from examples.Cache
|
||||
for scanner.Scan() {
|
||||
chk.E(scanner.Err())
|
||||
b := scanner.Bytes()
|
||||
ev := event.New()
|
||||
|
||||
// Unmarshal the event
|
||||
if _, err = ev.Unmarshal(b); chk.E(err) {
|
||||
ev.Free()
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
events = append(events, ev)
|
||||
if len(events) < 2 {
|
||||
t.Fatalf("Need at least 2 saved events, got %d", len(events))
|
||||
}
|
||||
|
||||
// Check for scanner errors
|
||||
if err = scanner.Err(); err != nil {
|
||||
t.Fatalf("Scanner error: %v", err)
|
||||
}
|
||||
|
||||
// Sort events by CreatedAt to ensure addressable events are processed in chronological order
|
||||
sort.Slice(events, func(i, j int) bool {
|
||||
return events[i].CreatedAt < events[j].CreatedAt
|
||||
})
|
||||
|
||||
// Count the number of events processed
|
||||
eventCount = 0
|
||||
skippedCount := 0
|
||||
var savedEvents []*event.E
|
||||
|
||||
// Now process each event in chronological order
|
||||
for _, ev := range events {
|
||||
// Save the event to the database
|
||||
if _, err = db.SaveEvent(ctx, ev); err != nil {
|
||||
// Skip events that fail validation (e.g., kind 3 without p tags)
|
||||
skippedCount++
|
||||
continue
|
||||
}
|
||||
|
||||
savedEvents = append(savedEvents, ev)
|
||||
eventCount++
|
||||
}
|
||||
|
||||
t.Logf("Successfully saved %d events to the database (skipped %d invalid events)", eventCount, skippedCount)
|
||||
events = savedEvents // Use saved events for the rest of the test
|
||||
|
||||
var idTsPk []*store.IdPkTs
|
||||
idTsPk, err = db.QueryForIds(
|
||||
idTsPk, err := db.QueryForIds(
|
||||
ctx, &filter.F{
|
||||
Authors: tag.NewFromBytesSlice(events[1].Pubkey),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to query for authors: %v", err)
|
||||
}
|
||||
|
||||
if len(idTsPk) < 1 {
|
||||
t.Fatalf(
|
||||
"got unexpected number of results, expect at least 1, got %d",
|
||||
@@ -168,26 +97,12 @@ func TestQueryForIds(t *testing.T) {
|
||||
|
||||
// Test querying by tag
|
||||
// Find an event with tags to use for testing
|
||||
var testEvent *event.E
|
||||
for _, ev := range events {
|
||||
if ev.Tags != nil && ev.Tags.Len() > 0 {
|
||||
// Find a tag with at least 2 elements and first element of length 1
|
||||
for _, tg := range *ev.Tags {
|
||||
if tg.Len() >= 2 && len(tg.Key()) == 1 {
|
||||
testEvent = ev
|
||||
break
|
||||
}
|
||||
}
|
||||
if testEvent != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
var testTag *tag.T
|
||||
var testEventForTag = findEventWithTag(events)
|
||||
|
||||
if testEvent != nil {
|
||||
if testEventForTag != nil {
|
||||
// Get the first tag with at least 2 elements and first element of length 1
|
||||
var testTag *tag.T
|
||||
for _, tg := range *testEvent.Tags {
|
||||
for _, tg := range *testEventForTag.Tags {
|
||||
if tg.Len() >= 2 && len(tg.Key()) == 1 {
|
||||
testTag = tg
|
||||
break
|
||||
@@ -296,7 +211,7 @@ func TestQueryForIds(t *testing.T) {
|
||||
// Test querying by kind and tag
|
||||
idTsPk, err = db.QueryForIds(
|
||||
ctx, &filter.F{
|
||||
Kinds: kind.NewS(kind.New(testEvent.Kind)),
|
||||
Kinds: kind.NewS(kind.New(testEventForTag.Kind)),
|
||||
Tags: tagsFilter,
|
||||
},
|
||||
)
|
||||
@@ -316,10 +231,10 @@ func TestQueryForIds(t *testing.T) {
|
||||
for _, ev := range events {
|
||||
if utils.FastEqual(result.Id, ev.ID) {
|
||||
found = true
|
||||
if ev.Kind != testEvent.Kind {
|
||||
if ev.Kind != testEventForTag.Kind {
|
||||
t.Fatalf(
|
||||
"result %d has incorrect kind, got %d, expected %d",
|
||||
i, ev.Kind, testEvent.Kind,
|
||||
i, ev.Kind, testEventForTag.Kind,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -356,8 +271,8 @@ func TestQueryForIds(t *testing.T) {
|
||||
// Test querying by kind, author, and tag
|
||||
idTsPk, err = db.QueryForIds(
|
||||
ctx, &filter.F{
|
||||
Kinds: kind.NewS(kind.New(testEvent.Kind)),
|
||||
Authors: tag.NewFromBytesSlice(testEvent.Pubkey),
|
||||
Kinds: kind.NewS(kind.New(testEventForTag.Kind)),
|
||||
Authors: tag.NewFromBytesSlice(testEventForTag.Pubkey),
|
||||
Tags: tagsFilter,
|
||||
},
|
||||
)
|
||||
@@ -377,17 +292,17 @@ func TestQueryForIds(t *testing.T) {
|
||||
for _, ev := range events {
|
||||
if utils.FastEqual(result.Id, ev.ID) {
|
||||
found = true
|
||||
if ev.Kind != testEvent.Kind {
|
||||
if ev.Kind != testEventForTag.Kind {
|
||||
t.Fatalf(
|
||||
"result %d has incorrect kind, got %d, expected %d",
|
||||
i, ev.Kind, testEvent.Kind,
|
||||
i, ev.Kind, testEventForTag.Kind,
|
||||
)
|
||||
}
|
||||
|
||||
if !utils.FastEqual(ev.Pubkey, testEvent.Pubkey) {
|
||||
if !utils.FastEqual(ev.Pubkey, testEventForTag.Pubkey) {
|
||||
t.Fatalf(
|
||||
"result %d has incorrect author, got %x, expected %x",
|
||||
i, ev.Pubkey, testEvent.Pubkey,
|
||||
i, ev.Pubkey, testEventForTag.Pubkey,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -424,7 +339,7 @@ func TestQueryForIds(t *testing.T) {
|
||||
// Test querying by author and tag
|
||||
idTsPk, err = db.QueryForIds(
|
||||
ctx, &filter.F{
|
||||
Authors: tag.NewFromBytesSlice(testEvent.Pubkey),
|
||||
Authors: tag.NewFromBytesSlice(testEventForTag.Pubkey),
|
||||
Tags: tagsFilter,
|
||||
},
|
||||
)
|
||||
@@ -445,10 +360,10 @@ func TestQueryForIds(t *testing.T) {
|
||||
if utils.FastEqual(result.Id, ev.ID) {
|
||||
found = true
|
||||
|
||||
if !utils.FastEqual(ev.Pubkey, testEvent.Pubkey) {
|
||||
if !utils.FastEqual(ev.Pubkey, testEventForTag.Pubkey) {
|
||||
t.Fatalf(
|
||||
"result %d has incorrect author, got %x, expected %x",
|
||||
i, ev.Pubkey, testEvent.Pubkey,
|
||||
i, ev.Pubkey, testEventForTag.Pubkey,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,113 +1,21 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"git.mleku.dev/mleku/nostr/encoders/event"
|
||||
"git.mleku.dev/mleku/nostr/encoders/event/examples"
|
||||
"git.mleku.dev/mleku/nostr/encoders/filter"
|
||||
"git.mleku.dev/mleku/nostr/encoders/kind"
|
||||
"git.mleku.dev/mleku/nostr/encoders/tag"
|
||||
"lol.mleku.dev/chk"
|
||||
"next.orly.dev/pkg/interfaces/store"
|
||||
"next.orly.dev/pkg/utils"
|
||||
)
|
||||
|
||||
func TestQueryForKindsAuthorsTags(t *testing.T) {
|
||||
// Create a temporary directory for the database
|
||||
tempDir, err := os.MkdirTemp("", "test-db-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temporary directory: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir) // Clean up after the test
|
||||
|
||||
// Create a context and cancel function for the database
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Initialize the database
|
||||
db, err := New(ctx, cancel, tempDir, "info")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create database: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Create a scanner to read events from examples.Cache
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
|
||||
scanner.Buffer(make([]byte, 0, 1_000_000_000), 1_000_000_000)
|
||||
|
||||
// Count the number of events processed
|
||||
eventCount := 0
|
||||
|
||||
var events []*event.E
|
||||
|
||||
// First, collect all events from examples.Cache
|
||||
for scanner.Scan() {
|
||||
chk.E(scanner.Err())
|
||||
b := scanner.Bytes()
|
||||
ev := event.New()
|
||||
|
||||
// Unmarshal the event
|
||||
if _, err = ev.Unmarshal(b); chk.E(err) {
|
||||
ev.Free()
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
events = append(events, ev)
|
||||
}
|
||||
|
||||
// Check for scanner errors
|
||||
if err = scanner.Err(); err != nil {
|
||||
t.Fatalf("Scanner error: %v", err)
|
||||
}
|
||||
|
||||
// Sort events by CreatedAt to ensure addressable events are processed in chronological order
|
||||
sort.Slice(events, func(i, j int) bool {
|
||||
return events[i].CreatedAt < events[j].CreatedAt
|
||||
})
|
||||
|
||||
// Count the number of events processed
|
||||
eventCount = 0
|
||||
skippedCount := 0
|
||||
var savedEvents []*event.E
|
||||
|
||||
// Now process each event in chronological order
|
||||
for _, ev := range events {
|
||||
// Save the event to the database
|
||||
if _, err = db.SaveEvent(ctx, ev); err != nil {
|
||||
// Skip events that fail validation (e.g., kind 3 without p tags)
|
||||
skippedCount++
|
||||
continue
|
||||
}
|
||||
|
||||
savedEvents = append(savedEvents, ev)
|
||||
eventCount++
|
||||
}
|
||||
|
||||
t.Logf("Successfully saved %d events to the database (skipped %d invalid events)", eventCount, skippedCount)
|
||||
events = savedEvents // Use saved events for the rest of the test
|
||||
// Use shared database (read-only test)
|
||||
db, ctx := GetSharedDB(t)
|
||||
events := GetSharedEvents(t)
|
||||
|
||||
// Find an event with tags to use for testing
|
||||
var testEvent *event.E
|
||||
for _, ev := range events {
|
||||
if ev.Tags != nil && ev.Tags.Len() > 0 {
|
||||
// Find a tag with at least 2 elements and first element of length 1
|
||||
for _, tg := range *ev.Tags {
|
||||
if tg.Len() >= 2 && len(tg.Key()) == 1 {
|
||||
testEvent = ev
|
||||
break
|
||||
}
|
||||
}
|
||||
if testEvent != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
testEvent := findEventWithTag(events)
|
||||
|
||||
if testEvent == nil {
|
||||
t.Skip("No suitable event with tags found for testing")
|
||||
@@ -123,8 +31,6 @@ func TestQueryForKindsAuthorsTags(t *testing.T) {
|
||||
}
|
||||
|
||||
// Test querying by kind, author, and tag
|
||||
var idTsPk []*store.IdPkTs
|
||||
|
||||
// Use the kind from the test event
|
||||
testKind := testEvent.Kind
|
||||
kindFilter := kind.NewS(kind.New(testKind))
|
||||
@@ -135,7 +41,7 @@ func TestQueryForKindsAuthorsTags(t *testing.T) {
|
||||
// Create a tags filter with the test tag
|
||||
tagsFilter := tag.NewS(testTag)
|
||||
|
||||
idTsPk, err = db.QueryForIds(
|
||||
idTsPk, err := db.QueryForIds(
|
||||
ctx, &filter.F{
|
||||
Kinds: kindFilter,
|
||||
Authors: authorFilter,
|
||||
|
||||
@@ -1,100 +1,24 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"git.mleku.dev/mleku/nostr/encoders/event"
|
||||
"git.mleku.dev/mleku/nostr/encoders/event/examples"
|
||||
"git.mleku.dev/mleku/nostr/encoders/filter"
|
||||
"git.mleku.dev/mleku/nostr/encoders/kind"
|
||||
"git.mleku.dev/mleku/nostr/encoders/tag"
|
||||
"lol.mleku.dev/chk"
|
||||
"next.orly.dev/pkg/interfaces/store"
|
||||
"next.orly.dev/pkg/utils"
|
||||
)
|
||||
|
||||
func TestQueryForKindsAuthors(t *testing.T) {
|
||||
// Create a temporary directory for the database
|
||||
tempDir, err := os.MkdirTemp("", "test-db-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temporary directory: %v", err)
|
||||
// Use shared database (read-only test)
|
||||
db, ctx := GetSharedDB(t)
|
||||
events := GetSharedEvents(t)
|
||||
|
||||
if len(events) < 2 {
|
||||
t.Fatalf("Need at least 2 saved events, got %d", len(events))
|
||||
}
|
||||
defer os.RemoveAll(tempDir) // Clean up after the test
|
||||
|
||||
// Create a context and cancel function for the database
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Initialize the database
|
||||
db, err := New(ctx, cancel, tempDir, "info")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create database: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Create a scanner to read events from examples.Cache
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
|
||||
scanner.Buffer(make([]byte, 0, 1_000_000_000), 1_000_000_000)
|
||||
|
||||
// Count the number of events processed
|
||||
eventCount := 0
|
||||
|
||||
var events []*event.E
|
||||
|
||||
// First, collect all events from examples.Cache
|
||||
for scanner.Scan() {
|
||||
chk.E(scanner.Err())
|
||||
b := scanner.Bytes()
|
||||
ev := event.New()
|
||||
|
||||
// Unmarshal the event
|
||||
if _, err = ev.Unmarshal(b); chk.E(err) {
|
||||
ev.Free()
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
events = append(events, ev)
|
||||
}
|
||||
|
||||
// Check for scanner errors
|
||||
if err = scanner.Err(); err != nil {
|
||||
t.Fatalf("Scanner error: %v", err)
|
||||
}
|
||||
|
||||
// Sort events by CreatedAt to ensure addressable events are processed in chronological order
|
||||
sort.Slice(events, func(i, j int) bool {
|
||||
return events[i].CreatedAt < events[j].CreatedAt
|
||||
})
|
||||
|
||||
// Count the number of events processed
|
||||
eventCount = 0
|
||||
skippedCount := 0
|
||||
var savedEvents []*event.E
|
||||
|
||||
// Now process each event in chronological order
|
||||
for _, ev := range events {
|
||||
// Save the event to the database
|
||||
if _, err = db.SaveEvent(ctx, ev); err != nil {
|
||||
// Skip events that fail validation (e.g., kind 3 without p tags)
|
||||
skippedCount++
|
||||
continue
|
||||
}
|
||||
|
||||
savedEvents = append(savedEvents, ev)
|
||||
eventCount++
|
||||
}
|
||||
|
||||
t.Logf("Successfully saved %d events to the database (skipped %d invalid events)", eventCount, skippedCount)
|
||||
events = savedEvents // Use saved events for the rest of the test
|
||||
|
||||
// Test querying by kind and author
|
||||
var idTsPk []*store.IdPkTs
|
||||
|
||||
// Find an event with a specific kind and author
|
||||
testKind := kind.New(1) // Kind 1 is typically text notes
|
||||
kindFilter := kind.NewS(testKind)
|
||||
@@ -102,7 +26,7 @@ func TestQueryForKindsAuthors(t *testing.T) {
|
||||
// Use the author from events[1]
|
||||
authorFilter := tag.NewFromBytesSlice(events[1].Pubkey)
|
||||
|
||||
idTsPk, err = db.QueryForIds(
|
||||
idTsPk, err := db.QueryForIds(
|
||||
ctx, &filter.F{
|
||||
Kinds: kindFilter,
|
||||
Authors: authorFilter,
|
||||
|
||||
@@ -1,113 +1,21 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"git.mleku.dev/mleku/nostr/encoders/event"
|
||||
"git.mleku.dev/mleku/nostr/encoders/event/examples"
|
||||
"git.mleku.dev/mleku/nostr/encoders/filter"
|
||||
"git.mleku.dev/mleku/nostr/encoders/kind"
|
||||
"git.mleku.dev/mleku/nostr/encoders/tag"
|
||||
"lol.mleku.dev/chk"
|
||||
"next.orly.dev/pkg/interfaces/store"
|
||||
"next.orly.dev/pkg/utils"
|
||||
)
|
||||
|
||||
func TestQueryForKindsTags(t *testing.T) {
|
||||
// Create a temporary directory for the database
|
||||
tempDir, err := os.MkdirTemp("", "test-db-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temporary directory: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir) // Clean up after the test
|
||||
|
||||
// Create a context and cancel function for the database
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Initialize the database
|
||||
db, err := New(ctx, cancel, tempDir, "info")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create database: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Create a scanner to read events from examples.Cache
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
|
||||
scanner.Buffer(make([]byte, 0, 1_000_000_000), 1_000_000_000)
|
||||
|
||||
// Count the number of events processed
|
||||
eventCount := 0
|
||||
|
||||
var events []*event.E
|
||||
|
||||
// First, collect all events from examples.Cache
|
||||
for scanner.Scan() {
|
||||
chk.E(scanner.Err())
|
||||
b := scanner.Bytes()
|
||||
ev := event.New()
|
||||
|
||||
// Unmarshal the event
|
||||
if _, err = ev.Unmarshal(b); chk.E(err) {
|
||||
ev.Free()
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
events = append(events, ev)
|
||||
}
|
||||
|
||||
// Check for scanner errors
|
||||
if err = scanner.Err(); err != nil {
|
||||
t.Fatalf("Scanner error: %v", err)
|
||||
}
|
||||
|
||||
// Sort events by CreatedAt to ensure addressable events are processed in chronological order
|
||||
sort.Slice(events, func(i, j int) bool {
|
||||
return events[i].CreatedAt < events[j].CreatedAt
|
||||
})
|
||||
|
||||
// Count the number of events processed
|
||||
eventCount = 0
|
||||
skippedCount := 0
|
||||
var savedEvents []*event.E
|
||||
|
||||
// Now process each event in chronological order
|
||||
for _, ev := range events {
|
||||
// Save the event to the database
|
||||
if _, err = db.SaveEvent(ctx, ev); err != nil {
|
||||
// Skip events that fail validation (e.g., kind 3 without p tags)
|
||||
skippedCount++
|
||||
continue
|
||||
}
|
||||
|
||||
savedEvents = append(savedEvents, ev)
|
||||
eventCount++
|
||||
}
|
||||
|
||||
t.Logf("Successfully saved %d events to the database (skipped %d invalid events)", eventCount, skippedCount)
|
||||
events = savedEvents // Use saved events for the rest of the test
|
||||
// Use shared database (read-only test)
|
||||
db, ctx := GetSharedDB(t)
|
||||
events := GetSharedEvents(t)
|
||||
|
||||
// Find an event with tags to use for testing
|
||||
var testEvent *event.E
|
||||
for _, ev := range events {
|
||||
if ev.Tags != nil && ev.Tags.Len() > 0 {
|
||||
// Find a tag with at least 2 elements and first element of length 1
|
||||
for _, tg := range *ev.Tags {
|
||||
if tg.Len() >= 2 && len(tg.Key()) == 1 {
|
||||
testEvent = ev
|
||||
break
|
||||
}
|
||||
}
|
||||
if testEvent != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
testEvent := findEventWithTag(events)
|
||||
|
||||
if testEvent == nil {
|
||||
t.Skip("No suitable event with tags found for testing")
|
||||
@@ -123,8 +31,6 @@ func TestQueryForKindsTags(t *testing.T) {
|
||||
}
|
||||
|
||||
// Test querying by kind and tag
|
||||
var idTsPk []*store.IdPkTs
|
||||
|
||||
// Use the kind from the test event
|
||||
testKind := testEvent.Kind
|
||||
kindFilter := kind.NewS(kind.New(testKind))
|
||||
@@ -132,7 +38,7 @@ func TestQueryForKindsTags(t *testing.T) {
|
||||
// Create a tags filter with the test tag
|
||||
tagsFilter := tag.NewS(testTag)
|
||||
|
||||
idTsPk, err = db.QueryForIds(
|
||||
idTsPk, err := db.QueryForIds(
|
||||
ctx, &filter.F{
|
||||
Kinds: kindFilter,
|
||||
Tags: tagsFilter,
|
||||
|
||||
@@ -1,100 +1,28 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"git.mleku.dev/mleku/nostr/encoders/event"
|
||||
"git.mleku.dev/mleku/nostr/encoders/event/examples"
|
||||
"git.mleku.dev/mleku/nostr/encoders/filter"
|
||||
"git.mleku.dev/mleku/nostr/encoders/kind"
|
||||
"lol.mleku.dev/chk"
|
||||
"next.orly.dev/pkg/interfaces/store"
|
||||
"next.orly.dev/pkg/utils"
|
||||
)
|
||||
|
||||
func TestQueryForKinds(t *testing.T) {
|
||||
// Create a temporary directory for the database
|
||||
tempDir, err := os.MkdirTemp("", "test-db-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temporary directory: %v", err)
|
||||
// Use shared database (read-only test)
|
||||
db, ctx := GetSharedDB(t)
|
||||
events := GetSharedEvents(t)
|
||||
|
||||
if len(events) == 0 {
|
||||
t.Fatal("Need at least 1 saved event")
|
||||
}
|
||||
defer os.RemoveAll(tempDir) // Clean up after the test
|
||||
|
||||
// Create a context and cancel function for the database
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Initialize the database
|
||||
db, err := New(ctx, cancel, tempDir, "info")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create database: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Create a scanner to read events from examples.Cache
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
|
||||
scanner.Buffer(make([]byte, 0, 1_000_000_000), 1_000_000_000)
|
||||
|
||||
// Count the number of events processed
|
||||
eventCount := 0
|
||||
|
||||
var events []*event.E
|
||||
|
||||
// First, collect all events from examples.Cache
|
||||
for scanner.Scan() {
|
||||
chk.E(scanner.Err())
|
||||
b := scanner.Bytes()
|
||||
ev := event.New()
|
||||
|
||||
// Unmarshal the event
|
||||
if _, err = ev.Unmarshal(b); chk.E(err) {
|
||||
ev.Free()
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
events = append(events, ev)
|
||||
}
|
||||
|
||||
// Check for scanner errors
|
||||
if err = scanner.Err(); err != nil {
|
||||
t.Fatalf("Scanner error: %v", err)
|
||||
}
|
||||
|
||||
// Sort events by CreatedAt to ensure addressable events are processed in chronological order
|
||||
sort.Slice(events, func(i, j int) bool {
|
||||
return events[i].CreatedAt < events[j].CreatedAt
|
||||
})
|
||||
|
||||
// Count the number of events processed
|
||||
eventCount = 0
|
||||
skippedCount := 0
|
||||
|
||||
// Now process each event in chronological order
|
||||
for _, ev := range events {
|
||||
// Save the event to the database
|
||||
if _, err = db.SaveEvent(ctx, ev); err != nil {
|
||||
// Skip events that fail validation (e.g., kind 3 without p tags)
|
||||
skippedCount++
|
||||
continue
|
||||
}
|
||||
|
||||
eventCount++
|
||||
}
|
||||
|
||||
t.Logf("Successfully saved %d events to the database (skipped %d invalid events)", eventCount, skippedCount)
|
||||
|
||||
// Test querying by kind
|
||||
var idTsPk []*store.IdPkTs
|
||||
// Find an event with a specific kind
|
||||
testKind := kind.New(1) // Kind 1 is typically text notes
|
||||
kindFilter := kind.NewS(testKind)
|
||||
|
||||
idTsPk, err = db.QueryForIds(
|
||||
idTsPk, err := db.QueryForIds(
|
||||
ctx, &filter.F{
|
||||
Kinds: kindFilter,
|
||||
},
|
||||
|
||||
@@ -1,108 +1,20 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"git.mleku.dev/mleku/nostr/encoders/event"
|
||||
"git.mleku.dev/mleku/nostr/encoders/event/examples"
|
||||
"git.mleku.dev/mleku/nostr/encoders/filter"
|
||||
"git.mleku.dev/mleku/nostr/encoders/tag"
|
||||
"lol.mleku.dev/chk"
|
||||
"next.orly.dev/pkg/interfaces/store"
|
||||
"next.orly.dev/pkg/utils"
|
||||
)
|
||||
|
||||
func TestQueryForTags(t *testing.T) {
|
||||
// Create a temporary directory for the database
|
||||
tempDir, err := os.MkdirTemp("", "test-db-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temporary directory: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir) // Clean up after the test
|
||||
|
||||
// Create a context and cancel function for the database
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Initialize the database
|
||||
db, err := New(ctx, cancel, tempDir, "info")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create database: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Create a scanner to read events from examples.Cache
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
|
||||
scanner.Buffer(make([]byte, 0, 1_000_000_000), 1_000_000_000)
|
||||
|
||||
var events []*event.E
|
||||
|
||||
// First, collect all events
|
||||
for scanner.Scan() {
|
||||
chk.E(scanner.Err())
|
||||
b := scanner.Bytes()
|
||||
ev := event.New()
|
||||
|
||||
// Unmarshal the event
|
||||
if _, err = ev.Unmarshal(b); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
events = append(events, ev)
|
||||
}
|
||||
|
||||
// Check for scanner errors
|
||||
if err = scanner.Err(); err != nil {
|
||||
t.Fatalf("Scanner error: %v", err)
|
||||
}
|
||||
|
||||
// Sort events by CreatedAt to ensure addressable events are processed in chronological order
|
||||
sort.Slice(events, func(i, j int) bool {
|
||||
return events[i].CreatedAt < events[j].CreatedAt
|
||||
})
|
||||
|
||||
// Count the number of events processed
|
||||
eventCount := 0
|
||||
skippedCount := 0
|
||||
var savedEvents []*event.E
|
||||
|
||||
// Process each event in chronological order
|
||||
for _, ev := range events {
|
||||
// Save the event to the database
|
||||
if _, err = db.SaveEvent(ctx, ev); err != nil {
|
||||
// Skip events that fail validation (e.g., kind 3 without p tags)
|
||||
skippedCount++
|
||||
continue
|
||||
}
|
||||
|
||||
savedEvents = append(savedEvents, ev)
|
||||
eventCount++
|
||||
}
|
||||
|
||||
t.Logf("Successfully saved %d events to the database (skipped %d invalid events)", eventCount, skippedCount)
|
||||
events = savedEvents // Use saved events for the rest of the test
|
||||
// Use shared database (read-only test)
|
||||
db, ctx := GetSharedDB(t)
|
||||
events := GetSharedEvents(t)
|
||||
|
||||
// Find an event with tags to use for testing
|
||||
var testEvent *event.E
|
||||
for _, ev := range events {
|
||||
if ev.Tags != nil && ev.Tags.Len() > 0 {
|
||||
// Find a tag with at least 2 elements and first element of length 1
|
||||
for _, tg := range *ev.Tags {
|
||||
if tg.Len() >= 2 && len(tg.Key()) == 1 {
|
||||
testEvent = ev
|
||||
break
|
||||
}
|
||||
}
|
||||
if testEvent != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
testEvent := findEventWithTag(events)
|
||||
|
||||
if testEvent == nil {
|
||||
t.Skip("No suitable event with tags found for testing")
|
||||
@@ -118,12 +30,10 @@ func TestQueryForTags(t *testing.T) {
|
||||
}
|
||||
|
||||
// Test querying by tag only
|
||||
var idTsPk []*store.IdPkTs
|
||||
|
||||
// Create a tags filter with the test tag
|
||||
tagsFilter := tag.NewS(testTag)
|
||||
|
||||
idTsPk, err = db.QueryForIds(
|
||||
idTsPk, err := db.QueryForIds(
|
||||
ctx, &filter.F{
|
||||
Tags: tagsFilter,
|
||||
},
|
||||
|
||||
@@ -1,14 +1,130 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"git.mleku.dev/mleku/nostr/encoders/event"
|
||||
"git.mleku.dev/mleku/nostr/encoders/event/examples"
|
||||
"lol.mleku.dev"
|
||||
"lol.mleku.dev/log"
|
||||
)
|
||||
|
||||
// Shared test fixtures - initialized once in TestMain
|
||||
var (
|
||||
sharedDB *D
|
||||
sharedDBDir string
|
||||
sharedDBCtx context.Context
|
||||
sharedDBCancel context.CancelFunc
|
||||
sharedDBOnce sync.Once
|
||||
sharedEvents []*event.E // Events that were successfully saved
|
||||
sharedSetupError error
|
||||
)
|
||||
|
||||
// initSharedDB initializes the shared test database with seeded data.
|
||||
// This is called once and shared across all tests that need seeded data.
|
||||
func initSharedDB() {
|
||||
sharedDBOnce.Do(func() {
|
||||
var err error
|
||||
|
||||
// Create a temporary directory for the shared database
|
||||
sharedDBDir, err = os.MkdirTemp("", "shared-test-db-*")
|
||||
if err != nil {
|
||||
sharedSetupError = err
|
||||
return
|
||||
}
|
||||
|
||||
// Create a context for the database
|
||||
sharedDBCtx, sharedDBCancel = context.WithCancel(context.Background())
|
||||
|
||||
// Initialize the database
|
||||
sharedDB, err = New(sharedDBCtx, sharedDBCancel, sharedDBDir, "info")
|
||||
if err != nil {
|
||||
sharedSetupError = err
|
||||
return
|
||||
}
|
||||
|
||||
// Seed the database with events from examples.Cache
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
|
||||
scanner.Buffer(make([]byte, 0, 1_000_000_000), 1_000_000_000)
|
||||
|
||||
var events []*event.E
|
||||
for scanner.Scan() {
|
||||
b := scanner.Bytes()
|
||||
ev := event.New()
|
||||
if _, err = ev.Unmarshal(b); err != nil {
|
||||
continue
|
||||
}
|
||||
events = append(events, ev)
|
||||
}
|
||||
|
||||
// Sort events by CreatedAt for consistent processing
|
||||
sort.Slice(events, func(i, j int) bool {
|
||||
return events[i].CreatedAt < events[j].CreatedAt
|
||||
})
|
||||
|
||||
// Save events to the database
|
||||
for _, ev := range events {
|
||||
if _, err = sharedDB.SaveEvent(sharedDBCtx, ev); err != nil {
|
||||
continue // Skip invalid events
|
||||
}
|
||||
sharedEvents = append(sharedEvents, ev)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// GetSharedDB returns the shared test database.
|
||||
// Returns nil if testing.Short() is set or if setup failed.
|
||||
func GetSharedDB(t *testing.T) (*D, context.Context) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test that requires seeded database in short mode")
|
||||
}
|
||||
|
||||
initSharedDB()
|
||||
|
||||
if sharedSetupError != nil {
|
||||
t.Fatalf("Failed to initialize shared database: %v", sharedSetupError)
|
||||
}
|
||||
|
||||
return sharedDB, sharedDBCtx
|
||||
}
|
||||
|
||||
// GetSharedEvents returns the events that were successfully saved to the shared database.
|
||||
func GetSharedEvents(t *testing.T) []*event.E {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test that requires seeded events in short mode")
|
||||
}
|
||||
|
||||
initSharedDB()
|
||||
|
||||
if sharedSetupError != nil {
|
||||
t.Fatalf("Failed to initialize shared database: %v", sharedSetupError)
|
||||
}
|
||||
|
||||
return sharedEvents
|
||||
}
|
||||
|
||||
// findEventWithTag finds an event with a single-character tag key and at least 2 elements.
|
||||
// Returns nil if no suitable event is found.
|
||||
func findEventWithTag(events []*event.E) *event.E {
|
||||
for _, ev := range events {
|
||||
if ev.Tags != nil && ev.Tags.Len() > 0 {
|
||||
for _, tg := range *ev.Tags {
|
||||
if tg.Len() >= 2 && len(tg.Key()) == 1 {
|
||||
return ev
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// Disable all logging during tests unless explicitly enabled
|
||||
if os.Getenv("TEST_LOG") == "" {
|
||||
@@ -29,5 +145,18 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
|
||||
// Run tests
|
||||
os.Exit(m.Run())
|
||||
code := m.Run()
|
||||
|
||||
// Cleanup shared database
|
||||
if sharedDBCancel != nil {
|
||||
sharedDBCancel()
|
||||
}
|
||||
if sharedDB != nil {
|
||||
sharedDB.Close()
|
||||
}
|
||||
if sharedDBDir != "" {
|
||||
os.RemoveAll(sharedDBDir)
|
||||
}
|
||||
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user