Migrate internal module imports to unified package path.
Replaced legacy `*.orly` module imports with `next.orly.dev/pkg` paths across the codebase for consistency. Removed legacy `go.mod` files from sub-packages, consolidating dependency management. Added Dockerfiles and configurations for benchmarking environments.
This commit is contained in:
277
pkg/protocol/ws/client_test.go
Normal file
277
pkg/protocol/ws/client_test.go
Normal file
@@ -0,0 +1,277 @@
|
||||
//go:build !js
|
||||
|
||||
package ws
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/websocket"
|
||||
"lol.mleku.dev/chk"
|
||||
"next.orly.dev/pkg/crypto/p256k"
|
||||
"next.orly.dev/pkg/encoders/event"
|
||||
"next.orly.dev/pkg/encoders/filter"
|
||||
"next.orly.dev/pkg/encoders/hex"
|
||||
"next.orly.dev/pkg/encoders/kind"
|
||||
"next.orly.dev/pkg/encoders/tag"
|
||||
"next.orly.dev/pkg/utils"
|
||||
"next.orly.dev/pkg/utils/normalize"
|
||||
)
|
||||
|
||||
func TestPublish(t *testing.T) {
|
||||
// test note to be sent over websocket
|
||||
priv, pub := makeKeyPair(t)
|
||||
textNote := &event.E{
|
||||
Kind: kind.TextNote.K,
|
||||
Content: []byte("hello"),
|
||||
CreatedAt: 1672068534, // random fixed timestamp
|
||||
Tags: tag.NewS(tag.NewFromAny("foo", "bar")),
|
||||
Pubkey: pub,
|
||||
}
|
||||
sign := &p256k.Signer{}
|
||||
var err error
|
||||
if err = sign.InitSec(priv); chk.E(err) {
|
||||
}
|
||||
err = textNote.Sign(sign)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// fake relay server
|
||||
var mu sync.Mutex // guards published to satisfy go test -race
|
||||
var published bool
|
||||
ws := newWebsocketServer(
|
||||
func(conn *websocket.Conn) {
|
||||
mu.Lock()
|
||||
published = true
|
||||
mu.Unlock()
|
||||
// verify the client sent exactly the textNote
|
||||
var raw []json.RawMessage
|
||||
err := websocket.JSON.Receive(conn, &raw)
|
||||
assert.NoError(t, err)
|
||||
|
||||
event := parseEventMessage(t, raw)
|
||||
assert.True(
|
||||
t, utils.FastEqual(event.Serialize(), textNote.Serialize()),
|
||||
)
|
||||
|
||||
// send back an ok nip-20 command result
|
||||
res := []any{"OK", hex.Enc(textNote.ID), true, ""}
|
||||
err = websocket.JSON.Send(conn, res)
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
)
|
||||
defer ws.Close()
|
||||
|
||||
// connect a client and send the text note
|
||||
rl := mustRelayConnect(t, ws.URL)
|
||||
err = rl.Publish(context.Background(), textNote)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.True(t, published, "fake relay server saw no event")
|
||||
}
|
||||
|
||||
// func TestPublishBlocked(t *testing.T) {
|
||||
// // test note to be sent over websocket
|
||||
// textNote := &event.E{
|
||||
// Kind: kind.TextNote, Content: []byte("hello"),
|
||||
// CreatedAt: timestamp.Now(),
|
||||
// }
|
||||
// textNote.ID = textNote.GetIDBytes()
|
||||
//
|
||||
// // fake relay server
|
||||
// ws := newWebsocketServer(
|
||||
// func(conn *websocket.Conn) {
|
||||
// // discard received message; not interested
|
||||
// var raw []json.RawMessage
|
||||
// err := websocket.JSON.Receive(conn, &raw)
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// // send back a not ok nip-20 command result
|
||||
// res := []any{"OK", textNote.IdString(), false, "blocked"}
|
||||
// websocket.JSON.Send(conn, res)
|
||||
// },
|
||||
// )
|
||||
// defer ws.Close()
|
||||
//
|
||||
// // connect a client and send a text note
|
||||
// rl := mustRelayConnect(t, ws.URL)
|
||||
// err := rl.Publish(context.Background(), textNote)
|
||||
// assert.Error(t, err)
|
||||
// }
|
||||
|
||||
func TestPublishWriteFailed(t *testing.T) {
|
||||
// test note to be sent over websocket
|
||||
textNote := &event.E{
|
||||
Kind: kind.TextNote.K,
|
||||
Content: []byte("hello"),
|
||||
CreatedAt: time.Now().Unix(),
|
||||
}
|
||||
textNote.ID = textNote.GetIDBytes()
|
||||
// fake relay server
|
||||
ws := newWebsocketServer(
|
||||
func(conn *websocket.Conn) {
|
||||
// reject receive - force send error
|
||||
conn.Close()
|
||||
},
|
||||
)
|
||||
defer ws.Close()
|
||||
// connect a client and send a text note
|
||||
rl := mustRelayConnect(t, ws.URL)
|
||||
// Force brief period of time so that publish always fails on closed socket.
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
err := rl.Publish(context.Background(), textNote)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestConnectContext(t *testing.T) {
|
||||
// fake relay server
|
||||
var mu sync.Mutex // guards connected to satisfy go test -race
|
||||
var connected bool
|
||||
ws := newWebsocketServer(
|
||||
func(conn *websocket.Conn) {
|
||||
mu.Lock()
|
||||
connected = true
|
||||
mu.Unlock()
|
||||
io.ReadAll(conn) // discard all input
|
||||
},
|
||||
)
|
||||
defer ws.Close()
|
||||
|
||||
// relay client
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
r, err := RelayConnect(ctx, ws.URL)
|
||||
assert.NoError(t, err)
|
||||
|
||||
defer r.Close()
|
||||
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
assert.True(t, connected, "fake relay server saw no client connect")
|
||||
}
|
||||
|
||||
func TestConnectContextCanceled(t *testing.T) {
|
||||
// fake relay server
|
||||
ws := newWebsocketServer(discardingHandler)
|
||||
defer ws.Close()
|
||||
|
||||
// relay client
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel() // make ctx expired
|
||||
_, err := RelayConnect(ctx, ws.URL)
|
||||
assert.ErrorIs(t, err, context.Canceled)
|
||||
}
|
||||
|
||||
func TestConnectWithOrigin(t *testing.T) {
|
||||
// fake relay server
|
||||
// default handler requires origin golang.org/x/net/websocket
|
||||
ws := httptest.NewServer(websocket.Handler(discardingHandler))
|
||||
defer ws.Close()
|
||||
|
||||
// relay client
|
||||
r := NewRelay(
|
||||
context.Background(), string(normalize.URL(ws.URL)),
|
||||
WithRequestHeader(http.Header{"origin": {"https://example.com"}}),
|
||||
)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
err := r.Connect(ctx)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func discardingHandler(conn *websocket.Conn) {
|
||||
io.ReadAll(conn) // discard all input
|
||||
}
|
||||
|
||||
func newWebsocketServer(handler func(*websocket.Conn)) *httptest.Server {
|
||||
return httptest.NewServer(
|
||||
&websocket.Server{
|
||||
Handshake: anyOriginHandshake,
|
||||
Handler: handler,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// anyOriginHandshake is an alternative to default in golang.org/x/net/websocket
|
||||
// which checks for origin. nostr client sends no origin and it makes no difference
|
||||
// for the tests here anyway.
|
||||
var anyOriginHandshake = func(conf *websocket.Config, r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeKeyPair(t *testing.T) (sec, pub []byte) {
|
||||
t.Helper()
|
||||
sign := &p256k.Signer{}
|
||||
var err error
|
||||
if err = sign.Generate(); chk.E(err) {
|
||||
return
|
||||
}
|
||||
sec = sign.Sec()
|
||||
pub = sign.Pub()
|
||||
assert.NoError(t, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func mustRelayConnect(t *testing.T, url string) *Client {
|
||||
t.Helper()
|
||||
|
||||
rl, err := RelayConnect(context.Background(), url)
|
||||
require.NoError(t, err)
|
||||
|
||||
return rl
|
||||
}
|
||||
|
||||
func parseEventMessage(t *testing.T, raw []json.RawMessage) *event.E {
|
||||
t.Helper()
|
||||
|
||||
assert.Condition(
|
||||
t, func() (success bool) {
|
||||
return len(raw) >= 2
|
||||
},
|
||||
)
|
||||
|
||||
var typ string
|
||||
err := json.Unmarshal(raw[0], &typ)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "EVENT", typ)
|
||||
|
||||
event := &event.E{}
|
||||
_, err = event.Unmarshal(raw[1])
|
||||
require.NoError(t, err)
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
func parseSubscriptionMessage(
|
||||
t *testing.T, raw []json.RawMessage,
|
||||
) (subid string, ff *filter.S) {
|
||||
t.Helper()
|
||||
|
||||
assert.Greater(t, len(raw), 3)
|
||||
|
||||
var typ string
|
||||
err := json.Unmarshal(raw[0], &typ)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "REQ", typ)
|
||||
|
||||
var id string
|
||||
err = json.Unmarshal(raw[1], &id)
|
||||
assert.NoError(t, err)
|
||||
ff = &filter.S{}
|
||||
for _, b := range raw[2:] {
|
||||
var f *filter.F
|
||||
err = json.Unmarshal(b, &f)
|
||||
assert.NoError(t, err)
|
||||
*ff = append(*ff, f)
|
||||
}
|
||||
return id, ff
|
||||
}
|
||||
Reference in New Issue
Block a user