protowire_test.mx raw

   1  package protowire
   2  
   3  import (
   4  	"testing"
   5  
   6  	"git.smesh.lol/musiquay/pkg/protocol"
   7  	"git.smesh.lol/nostr/pkg/event"
   8  	"git.smesh.lol/nostr/pkg/signer/p8k"
   9  	"git.smesh.lol/nostr/pkg/tag"
  10  )
  11  
  12  // pwTags wraps a protocol tag list in the *tag.S an event carries. The
  13  // protocol builds exactly-sized rows, so each row is copied into a tag with
  14  // the same capacity.
  15  func pwTags(rows [][]string) (s *tag.S) {
  16  	s = tag.NewSWithCap(int32(len(rows)))
  17  	for i := range rows {
  18  		tt := tag.NewWithCap(int32(len(rows[i])))
  19  		for j := range rows[i] {
  20  			tt.T = push(tt.T, rows[i][j])
  21  		}
  22  		s.T = push(s.T, tt)
  23  	}
  24  	return
  25  }
  26  
  27  func pwSigner(t *testing.T) (s *p8k.Signer) {
  28  	t.Helper()
  29  	s = p8k.MustNew()
  30  	if err := s.Generate(); err != nil {
  31  		t.Fatal(err)
  32  	}
  33  	return
  34  }
  35  
  36  func TestTagsOfRoundTrip(t *testing.T) {
  37  	s := pwSigner(t)
  38  	rows := [][]string{
  39  		[]string{"d", "isrc-1"},
  40  		[]string{"title", "A Song"},
  41  		[]string{"x", "abc123"},
  42  	}
  43  	ev := &event.E{
  44  		CreatedAt: 1700000000,
  45  		Kind:      protocol.KindTrack,
  46  		Tags:      pwTags(rows),
  47  		Content:   []byte("liner notes"),
  48  	}
  49  	if err := ev.Sign(s); err != nil {
  50  		t.Fatal(err)
  51  	}
  52  
  53  	got := TagsOf(ev)
  54  	if len(got) != len(rows) {
  55  		t.Fatalf("TagsOf length = %d, want %d", int32(len(got)), int32(len(rows)))
  56  	}
  57  	for i := range rows {
  58  		if len(got[i]) != len(rows[i]) {
  59  			t.Fatalf("tag %d length = %d, want %d", int32(i), int32(len(got[i])), int32(len(rows[i])))
  60  		}
  61  		for j := range rows[i] {
  62  			if got[i][j] != rows[i][j] {
  63  				t.Fatalf("tag %d elem %d = %q, want %q", int32(i), int32(j), got[i][j], rows[i][j])
  64  			}
  65  		}
  66  	}
  67  }
  68  
  69  func TestValidateEventTrack(t *testing.T) {
  70  	good := &protocol.Track{D: "isrc-1", Title: "Song"}
  71  	ev := &event.E{Kind: protocol.KindTrack, Tags: pwTags(good.Tags()), Content: []byte("")}
  72  	if err := ValidateEvent(ev); err != nil {
  73  		t.Fatalf("valid track rejected: %s", err.Error())
  74  	}
  75  
  76  	// music.mx's Track.Validate rejects an empty d tag; Tags() omits the tag
  77  	// entirely when D is empty.
  78  	bad := &protocol.Track{Title: "No identity"}
  79  	ev2 := &event.E{Kind: protocol.KindTrack, Tags: pwTags(bad.Tags()), Content: []byte("")}
  80  	if err := ValidateEvent(ev2); err == nil {
  81  		t.Fatal("track with an empty d tag was accepted")
  82  	}
  83  }
  84  
  85  func TestValidateEventUnparsedKind(t *testing.T) {
  86  	// The protocol defines 31922 but has no ParseCalendarDay; a payload for it
  87  	// must pass through, not be rejected by a parser that does not exist.
  88  	ev := &event.E{Kind: protocol.KindCalendarDay, Content: []byte("anything")}
  89  	if err := ValidateEvent(ev); err != nil {
  90  		t.Fatalf("unparsed kind rejected: %s", err.Error())
  91  	}
  92  	// A kind outside the vocabulary passes too.
  93  	ev2 := &event.E{Kind: 1, Content: []byte("note")}
  94  	if err := ValidateEvent(ev2); err != nil {
  95  		t.Fatalf("foreign kind rejected: %s", err.Error())
  96  	}
  97  }
  98  
  99  func TestValidateEventContentParsedKind(t *testing.T) {
 100  	good := &event.E{Kind: protocol.KindProduct, Content: []byte(`{"id":"p1","stall_id":"s1","name":"Vinyl","currency":"USD","price":20}`)}
 101  	if err := ValidateEvent(good); err != nil {
 102  		t.Fatalf("valid product rejected: %s", err.Error())
 103  	}
 104  	bad := &event.E{Kind: protocol.KindProduct, Content: []byte("not json")}
 105  	if err := ValidateEvent(bad); err == nil {
 106  		t.Fatal("product with non-JSON content was accepted")
 107  	}
 108  }
 109  
 110  // pwHex64 is a valid 64-lowercase-hex pubkey for address fixtures.
 111  func pwHex64() (s string) {
 112  	return "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
 113  }
 114  
 115  func TestAssetOf(t *testing.T) {
 116  	pub := pwHex64()
 117  	asset := &protocol.Address{Kind: protocol.KindTrack, Pubkey: pub, D: "track-d"}
 118  	addr := asset.String()
 119  
 120  	track := &event.E{
 121  		Kind: protocol.KindTrack,
 122  		Tags: pwTags([][]string{[]string{"a", addr}}),
 123  	}
 124  	gotTrack, okTrack := AssetOf(track)
 125  	if !okTrack || gotTrack != addr {
 126  		t.Fatalf("AssetOf(track) = %q, %v", gotTrack, okTrack)
 127  	}
 128  
 129  	receipt := &event.E{
 130  		Kind: protocol.KindDeliveryReceipt,
 131  		Tags: pwTags([][]string{[]string{"a", addr}}),
 132  	}
 133  	gotRec, okRec := AssetOf(receipt)
 134  	if !okRec || gotRec != addr {
 135  		t.Fatalf("AssetOf(receipt) = %q, %v", gotRec, okRec)
 136  	}
 137  
 138  	filter := &event.E{
 139  		Kind: protocol.KindAccessFilter,
 140  		Tags: pwTags([][]string{[]string{"d", protocol.FanFilterD(asset)}}),
 141  	}
 142  	gotFilt, okFilt := AssetOf(filter)
 143  	if !okFilt || gotFilt != addr {
 144  		t.Fatalf("AssetOf(filter) = %q, %v", gotFilt, okFilt)
 145  	}
 146  
 147  	// A subscription filter names no asset.
 148  	sub := &event.E{
 149  		Kind: protocol.KindAccessFilter,
 150  		Tags: pwTags([][]string{[]string{"d", protocol.SubFilterD(pub, "channel")}}),
 151  	}
 152  	gotSub, okSub := AssetOf(sub)
 153  	if okSub {
 154  		t.Fatalf("subscription filter reported asset %q", gotSub)
 155  	}
 156  
 157  	// The wrong kind has no asset even with an a tag.
 158  	album := &event.E{
 159  		Kind: protocol.KindAlbum,
 160  		Tags: pwTags([][]string{[]string{"a", addr}}),
 161  	}
 162  	gotAlbum, okAlbum := AssetOf(album)
 163  	if okAlbum {
 164  		t.Fatalf("album reported asset %q", gotAlbum)
 165  	}
 166  }
 167  
 168