github.com/ipld/go-ipld-prime@v0.21.0/node/bindnode/infer_test.go (about)

     1  package bindnode_test
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"html/template"
     8  	"os"
     9  	"os/exec"
    10  	"path/filepath"
    11  	"reflect"
    12  	"runtime/debug"
    13  	"strings"
    14  	"testing"
    15  
    16  	qt "github.com/frankban/quicktest"
    17  	"github.com/google/go-cmp/cmp"
    18  	"github.com/ipfs/go-cid"
    19  
    20  	"github.com/ipld/go-ipld-prime"
    21  	"github.com/ipld/go-ipld-prime/codec/dagjson"
    22  	"github.com/ipld/go-ipld-prime/datamodel"
    23  	cidlink "github.com/ipld/go-ipld-prime/linking/cid"
    24  	"github.com/ipld/go-ipld-prime/node/basicnode"
    25  	"github.com/ipld/go-ipld-prime/node/bindnode"
    26  	"github.com/ipld/go-ipld-prime/schema"
    27  )
    28  
    29  type anyScalar struct {
    30  	Bool   *bool
    31  	Int    *int64
    32  	Float  *float64
    33  	String *string
    34  	Bytes  *[]byte
    35  	Link   *datamodel.Link
    36  }
    37  
    38  type anyRecursive struct {
    39  	List *[]string
    40  	Map  *struct {
    41  		Keys   []string
    42  		Values map[string]string
    43  	}
    44  }
    45  
    46  var prototypeTests = []struct {
    47  	name      string
    48  	schemaSrc string
    49  	ptrType   interface{}
    50  
    51  	// Prettified for maintainability, valid DAG-JSON when compacted.
    52  	prettyDagJSON string
    53  }{
    54  	{
    55  		name: "Scalars",
    56  		schemaSrc: `type Root struct {
    57  				bool   Bool
    58  				int    Int
    59  				uint   Int
    60  				float  Float
    61  				string String
    62  				bytes  Bytes
    63  			}`,
    64  		ptrType: (*struct {
    65  			Bool   bool
    66  			Int    int64
    67  			Uint   uint32
    68  			Float  float64
    69  			String string
    70  			Bytes  []byte
    71  		})(nil),
    72  		prettyDagJSON: `{
    73  			"bool":   true,
    74  			"bytes":  {"/": {"bytes": "34cd"}},
    75  			"float":  12.5,
    76  			"int":    3,
    77  			"string": "foo",
    78  			"uint":   50
    79  		}`,
    80  	},
    81  	{
    82  		name: "Links",
    83  		schemaSrc: `type Root struct {
    84  				linkCID     Link
    85  				linkGeneric Link
    86  				linkImpl    Link
    87  			}`,
    88  		ptrType: (*struct {
    89  			LinkCID     cid.Cid
    90  			LinkGeneric datamodel.Link
    91  			LinkImpl    cidlink.Link
    92  		})(nil),
    93  		prettyDagJSON: `{
    94  			"linkCID":     {"/": "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"},
    95  			"linkGeneric": {"/": "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"},
    96  			"linkImpl":    {"/": "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"}
    97  		}`,
    98  	},
    99  	{
   100  		name: "Any",
   101  		// TODO: also Null
   102  		schemaSrc: `type Root struct {
   103  				anyNodeWithBool   Any
   104  				anyNodeWithInt    Any
   105  				anyNodeWithFloat  Any
   106  				anyNodeWithString Any
   107  				anyNodeWithBytes  Any
   108  				anyNodeWithList   Any
   109  				anyNodeWithMap    Any
   110  				anyNodeWithLink   Any
   111  
   112  				anyNodeBehindList [Any]
   113  				anyNodeBehindMap  {String:Any}
   114  			}`,
   115  		ptrType: (*struct {
   116  			AnyNodeWithBool   datamodel.Node
   117  			AnyNodeWithInt    datamodel.Node
   118  			AnyNodeWithFloat  datamodel.Node
   119  			AnyNodeWithString datamodel.Node
   120  			AnyNodeWithBytes  datamodel.Node
   121  			AnyNodeWithList   datamodel.Node
   122  			AnyNodeWithMap    datamodel.Node
   123  			AnyNodeWithLink   datamodel.Node
   124  
   125  			AnyNodeBehindList []datamodel.Node
   126  			AnyNodeBehindMap  struct {
   127  				Keys   []string
   128  				Values map[string]datamodel.Node
   129  			}
   130  		})(nil),
   131  		prettyDagJSON: `{
   132  			"anyNodeBehindList": [12.5, {"x": false}],
   133  			"anyNodeBehindMap":  {"x": 123, "y": [true, false]},
   134  			"anyNodeWithBool":   true,
   135  			"anyNodeWithBytes":  {"/": {"bytes": "34cd"}},
   136  			"anyNodeWithFloat":  12.5,
   137  			"anyNodeWithInt":    3,
   138  			"anyNodeWithLink":   {"/": "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"},
   139  			"anyNodeWithList":   [3, 2, 1],
   140  			"anyNodeWithMap":    {"a": "x", "b": "y"},
   141  			"anyNodeWithString": "foo"
   142  		}`,
   143  	},
   144  	{
   145  		name: "Enums",
   146  		schemaSrc: `type Root struct {
   147  				stringAsString       EnumAsString
   148  				stringAsStringCustom EnumAsString
   149  				stringAsInt          EnumAsInt
   150  				intAsInt             EnumAsInt
   151  				uintAsInt            EnumAsInt
   152  			}
   153  			type EnumAsString enum {
   154  				| Nope ("No")
   155  				| Yep  ("Yes")
   156  				| Maybe
   157  			}
   158  			type EnumAsInt enum {
   159  				| Nope  ("10")
   160  				| Yep   ("11")
   161  				| Maybe ("12")
   162  			} representation int`,
   163  		ptrType: (*struct {
   164  			StringAsString       string
   165  			StringAsStringCustom string
   166  			StringAsInt          string
   167  			IntAsInt             int32
   168  			UintAsInt            uint16
   169  		})(nil),
   170  		prettyDagJSON: `{
   171  			"intAsInt":             12,
   172  			"stringAsInt":          10,
   173  			"stringAsString":       "Maybe",
   174  			"stringAsStringCustom": "Yes",
   175  			"uintAsInt":            11
   176  		}`,
   177  	},
   178  	{
   179  		name: "ScalarKindedUnions",
   180  		// TODO: should we use an "Any" type from the prelude?
   181  		schemaSrc: `type Root struct {
   182  				boolAny   AnyScalar
   183  				intAny    AnyScalar
   184  				floatAny  AnyScalar
   185  				stringAny AnyScalar
   186  				bytesAny  AnyScalar
   187  				linkAny   AnyScalar
   188  			}
   189  
   190  			type AnyScalar union {
   191  				| Bool   bool
   192  				| Int    int
   193  				| Float  float
   194  				| String string
   195  				| Bytes  bytes
   196  				| Link   link
   197  			} representation kinded`,
   198  		ptrType: (*struct {
   199  			BoolAny   anyScalar
   200  			IntAny    anyScalar
   201  			FloatAny  anyScalar
   202  			StringAny anyScalar
   203  			BytesAny  anyScalar
   204  			LinkAny   anyScalar
   205  		})(nil),
   206  		prettyDagJSON: `{
   207  			"boolAny":   true,
   208  			"bytesAny":  {"/": {"bytes": "34cd"}},
   209  			"floatAny":  12.5,
   210  			"intAny":    3,
   211  			"linkAny":   {"/": "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"},
   212  			"stringAny": "foo"
   213  		}`,
   214  	},
   215  	{
   216  		name: "RecursiveKindedUnions",
   217  		// TODO: should we use an "Any" type from the prelude?
   218  		// Especially since we use String map/list element types.
   219  		// TODO: use inline map/list defs once schema and dsl+dmt support it.
   220  		schemaSrc: `type Root struct {
   221  				listAny AnyRecursive
   222  				mapAny  AnyRecursive
   223  			}
   224  
   225  			type List_String [String]
   226  			type Map_String {String:String}
   227  
   228  			type AnyRecursive union {
   229  				| List_String list
   230  				| Map_String  map
   231  			} representation kinded`,
   232  		ptrType: (*struct {
   233  			ListAny anyRecursive
   234  			MapAny  anyRecursive
   235  		})(nil),
   236  		prettyDagJSON: `{
   237  			"listAny": ["foo", "bar"],
   238  			"mapAny":  {"a": "x", "b": "y"}
   239  		}`,
   240  	},
   241  }
   242  
   243  func compactJSON(t *testing.T, pretty string) string {
   244  	var buf bytes.Buffer
   245  	err := json.Compact(&buf, []byte(pretty))
   246  	qt.Assert(t, err, qt.IsNil)
   247  	return buf.String()
   248  }
   249  
   250  func dagjsonEncode(t *testing.T, node datamodel.Node) string {
   251  	var sb strings.Builder
   252  	err := dagjson.Encode(node, &sb)
   253  	qt.Assert(t, err, qt.IsNil)
   254  	return sb.String()
   255  }
   256  
   257  func dagjsonDecode(t *testing.T, proto datamodel.NodePrototype, src string) datamodel.Node {
   258  	nb := proto.NewBuilder()
   259  	err := dagjson.Decode(nb, strings.NewReader(src))
   260  	qt.Assert(t, err, qt.IsNil)
   261  	return nb.Build()
   262  }
   263  
   264  func TestPrototype(t *testing.T) {
   265  	t.Parallel()
   266  
   267  	for _, test := range prototypeTests {
   268  		test := test // don't reuse the range var
   269  
   270  		for _, onlySchema := range []bool{false, true} {
   271  			onlySchema := onlySchema // don't reuse the range var
   272  			suffix := ""
   273  			if onlySchema {
   274  				suffix = "_onlySchema"
   275  			}
   276  			t.Run(test.name+suffix, func(t *testing.T) {
   277  				t.Parallel()
   278  
   279  				ts, err := ipld.LoadSchemaBytes([]byte(test.schemaSrc))
   280  				qt.Assert(t, err, qt.IsNil)
   281  				schemaType := ts.TypeByName("Root")
   282  				qt.Assert(t, schemaType, qt.Not(qt.IsNil))
   283  
   284  				ptrType := test.ptrType // don't write to the shared test value
   285  				if onlySchema {
   286  					ptrType = nil
   287  				}
   288  				proto := bindnode.Prototype(ptrType, schemaType)
   289  
   290  				wantEncoded := compactJSON(t, test.prettyDagJSON)
   291  				node := dagjsonDecode(t, proto.Representation(), wantEncoded).(schema.TypedNode)
   292  				// TODO: assert node type matches ptrType
   293  
   294  				encoded := dagjsonEncode(t, node.Representation())
   295  				qt.Assert(t, encoded, qt.Equals, wantEncoded)
   296  
   297  				// Verify that doing a dag-json encode of the non-repr node works.
   298  				_ = dagjsonEncode(t, node)
   299  			})
   300  		}
   301  	}
   302  }
   303  
   304  func TestPrototypePointerCombinations(t *testing.T) {
   305  	t.Parallel()
   306  
   307  	// TODO: Null
   308  	// TODO: cover more schema types and repr strategies.
   309  	// Some of them are still using w.val directly without "nonPtr" calls.
   310  	kindTests := []struct {
   311  		name         string
   312  		schemaType   string
   313  		fieldPtrType interface{}
   314  		fieldDagJSON string
   315  	}{
   316  		{"Bool", "Bool", (*bool)(nil), `true`},
   317  		{"Int", "Int", (*int64)(nil), `23`},
   318  		{"Float", "Float", (*float64)(nil), `34.5`},
   319  		{"String", "String", (*string)(nil), `"foo"`},
   320  		{"Bytes", "Bytes", (*[]byte)(nil), `{"/": {"bytes": "34cd"}}`},
   321  		{"Link_CID", "Link", (*cid.Cid)(nil), `{"/": "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"}`},
   322  		{"Link_Impl", "Link", (*cidlink.Link)(nil), `{"/": "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"}`},
   323  		{"Link_Generic", "Link", (*datamodel.Link)(nil), `{"/": "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"}`},
   324  		{"List_String", "[String]", (*[]string)(nil), `["foo", "bar"]`},
   325  		{"Any_Node_Int", "Any", (*datamodel.Node)(nil), `23`},
   326  		// TODO: reenable once we don't require pointers for nullable
   327  		// {"Any_Pointer_Int", "{String: nullable Any}",
   328  		// 	(*struct {
   329  		// 		Keys   []string
   330  		// 		Values map[string]datamodel.Node
   331  		// 	})(nil), `{"x":3,"y":"bar","z":[2.3,4.5]}`},
   332  		{"Map_String_Int", "{String:Int}", (*struct {
   333  			Keys   []string
   334  			Values map[string]int64
   335  		})(nil), `{"x":3,"y":4}`},
   336  	}
   337  
   338  	// For each IPLD kind, we test a matrix of combinations for IPLD's optional
   339  	// and nullable fields alongside pointer usage on the Go field side.
   340  	modifiers := []struct {
   341  		schemaField string // "", "optional", "nullable", "optional nullable"
   342  		goPointers  int    // 0 (T), 1 (*T), 2 (**T)
   343  	}{
   344  		{"", 0},                  // regular IPLD field with Go's T
   345  		{"", 1},                  // regular IPLD field with Go's *T
   346  		{"optional", 0},          // optional IPLD field with Go's T (skipped unless T is nilable)
   347  		{"optional", 1},          // optional IPLD field with Go's *T
   348  		{"nullable", 0},          // nullable IPLD field with Go's T (skipped unless T is nilable)
   349  		{"nullable", 1},          // nullable IPLD field with Go's *T
   350  		{"optional nullable", 2}, // optional and nullable IPLD field with Go's **T
   351  	}
   352  	for _, kindTest := range kindTests {
   353  		for _, modifier := range modifiers {
   354  			// don't reuse range vars
   355  			kindTest := kindTest
   356  			modifier := modifier
   357  			goFieldType := reflect.TypeOf(kindTest.fieldPtrType)
   358  			switch modifier.goPointers {
   359  			case 0:
   360  				goFieldType = goFieldType.Elem() // dereference fieldPtrType
   361  			case 1:
   362  				// fieldPtrType already uses one pointer
   363  			case 2:
   364  				goFieldType = reflect.PtrTo(goFieldType) // dereference fieldPtrType
   365  			}
   366  			if modifier.schemaField != "" && !nilable(goFieldType.Kind()) {
   367  				continue
   368  			}
   369  			t.Run(fmt.Sprintf("%s/%s-%dptr", kindTest.name, modifier.schemaField, modifier.goPointers), func(t *testing.T) {
   370  				t.Parallel()
   371  
   372  				var buf bytes.Buffer
   373  				err := template.Must(template.New("").Parse(`
   374  						type Root struct {
   375  							field {{.Modifier}} {{.Type}}
   376  						}`)).Execute(&buf,
   377  					struct {
   378  						Type, Modifier string
   379  					}{kindTest.schemaType, modifier.schemaField})
   380  				qt.Assert(t, err, qt.IsNil)
   381  				schemaSrc := buf.String()
   382  				t.Logf("IPLD schema: %s", schemaSrc)
   383  
   384  				// *struct { Field {{.goFieldType}} }
   385  				goType := reflect.Zero(reflect.PtrTo(reflect.StructOf([]reflect.StructField{
   386  					{Name: "Field", Type: goFieldType},
   387  				}))).Interface()
   388  				t.Logf("Go type: %T", goType)
   389  
   390  				ts, err := ipld.LoadSchemaBytes([]byte(schemaSrc))
   391  				qt.Assert(t, err, qt.IsNil)
   392  				schemaType := ts.TypeByName("Root")
   393  				qt.Assert(t, schemaType, qt.Not(qt.IsNil))
   394  
   395  				proto := bindnode.Prototype(goType, schemaType)
   396  				wantEncodedBytes, err := json.Marshal(map[string]interface{}{"field": json.RawMessage(kindTest.fieldDagJSON)})
   397  				qt.Assert(t, err, qt.IsNil)
   398  				wantEncoded := string(wantEncodedBytes)
   399  
   400  				node := dagjsonDecode(t, proto.Representation(), wantEncoded).(schema.TypedNode)
   401  
   402  				encoded := dagjsonEncode(t, node.Representation())
   403  				qt.Assert(t, encoded, qt.Equals, wantEncoded)
   404  
   405  				// Assigning with the missing field should only work with optional.
   406  				nb := proto.NewBuilder()
   407  				err = dagjson.Decode(nb, strings.NewReader(`{}`))
   408  				switch modifier.schemaField {
   409  				case "optional", "optional nullable":
   410  					qt.Assert(t, err, qt.IsNil)
   411  					node := nb.Build()
   412  					// The resulting node should be non-nil with a nil field.
   413  					nodeVal := reflect.ValueOf(bindnode.Unwrap(node))
   414  					qt.Assert(t, nodeVal.Elem().FieldByName("Field").IsNil(), qt.IsTrue)
   415  				default:
   416  					qt.Assert(t, err, qt.Not(qt.IsNil))
   417  				}
   418  
   419  				// Assigning with a null field should only work with nullable.
   420  				nb = proto.NewBuilder()
   421  				err = dagjson.Decode(nb, strings.NewReader(`{"field":null}`))
   422  				switch modifier.schemaField {
   423  				case "nullable", "optional nullable":
   424  					qt.Assert(t, err, qt.IsNil)
   425  					node := nb.Build()
   426  					// The resulting node should be non-nil with a nil field.
   427  					nodeVal := reflect.ValueOf(bindnode.Unwrap(node))
   428  					if modifier.schemaField == "nullable" {
   429  						qt.Assert(t, nodeVal.Elem().FieldByName("Field").IsNil(), qt.IsTrue)
   430  					} else {
   431  						qt.Assert(t, nodeVal.Elem().FieldByName("Field").Elem().IsNil(), qt.IsTrue)
   432  					}
   433  				default:
   434  					qt.Assert(t, err, qt.Not(qt.IsNil))
   435  				}
   436  			})
   437  		}
   438  	}
   439  }
   440  
   441  func nilable(kind reflect.Kind) bool {
   442  	switch kind {
   443  	case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
   444  		return true
   445  	default:
   446  		return false
   447  	}
   448  }
   449  
   450  func assembleAsKind(proto datamodel.NodePrototype, schemaType schema.Type, asKind datamodel.Kind) (ipld.Node, error) {
   451  	nb := proto.NewBuilder()
   452  	switch asKind {
   453  	case datamodel.Kind_Bool:
   454  		if err := nb.AssignBool(true); err != nil {
   455  			return nil, err
   456  		}
   457  	case datamodel.Kind_Int:
   458  		if err := nb.AssignInt(123); err != nil {
   459  			return nil, err
   460  		}
   461  	case datamodel.Kind_Float:
   462  		if err := nb.AssignFloat(12.5); err != nil {
   463  			return nil, err
   464  		}
   465  	case datamodel.Kind_String:
   466  		if err := nb.AssignString("foo"); err != nil {
   467  			return nil, err
   468  		}
   469  	case datamodel.Kind_Bytes:
   470  		if err := nb.AssignBytes([]byte("\x00bar")); err != nil {
   471  			return nil, err
   472  		}
   473  	case datamodel.Kind_Link:
   474  		someCid, err := cid.Decode("bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi")
   475  		if err != nil {
   476  			return nil, err
   477  		}
   478  		if err := nb.AssignLink(cidlink.Link{Cid: someCid}); err != nil {
   479  			return nil, err
   480  		}
   481  	case datamodel.Kind_Map:
   482  		asm, err := nb.BeginMap(-1)
   483  		if err != nil {
   484  			return nil, err
   485  		}
   486  		// First via AssembleKey.
   487  		if err := asm.AssembleKey().AssignString("F1"); err != nil {
   488  			return nil, err
   489  		}
   490  		if err := asm.AssembleValue().AssignInt(101); err != nil {
   491  			return nil, err
   492  		}
   493  		// Then via AssembleEntry.
   494  		entryAsm, err := asm.AssembleEntry("F2")
   495  		if err != nil {
   496  			return nil, err
   497  		}
   498  		if err := entryAsm.AssignInt(102); err != nil {
   499  			return nil, err
   500  		}
   501  
   502  		// If this is a struct, using a missing field should error.
   503  		if _, ok := schemaType.(*schema.TypeStruct); ok {
   504  			if err := asm.AssembleKey().AssignString("MissingKey"); err != nil {
   505  				return nil, err
   506  			}
   507  			if err := asm.AssembleValue().AssignInt(101); err == nil {
   508  				return nil, fmt.Errorf("expected error on missing struct key")
   509  			}
   510  		}
   511  		if err := asm.Finish(); err != nil {
   512  			return nil, err
   513  		}
   514  	case datamodel.Kind_List:
   515  		asm, err := nb.BeginList(-1)
   516  		if err != nil {
   517  			return nil, err
   518  		}
   519  		// Note that we want the list to have two integer elements,
   520  		// which matches the map entries above,
   521  		// so that the struct with tuple repr just works too.
   522  		if err := asm.AssembleValue().AssignInt(101); err != nil {
   523  			return nil, err
   524  		}
   525  		if err := asm.AssembleValue().AssignInt(102); err != nil {
   526  			return nil, err
   527  		}
   528  		// If this is a struct, assembling one more tuple entry should error.
   529  		if _, ok := schemaType.(*schema.TypeStruct); ok {
   530  			if err := asm.AssembleValue().AssignInt(103); err == nil {
   531  				return nil, fmt.Errorf("expected error on extra tuple entry")
   532  			}
   533  		}
   534  		if err := asm.Finish(); err != nil {
   535  			return nil, err
   536  		}
   537  	}
   538  	node := nb.Build()
   539  	if node == nil {
   540  		// If we succeeded, node must never be nil.
   541  		return nil, fmt.Errorf("built node is nil")
   542  	}
   543  	return node, nil
   544  }
   545  
   546  func useNodeAsKind(node datamodel.Node, asKind datamodel.Kind) error {
   547  	if gotKind := node.Kind(); gotKind != asKind {
   548  		// Return a dummy error to signal when the kind doesn't match.
   549  		return datamodel.ErrWrongKind{MethodName: "TestKindMismatches_Dummy_Kind"}
   550  	}
   551  	// Just check that IsAbsent and IsNull don't panic, for now.
   552  	_ = node.IsAbsent()
   553  	_ = node.IsNull()
   554  
   555  	proto := node.Prototype()
   556  	if proto == nil {
   557  		return fmt.Errorf("got a null Prototype")
   558  	}
   559  
   560  	// TODO: also check LookupByNode, LookupBySegment
   561  	switch asKind {
   562  	case datamodel.Kind_Bool:
   563  		if _, err := node.AsBool(); err != nil {
   564  			return err
   565  		}
   566  	case datamodel.Kind_Int:
   567  		if _, err := node.AsInt(); err != nil {
   568  			return err
   569  		}
   570  	case datamodel.Kind_Float:
   571  		if _, err := node.AsFloat(); err != nil {
   572  			return err
   573  		}
   574  	case datamodel.Kind_String:
   575  		if _, err := node.AsString(); err != nil {
   576  			return err
   577  		}
   578  	case datamodel.Kind_Bytes:
   579  		if _, err := node.AsBytes(); err != nil {
   580  			return err
   581  		}
   582  	case datamodel.Kind_Link:
   583  		if _, err := node.AsLink(); err != nil {
   584  			return err
   585  		}
   586  	case datamodel.Kind_Map:
   587  		iter := node.MapIterator()
   588  		if iter == nil {
   589  			// Return a dummy error to signal whether iter is nil or not.
   590  			return datamodel.ErrWrongKind{MethodName: "TestKindMismatches_Dummy_MapIterator"}
   591  		}
   592  		for !iter.Done() {
   593  			_, _, err := iter.Next()
   594  			if err != nil {
   595  				return err
   596  			}
   597  		}
   598  
   599  		// valid element
   600  		entryNode, err := node.LookupByString("F1")
   601  		if err != nil {
   602  			return err
   603  		}
   604  		if err := useNodeAsKind(entryNode, datamodel.Kind_Int); err != nil {
   605  			return err
   606  		}
   607  
   608  		// missing element
   609  		_, missingErr := node.LookupByString("MissingKey")
   610  		switch err := missingErr.(type) {
   611  		case nil:
   612  			return fmt.Errorf("lookup of a missing key succeeded")
   613  		case datamodel.ErrNotExists: // expected for maps
   614  		case schema.ErrInvalidKey: // expected for structs
   615  		default:
   616  			return err
   617  		}
   618  
   619  		switch l := node.Length(); l {
   620  		case 2:
   621  		case -1:
   622  			// Return a dummy error to signal whether Length failed.
   623  			return datamodel.ErrWrongKind{MethodName: "TestKindMismatches_Dummy_Length"}
   624  		default:
   625  			return fmt.Errorf("unexpected Length: %d", l)
   626  		}
   627  	case datamodel.Kind_List:
   628  		iter := node.ListIterator()
   629  		if iter == nil {
   630  			// Return a dummy error to signal whether iter is nil or not.
   631  			return datamodel.ErrWrongKind{MethodName: "TestKindMismatches_Dummy_ListIterator"}
   632  		}
   633  		for !iter.Done() {
   634  			_, _, err := iter.Next()
   635  			if err != nil {
   636  				return err
   637  			}
   638  		}
   639  
   640  		// valid element
   641  		entryNode, err := node.LookupByIndex(1)
   642  		if err != nil {
   643  			return err
   644  		}
   645  		if err := useNodeAsKind(entryNode, datamodel.Kind_Int); err != nil {
   646  			return err
   647  		}
   648  
   649  		// missing element
   650  		_, missingErr := node.LookupByIndex(30)
   651  		switch err := missingErr.(type) {
   652  		case nil:
   653  			return fmt.Errorf("lookup of a missing key succeeded")
   654  		case datamodel.ErrNotExists: // expected for maps
   655  		case schema.ErrInvalidKey: // expected for structs
   656  		default:
   657  			return err
   658  		}
   659  
   660  		switch l := node.Length(); l {
   661  		case 2:
   662  		case -1:
   663  			// Return a dummy error to signal whether Length failed.
   664  			return datamodel.ErrWrongKind{MethodName: "TestKindMismatches_Dummy_Length"}
   665  		default:
   666  			return fmt.Errorf("unexpected Length: %d", l)
   667  		}
   668  	}
   669  	return nil
   670  }
   671  
   672  func TestKindMismatches(t *testing.T) {
   673  	t.Parallel()
   674  
   675  	kindTests := []struct {
   676  		name      string
   677  		schemaSrc string
   678  	}{
   679  		{"Bool", "type Root bool"},
   680  		{"Int", "type Root int"},
   681  		{"Float", "type Root float"},
   682  		{"String", "type Root string"},
   683  		{"Bytes", "type Root bytes"},
   684  		{"Map", `
   685  			type Root {String:Int}
   686  		`},
   687  		{"Struct", `
   688  			type Root struct {
   689  				F1 Int
   690  				F2 Int
   691  			}
   692  		`},
   693  		{"Struct_Tuple", `
   694  			type Root struct {
   695  				F1 Int
   696  				F2 Int
   697  			} representation tuple
   698  		`},
   699  		{"Enum", `
   700  			type Root enum {
   701  				| Foo ("foo")
   702  				| Bar ("bar")
   703  				| Either
   704  			}
   705  		`},
   706  		{"Union_Kinded_onlyString", `
   707  			type Root union {
   708  				| String string
   709  			} representation kinded
   710  		`},
   711  		{"Union_Kinded_onlyList", `
   712  			type Root union {
   713  				| List list
   714  			} representation kinded
   715  		`},
   716  		// TODO: more schema types and repr strategies
   717  	}
   718  
   719  	allKinds := []datamodel.Kind{
   720  		// datamodel.Kind_Null, TODO
   721  		datamodel.Kind_Bool,
   722  		datamodel.Kind_Int,
   723  		datamodel.Kind_Float,
   724  		datamodel.Kind_String,
   725  		datamodel.Kind_Bytes,
   726  		datamodel.Kind_Link,
   727  		datamodel.Kind_Map,
   728  		datamodel.Kind_List,
   729  	}
   730  
   731  	// TODO: also test for non-repr assemblers and nodes
   732  
   733  	for _, kindTest := range kindTests {
   734  		// don't reuse range vars
   735  		kindTest := kindTest
   736  		t.Run(kindTest.name, func(t *testing.T) {
   737  			t.Parallel()
   738  			defer func() {
   739  				if r := recover(); r != nil {
   740  					// Note that debug.Stack inside the recover will include the
   741  					// stack trace for the original panic call.
   742  					t.Errorf("caught panic:\n%v\n%s", r, debug.Stack())
   743  				}
   744  			}()
   745  
   746  			ts, err := ipld.LoadSchemaBytes([]byte(kindTest.schemaSrc))
   747  			qt.Assert(t, err, qt.IsNil)
   748  			schemaType := ts.TypeByName("Root")
   749  			qt.Assert(t, schemaType, qt.Not(qt.IsNil))
   750  
   751  			// Note that the Go type is inferred.
   752  			proto := bindnode.Prototype(nil, schemaType).Representation()
   753  
   754  			reprBehaviorKind := schemaType.RepresentationBehavior()
   755  			if reprBehaviorKind == datamodel.Kind_Invalid {
   756  				// For now, this only applies to kinded unions.
   757  				// We'll need to modify this when we test with Any.
   758  				members := schemaType.(*schema.TypeUnion).Members()
   759  				qt.Assert(t, members, qt.HasLen, 1)
   760  				reprBehaviorKind = members[0].RepresentationBehavior()
   761  			}
   762  			qt.Assert(t, reprBehaviorKind, qt.Not(qt.Equals), datamodel.Kind_Invalid)
   763  
   764  			for _, kind := range allKinds {
   765  				_, err := assembleAsKind(proto, schemaType, kind)
   766  				comment := qt.Commentf("assigned as %v", kind)
   767  				// Assembling should succed iff we used the right kind.
   768  				if kind == reprBehaviorKind {
   769  					qt.Assert(t, err, qt.IsNil, comment)
   770  				} else {
   771  					qt.Assert(t, err, qt.Not(qt.IsNil), comment)
   772  					qt.Assert(t, err, qt.ErrorAs, new(datamodel.ErrWrongKind), comment)
   773  				}
   774  			}
   775  
   776  			node, err := assembleAsKind(proto, schemaType, reprBehaviorKind)
   777  			qt.Assert(t, err, qt.IsNil)
   778  			node = node.(schema.TypedNode).Representation()
   779  			nodeKind := node.Kind()
   780  
   781  			for _, kind := range allKinds {
   782  				err := useNodeAsKind(node, kind)
   783  				comment := qt.Commentf("used as %v", kind)
   784  				// Using the node should succed iff we used the right kind.
   785  				if kind == nodeKind {
   786  					qt.Assert(t, err, qt.IsNil, comment)
   787  				} else {
   788  					qt.Assert(t, err, qt.Not(qt.IsNil), comment)
   789  					qt.Assert(t, err, qt.ErrorAs, new(datamodel.ErrWrongKind), comment)
   790  				}
   791  			}
   792  		})
   793  	}
   794  }
   795  
   796  type verifyBadType struct {
   797  	ptrType     interface{}
   798  	panicRegexp string
   799  }
   800  
   801  type (
   802  	namedBool    bool
   803  	namedInt64   int64
   804  	namedFloat64 float64
   805  	namedString  string
   806  	namedBytes   []byte
   807  )
   808  
   809  var verifyTests = []struct {
   810  	name      string
   811  	schemaSrc string
   812  
   813  	goodTypes []interface{}
   814  	badTypes  []verifyBadType
   815  }{
   816  	{
   817  		name:      "Bool",
   818  		schemaSrc: `type Root bool`,
   819  		goodTypes: []interface{}{
   820  			(*bool)(nil),
   821  			(*namedBool)(nil),
   822  		},
   823  		badTypes: []verifyBadType{
   824  			{(*string)(nil), `.*type Root .* type string: kind mismatch;.*`},
   825  		},
   826  	},
   827  	{
   828  		name:      "Int",
   829  		schemaSrc: `type Root int`,
   830  		goodTypes: []interface{}{
   831  			(*int)(nil),
   832  			(*namedInt64)(nil),
   833  			(*int8)(nil),
   834  			(*int16)(nil),
   835  			(*int32)(nil),
   836  			(*int64)(nil),
   837  		},
   838  		badTypes: []verifyBadType{
   839  			{(*string)(nil), `.*type Root .* type string: kind mismatch;.*`},
   840  		},
   841  	},
   842  	{
   843  		name:      "Float",
   844  		schemaSrc: `type Root float`,
   845  		goodTypes: []interface{}{
   846  			(*float64)(nil),
   847  			(*namedFloat64)(nil),
   848  			(*float32)(nil),
   849  		},
   850  		badTypes: []verifyBadType{
   851  			{(*string)(nil), `.*type Root .* type string: kind mismatch;.*`},
   852  		},
   853  	},
   854  	{
   855  		name:      "String",
   856  		schemaSrc: `type Root string`,
   857  		goodTypes: []interface{}{
   858  			(*string)(nil),
   859  			(*namedString)(nil),
   860  		},
   861  		badTypes: []verifyBadType{
   862  			{(*int)(nil), `.*type Root .* type int: kind mismatch;.*`},
   863  		},
   864  	},
   865  	{
   866  		name:      "Bytes",
   867  		schemaSrc: `type Root bytes`,
   868  		goodTypes: []interface{}{
   869  			(*[]byte)(nil),
   870  			(*namedBytes)(nil),
   871  			(*[]uint8)(nil), // alias of byte
   872  		},
   873  		badTypes: []verifyBadType{
   874  			{(*int)(nil), `.*type Root .* type int: kind mismatch;.*`},
   875  			{(*[]int)(nil), `.*type Root .* type \[\]int: kind mismatch;.*`},
   876  		},
   877  	},
   878  	{
   879  		name:      "List",
   880  		schemaSrc: `type Root [String]`,
   881  		goodTypes: []interface{}{
   882  			(*[]string)(nil),
   883  			(*[]namedString)(nil),
   884  		},
   885  		badTypes: []verifyBadType{
   886  			{(*string)(nil), `.*type Root .* type string: kind mismatch;.*`},
   887  			{(*[]int)(nil), `.*type String .* type int: kind mismatch;.*`},
   888  			{(*[3]string)(nil), `.*type Root .* type \[3\]string: kind mismatch;.*`},
   889  		},
   890  	},
   891  	{
   892  		name: "Struct",
   893  		schemaSrc: `type Root struct {
   894  				int Int
   895  			}`,
   896  		goodTypes: []interface{}{
   897  			(*struct{ Int int })(nil),
   898  			(*struct{ Int namedInt64 })(nil),
   899  		},
   900  		badTypes: []verifyBadType{
   901  			{(*string)(nil), `.*type Root .* type string: kind mismatch;.*`},
   902  			{(*struct{ Int bool })(nil), `.*type Int .* type bool: kind mismatch;.*`},
   903  			{(*struct{ Int1, Int2 int })(nil), `.*type Root .* type struct {.*}: 2 vs 1 fields`},
   904  		},
   905  	},
   906  	{
   907  		name:      "Map",
   908  		schemaSrc: `type Root {String:Int}`,
   909  		goodTypes: []interface{}{
   910  			(*struct {
   911  				Keys   []string
   912  				Values map[string]int
   913  			})(nil),
   914  			(*struct {
   915  				Keys   []namedString
   916  				Values map[namedString]namedInt64
   917  			})(nil),
   918  		},
   919  		badTypes: []verifyBadType{
   920  			{(*string)(nil), `.*type Root .* type string: kind mismatch;.*`},
   921  			{(*struct{ Keys []string })(nil), `.*type Root .*: 1 vs 2 fields`},
   922  			{(*struct{ Values map[string]int })(nil), `.*type Root .*: 1 vs 2 fields`},
   923  			{(*struct {
   924  				Keys   string
   925  				Values map[string]int
   926  			})(nil), `.*type Root .*: kind mismatch;.*`},
   927  			{(*struct {
   928  				Keys   []string
   929  				Values string
   930  			})(nil), `.*type Root .*: kind mismatch;.*`},
   931  		},
   932  	},
   933  	{
   934  		name:      "MapNullableAny",
   935  		schemaSrc: `type Root {String:nullable Any}`,
   936  		goodTypes: []interface{}{
   937  			(*struct {
   938  				Keys   []string
   939  				Values map[string]*datamodel.Node
   940  			})(nil),
   941  			(*struct {
   942  				Keys   []string
   943  				Values map[string]datamodel.Node
   944  			})(nil),
   945  		},
   946  		badTypes: []verifyBadType{
   947  			{(*string)(nil), `.*type Root .* type string: kind mismatch;.*`},
   948  		},
   949  	},
   950  	{
   951  		name: "Union",
   952  		schemaSrc: `type Root union {
   953  				| List_String list
   954  				| String      string
   955  			} representation kinded
   956  
   957  			type List_String [String]
   958  			`,
   959  		goodTypes: []interface{}{
   960  			(*struct {
   961  				List   *[]string
   962  				String *string
   963  			})(nil),
   964  			(*struct {
   965  				List   []string
   966  				String *string
   967  			})(nil),
   968  			(*struct {
   969  				List   *[]namedString
   970  				String *namedString
   971  			})(nil),
   972  		},
   973  		badTypes: []verifyBadType{
   974  			{(*string)(nil), `.*type Root .* type string: kind mismatch;.*`},
   975  			{(*struct{ List *[]string })(nil), `.*type Root .*: 1 vs 2 members`},
   976  			{(*struct {
   977  				List   []string
   978  				String string
   979  			})(nil), `.*type Root .*: union members must be nilable`},
   980  			{(*struct {
   981  				List   *[]string
   982  				String *int
   983  			})(nil), `.*type String .*: kind mismatch;.*`},
   984  		},
   985  	},
   986  }
   987  
   988  func TestSchemaVerify(t *testing.T) {
   989  	t.Parallel()
   990  
   991  	for _, test := range verifyTests {
   992  		test := test // don't reuse the range var
   993  
   994  		t.Run(test.name, func(t *testing.T) {
   995  			t.Parallel()
   996  
   997  			ts, err := ipld.LoadSchemaBytes([]byte(test.schemaSrc))
   998  			qt.Assert(t, err, qt.IsNil)
   999  			schemaType := ts.TypeByName("Root")
  1000  			qt.Assert(t, schemaType, qt.Not(qt.IsNil))
  1001  
  1002  			for _, ptrType := range test.goodTypes {
  1003  				proto := bindnode.Prototype(ptrType, schemaType)
  1004  				qt.Assert(t, proto, qt.Not(qt.IsNil))
  1005  
  1006  				ptrVal := reflect.New(reflect.TypeOf(ptrType).Elem()).Interface()
  1007  				node := bindnode.Wrap(ptrVal, schemaType)
  1008  				qt.Assert(t, node, qt.Not(qt.IsNil))
  1009  			}
  1010  
  1011  			for _, bad := range test.badTypes {
  1012  				qt.Check(t, func() { bindnode.Prototype(bad.ptrType, schemaType) },
  1013  					qt.PanicMatches, bad.panicRegexp)
  1014  
  1015  				ptrVal := reflect.New(reflect.TypeOf(bad.ptrType).Elem()).Interface()
  1016  				qt.Check(t, func() { bindnode.Wrap(ptrVal, schemaType) },
  1017  					qt.PanicMatches, bad.panicRegexp)
  1018  			}
  1019  		})
  1020  	}
  1021  }
  1022  
  1023  func TestProduceGoTypes(t *testing.T) {
  1024  	t.Parallel()
  1025  
  1026  	for _, test := range prototypeTests {
  1027  		test := test // don't reuse the range var
  1028  
  1029  		t.Run(test.name, func(t *testing.T) {
  1030  			t.Parallel()
  1031  
  1032  			ts, err := ipld.LoadSchemaBytes([]byte(test.schemaSrc))
  1033  			qt.Assert(t, err, qt.IsNil)
  1034  
  1035  			// Include a package line and the datamodel import.
  1036  			buf := new(bytes.Buffer)
  1037  			fmt.Fprintln(buf, `package p`)
  1038  			fmt.Fprintln(buf, `import "github.com/ipld/go-ipld-prime/datamodel"`)
  1039  			fmt.Fprintln(buf, `var _ datamodel.Link // always used`)
  1040  			err = bindnode.ProduceGoTypes(buf, ts)
  1041  			qt.Assert(t, err, qt.IsNil)
  1042  
  1043  			// Ensure that the output builds, i.e. typechecks.
  1044  			genPath := filepath.Join(t.TempDir(), "gen.go")
  1045  			err = os.WriteFile(genPath, buf.Bytes(), 0o666)
  1046  			qt.Assert(t, err, qt.IsNil)
  1047  
  1048  			out, err := exec.Command("go", "build", genPath).CombinedOutput()
  1049  			qt.Assert(t, err, qt.IsNil, qt.Commentf("output: %s", out))
  1050  
  1051  			// TODO: check that the generated types are compatible with the schema.
  1052  		})
  1053  	}
  1054  }
  1055  
  1056  func TestRenameAssignNode(t *testing.T) {
  1057  	type Foo struct{ I int }
  1058  
  1059  	ts, _ := ipld.LoadSchemaBytes([]byte(`
  1060  type Foo struct {
  1061  	I Int (rename "J")
  1062  }
  1063  `))
  1064  	FooProto := bindnode.Prototype((*Foo)(nil), ts.TypeByName("Foo"))
  1065  
  1066  	// Decode straight into bindnode typed builder
  1067  	nb := FooProto.Representation().NewBuilder()
  1068  	err := dagjson.Decode(nb, bytes.NewReader([]byte(`{"J":100}`)))
  1069  	qt.Assert(t, err, qt.IsNil)
  1070  	nb.Build()
  1071  
  1072  	// decode into basicnode builder
  1073  	nb = basicnode.Prototype.Any.NewBuilder()
  1074  	err = dagjson.Decode(nb, bytes.NewReader([]byte(`{"J":100}`)))
  1075  	qt.Assert(t, err, qt.IsNil)
  1076  	node := nb.Build()
  1077  
  1078  	// AssignNode from the basicnode form
  1079  	nb = FooProto.Representation().NewBuilder()
  1080  	err = nb.AssignNode(node)
  1081  	qt.Assert(t, err, qt.IsNil)
  1082  	nb.Build()
  1083  }
  1084  
  1085  func TestEmptyTypedAssignNode(t *testing.T) {
  1086  	type Foo struct {
  1087  		I string
  1088  		J string
  1089  		K int
  1090  	}
  1091  	type Foo1Optional struct {
  1092  		I string
  1093  		J string
  1094  		K *int
  1095  	}
  1096  	type Foo2Optional struct {
  1097  		I string
  1098  		J *string
  1099  		K *int
  1100  	}
  1101  	tupleSchema := `type Foo struct {
  1102  		I String
  1103  		J String
  1104  		K Int
  1105  	} representation tuple`
  1106  	tuple1OptionalSchema := `type Foo struct {
  1107  		I String
  1108  		J String
  1109  		K optional Int
  1110  	} representation tuple`
  1111  	tuple2OptionalSchema := `type Foo struct {
  1112  		I String
  1113  		J optional String
  1114  		K optional Int
  1115  	} representation tuple`
  1116  
  1117  	testCases := map[string]struct {
  1118  		schema  string
  1119  		typ     interface{}
  1120  		dagJson string
  1121  		err     string
  1122  	}{
  1123  		"tuple": {
  1124  			schema:  tupleSchema,
  1125  			typ:     (*Foo)(nil),
  1126  			dagJson: `["","",0]`,
  1127  		},
  1128  		"tuple with 2 absents": {
  1129  			schema:  tupleSchema,
  1130  			typ:     (*Foo)(nil),
  1131  			dagJson: `[""]`,
  1132  			err:     "missing required fields: J,K",
  1133  		},
  1134  		"tuple with 1 optional": {
  1135  			schema:  tuple1OptionalSchema,
  1136  			typ:     (*Foo1Optional)(nil),
  1137  			dagJson: `["","",0]`,
  1138  		},
  1139  		"tuple with 1 optional and absent": {
  1140  			schema:  tuple1OptionalSchema,
  1141  			typ:     (*Foo1Optional)(nil),
  1142  			dagJson: `["",""]`,
  1143  		},
  1144  		"tuple with 1 optional and 2 absents": {
  1145  			schema:  tuple1OptionalSchema,
  1146  			typ:     (*Foo1Optional)(nil),
  1147  			dagJson: `[""]`,
  1148  			err:     "missing required fields: J",
  1149  		},
  1150  		"tuple with 2 optional": {
  1151  			schema:  tuple2OptionalSchema,
  1152  			typ:     (*Foo2Optional)(nil),
  1153  			dagJson: `["","",0]`,
  1154  		},
  1155  		"tuple with 2 optional and 1 absent": {
  1156  			schema:  tuple2OptionalSchema,
  1157  			typ:     (*Foo2Optional)(nil),
  1158  			dagJson: `["",""]`,
  1159  		},
  1160  		"tuple with 2 optional and 2 absent": {
  1161  			schema:  tuple2OptionalSchema,
  1162  			typ:     (*Foo2Optional)(nil),
  1163  			dagJson: `[""]`,
  1164  		},
  1165  		"tuple with 2 optional and 3 absent": {
  1166  			schema:  tuple2OptionalSchema,
  1167  			typ:     (*Foo2Optional)(nil),
  1168  			dagJson: `[]`,
  1169  			err:     "missing required fields: I",
  1170  		},
  1171  		"map": {
  1172  			schema: `type Foo struct {
  1173  				I String
  1174  				J String
  1175  				K Int
  1176  			} representation map
  1177  			`,
  1178  			typ:     (*Foo)(nil),
  1179  			dagJson: `{"I":"","J":"","K":0}`,
  1180  		},
  1181  	}
  1182  
  1183  	for testCase, data := range testCases {
  1184  		t.Run(testCase, func(t *testing.T) {
  1185  			ts, _ := ipld.LoadSchemaBytes([]byte(data.schema))
  1186  			FooProto := bindnode.Prototype(data.typ, ts.TypeByName("Foo"))
  1187  
  1188  			// decode an "empty" object into Foo, these are all default values
  1189  			nb := basicnode.Prototype.Any.NewBuilder()
  1190  			err := dagjson.Decode(nb, bytes.NewReader([]byte(data.dagJson)))
  1191  			qt.Assert(t, err, qt.IsNil)
  1192  			node := nb.Build()
  1193  
  1194  			// AssignNode from the basicnode form
  1195  			nb = FooProto.Representation().NewBuilder()
  1196  			err = nb.AssignNode(node)
  1197  			if data.err == "" {
  1198  				qt.Assert(t, err, qt.IsNil)
  1199  			} else {
  1200  				qt.Assert(t, err, qt.ErrorMatches, data.err)
  1201  			}
  1202  			nb.Build()
  1203  
  1204  			// make an "empty" form, although none of the fields are optional so we should end up with defaults
  1205  			nb = FooProto.Representation().NewBuilder()
  1206  			empty := nb.Build()
  1207  
  1208  			// AssignNode from the representation of the "empty" form, which should pass through default values
  1209  			nb = FooProto.Representation().NewBuilder()
  1210  			err = nb.AssignNode(empty.(schema.TypedNode).Representation())
  1211  			qt.Assert(t, err, qt.IsNil)
  1212  		})
  1213  	}
  1214  }
  1215  
  1216  func TestInferLinksAndAny(t *testing.T) {
  1217  	link, err := cid.Decode("bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi")
  1218  	qt.Assert(t, err, qt.IsNil)
  1219  
  1220  	type Nd struct {
  1221  		A cid.Cid
  1222  		B cidlink.Link
  1223  		C datamodel.Link
  1224  		D datamodel.Node
  1225  	}
  1226  
  1227  	proto := bindnode.Prototype(&Nd{}, nil)
  1228  
  1229  	expected := &Nd{
  1230  		A: link,
  1231  		B: cidlink.Link{Cid: link},
  1232  		C: cidlink.Link{Cid: link},
  1233  		D: basicnode.NewString("Any Here"),
  1234  	}
  1235  
  1236  	node := bindnode.Wrap(expected, proto.Type())
  1237  
  1238  	byts, err := ipld.Encode(node, dagjson.Encode)
  1239  	qt.Assert(t, err, qt.IsNil)
  1240  
  1241  	qt.Assert(t, string(byts), qt.Equals, `{"A":{"/":"bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"},"B":{"/":"bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"},"C":{"/":"bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"},"D":"Any Here"}`)
  1242  
  1243  	nodeRt, err := ipld.DecodeUsingPrototype(byts, dagjson.Decode, proto)
  1244  	qt.Assert(t, err, qt.IsNil)
  1245  
  1246  	if actual, ok := bindnode.Unwrap(nodeRt).(*Nd); ok {
  1247  		qt.Assert(t, actual, qt.CmpEquals(cmp.Comparer(func(x, y cid.Cid) bool { return x.Equals(y) })), expected)
  1248  	} else {
  1249  		t.Error("expected *Nd from Unwrap")
  1250  	}
  1251  }