server_test.mx raw

   1  package server
   2  
   3  import (
   4  	"net"
   5  	"net/http"
   6  	"testing"
   7  	"time"
   8  
   9  	"smesh.lol/pkg/acl"
  10  	"smesh.lol/pkg/nostr/event"
  11  	"smesh.lol/pkg/nostr/filter"
  12  	"smesh.lol/pkg/nostr/kind"
  13  	"smesh.lol/pkg/nostr/signer/p8k"
  14  	"smesh.lol/pkg/nostr/tag"
  15  	"smesh.lol/pkg/nostr/ws"
  16  	"smesh.lol/pkg/relay/pipeline"
  17  	"smesh.lol/pkg/store"
  18  )
  19  
  20  func startRelay(t *testing.T) (addr string) {
  21  	t.Helper()
  22  	eng, err := store.Open(t.TempDir())
  23  	if err != nil {
  24  		t.Fatal(err)
  25  	}
  26  	t.Cleanup(func() { eng.Close() })
  27  
  28  	srv := New(eng, acl.Open{}, nil, pipeline.DefaultConfig())
  29  	ln, err := net.Listen("tcp", "127.0.0.1:0")
  30  	if err != nil {
  31  		t.Fatal(err)
  32  	}
  33  	t.Cleanup(func() { ln.Close() })
  34  	go http.Serve(ln, srv)
  35  	return ln.Addr().String()
  36  }
  37  
  38  func TestUpgradeAndClose(t *testing.T) {
  39  	addr := startRelay(t)
  40  	c, err := ws.Dial("ws://" + addr)
  41  	if err != nil {
  42  		t.Fatalf("dial: %v", err)
  43  	}
  44  	c.Close()
  45  }
  46  
  47  func TestPublishAndSubscribe(t *testing.T) {
  48  	addr := startRelay(t)
  49  	url := "ws://" + addr
  50  
  51  	// Client 1: publisher.
  52  	pub, err := ws.Connect(url)
  53  	if err != nil {
  54  		t.Fatalf("connect publisher: %v", err)
  55  	}
  56  	defer pub.Close()
  57  
  58  	signer := p8k.MustNew()
  59  	if err := signer.Generate(); err != nil {
  60  		t.Fatal(err)
  61  	}
  62  
  63  	ev := &event.E{
  64  		CreatedAt: time.Now().Unix(),
  65  		Kind:      1,
  66  		Content:   []byte("hello relay"),
  67  	}
  68  	if err := ev.Sign(signer); err != nil {
  69  		t.Fatal(err)
  70  	}
  71  
  72  	if err := pub.Publish(ev); err != nil {
  73  		t.Fatalf("publish: %v", err)
  74  	}
  75  
  76  	// Allow processing.
  77  	time.Sleep(100 * time.Millisecond)
  78  
  79  	// Client 2: subscriber queries stored events.
  80  	sub, err := ws.Connect(url)
  81  	if err != nil {
  82  		t.Fatalf("connect subscriber: %v", err)
  83  	}
  84  	defer sub.Close()
  85  
  86  	f := &filter.F{
  87  		Kinds:   kind.NewS(kind.New(uint16(1))),
  88  		Authors: tag.NewFromBytesSlice(signer.Pub()),
  89  	}
  90  	limit := uint(10)
  91  	f.Limit = &limit
  92  
  93  	subscription, err := sub.Subscribe(f)
  94  	if err != nil {
  95  		t.Fatalf("subscribe: %v", err)
  96  	}
  97  
  98  	var received []*event.E
  99  	timeout := time.After(3 * time.Second)
 100  loop:
 101  	for {
 102  		select {
 103  		case got, ok := <-subscription.Events:
 104  			if !ok {
 105  				break loop
 106  			}
 107  			if got != nil {
 108  				received = append(received, got)
 109  			}
 110  		case <-subscription.EOSE:
 111  			break loop
 112  		case <-timeout:
 113  			t.Fatal("timeout waiting for EOSE")
 114  		}
 115  	}
 116  
 117  	if len(received) != 1 {
 118  		t.Fatalf("expected 1 event, got %d", len(received))
 119  	}
 120  	if string(received[0].Content) != "hello relay" {
 121  		t.Fatalf("wrong content: %s", received[0].Content)
 122  	}
 123  }
 124  
 125  func TestRealTimeBroadcast(t *testing.T) {
 126  	addr := startRelay(t)
 127  	url := "ws://" + addr
 128  
 129  	signer := p8k.MustNew()
 130  	if err := signer.Generate(); err != nil {
 131  		t.Fatal(err)
 132  	}
 133  
 134  	// Client 1: subscribe first.
 135  	c1, err := ws.Connect(url)
 136  	if err != nil {
 137  		t.Fatal(err)
 138  	}
 139  	defer c1.Close()
 140  
 141  	f := &filter.F{
 142  		Kinds:   kind.NewS(kind.New(uint16(1))),
 143  		Authors: tag.NewFromBytesSlice(signer.Pub()),
 144  	}
 145  	limit := uint(10)
 146  	f.Limit = &limit
 147  	subscription, err := c1.Subscribe(f)
 148  	if err != nil {
 149  		t.Fatal(err)
 150  	}
 151  
 152  	// Wait for EOSE (subscription active, no stored events yet).
 153  	select {
 154  	case <-subscription.EOSE:
 155  	case <-time.After(3 * time.Second):
 156  		t.Fatal("timeout waiting for EOSE")
 157  	}
 158  
 159  	// Client 2: publish after subscription is active.
 160  	c2, err := ws.Connect(url)
 161  	if err != nil {
 162  		t.Fatal(err)
 163  	}
 164  	defer c2.Close()
 165  
 166  	ev := &event.E{
 167  		CreatedAt: time.Now().Unix(),
 168  		Kind:      1,
 169  		Content:   []byte("real-time!"),
 170  	}
 171  	if err := ev.Sign(signer); err != nil {
 172  		t.Fatal(err)
 173  	}
 174  	if err := c2.Publish(ev); err != nil {
 175  		t.Fatal(err)
 176  	}
 177  
 178  	// Client 1 should receive the event via broadcast.
 179  	select {
 180  	case got := <-subscription.Events:
 181  		if got == nil {
 182  			t.Fatal("got nil event")
 183  		}
 184  		if string(got.Content) != "real-time!" {
 185  			t.Fatalf("wrong content: %s", got.Content)
 186  		}
 187  	case <-time.After(3 * time.Second):
 188  		t.Fatal("timeout waiting for real-time event")
 189  	}
 190  }
 191  
 192  func TestNIP11Info(t *testing.T) {
 193  	addr := startRelay(t)
 194  
 195  	req, _ := http.NewRequest("GET", "http://"+addr, nil)
 196  	req.Header.Set("Accept", "application/nostr+json")
 197  	resp, err := http.DefaultClient.Do(req)
 198  	if err != nil {
 199  		t.Fatalf("NIP-11 request: %v", err)
 200  	}
 201  	defer resp.Body.Close()
 202  
 203  	if resp.Header.Get("Content-Type") != "application/nostr+json" {
 204  		t.Fatalf("wrong content-type: %s", resp.Header.Get("Content-Type"))
 205  	}
 206  	buf := []byte{:1024}
 207  	n, _ := resp.Body.Read(buf)
 208  	body := string(buf[:n])
 209  	if len(body) == 0 || body[0] != '{' {
 210  		t.Fatalf("bad NIP-11 body: %s", body)
 211  	}
 212  }
 213