Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

token_test.gno

7.37 Kb · 247 lines
  1package metadata
  2
  3import (
  4	"testing"
  5
  6	"gno.land/p/nt/grc721/v0"
  7	"gno.land/p/nt/seqid/v0"
  8	"gno.land/p/nt/testutils/v0"
  9	"gno.land/p/nt/uassert/v0"
 10	"gno.land/p/nt/urequire/v0"
 11)
 12
 13// newToken builds via same-realm cross (NewToken requires rlm.IsCurrent()).
 14func newToken(name, symbol string, id seqid.ID, rlm realm) (tok *grc721.Token, led *grc721.PrivateLedger) {
 15	func(cur realm) {
 16		tok, led = grc721.NewToken(name, symbol, id, cur)
 17	}(cross(rlm))
 18	return
 19}
 20
 21func newMeta(id seqid.ID, rlm realm) (*grc721.PrivateLedger, *Metadata, *Ledger) {
 22	_, tokenLedger := newToken("Foo", "FOO", id, rlm)
 23	meta, metaLedger := NewMetadata(tokenLedger)
 24	return tokenLedger, meta, metaLedger
 25}
 26
 27func TestNewMetadata(cur realm, t *testing.T) {
 28	tests := []struct {
 29		name      string
 30		nilLedger bool
 31	}{
 32		{"nil token ledger panics", true},
 33		{"valid token ledger constructs read view and ledger", false},
 34	}
 35
 36	for _, tt := range tests {
 37		t.Run(tt.name, func(t *testing.T) {
 38			if tt.nilLedger {
 39				uassert.PanicsWithMessage(t, cur, "metadata: nil core ledger", func() {
 40					NewMetadata(nil)
 41				})
 42				return
 43			}
 44
 45			_, tokenLedger := newToken("Foo", "FOO", 0, cur)
 46			meta, metaLedger := NewMetadata(tokenLedger)
 47
 48			uassert.True(t, meta != nil)
 49			uassert.True(t, metaLedger != nil)
 50			uassert.Equal(t, Kind, meta.ExtensionKind())
 51			uassert.Equal(t, "metadata", meta.ExtensionKind())
 52			uassert.Equal(t, tokenLedger.ReadToken().ID(), meta.TokenID())
 53		})
 54	}
 55}
 56
 57func TestSetTokenURI(cur realm, t *testing.T) {
 58	tests := []struct {
 59		name    string
 60		mint    bool
 61		tid     grc721.TokenID
 62		uri     string
 63		wantErr error
 64	}{
 65		{"unminted token returns ErrTokenNotMinted", false, "404", "ipfs://x", ErrTokenNotMinted},
 66		{"minted token sets uri", true, "1", "ipfs://uri-1", nil},
 67		{"empty uri on minted token is allowed", true, "1", "", nil},
 68	}
 69
 70	for _, tt := range tests {
 71		t.Run(tt.name, func(t *testing.T) {
 72			tokenLedger, meta, metaLedger := newMeta(0, cur)
 73			if tt.mint {
 74				urequire.NoError(t, tokenLedger.Mint(testutils.TestAddress("alice"), tt.tid))
 75			}
 76
 77			err := metaLedger.SetTokenURI(tt.tid, tt.uri)
 78			if tt.wantErr != nil {
 79				uassert.ErrorIs(t, err, tt.wantErr)
 80				uassert.False(t, meta.HasTokenURI(tt.tid))
 81				return
 82			}
 83
 84			urequire.NoError(t, err)
 85			uassert.True(t, meta.HasTokenURI(tt.tid))
 86			got, err := meta.TokenURI(tt.tid)
 87			urequire.NoError(t, err)
 88			uassert.Equal(t, tt.uri, got)
 89		})
 90	}
 91}
 92
 93func TestSetTokenURIOverwrite(cur realm, t *testing.T) {
 94	tokenLedger, meta, metaLedger := newMeta(0, cur)
 95	urequire.NoError(t, tokenLedger.Mint(testutils.TestAddress("alice"), "1"))
 96
 97	urequire.NoError(t, metaLedger.SetTokenURI("1", "ipfs://first"))
 98	urequire.NoError(t, metaLedger.SetTokenURI("1", "ipfs://second"))
 99
100	got, err := meta.TokenURI("1")
101	urequire.NoError(t, err)
102	uassert.Equal(t, "ipfs://second", got)
103}
104
105func TestSetTokenMetadata(cur realm, t *testing.T) {
106	full := Data{
107		Image:           "ipfs://img-1",
108		ImageData:       "<svg/>",
109		ExternalURL:     "https://example.com/1",
110		Description:     "first",
111		Name:            "Token One",
112		Attributes:      []Trait{{DisplayType: "string", TraitType: "rarity", Value: "rare"}},
113		BackgroundColor: "ffffff",
114		AnimationURL:    "ipfs://anim-1",
115		YoutubeURL:      "https://youtu.be/abc",
116	}
117
118	tests := []struct {
119		name    string
120		mint    bool
121		tid     grc721.TokenID
122		data    Data
123		wantErr error
124	}{
125		{"unminted token returns ErrTokenNotMinted", false, "404", Data{Name: "x"}, ErrTokenNotMinted},
126		{"minted token stores full metadata", true, "1", full, nil},
127		{"minted token stores zero-value metadata", true, "2", Data{}, nil},
128	}
129
130	for _, tt := range tests {
131		t.Run(tt.name, func(t *testing.T) {
132			tokenLedger, meta, metaLedger := newMeta(0, cur)
133			if tt.mint {
134				urequire.NoError(t, tokenLedger.Mint(testutils.TestAddress("alice"), tt.tid))
135			}
136
137			err := metaLedger.SetTokenMetadata(tt.tid, tt.data)
138			if tt.wantErr != nil {
139				uassert.ErrorIs(t, err, tt.wantErr)
140				uassert.False(t, meta.HasMetadata(tt.tid))
141				return
142			}
143
144			urequire.NoError(t, err)
145			uassert.True(t, meta.HasMetadata(tt.tid))
146			got, err := meta.TokenMetadata(tt.tid)
147			urequire.NoError(t, err)
148			uassert.Equal(t, tt.data.Name, got.Name)
149			uassert.Equal(t, tt.data.Description, got.Description)
150			uassert.Equal(t, tt.data.Image, got.Image)
151			uassert.Equal(t, len(tt.data.Attributes), len(got.Attributes))
152			if len(tt.data.Attributes) > 0 {
153				uassert.Equal(t, tt.data.Attributes[0].Value, got.Attributes[0].Value)
154				uassert.Equal(t, tt.data.Attributes[0].TraitType, got.Attributes[0].TraitType)
155			}
156		})
157	}
158}
159
160func TestSetTokenMetadataOverwrite(cur realm, t *testing.T) {
161	tokenLedger, meta, metaLedger := newMeta(0, cur)
162	urequire.NoError(t, tokenLedger.Mint(testutils.TestAddress("alice"), "1"))
163
164	urequire.NoError(t, metaLedger.SetTokenMetadata("1", Data{Name: "first"}))
165	urequire.NoError(t, metaLedger.SetTokenMetadata("1", Data{Name: "second"}))
166
167	got, err := meta.TokenMetadata("1")
168	urequire.NoError(t, err)
169	uassert.Equal(t, "second", got.Name)
170}
171
172func TestReadsOnUnsetToken(cur realm, t *testing.T) {
173	tests := []struct {
174		name string
175		fn   func(t *testing.T, meta *Metadata)
176	}{
177		{"TokenURI on unset token returns ErrNoTokenURI", func(t *testing.T, meta *Metadata) {
178			uri, err := meta.TokenURI("1")
179			uassert.Equal(t, "", uri)
180			uassert.ErrorIs(t, err, ErrNoTokenURI)
181		}},
182		{"TokenMetadata on unset token returns ErrNoMetadata", func(t *testing.T, meta *Metadata) {
183			data, err := meta.TokenMetadata("1")
184			uassert.Equal(t, "", data.Name)
185			uassert.ErrorIs(t, err, ErrNoMetadata)
186		}},
187		{"HasTokenURI is false on unset token", func(t *testing.T, meta *Metadata) {
188			uassert.False(t, meta.HasTokenURI("1"))
189		}},
190		{"HasMetadata is false on unset token", func(t *testing.T, meta *Metadata) {
191			uassert.False(t, meta.HasMetadata("1"))
192		}},
193	}
194
195	for _, tt := range tests {
196		t.Run(tt.name, func(t *testing.T) {
197			_, meta, _ := newMeta(0, cur)
198			tt.fn(t, meta)
199		})
200	}
201}
202
203func TestOnBurnClearsMetadata(cur realm, t *testing.T) {
204	alice := testutils.TestAddress("alice")
205	tokenLedger, meta, metaLedger := newMeta(0, cur)
206
207	urequire.NoError(t, tokenLedger.Mint(alice, "1"))
208	urequire.NoError(t, metaLedger.SetTokenURI("1", "ipfs://uri-1"))
209	urequire.NoError(t, metaLedger.SetTokenMetadata("1", Data{Name: "one"}))
210	uassert.True(t, meta.HasTokenURI("1"))
211	uassert.True(t, meta.HasMetadata("1"))
212
213	urequire.NoError(t, tokenLedger.Burn("1"))
214	uassert.False(t, meta.HasTokenURI("1"))
215	uassert.False(t, meta.HasMetadata("1"))
216
217	_, err := meta.TokenURI("1")
218	uassert.ErrorIs(t, err, ErrNoTokenURI)
219	_, err = meta.TokenMetadata("1")
220	uassert.ErrorIs(t, err, ErrNoMetadata)
221
222	urequire.NoError(t, tokenLedger.Mint(alice, "1"))
223	uassert.False(t, meta.HasTokenURI("1"))
224	uassert.False(t, meta.HasMetadata("1"))
225}
226
227func TestHooks(cur realm, t *testing.T) {
228	alice := testutils.TestAddress("alice")
229	bob := testutils.TestAddress("bob")
230	tokenLedger, meta, metaLedger := newMeta(0, cur)
231
232	urequire.NoError(t, tokenLedger.Mint(alice, "1"))
233	urequire.NoError(t, metaLedger.SetTokenURI("1", "ipfs://uri-1"))
234	urequire.NoError(t, metaLedger.SetTokenMetadata("1", Data{Name: "one"}))
235
236	metaLedger.OnMint(alice, "1")
237	metaLedger.OnTransfer(alice, bob, "1")
238	uassert.True(t, meta.HasTokenURI("1"))
239	uassert.True(t, meta.HasMetadata("1"))
240	uri, err := meta.TokenURI("1")
241	urequire.NoError(t, err)
242	uassert.Equal(t, "ipfs://uri-1", uri)
243
244	metaLedger.OnBurn("1")
245	uassert.False(t, meta.HasTokenURI("1"))
246	uassert.False(t, meta.HasMetadata("1"))
247}