mirror of
https://github.com/aljazceru/crawler_v2.git
synced 2025-12-17 07:24:21 +01:00
470 lines
9.8 KiB
Go
470 lines
9.8 KiB
Go
package redb
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"github/pippellia-btc/crawler/pkg/graph"
|
|
"github/pippellia-btc/crawler/pkg/pagerank"
|
|
"github/pippellia-btc/crawler/pkg/walks"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/nbd-wtf/go-nostr"
|
|
"github.com/redis/go-redis/v9"
|
|
)
|
|
|
|
var ctx = context.Background()
|
|
|
|
func TestParseNode(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
fields map[string]string
|
|
expected *graph.Node
|
|
err error
|
|
}{
|
|
{
|
|
name: "nil map",
|
|
},
|
|
{
|
|
name: "empty map",
|
|
fields: map[string]string{},
|
|
},
|
|
{
|
|
name: "valid no records",
|
|
fields: map[string]string{
|
|
NodeID: "19",
|
|
NodePubkey: "nineteen",
|
|
NodeStatus: graph.StatusActive,
|
|
},
|
|
expected: &graph.Node{
|
|
ID: "19",
|
|
Pubkey: "nineteen",
|
|
Status: graph.StatusActive,
|
|
},
|
|
},
|
|
{
|
|
name: "valid with record",
|
|
fields: map[string]string{
|
|
NodeID: "19",
|
|
NodePubkey: "nineteen",
|
|
NodeStatus: graph.StatusActive,
|
|
NodeAddedTS: "1",
|
|
},
|
|
expected: &graph.Node{
|
|
ID: "19",
|
|
Pubkey: "nineteen",
|
|
Status: graph.StatusActive,
|
|
Records: []graph.Record{
|
|
{Kind: graph.Addition, Timestamp: time.Unix(1, 0)},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
node, err := parseNode(test.fields)
|
|
if !errors.Is(err, test.err) {
|
|
t.Fatalf("expected %v got %v", test.err, err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(node, test.expected) {
|
|
t.Fatalf("ParseNode(): expected node %v got %v", test.expected, node)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAddNode(t *testing.T) {
|
|
t.Run("node already exists", func(t *testing.T) {
|
|
db, err := OneNode()
|
|
if err != nil {
|
|
t.Fatalf("setup failed: %v", err)
|
|
}
|
|
defer db.flushAll()
|
|
|
|
if _, err = db.AddNode(ctx, "0"); !errors.Is(err, graph.ErrNodeAlreadyExists) {
|
|
t.Fatalf("expected error %v, got %v", graph.ErrNodeAlreadyExists, err)
|
|
}
|
|
})
|
|
|
|
t.Run("valid", func(t *testing.T) {
|
|
db, err := OneNode()
|
|
if err != nil {
|
|
t.Fatalf("setup failed: %v", err)
|
|
}
|
|
defer db.flushAll()
|
|
|
|
ID, err := db.AddNode(ctx, "xxx")
|
|
if err != nil {
|
|
t.Fatalf("expected nil, got %v", err)
|
|
}
|
|
|
|
expected := &graph.Node{
|
|
ID: "1",
|
|
Pubkey: "xxx",
|
|
Status: graph.StatusInactive,
|
|
Records: []graph.Record{{Kind: graph.Addition, Timestamp: time.Unix(time.Now().Unix(), 0)}},
|
|
}
|
|
|
|
if ID != expected.ID {
|
|
t.Fatalf("expected ID %s, got %s", expected.ID, ID)
|
|
}
|
|
|
|
node, err := db.NodeByKey(ctx, "xxx")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(node, expected) {
|
|
t.Fatalf("expected node %v, got %v", expected, node)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestMembers(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
setup func() (RedisDB, error)
|
|
node graph.ID
|
|
expected []graph.ID
|
|
err error
|
|
}{
|
|
{
|
|
name: "empty database",
|
|
setup: Empty,
|
|
node: "0",
|
|
err: graph.ErrNodeNotFound,
|
|
},
|
|
{
|
|
name: "node not found",
|
|
setup: OneNode,
|
|
node: "1",
|
|
err: graph.ErrNodeNotFound,
|
|
},
|
|
{
|
|
name: "dandling node",
|
|
setup: OneNode,
|
|
node: "0",
|
|
expected: []graph.ID{},
|
|
},
|
|
{
|
|
name: "valid",
|
|
setup: Simple,
|
|
node: "0",
|
|
expected: []graph.ID{"1"},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
db, err := test.setup()
|
|
if err != nil {
|
|
t.Fatalf("setup failed: %v", err)
|
|
}
|
|
defer db.flushAll()
|
|
|
|
follows, err := db.members(ctx, follows, test.node)
|
|
if !errors.Is(err, test.err) {
|
|
t.Fatalf("expected error %v, got %v", test.err, err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(follows, test.expected) {
|
|
t.Errorf("expected follows %v, got %v", test.expected, follows)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBulkMembers(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
setup func() (RedisDB, error)
|
|
nodes []graph.ID
|
|
expected [][]graph.ID
|
|
err error
|
|
}{
|
|
{
|
|
name: "empty database",
|
|
setup: Empty,
|
|
nodes: []graph.ID{"0"},
|
|
err: graph.ErrNodeNotFound,
|
|
},
|
|
{
|
|
name: "node not found",
|
|
setup: OneNode,
|
|
nodes: []graph.ID{"0", "1"},
|
|
err: graph.ErrNodeNotFound,
|
|
},
|
|
{
|
|
name: "dandling node",
|
|
setup: OneNode,
|
|
nodes: []graph.ID{"0"},
|
|
expected: [][]graph.ID{{}},
|
|
},
|
|
{
|
|
name: "valid",
|
|
setup: Simple,
|
|
nodes: []graph.ID{"0", "1"},
|
|
expected: [][]graph.ID{{"1"}, {}},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
db, err := test.setup()
|
|
if err != nil {
|
|
t.Fatalf("setup failed: %v", err)
|
|
}
|
|
defer db.flushAll()
|
|
|
|
follows, err := db.bulkMembers(ctx, follows, test.nodes)
|
|
if !errors.Is(err, test.err) {
|
|
t.Fatalf("expected error %v, got %v", test.err, err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(follows, test.expected) {
|
|
t.Errorf("expected follows %v, got %v", test.expected, follows)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUpdateFollows(t *testing.T) {
|
|
db, err := Simple()
|
|
if err != nil {
|
|
t.Fatalf("setup failed: %v", err)
|
|
}
|
|
defer db.flushAll()
|
|
|
|
delta := graph.Delta{
|
|
Kind: nostr.KindFollowList,
|
|
Node: "0",
|
|
Remove: []graph.ID{"1"},
|
|
Add: []graph.ID{"2"},
|
|
}
|
|
|
|
if err := db.Update(ctx, delta); err != nil {
|
|
t.Fatalf("expected error nil, got %v", err)
|
|
}
|
|
|
|
follows, err := db.Follows(ctx, "0")
|
|
if err != nil {
|
|
t.Fatalf("expected nil got %v", err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(follows, []graph.ID{"2"}) {
|
|
t.Fatalf("expected follows(0) %v, got %v", []graph.ID{"2"}, follows)
|
|
}
|
|
|
|
followers, err := db.Followers(ctx, "1")
|
|
if err != nil {
|
|
t.Fatalf("expected nil got %v", err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(followers, []graph.ID{}) {
|
|
t.Fatalf("expected followers(1) %v, got %v", []graph.ID{}, followers)
|
|
}
|
|
|
|
followers, err = db.Followers(ctx, "2")
|
|
if err != nil {
|
|
t.Fatalf("expected nil got %v", err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(followers, []graph.ID{"0"}) {
|
|
t.Fatalf("expected followers(2) %v, got %v", []graph.ID{"0"}, followers)
|
|
}
|
|
}
|
|
|
|
func TestNodeIDs(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
setup func() (RedisDB, error)
|
|
pubkeys []string
|
|
expected []graph.ID
|
|
}{
|
|
{
|
|
name: "empty database",
|
|
setup: Empty,
|
|
pubkeys: []string{"0"},
|
|
expected: []graph.ID{""},
|
|
},
|
|
{
|
|
name: "node not found",
|
|
setup: OneNode,
|
|
pubkeys: []string{"1"},
|
|
expected: []graph.ID{""},
|
|
},
|
|
{
|
|
name: "valid",
|
|
setup: Simple,
|
|
pubkeys: []string{"0", "1", "69"},
|
|
expected: []graph.ID{"0", "1", ""}, // last is not found
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
db, err := test.setup()
|
|
if err != nil {
|
|
t.Fatalf("setup failed: %v", err)
|
|
}
|
|
defer db.flushAll()
|
|
|
|
nodes, err := db.NodeIDs(ctx, test.pubkeys...)
|
|
if err != nil {
|
|
t.Fatalf("expected error nil, got %v", err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(nodes, test.expected) {
|
|
t.Fatalf("expected nodes %v, got %v", test.expected, nodes)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestResolve(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
setup func() (RedisDB, error)
|
|
pubkeys []string
|
|
onMissing MissingHandler
|
|
expected []graph.ID
|
|
}{
|
|
{
|
|
name: "empty database",
|
|
setup: Empty,
|
|
pubkeys: []string{"0"},
|
|
onMissing: Sentinel,
|
|
expected: []graph.ID{"-1"},
|
|
},
|
|
{
|
|
name: "node not found, ignore",
|
|
setup: OneNode,
|
|
pubkeys: []string{"1"},
|
|
onMissing: Ignore,
|
|
expected: []graph.ID{},
|
|
},
|
|
{
|
|
name: "valid",
|
|
setup: Simple,
|
|
pubkeys: []string{"0", "69", "1"},
|
|
onMissing: Ignore,
|
|
expected: []graph.ID{"0", "1"},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
db, err := test.setup()
|
|
if err != nil {
|
|
t.Fatalf("setup failed: %v", err)
|
|
}
|
|
defer db.flushAll()
|
|
|
|
nodes, err := db.Resolve(ctx, test.pubkeys, test.onMissing)
|
|
if err != nil {
|
|
t.Fatalf("expected error nil, got %v", err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(nodes, test.expected) {
|
|
t.Fatalf("expected nodes %v, got %v", test.expected, nodes)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPubkeys(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
setup func() (RedisDB, error)
|
|
nodes []graph.ID
|
|
expected []string
|
|
}{
|
|
{
|
|
name: "empty database",
|
|
setup: Empty,
|
|
nodes: []graph.ID{"0"},
|
|
expected: []string{""},
|
|
},
|
|
{
|
|
name: "node not found",
|
|
setup: OneNode,
|
|
nodes: []graph.ID{"1"},
|
|
expected: []string{""},
|
|
},
|
|
{
|
|
name: "valid",
|
|
setup: Simple,
|
|
nodes: []graph.ID{"0", "1", "69"},
|
|
expected: []string{"0", "1", ""}, // last is not found
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
db, err := test.setup()
|
|
if err != nil {
|
|
t.Fatalf("setup failed: %v", err)
|
|
}
|
|
defer db.flushAll()
|
|
|
|
pubkeys, err := db.Pubkeys(ctx, test.nodes...)
|
|
if err != nil {
|
|
t.Fatalf("expected error nil, got %v", err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(pubkeys, test.expected) {
|
|
t.Fatalf("expected pubkeys %v, got %v", test.expected, pubkeys)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestInterfaces(t *testing.T) {
|
|
var _ walks.Walker = RedisDB{}
|
|
var _ pagerank.VisitCounter = RedisDB{}
|
|
var _ pagerank.PersonalizedLoader = RedisDB{}
|
|
//var _ pipe.PubkeyChecker = RedisDB{}
|
|
}
|
|
|
|
// ------------------------------------- HELPERS -------------------------------
|
|
|
|
func Empty() (RedisDB, error) {
|
|
return RedisDB{client: redis.NewClient(&redis.Options{Addr: testAddress})}, nil
|
|
}
|
|
|
|
func OneNode() (RedisDB, error) {
|
|
db := RedisDB{client: redis.NewClient(&redis.Options{Addr: testAddress})}
|
|
if _, err := db.AddNode(ctx, "0"); err != nil {
|
|
db.flushAll()
|
|
return RedisDB{}, err
|
|
}
|
|
|
|
return db, nil
|
|
}
|
|
|
|
func Simple() (RedisDB, error) {
|
|
db := RedisDB{client: redis.NewClient(&redis.Options{Addr: testAddress})}
|
|
for _, pk := range []string{"0", "1", "2"} {
|
|
if _, err := db.AddNode(ctx, pk); err != nil {
|
|
db.flushAll()
|
|
return RedisDB{}, err
|
|
}
|
|
}
|
|
|
|
// 0 ---> 1
|
|
if err := db.client.SAdd(ctx, follows("0"), "1").Err(); err != nil {
|
|
db.flushAll()
|
|
return RedisDB{}, err
|
|
}
|
|
|
|
if err := db.client.SAdd(ctx, followers("1"), "0").Err(); err != nil {
|
|
db.flushAll()
|
|
return RedisDB{}, err
|
|
}
|
|
|
|
return db, nil
|
|
}
|