grapevine_test.mx raw
1 package grapevine
2
3 import (
4 "testing"
5
6 "smesh.lol/pkg/nostr/event"
7 "smesh.lol/pkg/nostr/kind"
8 "smesh.lol/pkg/nostr/signer/p8k"
9 "smesh.lol/pkg/nostr/tag"
10 "smesh.lol/pkg/store"
11 )
12
13 func openTmp(t *testing.T) *store.Engine {
14 t.Helper()
15 eng, err := store.Open(t.TempDir())
16 if err != nil {
17 t.Fatal(err)
18 }
19 t.Cleanup(func() { eng.Close() })
20 return eng
21 }
22
23 func genSigner(t *testing.T) *p8k.Signer {
24 t.Helper()
25 s := p8k.MustNew()
26 if err := s.Generate(); err != nil {
27 t.Fatal(err)
28 }
29 return s
30 }
31
32 func binPubkey(s *p8k.Signer) []byte {
33 pk := []byte{:33}
34 copy(pk, s.Pub())
35 // pk[32] = 0 already
36 return pk
37 }
38
39 func makeFollowList(t *testing.T, signer *p8k.Signer, follows ...*p8k.Signer) *event.E {
40 t.Helper()
41 tags := tag.NewSWithCap(len(follows))
42 for _, f := range follows {
43 *tags = append(*tags, tag.NewFromBytesSlice([]byte("p"), binPubkey(f)))
44 }
45 ev := &event.E{
46 CreatedAt: 1700000000,
47 Kind: kind.FollowList.K,
48 Tags: tags,
49 }
50 if err := ev.Sign(signer); err != nil {
51 t.Fatal(err)
52 }
53 return ev
54 }
55
56 func TestComputeDepth1(t *testing.T) {
57 eng := openTmp(t)
58 seed := genSigner(t)
59 alice := genSigner(t)
60 bob := genSigner(t)
61
62 // seed follows alice and bob
63 if err := eng.SaveEvent(makeFollowList(t, seed, alice, bob)); err != nil {
64 t.Fatal(err)
65 }
66
67 w := New(eng)
68 scores := w.Compute(seed.Pub(), 1)
69 if len(scores) != 2 {
70 t.Fatalf("expected 2 scores, got %d", len(scores))
71 }
72 // depth 1 decay = 1/2^0 = 1.0
73 for _, sc := range scores {
74 if sc.Value != 1.0 {
75 t.Errorf("expected score 1.0, got %f", sc.Value)
76 }
77 if sc.Depth != 1 {
78 t.Errorf("expected depth 1, got %d", sc.Depth)
79 }
80 }
81 }
82
83 func TestComputeDepth2(t *testing.T) {
84 eng := openTmp(t)
85 seed := genSigner(t)
86 alice := genSigner(t)
87 bob := genSigner(t)
88
89 // seed -> alice -> bob
90 if err := eng.SaveEvent(makeFollowList(t, seed, alice)); err != nil {
91 t.Fatal(err)
92 }
93 if err := eng.SaveEvent(makeFollowList(t, alice, bob)); err != nil {
94 t.Fatal(err)
95 }
96
97 w := New(eng)
98 scores := w.Compute(seed.Pub(), 2)
99
100 found := false
101 for _, sc := range scores {
102 if string(sc.Pubkey) == string(bob.Pub()) {
103 found = true
104 // depth 2 decay = 1/2^1 = 0.5
105 if sc.Value != 0.5 {
106 t.Errorf("expected bob score 0.5, got %f", sc.Value)
107 }
108 }
109 }
110 if !found {
111 t.Fatal("bob not found in scores")
112 }
113 }
114
115 func TestIsTrusted(t *testing.T) {
116 scores := []Score{
117 {Pubkey: []byte("aaa"), Value: 0.8},
118 {Pubkey: []byte("bbb"), Value: 0.3},
119 }
120 if !IsTrusted(scores, []byte("aaa"), 0.5) {
121 t.Error("aaa should be trusted at 0.5 threshold")
122 }
123 if IsTrusted(scores, []byte("bbb"), 0.5) {
124 t.Error("bbb should not be trusted at 0.5 threshold")
125 }
126 if IsTrusted(scores, []byte("ccc"), 0.1) {
127 t.Error("unknown pubkey should not be trusted")
128 }
129 }
130