crawler_test.mx raw

   1  package crawler
   2  
   3  import (
   4  	"os"
   5  	"testing"
   6  
   7  	"git.smesh.lol/nostr/pkg/event"
   8  	"git.smesh.lol/nostr/pkg/kind"
   9  	"git.smesh.lol/nostr/pkg/signer/p8k"
  10  	"git.smesh.lol/nostr/pkg/tag"
  11  	"git.smesh.lol/morly/pkg/store"
  12  )
  13  
  14  // openTmp opens a store in a fresh directory. The caller owns both: defer
  15  // os.RemoveAll(dir) and defer eng.Close(). A t.Cleanup closure would have to
  16  // capture eng, which Moxie closures do not do.
  17  func openTmp(t *testing.T) (p *store.Engine, dir string) {
  18  	t.Helper()
  19  	dir, derr := os.MkdirTemp("", "moxie-crawler")
  20  	if derr != nil {
  21  		t.Fatal(derr)
  22  	}
  23  	eng, err := store.Open(dir)
  24  	if err != nil {
  25  		t.Fatal(err)
  26  	}
  27  	return eng, dir
  28  }
  29  
  30  func makeRelayList(t *testing.T, s *p8k.Signer, relays ...string) (p *event.E) {
  31  	t.Helper()
  32  	kind.Ensure()
  33  	tags := tag.NewSWithCap(len(relays))
  34  	for _, r := range relays {
  35  		// tags is a *tag.S, whose element slice is T; pushing onto the struct
  36  		// itself is `push on non-slice type` (stage4 used to emit IR for it
  37  		// instead of the error, which read as an IR-emit bug).
  38  		tags.T = push(tags.T, tag.NewFromBytesSlice([]byte("r"), []byte(r)))
  39  	}
  40  	ev := &event.E{
  41  		CreatedAt: 1700000000,
  42  		Kind:      kind.RelayListMetadata.K,
  43  		Tags:      tags,
  44  	}
  45  	if err := ev.Sign(s); err != nil {
  46  		t.Fatal(err)
  47  	}
  48  	return ev
  49  }
  50  
  51  func TestDiscoverRelays(t *testing.T) {
  52  	eng, dir := openTmp(t)
  53  	defer os.RemoveAll(dir)
  54  	defer eng.Close()
  55  	s := p8k.MustNew()
  56  	if err := s.Generate(); err != nil {
  57  		t.Fatal(err)
  58  	}
  59  
  60  	ev := makeRelayList(t, s, "wss://relay.example.com", "wss://another.relay")
  61  	if err := eng.SaveEvent(ev); err != nil {
  62  		t.Fatal(err)
  63  	}
  64  
  65  	c := New(eng)
  66  	relays := c.Discover()
  67  	if len(relays) != 2 {
  68  		t.Fatalf("expected 2 relays, got %d", len(relays))
  69  	}
  70  
  71  	urls := map[string]bool{}
  72  	for _, r := range relays {
  73  		urls[r.URL] = true
  74  		// No hint means both read+write
  75  		if !r.Read || !r.Write {
  76  			t.Errorf("relay %s should be read+write", r.URL)
  77  		}
  78  	}
  79  	if !urls["wss://relay.example.com"] || !urls["wss://another.relay"] {
  80  		t.Error("missing expected relay URLs")
  81  	}
  82  }
  83  
  84  func TestDiscoverDedup(t *testing.T) {
  85  	eng, dir := openTmp(t)
  86  	defer os.RemoveAll(dir)
  87  	defer eng.Close()
  88  
  89  	s1 := p8k.MustNew()
  90  	if err := s1.Generate(); err != nil {
  91  		t.Fatal(err)
  92  	}
  93  	s2 := p8k.MustNew()
  94  	if err := s2.Generate(); err != nil {
  95  		t.Fatal(err)
  96  	}
  97  
  98  	// Two users list the same relay
  99  	if err := eng.SaveEvent(makeRelayList(t, s1, "wss://shared.relay")); err != nil {
 100  		t.Fatal(err)
 101  	}
 102  	if err := eng.SaveEvent(makeRelayList(t, s2, "wss://shared.relay")); err != nil {
 103  		t.Fatal(err)
 104  	}
 105  
 106  	c := New(eng)
 107  	relays := c.Discover()
 108  	if len(relays) != 1 {
 109  		t.Fatalf("expected 1 deduplicated relay, got %d", len(relays))
 110  	}
 111  }
 112  
 113  func TestDiscoverURLs(t *testing.T) {
 114  	eng, dir := openTmp(t)
 115  	defer os.RemoveAll(dir)
 116  	defer eng.Close()
 117  	s := p8k.MustNew()
 118  	if err := s.Generate(); err != nil {
 119  		t.Fatal(err)
 120  	}
 121  
 122  	if err := eng.SaveEvent(makeRelayList(t, s, "wss://relay.one")); err != nil {
 123  		t.Fatal(err)
 124  	}
 125  
 126  	c := New(eng)
 127  	urls := c.DiscoverURLs()
 128  	if len(urls) != 1 || urls[0] != "wss://relay.one" {
 129  		t.Fatalf("expected [wss://relay.one], got %v", urls)
 130  	}
 131  }
 132