- Introduced a new `spider` package to manage connections to admin relays and synchronize events for followed pubkeys. - Added configuration options for spider mode in the application settings, allowing for different operational modes (e.g., follows). - Implemented callback mechanisms to dynamically retrieve admin relays and follow lists. - Enhanced the main application to initialize and manage the spider, including starting and stopping its operation. - Added tests to validate spider creation, callbacks, and operational behavior. - Bumped version to v0.17.14.
245 lines
5.6 KiB
Go
245 lines
5.6 KiB
Go
package spider
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"next.orly.dev/pkg/database"
|
|
)
|
|
|
|
func TestSpiderCreation(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
// Create a temporary database for testing
|
|
tempDir, err := os.MkdirTemp("", "spider-test-*")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temp dir: %v", err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
db, err := database.New(ctx, cancel, tempDir, "error")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create test database: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
// Test spider creation
|
|
spider, err := New(ctx, db, nil, "follows")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create spider: %v", err)
|
|
}
|
|
|
|
if spider == nil {
|
|
t.Fatal("Spider is nil")
|
|
}
|
|
|
|
// Test that spider is not running initially
|
|
spider.mu.RLock()
|
|
running := spider.running
|
|
spider.mu.RUnlock()
|
|
|
|
if running {
|
|
t.Error("Spider should not be running initially")
|
|
}
|
|
}
|
|
|
|
func TestSpiderCallbacks(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
// Create a temporary database for testing
|
|
tempDir, err := os.MkdirTemp("", "spider-test-*")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temp dir: %v", err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
db, err := database.New(ctx, cancel, tempDir, "error")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create test database: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
spider, err := New(ctx, db, nil, "follows")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create spider: %v", err)
|
|
}
|
|
|
|
// Test callback setup
|
|
testRelays := []string{"wss://relay1.example.com", "wss://relay2.example.com"}
|
|
testPubkeys := [][]byte{{1, 2, 3}, {4, 5, 6}}
|
|
|
|
spider.SetCallbacks(
|
|
func() []string { return testRelays },
|
|
func() [][]byte { return testPubkeys },
|
|
)
|
|
|
|
// Verify callbacks are set
|
|
spider.mu.RLock()
|
|
hasCallbacks := spider.getAdminRelays != nil && spider.getFollowList != nil
|
|
spider.mu.RUnlock()
|
|
|
|
if !hasCallbacks {
|
|
t.Error("Callbacks should be set")
|
|
}
|
|
|
|
// Test that start fails without callbacks being set first
|
|
spider2, err := New(ctx, db, nil, "follows")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create second spider: %v", err)
|
|
}
|
|
|
|
err = spider2.Start()
|
|
if err == nil {
|
|
t.Error("Start should fail when callbacks are not set")
|
|
}
|
|
}
|
|
|
|
func TestSpiderModeValidation(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
// Create a temporary database for testing
|
|
tempDir, err := os.MkdirTemp("", "spider-test-*")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temp dir: %v", err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
db, err := database.New(ctx, cancel, tempDir, "error")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create test database: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
// Test valid mode
|
|
spider, err := New(ctx, db, nil, "follows")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create spider with valid mode: %v", err)
|
|
}
|
|
if spider == nil {
|
|
t.Fatal("Spider should not be nil for valid mode")
|
|
}
|
|
|
|
// Test invalid mode
|
|
_, err = New(ctx, db, nil, "invalid")
|
|
if err == nil {
|
|
t.Error("Should fail with invalid mode")
|
|
}
|
|
|
|
// Test none mode (should succeed but be a no-op)
|
|
spider2, err := New(ctx, db, nil, "none")
|
|
if err != nil {
|
|
t.Errorf("Should succeed with 'none' mode: %v", err)
|
|
}
|
|
if spider2 == nil {
|
|
t.Error("Spider should not be nil for 'none' mode")
|
|
}
|
|
|
|
// Test that 'none' mode doesn't require callbacks
|
|
err = spider2.Start()
|
|
if err != nil {
|
|
t.Errorf("'none' mode should start without callbacks: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSpiderBatching(t *testing.T) {
|
|
// Test batch creation logic
|
|
followList := make([][]byte, 50) // 50 pubkeys
|
|
for i := range followList {
|
|
followList[i] = make([]byte, 32)
|
|
for j := range followList[i] {
|
|
followList[i][j] = byte(i)
|
|
}
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
rc := &RelayConnection{
|
|
url: "wss://test.relay.com",
|
|
ctx: ctx,
|
|
}
|
|
|
|
batches := rc.createBatches(followList)
|
|
|
|
// Should create 3 batches: 20, 20, 10
|
|
expectedBatches := 3
|
|
if len(batches) != expectedBatches {
|
|
t.Errorf("Expected %d batches, got %d", expectedBatches, len(batches))
|
|
}
|
|
|
|
// Check batch sizes
|
|
if len(batches[0]) != BatchSize {
|
|
t.Errorf("First batch should have %d pubkeys, got %d", BatchSize, len(batches[0]))
|
|
}
|
|
if len(batches[1]) != BatchSize {
|
|
t.Errorf("Second batch should have %d pubkeys, got %d", BatchSize, len(batches[1]))
|
|
}
|
|
if len(batches[2]) != 10 {
|
|
t.Errorf("Third batch should have 10 pubkeys, got %d", len(batches[2]))
|
|
}
|
|
}
|
|
|
|
func TestSpiderStartStop(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
// Create a temporary database for testing
|
|
tempDir, err := os.MkdirTemp("", "spider-test-*")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temp dir: %v", err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
db, err := database.New(ctx, cancel, tempDir, "error")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create test database: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
spider, err := New(ctx, db, nil, "follows")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create spider: %v", err)
|
|
}
|
|
|
|
// Set up callbacks
|
|
spider.SetCallbacks(
|
|
func() []string { return []string{"wss://test.relay.com"} },
|
|
func() [][]byte { return [][]byte{{1, 2, 3}} },
|
|
)
|
|
|
|
// Test start
|
|
err = spider.Start()
|
|
if err != nil {
|
|
t.Fatalf("Failed to start spider: %v", err)
|
|
}
|
|
|
|
// Verify spider is running
|
|
spider.mu.RLock()
|
|
running := spider.running
|
|
spider.mu.RUnlock()
|
|
|
|
if !running {
|
|
t.Error("Spider should be running after start")
|
|
}
|
|
|
|
// Test stop
|
|
spider.Stop()
|
|
|
|
// Give it a moment to stop
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
// Verify spider is stopped
|
|
spider.mu.RLock()
|
|
running = spider.running
|
|
spider.mu.RUnlock()
|
|
|
|
if running {
|
|
t.Error("Spider should not be running after stop")
|
|
}
|
|
}
|