Skip to content

Commit 58547ab

Browse files
committed
proof: add additional tests for encode+decode asset meta
1 parent a6d5cc5 commit 58547ab

File tree

1 file changed

+163
-0
lines changed

1 file changed

+163
-0
lines changed

proof/meta_test.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ package proof
33
import (
44
"bytes"
55
"encoding/hex"
6+
"encoding/json"
7+
"errors"
68
"net/url"
79
"os"
810
"path/filepath"
11+
"reflect"
912
"strings"
1013
"testing"
1114

@@ -15,6 +18,7 @@ import (
1518
"github.com/lightninglabs/taproot-assets/internal/test"
1619
"github.com/lightningnetwork/lnd/tlv"
1720
"github.com/stretchr/testify/require"
21+
"pgregory.net/rapid"
1822
)
1923

2024
var (
@@ -465,3 +469,162 @@ func TestDecodeNewMetaReveal(t *testing.T) {
465469
require.Equal(t, fn.None[[]url.URL](), decoded.CanonicalUniverses)
466470
require.Equal(t, fn.None[btcec.PublicKey](), decoded.DelegationKey)
467471
}
472+
473+
// TestDecodeMetaJSONEmptyBytes tests DecodeMetaJSON with an empty byte slice.
474+
func TestDecodeMetaJSONEmptyBytes(t *testing.T) {
475+
t.Parallel()
476+
477+
_, err := DecodeMetaJSON([]byte{})
478+
require.ErrorIs(t, err, ErrInvalidJSON)
479+
}
480+
481+
// TestEncodeMetaJSONEmptyOrNilMap tests EncodeMetaJSON with nil and empty maps.
482+
func TestEncodeMetaJSONEmptyOrNilMap(t *testing.T) {
483+
t.Parallel()
484+
485+
t.Run("nil map", func(t *testing.T) {
486+
nilMapBytes, err := EncodeMetaJSON(nil)
487+
require.NoError(t, err)
488+
require.Equal(
489+
t, []byte{}, nilMapBytes,
490+
"encoding nil map should produce empty bytes",
491+
)
492+
})
493+
494+
t.Run("empty map", func(t *testing.T) {
495+
emptyMap := make(map[string]interface{})
496+
emptyMapBytes, err := EncodeMetaJSON(emptyMap)
497+
require.NoError(t, err)
498+
require.Equal(t, []byte("{}"), emptyMapBytes,
499+
"Encoding empty map should produce '{}'")
500+
})
501+
}
502+
503+
// simpleJSONMap is a helper struct for generating map[string]interface{}
504+
// values for property-based testing.
505+
type simpleJSONMap struct {
506+
StringEntries map[string]string
507+
FloatEntries map[string]float64
508+
BoolEntries map[string]bool
509+
}
510+
511+
// toMap converts a simpleJSONMap to a map[string]interface{}.
512+
func (s simpleJSONMap) toMap() map[string]interface{} {
513+
m := make(map[string]interface{})
514+
for k, v := range s.StringEntries {
515+
// To avoid key collisions if rapid generates identical keys for
516+
// different entry types, we could prefix them, but for
517+
// simplicity, we'll assume distinct keys or accept overwrites
518+
// if rapid generates identical string keys for different map
519+
// types (unlikely to be an issue for typical map generation).
520+
m[k] = v
521+
}
522+
for k, v := range s.FloatEntries {
523+
m[k] = v
524+
}
525+
for k, v := range s.BoolEntries {
526+
m[k] = v
527+
}
528+
return m
529+
}
530+
531+
// TestMetaJsonEncodeDecodeProperties tests the EncodeMetaJSON and
532+
// DecodeMetaJSON functions using property-based testing. It ensures that
533+
// encoding and then decoding a map yields the original map.
534+
func TestMetaJsonEncodeDecodeProperties(t *testing.T) {
535+
t.Parallel()
536+
537+
rapid.Check(t, func(rt *rapid.T) {
538+
// Generate a simpleJSONMap instance.
539+
sjm := rapid.Custom(func(t *rapid.T) simpleJSONMap {
540+
return simpleJSONMap{
541+
StringEntries: rapid.MapOf(
542+
rapid.String(), rapid.String(),
543+
).Draw(t, "StringEntries"),
544+
FloatEntries: rapid.MapOf(
545+
rapid.String(), rapid.Float64(),
546+
).Draw(t, "FloatEntries"),
547+
BoolEntries: rapid.MapOf(
548+
rapid.String(), rapid.Bool(),
549+
).Draw(t, "BoolEntries"),
550+
}
551+
}).Draw(rt, "sjm")
552+
553+
originalMap := sjm.toMap()
554+
555+
encodedBytes, err := EncodeMetaJSON(originalMap)
556+
if err != nil {
557+
// If the generated map is too large, EncodeMetaJSON
558+
// will return ErrMetaDataTooLarge. This is an expected
559+
// behavior, not a property failure.
560+
if errors.Is(err, ErrMetaDataTooLarge) {
561+
return
562+
}
563+
564+
// Any other encoding error is a failure.
565+
rt.Fatalf("EncodeMetaJSON failed for map %#v: %v",
566+
originalMap, err)
567+
}
568+
569+
decodedMap, err := DecodeMetaJSON(encodedBytes)
570+
if err != nil {
571+
rt.Fatalf("DecodeMetaJSON failed for bytes %s "+
572+
"(from map %#v): %v",
573+
string(encodedBytes), originalMap, err)
574+
}
575+
576+
if !reflect.DeepEqual(originalMap, decodedMap) {
577+
rt.Fatalf("Decoded map does not match original map.\n"+
578+
"Original: %#v\nEncoded: %s\nDecoded: %#v",
579+
originalMap, string(encodedBytes), decodedMap)
580+
}
581+
})
582+
}
583+
584+
// TestDecodeMetaJSONOversizedProperty tests that DecodeMetaJSON returns an
585+
// error when the input byte slice is larger than MetaDataMaxSizeBytes.
586+
func TestDecodeMetaJSONOversizedProperty(t *testing.T) {
587+
t.Parallel()
588+
589+
rapid.Check(t, func(rt *rapid.T) {
590+
// Generate a byte slice that is guaranteed to be too large. We
591+
// add a small delta to avoid generating slices that are
592+
// excessively large and slow down the test.
593+
oversizedBytes := rapid.SliceOfN(
594+
rapid.Byte(),
595+
MetaDataMaxSizeBytes+1,
596+
MetaDataMaxSizeBytes+10,
597+
).Draw(rt, "oversizedBytes")
598+
599+
_, err := DecodeMetaJSON(oversizedBytes)
600+
require.ErrorIs(rt, err, ErrMetaDataTooLarge)
601+
})
602+
}
603+
604+
// TestDecodeMetaJSONInvalidJSONProperty tests that DecodeMetaJSON returns an
605+
// error for byte slices that are not valid JSON but are within size limits.
606+
func TestDecodeMetaJSONInvalidJSONProperty(t *testing.T) {
607+
t.Parallel()
608+
609+
rapid.Check(t, func(rt *rapid.T) {
610+
// Generate a byte slice that is within the size limit. There's
611+
// a high chance this random byte slice won't be valid JSON.
612+
inputBytes := rapid.SliceOfN(
613+
rapid.Byte(),
614+
0, MetaDataMaxSizeBytes,
615+
).Draw(rt, "inputBytes")
616+
617+
// If, by chance, we generated valid JSON or an empty slice
618+
// (which DecodeMetaJSON handles as valid empty JSON), we skip
619+
// this iteration as we want to test invalid JSON.
620+
// DecodeMetaJSON considers empty bytes as valid (empty map).
621+
if json.Valid(inputBytes) {
622+
rt.Skip("Generated valid JSON or empty " +
623+
"slice, skipping to test invalid case")
624+
return
625+
}
626+
627+
_, err := DecodeMetaJSON(inputBytes)
628+
require.ErrorIs(rt, err, ErrInvalidJSON)
629+
})
630+
}

0 commit comments

Comments
 (0)