Skip to content

Commit 2b8652a

Browse files
committed
test: gsoc api
1 parent 0cadf85 commit 2b8652a

2 files changed

Lines changed: 172 additions & 0 deletions

File tree

pkg/api/gsoc_test.go

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// Copyright 2024 The Swarm Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package api_test
6+
7+
import (
8+
"encoding/hex"
9+
"fmt"
10+
"net/url"
11+
"strings"
12+
"testing"
13+
"time"
14+
15+
"github.com/ethersphere/bee/v2/pkg/cac"
16+
"github.com/ethersphere/bee/v2/pkg/crypto"
17+
"github.com/ethersphere/bee/v2/pkg/gsoc"
18+
"github.com/ethersphere/bee/v2/pkg/log"
19+
mockbatchstore "github.com/ethersphere/bee/v2/pkg/postage/batchstore/mock"
20+
"github.com/ethersphere/bee/v2/pkg/soc"
21+
mockstorer "github.com/ethersphere/bee/v2/pkg/storer/mock"
22+
"github.com/ethersphere/bee/v2/pkg/swarm"
23+
"github.com/ethersphere/bee/v2/pkg/util/testutil"
24+
"github.com/gorilla/websocket"
25+
)
26+
27+
// TestGsocWebsocketSingleHandler creates a single websocket handler on a chunk address, and receives a message
28+
func TestGsocWebsocketSingleHandler(t *testing.T) {
29+
t.Parallel()
30+
31+
var (
32+
id = make([]byte, 32)
33+
g, cl, signer, _ = newGsocTest(t, id, 0)
34+
respC = make(chan error, 1)
35+
payload = []byte("hello there!")
36+
)
37+
38+
err := cl.SetReadDeadline(time.Now().Add(2 * time.Second))
39+
if err != nil {
40+
t.Fatal(err)
41+
}
42+
cl.SetReadLimit(swarm.ChunkSize)
43+
44+
ch, _ := cac.New(payload)
45+
socCh := soc.New(id, ch)
46+
ch, _ = socCh.Sign(signer)
47+
socCh, _ = soc.FromChunk(ch)
48+
g.Handler(*socCh)
49+
50+
go expectMessage(t, cl, respC, payload)
51+
if err := <-respC; err != nil {
52+
t.Fatal(err)
53+
}
54+
}
55+
56+
func TestGsocWebsocketMultiHandler(t *testing.T) {
57+
t.Parallel()
58+
59+
var (
60+
id = make([]byte, 32)
61+
g, cl, signer, listener = newGsocTest(t, make([]byte, 32), 0)
62+
owner, _ = signer.EthereumAddress()
63+
chunkAddr, _ = soc.CreateAddress(id, owner.Bytes())
64+
u = url.URL{Scheme: "ws", Host: listener, Path: fmt.Sprintf("/gsoc/subscribe/%s", hex.EncodeToString(chunkAddr.Bytes()))}
65+
cl2, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
66+
respC = make(chan error, 2)
67+
)
68+
if err != nil {
69+
t.Fatalf("dial: %v. url %v", err, u.String())
70+
}
71+
testutil.CleanupCloser(t, cl2)
72+
73+
err = cl.SetReadDeadline(time.Now().Add(2 * time.Second))
74+
if err != nil {
75+
t.Fatal(err)
76+
}
77+
cl.SetReadLimit(swarm.ChunkSize)
78+
79+
ch, _ := cac.New(payload)
80+
socCh := soc.New(id, ch)
81+
ch, _ = socCh.Sign(signer)
82+
socCh, _ = soc.FromChunk(ch)
83+
84+
// close the websocket before calling pss with the message
85+
err = cl.WriteMessage(websocket.CloseMessage, []byte{})
86+
if err != nil {
87+
t.Fatal(err)
88+
}
89+
90+
g.Handler(*socCh)
91+
92+
go expectMessage(t, cl, respC, payload)
93+
go expectMessage(t, cl2, respC, payload)
94+
if err := <-respC; err != nil {
95+
t.Fatal(err)
96+
}
97+
if err := <-respC; err != nil {
98+
t.Fatal(err)
99+
}
100+
}
101+
102+
// TestGsocPong tests that the websocket api adheres to the websocket standard
103+
// and sends ping-pong messages to keep the connection alive.
104+
// The test opens a websocket, keeps it alive for 500ms, then receives a pss message.
105+
func TestGsocPong(t *testing.T) {
106+
t.Parallel()
107+
id := make([]byte, 32)
108+
109+
var (
110+
g, cl, signer, _ = newGsocTest(t, id, 90*time.Millisecond)
111+
112+
respC = make(chan error, 1)
113+
pongWait = 1 * time.Millisecond
114+
)
115+
116+
cl.SetReadLimit(swarm.ChunkSize)
117+
err := cl.SetReadDeadline(time.Now().Add(pongWait))
118+
if err != nil {
119+
t.Fatal(err)
120+
}
121+
122+
time.Sleep(500 * time.Millisecond) // wait to see that the websocket is kept alive
123+
ch, _ := cac.New([]byte("hello there!"))
124+
socCh := soc.New(id, ch)
125+
ch, _ = socCh.Sign(signer)
126+
socCh, _ = soc.FromChunk(ch)
127+
128+
g.Handler(*socCh)
129+
130+
go expectMessage(t, cl, respC, nil)
131+
if err := <-respC; err == nil || !strings.Contains(err.Error(), "i/o timeout") {
132+
// note: error has *websocket.netError type so we need to check error by checking message
133+
t.Fatal("want timeout error")
134+
}
135+
}
136+
137+
func newGsocTest(t *testing.T, socId []byte, pingPeriod time.Duration) (gsoc.Listener, *websocket.Conn, crypto.Signer, string) {
138+
t.Helper()
139+
if pingPeriod == 0 {
140+
pingPeriod = 10 * time.Second
141+
}
142+
var (
143+
batchStore = mockbatchstore.New()
144+
storer = mockstorer.New()
145+
)
146+
147+
privKey, err := crypto.GenerateSecp256k1Key()
148+
if err != nil {
149+
t.Fatal(err)
150+
}
151+
signer := crypto.NewDefaultSigner(privKey)
152+
owner, err := signer.EthereumAddress()
153+
if err != nil {
154+
t.Fatal(err)
155+
}
156+
chunkAddr, _ := soc.CreateAddress(socId, owner.Bytes())
157+
158+
gsoc := gsoc.New()
159+
testutil.CleanupCloser(t, gsoc)
160+
161+
_, cl, listener, _ := newTestServer(t, testServerOptions{
162+
Gsoc: gsoc,
163+
WsPath: fmt.Sprintf("/gsoc/subscribe/%s", hex.EncodeToString(chunkAddr.Bytes())),
164+
Storer: storer,
165+
BatchStore: batchStore,
166+
Logger: log.Noop,
167+
WsPingPeriod: pingPeriod,
168+
})
169+
170+
return gsoc, cl, signer, listener
171+
}

pkg/gsoc/gsoc.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
type Listener interface {
1616
Register(address [32]byte, handler handler) (cleanup func())
1717
Handler(c soc.SOC)
18+
Close() error
1819
}
1920

2021
type listener struct {

0 commit comments

Comments
 (0)