Files
next.orly.dev/pkg/database/pubkey-graph_test.go

366 lines
10 KiB
Go

package database
import (
"context"
"testing"
"github.com/dgraph-io/badger/v4"
"next.orly.dev/pkg/database/indexes"
"next.orly.dev/pkg/database/indexes/types"
"next.orly.dev/pkg/encoders/event"
"next.orly.dev/pkg/encoders/hex"
"next.orly.dev/pkg/encoders/tag"
)
func TestPubkeySerialAssignment(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
db, err := New(ctx, cancel, t.TempDir(), "info")
if err != nil {
t.Fatalf("Failed to create database: %v", err)
}
defer db.Close()
// Create a test pubkey
pubkey1 := make([]byte, 32)
for i := range pubkey1 {
pubkey1[i] = byte(i)
}
// Get or create serial for the first time
t.Logf("First call: GetOrCreatePubkeySerial for pubkey %s", hex.Enc(pubkey1))
ser1, err := db.GetOrCreatePubkeySerial(pubkey1)
if err != nil {
t.Fatalf("Failed to get or create pubkey serial: %v", err)
}
if ser1 == nil {
t.Fatal("Serial should not be nil")
}
t.Logf("First call returned serial: %d", ser1.Get())
// Debug: List all keys in database
var keyCount int
db.View(func(txn *badger.Txn) error {
it := txn.NewIterator(badger.DefaultIteratorOptions)
defer it.Close()
for it.Rewind(); it.Valid(); it.Next() {
key := it.Item().KeyCopy(nil)
t.Logf("Found key: %s (len=%d)", hex.Enc(key), len(key))
keyCount++
if keyCount > 20 {
break // Limit output
}
}
return nil
})
t.Logf("Total keys found (first 20): %d", keyCount)
// Debug: what prefix should we be looking for?
pubHash := new(types.PubHash)
pubHash.FromPubkey(pubkey1)
expectedPrefix := []byte(indexes.PubkeySerialPrefix)
t.Logf("Expected PubkeySerial prefix: %s = %s", string(expectedPrefix), hex.Enc(expectedPrefix))
// Try direct lookup
t.Logf("Direct lookup: GetPubkeySerial for same pubkey")
serDirect, err := db.GetPubkeySerial(pubkey1)
if err != nil {
t.Logf("Direct lookup failed: %v", err)
} else {
t.Logf("Direct lookup returned serial: %d", serDirect.Get())
}
// Get the same pubkey again - should return the same serial
t.Logf("Second call: GetOrCreatePubkeySerial for same pubkey")
ser2, err := db.GetOrCreatePubkeySerial(pubkey1)
if err != nil {
t.Fatalf("Failed to get existing pubkey serial: %v", err)
}
t.Logf("Second call returned serial: %d", ser2.Get())
if ser1.Get() != ser2.Get() {
t.Errorf("Expected same serial, got %d and %d", ser1.Get(), ser2.Get())
}
// Create a different pubkey
pubkey2 := make([]byte, 32)
for i := range pubkey2 {
pubkey2[i] = byte(i + 100)
}
ser3, err := db.GetOrCreatePubkeySerial(pubkey2)
if err != nil {
t.Fatalf("Failed to get or create second pubkey serial: %v", err)
}
if ser3.Get() == ser1.Get() {
t.Error("Different pubkeys should have different serials")
}
// Test reverse lookup: serial -> pubkey
retrievedPubkey1, err := db.GetPubkeyBySerial(ser1)
if err != nil {
t.Fatalf("Failed to get pubkey by serial: %v", err)
}
if hex.Enc(retrievedPubkey1) != hex.Enc(pubkey1) {
t.Errorf("Retrieved pubkey doesn't match. Expected %s, got %s",
hex.Enc(pubkey1), hex.Enc(retrievedPubkey1))
}
}
func TestEventPubkeyGraph(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
db, err := New(ctx, cancel, t.TempDir(), "info")
if err != nil {
t.Fatalf("Failed to create database: %v", err)
}
defer db.Close()
// Create test event with author and p-tags
authorPubkey, _ := hex.Dec("0000000000000000000000000000000000000000000000000000000000000001")
pTagPubkey1, _ := hex.Dec("0000000000000000000000000000000000000000000000000000000000000002")
pTagPubkey2, _ := hex.Dec("0000000000000000000000000000000000000000000000000000000000000003")
eventID := make([]byte, 32)
eventID[0] = 1
eventSig := make([]byte, 64)
eventSig[0] = 1
ev := &event.E{
ID: eventID,
Pubkey: authorPubkey,
CreatedAt: 1234567890,
Kind: 1, // text note
Content: []byte("Test event with p-tags"),
Sig: eventSig,
Tags: tag.NewS(
tag.NewFromAny("p", hex.Enc(pTagPubkey1)),
tag.NewFromAny("p", hex.Enc(pTagPubkey2)),
tag.NewFromAny("e", "someeventid"),
),
}
// Save the event - this should create pubkey serials and graph edges
_, err = db.SaveEvent(ctx, ev)
if err != nil {
t.Fatalf("Failed to save event: %v", err)
}
// Verify that pubkey serials were created
authorSerial, err := db.GetPubkeySerial(authorPubkey)
if err != nil {
t.Fatalf("Failed to get author pubkey serial: %v", err)
}
if authorSerial == nil {
t.Fatal("Author serial should not be nil")
}
pTag1Serial, err := db.GetPubkeySerial(pTagPubkey1)
if err != nil {
t.Fatalf("Failed to get p-tag1 pubkey serial: %v", err)
}
if pTag1Serial == nil {
t.Fatal("P-tag1 serial should not be nil")
}
pTag2Serial, err := db.GetPubkeySerial(pTagPubkey2)
if err != nil {
t.Fatalf("Failed to get p-tag2 pubkey serial: %v", err)
}
if pTag2Serial == nil {
t.Fatal("P-tag2 serial should not be nil")
}
// Verify all three pubkeys have different serials
if authorSerial.Get() == pTag1Serial.Get() || authorSerial.Get() == pTag2Serial.Get() || pTag1Serial.Get() == pTag2Serial.Get() {
t.Error("All pubkey serials should be unique")
}
t.Logf("Event saved successfully with graph edges:")
t.Logf(" Author serial: %d", authorSerial.Get())
t.Logf(" P-tag1 serial: %d", pTag1Serial.Get())
t.Logf(" P-tag2 serial: %d", pTag2Serial.Get())
}
func TestMultipleEventsWithSamePubkeys(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
db, err := New(ctx, cancel, t.TempDir(), "info")
if err != nil {
t.Fatalf("Failed to create database: %v", err)
}
defer db.Close()
// Create two events from the same author mentioning the same person
authorPubkey, _ := hex.Dec("0000000000000000000000000000000000000000000000000000000000000001")
pTagPubkey, _ := hex.Dec("0000000000000000000000000000000000000000000000000000000000000002")
eventID1 := make([]byte, 32)
eventID1[0] = 1
eventSig1 := make([]byte, 64)
eventSig1[0] = 1
ev1 := &event.E{
ID: eventID1,
Pubkey: authorPubkey,
CreatedAt: 1234567890,
Kind: 1,
Content: []byte("First event"),
Sig: eventSig1,
Tags: tag.NewS(
tag.NewFromAny("p", hex.Enc(pTagPubkey)),
),
}
eventID2 := make([]byte, 32)
eventID2[0] = 2
eventSig2 := make([]byte, 64)
eventSig2[0] = 2
ev2 := &event.E{
ID: eventID2,
Pubkey: authorPubkey,
CreatedAt: 1234567891,
Kind: 1,
Content: []byte("Second event"),
Sig: eventSig2,
Tags: tag.NewS(
tag.NewFromAny("p", hex.Enc(pTagPubkey)),
),
}
// Save both events
_, err = db.SaveEvent(ctx, ev1)
if err != nil {
t.Fatalf("Failed to save event 1: %v", err)
}
_, err = db.SaveEvent(ctx, ev2)
if err != nil {
t.Fatalf("Failed to save event 2: %v", err)
}
// Verify the same pubkeys got the same serials
authorSerial1, _ := db.GetPubkeySerial(authorPubkey)
pTagSerial1, _ := db.GetPubkeySerial(pTagPubkey)
if authorSerial1 == nil || pTagSerial1 == nil {
t.Fatal("Pubkey serials should exist after saving events")
}
t.Logf("Both events share the same pubkey serials:")
t.Logf(" Author serial: %d", authorSerial1.Get())
t.Logf(" P-tag serial: %d", pTagSerial1.Get())
}
func TestPubkeySerialEdgeCases(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
db, err := New(ctx, cancel, t.TempDir(), "info")
if err != nil {
t.Fatalf("Failed to create database: %v", err)
}
defer db.Close()
// Test with invalid pubkey length
invalidPubkey := make([]byte, 16) // Wrong length
_, err = db.GetOrCreatePubkeySerial(invalidPubkey)
if err == nil {
t.Error("Should reject pubkey with invalid length")
}
// Test GetPubkeySerial for non-existent pubkey
nonExistentPubkey := make([]byte, 32)
for i := range nonExistentPubkey {
nonExistentPubkey[i] = 0xFF
}
_, err = db.GetPubkeySerial(nonExistentPubkey)
if err == nil {
t.Error("Should return error for non-existent pubkey serial")
}
}
func TestGraphEdgeDirections(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
db, err := New(ctx, cancel, t.TempDir(), "info")
if err != nil {
t.Fatalf("Failed to create database: %v", err)
}
defer db.Close()
// Create test event with author and p-tags
authorPubkey, _ := hex.Dec("0000000000000000000000000000000000000000000000000000000000000001")
pTagPubkey, _ := hex.Dec("0000000000000000000000000000000000000000000000000000000000000002")
eventID := make([]byte, 32)
eventID[0] = 1
eventSig := make([]byte, 64)
eventSig[0] = 1
ev := &event.E{
ID: eventID,
Pubkey: authorPubkey,
CreatedAt: 1234567890,
Kind: 1, // text note
Content: []byte("Test event"),
Sig: eventSig,
Tags: tag.NewS(
tag.NewFromAny("p", hex.Enc(pTagPubkey)),
),
}
// Save the event
_, err = db.SaveEvent(ctx, ev)
if err != nil {
t.Fatalf("Failed to save event: %v", err)
}
// Verify graph edges with correct direction bytes
// Look for PubkeyEventGraph keys and check direction byte
var foundAuthorEdge, foundPTagEdge bool
db.View(func(txn *badger.Txn) error {
it := txn.NewIterator(badger.DefaultIteratorOptions)
defer it.Close()
prefix := []byte(indexes.PubkeyEventGraphPrefix)
for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() {
key := it.Item().KeyCopy(nil)
// Key format: peg(3)|pubkey_serial(5)|kind(2)|direction(1)|event_serial(5) = 16 bytes
if len(key) == 16 {
direction := key[10] // Byte at position 10 is the direction
t.Logf("Found PubkeyEventGraph edge: key=%s, direction=%d", hex.Enc(key), direction)
if direction == types.EdgeDirectionAuthor {
foundAuthorEdge = true
t.Logf(" ✓ Found author edge (direction=0)")
} else if direction == types.EdgeDirectionPTagIn {
foundPTagEdge = true
t.Logf(" ✓ Found p-tag inbound edge (direction=2)")
}
}
}
return nil
})
if !foundAuthorEdge {
t.Error("Did not find author edge with direction=0")
}
if !foundPTagEdge {
t.Error("Did not find p-tag inbound edge with direction=2")
}
t.Logf("Graph edges correctly stored with direction bytes:")
t.Logf(" Author edge: %v (direction=0)", foundAuthorEdge)
t.Logf(" P-tag inbound edge: %v (direction=2)", foundPTagEdge)
}