forked from nbd-wtf/go-nostr
-
Notifications
You must be signed in to change notification settings - Fork 1
/
relaypool_test.go
140 lines (129 loc) · 3.76 KB
/
relaypool_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package nostr
import (
"bytes"
"context"
"encoding/json"
"fmt"
"sync"
"testing"
"time"
"golang.org/x/net/websocket"
)
func TestRelayPoolSubUnique(t *testing.T) {
// prepare test notes to send to a client subs
priv, pub := makeKeyPair(t)
notesMap := make(map[string]Event)
notesFilter := Filter{}
for i := 0; i < 10; i++ {
note := Event{
Kind: 1,
Content: fmt.Sprintf("hello %d", i),
CreatedAt: time.Unix(1672068534+int64(i), 0),
PubKey: pub,
}
mustSignEvent(t, priv, ¬e)
notesMap[note.ID] = note
notesFilter.IDs = append(notesFilter.IDs, note.ID)
}
var mu sync.Mutex // guards subscribed and seenSubID to satisfy go test -race
var (
subscribed1, subscribed2 bool
seenSubID1, seenSubID2 string
)
// fake relay server 1
ws1 := newWebsocketServer(func(conn *websocket.Conn) {
mu.Lock()
subscribed1 = true
mu.Unlock()
// verify the client sent a good sub request
var raw []json.RawMessage
if err := websocket.JSON.Receive(conn, &raw); err != nil {
t.Errorf("ws1: websocket.JSON.Receive: %v", err)
}
subid, filters := parseSubscriptionMessage(t, raw)
seenSubID1 = subid
if len(filters) != 1 || !FilterEqual(filters[0], notesFilter) {
t.Errorf("ws1: client sent filters:\n%+v\nwant:\n%+v", filters, Filters{notesFilter})
}
// send back all the notes
for id, note := range notesMap {
if err := websocket.JSON.Send(conn, []any{"EVENT", subid, note}); err != nil {
t.Errorf("ws1: %s: websocket.JSON.Send: %v", id, err)
}
}
})
defer ws1.Close()
// fake relay server 2
ws2 := newWebsocketServer(func(conn *websocket.Conn) {
mu.Lock()
subscribed2 = true
mu.Unlock()
// verify the client sent a good sub request
var raw []json.RawMessage
if err := websocket.JSON.Receive(conn, &raw); err != nil {
t.Errorf("ws2: websocket.JSON.Receive: %v", err)
}
subid, filters := parseSubscriptionMessage(t, raw)
seenSubID2 = subid
if len(filters) != 1 || !FilterEqual(filters[0], notesFilter) {
t.Errorf("ws2: client sent filters:\n%+v\nwant:\n%+v", filters, Filters{notesFilter})
}
// send back all the notes
for id, note := range notesMap {
if err := websocket.JSON.Send(conn, []any{"EVENT", subid, note}); err != nil {
t.Errorf("ws2: %s: websocket.JSON.Send: %v", id, err)
}
}
})
defer ws2.Close()
// connect a client, sub and verify it receives all events without duplicates
pool := mustRelayPoolConnect(ws1.URL, ws2.URL)
subid, ch, _ := pool.Sub(Filters{notesFilter})
uniq := Unique(ch)
seen := make(map[string]bool)
loop:
for {
select {
case event := <-uniq:
wantNote, ok := notesMap[event.ID]
if !ok {
t.Errorf("received unknown event: %+v", event)
continue
}
if seen[event.ID] {
t.Errorf("client already seen event %s", event.ID)
continue
}
if !bytes.Equal(event.Serialize(), wantNote.Serialize()) {
t.Errorf("received event:\n%+v\nwant:\n%+v", event, wantNote)
}
seen[event.ID] = true
if len(seen) == len(notesMap) {
break loop
}
case <-time.After(2 * time.Second):
t.Errorf("took too long to receive from sub; seen %d out of %d events", len(seen), len(notesMap))
break loop
}
}
mu.Lock()
defer mu.Unlock()
if !subscribed1 || !subscribed2 {
t.Errorf("subscribed1=%v subscribed2=%v; want both true", subscribed1, subscribed2)
}
if seenSubID1 != subid || seenSubID2 != subid {
t.Errorf("relay saw seenSubID1=%q seenSubID2=%q; want %q", seenSubID1, seenSubID2, subid)
}
}
func mustRelayPoolConnect(url ...string) *RelayPool {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
pool := NewRelayPool()
readwrite := SimplePolicy{Read: true, Write: true}
for _, u := range url {
if err := pool.AddContext(ctx, u, readwrite); err != nil {
panic(err.Error())
}
}
return pool
}