github.com/ipld/go-ipld-prime@v0.21.0/codec/dagcbor/roundtrip_test.go (about)

     1  package dagcbor
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/rand"
     6  	"encoding/hex"
     7  	"math"
     8  	"strings"
     9  	"testing"
    10  
    11  	qt "github.com/frankban/quicktest"
    12  	cid "github.com/ipfs/go-cid"
    13  
    14  	"github.com/ipld/go-ipld-prime/datamodel"
    15  	"github.com/ipld/go-ipld-prime/fluent"
    16  	cidlink "github.com/ipld/go-ipld-prime/linking/cid"
    17  	"github.com/ipld/go-ipld-prime/node/basicnode"
    18  	nodetests "github.com/ipld/go-ipld-prime/node/tests"
    19  )
    20  
    21  var n = fluent.MustBuildMap(basicnode.Prototype.Map, 4, func(na fluent.MapAssembler) {
    22  	na.AssembleEntry("plain").AssignString("olde string")
    23  	na.AssembleEntry("map").CreateMap(2, func(na fluent.MapAssembler) {
    24  		na.AssembleEntry("one").AssignInt(1)
    25  		na.AssembleEntry("two").AssignInt(2)
    26  	})
    27  	na.AssembleEntry("list").CreateList(2, func(na fluent.ListAssembler) {
    28  		na.AssembleValue().AssignString("three")
    29  		na.AssembleValue().AssignString("four")
    30  	})
    31  	na.AssembleEntry("nested").CreateMap(1, func(na fluent.MapAssembler) {
    32  		na.AssembleEntry("deeper").CreateList(1, func(na fluent.ListAssembler) {
    33  			na.AssembleValue().AssignString("things")
    34  		})
    35  	})
    36  })
    37  var nSorted = fluent.MustBuildMap(basicnode.Prototype.Map, 4, func(na fluent.MapAssembler) {
    38  	na.AssembleEntry("map").CreateMap(2, func(na fluent.MapAssembler) {
    39  		na.AssembleEntry("one").AssignInt(1)
    40  		na.AssembleEntry("two").AssignInt(2)
    41  	})
    42  	na.AssembleEntry("list").CreateList(2, func(na fluent.ListAssembler) {
    43  		na.AssembleValue().AssignString("three")
    44  		na.AssembleValue().AssignString("four")
    45  	})
    46  	na.AssembleEntry("plain").AssignString("olde string")
    47  	na.AssembleEntry("nested").CreateMap(1, func(na fluent.MapAssembler) {
    48  		na.AssembleEntry("deeper").CreateList(1, func(na fluent.ListAssembler) {
    49  			na.AssembleValue().AssignString("things")
    50  		})
    51  	})
    52  })
    53  var serial = "\xa4cmap\xa2cone\x01ctwo\x02dlist\x82ethreedfoureplainkolde stringfnested\xa1fdeeper\x81fthings"
    54  
    55  func TestRoundtrip(t *testing.T) {
    56  	t.Run("encoding", func(t *testing.T) {
    57  		var buf bytes.Buffer
    58  		err := Encode(n, &buf)
    59  		qt.Assert(t, err, qt.IsNil)
    60  		qt.Check(t, buf.String(), qt.Equals, serial)
    61  	})
    62  	t.Run("length", func(t *testing.T) {
    63  		length, err := EncodedLength(n)
    64  		qt.Assert(t, err, qt.IsNil)
    65  		qt.Check(t, length, qt.Equals, int64(len(serial)))
    66  	})
    67  	t.Run("decoding", func(t *testing.T) {
    68  		buf := strings.NewReader(serial)
    69  		nb := basicnode.Prototype.Map.NewBuilder()
    70  		err := Decode(nb, buf)
    71  		qt.Assert(t, err, qt.IsNil)
    72  		qt.Check(t, nb.Build(), nodetests.NodeContentEquals, nSorted)
    73  	})
    74  }
    75  
    76  func TestRoundtripScalar(t *testing.T) {
    77  	nb := basicnode.Prototype__String{}.NewBuilder()
    78  	nb.AssignString("applesauce")
    79  	simple := nb.Build()
    80  	t.Run("encoding", func(t *testing.T) {
    81  		var buf bytes.Buffer
    82  		err := Encode(simple, &buf)
    83  		qt.Assert(t, err, qt.IsNil)
    84  		qt.Check(t, buf.String(), qt.Equals, `japplesauce`)
    85  	})
    86  	t.Run("decoding", func(t *testing.T) {
    87  		buf := strings.NewReader(`japplesauce`)
    88  		nb := basicnode.Prototype__String{}.NewBuilder()
    89  		err := Decode(nb, buf)
    90  		qt.Assert(t, err, qt.IsNil)
    91  		qt.Check(t, nb.Build(), nodetests.NodeContentEquals, simple)
    92  	})
    93  }
    94  
    95  func TestRoundtripLinksAndBytes(t *testing.T) {
    96  	lnk := cidlink.LinkPrototype{Prefix: cid.Prefix{
    97  		Version:  1,
    98  		Codec:    0x71,
    99  		MhType:   0x13,
   100  		MhLength: 4,
   101  	}}.BuildLink([]byte{1, 2, 3, 4}) // dummy value, content does not matter to this test.
   102  
   103  	var linkByteNode = fluent.MustBuildMap(basicnode.Prototype.Map, 4, func(na fluent.MapAssembler) {
   104  		nva := na.AssembleEntry("Link")
   105  		nva.AssignLink(lnk)
   106  		nva = na.AssembleEntry("Bytes")
   107  		bytes := make([]byte, 100)
   108  		_, _ = rand.Read(bytes)
   109  		nva.AssignBytes(bytes)
   110  	})
   111  
   112  	buf := bytes.Buffer{}
   113  	err := Encode(linkByteNode, &buf)
   114  	qt.Assert(t, err, qt.IsNil)
   115  	nb := basicnode.Prototype.Map.NewBuilder()
   116  	err = Decode(nb, &buf)
   117  	qt.Assert(t, err, qt.IsNil)
   118  	reconstructed := nb.Build()
   119  	qt.Check(t, reconstructed, nodetests.NodeContentEquals, linkByteNode)
   120  }
   121  
   122  func TestInts(t *testing.T) {
   123  	data := []struct {
   124  		name      string
   125  		hex       string
   126  		value     uint64
   127  		intValue  int64
   128  		intErr    string
   129  		decodeErr string
   130  	}{
   131  		{"max uint64", "1bffffffffffffffff", math.MaxUint64, 0, "unsigned integer out of range of int64 type", ""},
   132  		{"max int64", "1b7fffffffffffffff", math.MaxInt64, math.MaxInt64, "", ""},
   133  		{"1", "01", 1, 1, "", ""},
   134  		{"0", "00", 0, 0, "", ""},
   135  		{"-1", "20", 0, -1, "", ""},
   136  		{"min int64", "3b7fffffffffffffff", 0, math.MinInt64, "", ""},
   137  		{"~min uint64", "3bfffffffffffffffe", 0, 0, "", "cbor: negative integer out of rage of int64 type"},
   138  		// TODO: 3bffffffffffffffff isn't properly handled by refmt, it's coerced to zero
   139  		// MaxUint64 gets overflowed here: https://github.com/polydawn/refmt/blob/30ac6d18308e584ca6a2e74ba81475559db94c5f/cbor/cborDecoderTerminals.go#L75
   140  	}
   141  
   142  	for _, td := range data {
   143  		t.Run(td.name, func(t *testing.T) {
   144  			buf, err := hex.DecodeString(td.hex) // max uint64
   145  			qt.Assert(t, err, qt.IsNil)
   146  			nb := basicnode.Prototype.Any.NewBuilder()
   147  			err = Decode(nb, bytes.NewReader(buf))
   148  			if td.decodeErr != "" {
   149  				qt.Assert(t, err, qt.IsNotNil)
   150  				qt.Assert(t, err.Error(), qt.Equals, td.decodeErr)
   151  				return
   152  			}
   153  			qt.Assert(t, err, qt.IsNil)
   154  			n := nb.Build()
   155  
   156  			ii, err := n.AsInt()
   157  			if td.intErr != "" {
   158  				qt.Assert(t, err.Error(), qt.Equals, td.intErr)
   159  			} else {
   160  				qt.Assert(t, err, qt.IsNil)
   161  				qt.Assert(t, ii, qt.Equals, int64(td.intValue))
   162  			}
   163  
   164  			// if the number is outside of the positive int64 range, we should be able
   165  			// to access it as a UintNode and be able to access the full int64 range
   166  			uin, ok := n.(datamodel.UintNode)
   167  			if td.value <= math.MaxInt64 {
   168  				qt.Assert(t, ok, qt.IsFalse)
   169  			} else {
   170  				qt.Assert(t, ok, qt.IsTrue)
   171  				val, err := uin.AsUint()
   172  				qt.Assert(t, err, qt.IsNil)
   173  				qt.Assert(t, val, qt.Equals, uint64(td.value))
   174  			}
   175  
   176  			var byts bytes.Buffer
   177  			err = Encode(n, &byts)
   178  			qt.Assert(t, err, qt.IsNil)
   179  			qt.Assert(t, hex.EncodeToString(byts.Bytes()), qt.Equals, td.hex)
   180  		})
   181  	}
   182  }