github.com/ipld/go-ipld-prime@v0.21.0/node/basicnode/map_test.go (about)

     1  package basicnode_test
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	qt "github.com/frankban/quicktest"
     8  	"github.com/ipld/go-ipld-prime/datamodel"
     9  	"github.com/ipld/go-ipld-prime/must"
    10  	"github.com/ipld/go-ipld-prime/node/basicnode"
    11  	"github.com/ipld/go-ipld-prime/node/tests"
    12  	"github.com/ipld/go-ipld-prime/printer"
    13  )
    14  
    15  func TestMap(t *testing.T) {
    16  	tests.SpecTestMapStrInt(t, basicnode.Prototype.Map)
    17  	tests.SpecTestMapStrMapStrInt(t, basicnode.Prototype.Map)
    18  	tests.SpecTestMapStrListStr(t, basicnode.Prototype.Map)
    19  }
    20  
    21  func BenchmarkMapStrInt_3n_AssembleStandard(b *testing.B) {
    22  	tests.SpecBenchmarkMapStrInt_3n_AssembleStandard(b, basicnode.Prototype.Map)
    23  }
    24  func BenchmarkMapStrInt_3n_AssembleEntry(b *testing.B) {
    25  	tests.SpecBenchmarkMapStrInt_3n_AssembleEntry(b, basicnode.Prototype.Map)
    26  }
    27  func BenchmarkMapStrInt_3n_Iteration(b *testing.B) {
    28  	tests.SpecBenchmarkMapStrInt_3n_Iteration(b, basicnode.Prototype.Map)
    29  }
    30  
    31  func BenchmarkMapStrInt_25n_AssembleStandard(b *testing.B) {
    32  	tests.SpecBenchmarkMapStrInt_25n_AssembleStandard(b, basicnode.Prototype.Map)
    33  }
    34  func BenchmarkMapStrInt_25n_AssembleEntry(b *testing.B) {
    35  	tests.SpecBenchmarkMapStrInt_25n_AssembleEntry(b, basicnode.Prototype.Map)
    36  }
    37  func BenchmarkMapStrInt_25n_Iteration(b *testing.B) {
    38  	tests.SpecBenchmarkMapStrInt_25n_Iteration(b, basicnode.Prototype.Map)
    39  }
    40  
    41  func BenchmarkSpec_Marshal_Map3StrInt(b *testing.B) {
    42  	tests.BenchmarkSpec_Marshal_Map3StrInt(b, basicnode.Prototype.Map)
    43  }
    44  func BenchmarkSpec_Marshal_MapNStrMap3StrInt(b *testing.B) {
    45  	tests.BenchmarkSpec_Marshal_MapNStrMap3StrInt(b, basicnode.Prototype.Map)
    46  }
    47  
    48  func BenchmarkSpec_Unmarshal_Map3StrInt(b *testing.B) {
    49  	tests.BenchmarkSpec_Unmarshal_Map3StrInt(b, basicnode.Prototype.Map)
    50  }
    51  func BenchmarkSpec_Unmarshal_MapNStrMap3StrInt(b *testing.B) {
    52  	tests.BenchmarkSpec_Unmarshal_MapNStrMap3StrInt(b, basicnode.Prototype.Map)
    53  }
    54  
    55  // Test that the map builder cannot be assigned arbitrary values, and trying to
    56  // will result in a sensible error
    57  func TestMapAssignError(t *testing.T) {
    58  	b := basicnode.Prototype.Map.NewBuilder()
    59  	err := b.AssignBool(true)
    60  	errExpect := `func called on wrong kind: "AssignBool" called on a map node \(kind: map\), but only makes sense on bool`
    61  	qt.Check(t, err, qt.ErrorMatches, errExpect)
    62  
    63  	err = b.AssignInt(3)
    64  	errExpect = `func called on wrong kind: "AssignInt" called on a map node \(kind: map\), but only makes sense on int`
    65  	qt.Check(t, err, qt.ErrorMatches, errExpect)
    66  
    67  	err = b.AssignFloat(5.7)
    68  	errExpect = `func called on wrong kind: "AssignFloat" called on a map node \(kind: map\), but only makes sense on float`
    69  	qt.Check(t, err, qt.ErrorMatches, errExpect)
    70  
    71  	err = b.AssignString("hi")
    72  	errExpect = `func called on wrong kind: "AssignString" called on a map node \(kind: map\), but only makes sense on string`
    73  	qt.Check(t, err, qt.ErrorMatches, errExpect)
    74  
    75  	err = b.AssignNode(basicnode.NewInt(3))
    76  	errExpect = `func called on wrong kind: "AssignNode" called on a map node \(kind: int\), but only makes sense on map`
    77  	qt.Check(t, err, qt.ErrorMatches, errExpect)
    78  
    79  	// TODO(dustmop): BeginList, AssignNull, AssignBytes, AssignLink
    80  }
    81  
    82  // Test that the map builder can create map nodes, and AssignNode will copy
    83  // such a node, and lookup methods on that node will work correctly
    84  func TestMapBuilder(t *testing.T) {
    85  	b := basicnode.Prototype.Map.NewBuilder()
    86  
    87  	// construct a map of three keys, using the MapBuilder
    88  	ma, err := b.BeginMap(3)
    89  	if err != nil {
    90  		t.Fatal(err)
    91  	}
    92  	a := ma.AssembleKey()
    93  	a.AssignString("cat")
    94  	a = ma.AssembleValue()
    95  	a.AssignString("meow")
    96  
    97  	a, err = ma.AssembleEntry("dog")
    98  	if err != nil {
    99  		t.Fatal(err)
   100  	}
   101  	a.AssignString("bark")
   102  
   103  	a = ma.AssembleKey()
   104  	a.AssignString("eel")
   105  	a = ma.AssembleValue()
   106  	a.AssignString("zap")
   107  
   108  	err = ma.Finish()
   109  	if err != nil {
   110  		t.Fatal(err)
   111  	}
   112  
   113  	// test the builder's prototypes and its key and value prototypes, while we're here
   114  	np := b.Prototype()
   115  	qt.Check(t, fmt.Sprintf("%T", np), qt.Equals, "basicnode.Prototype__Map")
   116  	np = ma.KeyPrototype()
   117  	qt.Check(t, fmt.Sprintf("%T", np), qt.Equals, "basicnode.Prototype__String")
   118  	np = ma.ValuePrototype("")
   119  	qt.Check(t, fmt.Sprintf("%T", np), qt.Equals, "basicnode.Prototype__Any")
   120  
   121  	// compare the printed map
   122  	mapNode := b.Build()
   123  	actual := printer.Sprint(mapNode)
   124  
   125  	expect := `map{
   126  	string{"cat"}: string{"meow"}
   127  	string{"dog"}: string{"bark"}
   128  	string{"eel"}: string{"zap"}
   129  }`
   130  	qt.Check(t, expect, qt.Equals, actual)
   131  
   132  	// copy the map using AssignNode
   133  	c := basicnode.Prototype.Map.NewBuilder()
   134  	err = c.AssignNode(mapNode)
   135  	if err != nil {
   136  		t.Fatal(err)
   137  	}
   138  	anotherNode := c.Build()
   139  
   140  	actual = printer.Sprint(anotherNode)
   141  	qt.Assert(t, expect, qt.Equals, actual)
   142  
   143  	// access values of map, using string
   144  	r, err := anotherNode.LookupByString("cat")
   145  	if err != nil {
   146  		t.Fatal(err)
   147  	}
   148  	qt.Check(t, "meow", qt.Equals, must.String(r))
   149  
   150  	// access values of map, using node
   151  	r, err = anotherNode.LookupByNode(basicnode.NewString("dog"))
   152  	if err != nil {
   153  		t.Fatal(err)
   154  	}
   155  	qt.Check(t, "bark", qt.Equals, must.String(r))
   156  
   157  	// access values of map, using PathSegment
   158  	r, err = anotherNode.LookupBySegment(datamodel.ParsePathSegment("eel"))
   159  	if err != nil {
   160  		t.Fatal(err)
   161  	}
   162  	qt.Check(t, "zap", qt.Equals, must.String(r))
   163  
   164  	// validate the node's prototype
   165  	np = anotherNode.Prototype()
   166  	qt.Check(t, fmt.Sprintf("%T", np), qt.Equals, "basicnode.Prototype__Map")
   167  }
   168  
   169  // test that AssignNode will fail if called twice, it expects an empty
   170  // node to assign to
   171  func TestMapCantAssignNodeTwice(t *testing.T) {
   172  	b := basicnode.Prototype.Map.NewBuilder()
   173  
   174  	// construct a map of three keys, using the MapBuilder
   175  	ma, err := b.BeginMap(3)
   176  	if err != nil {
   177  		t.Fatal(err)
   178  	}
   179  	a := ma.AssembleKey()
   180  	a.AssignString("cat")
   181  	a = ma.AssembleValue()
   182  	a.AssignString("meow")
   183  
   184  	a, err = ma.AssembleEntry("dog")
   185  	if err != nil {
   186  		t.Fatal(err)
   187  	}
   188  	a.AssignString("bark")
   189  
   190  	a = ma.AssembleKey()
   191  	a.AssignString("eel")
   192  	a = ma.AssembleValue()
   193  	a.AssignString("zap")
   194  
   195  	err = ma.Finish()
   196  	if err != nil {
   197  		t.Fatal(err)
   198  	}
   199  	mapNode := b.Build()
   200  
   201  	// copy the map using AssignNode, works the first time
   202  	c := basicnode.Prototype.Map.NewBuilder()
   203  	err = c.AssignNode(mapNode)
   204  	if err != nil {
   205  		t.Fatal(err)
   206  	}
   207  	qt.Assert(t,
   208  		func() {
   209  			_ = c.AssignNode(mapNode)
   210  		},
   211  		qt.PanicMatches,
   212  		// TODO(dustmop): Error message here should be better
   213  		`misuse`)
   214  }
   215  
   216  func TestMapLookupError(t *testing.T) {
   217  	b := basicnode.Prototype.Map.NewBuilder()
   218  
   219  	// construct a map of three keys, using the MapBuilder
   220  	ma, err := b.BeginMap(3)
   221  	if err != nil {
   222  		t.Fatal(err)
   223  	}
   224  	a := ma.AssembleKey()
   225  	a.AssignString("cat")
   226  	a = ma.AssembleValue()
   227  	a.AssignString("meow")
   228  
   229  	a, err = ma.AssembleEntry("dog")
   230  	if err != nil {
   231  		t.Fatal(err)
   232  	}
   233  	a.AssignString("bark")
   234  
   235  	a = ma.AssembleKey()
   236  	a.AssignString("eel")
   237  	a = ma.AssembleValue()
   238  	a.AssignString("zap")
   239  
   240  	err = ma.Finish()
   241  	if err != nil {
   242  		t.Fatal(err)
   243  	}
   244  
   245  	mapNode := b.Build()
   246  
   247  	_, err = mapNode.LookupByString("frog")
   248  	qt.Check(t, err, qt.ErrorMatches, `key not found: "frog"`)
   249  
   250  	_, err = mapNode.LookupByNode(basicnode.NewInt(3))
   251  	// TODO(dustmop): This error message is not great. It's about how the
   252  	// int node could not be converted when the real problem is that this
   253  	// method should not accept ints as parameters
   254  	qt.Check(t, err, qt.ErrorMatches, `func called on wrong kind: "AsString" called on a int node \(kind: int\), but only makes sense on string`)
   255  
   256  	_, err = mapNode.LookupByIndex(0)
   257  	qt.Check(t, err, qt.ErrorMatches, `func called on wrong kind: "LookupByIndex" called on a map node \(kind: map\), but only makes sense on list`)
   258  }
   259  
   260  func TestMapNewBuilderUsageError(t *testing.T) {
   261  	qt.Assert(t,
   262  		func() {
   263  			b := basicnode.Prototype.Map.NewBuilder()
   264  			_ = b.Build()
   265  		},
   266  		qt.PanicMatches,
   267  		`invalid state: assembler must be 'finished' before Build can be called!`)
   268  
   269  	// construct an empty map
   270  	b := basicnode.Prototype.Map.NewBuilder()
   271  	ma, err := b.BeginMap(0)
   272  	if err != nil {
   273  		t.Fatal(err)
   274  	}
   275  	err = ma.Finish()
   276  	if err != nil {
   277  		t.Fatal(err)
   278  	}
   279  	mapNode := b.Build()
   280  	actual := printer.Sprint(mapNode)
   281  
   282  	expect := `map{}`
   283  	qt.Check(t, expect, qt.Equals, actual)
   284  
   285  	// reset will return the state to 'initial', so Build will panic once again
   286  	b.Reset()
   287  	qt.Assert(t,
   288  		func() {
   289  			_ = b.Build()
   290  		},
   291  		qt.PanicMatches,
   292  		`invalid state: assembler must be 'finished' before Build can be called!`)
   293  
   294  	// assembling a key without a value will cause Finish to panic
   295  	b.Reset()
   296  	ma, err = b.BeginMap(0)
   297  	if err != nil {
   298  		t.Fatal(err)
   299  	}
   300  	a := ma.AssembleKey()
   301  	a.AssignString("cat")
   302  	qt.Assert(t,
   303  		func() {
   304  			_ = ma.Finish()
   305  		},
   306  		qt.PanicMatches,
   307  		// TODO(dustmop): Error message here should be better
   308  		`misuse`)
   309  }
   310  
   311  func TestMapDupKeyError(t *testing.T) {
   312  	b := basicnode.Prototype.Map.NewBuilder()
   313  
   314  	// construct a map with duplicate keys
   315  	ma, err := b.BeginMap(3)
   316  	if err != nil {
   317  		t.Fatal(err)
   318  	}
   319  	a := ma.AssembleKey()
   320  	a.AssignString("cat")
   321  	a = ma.AssembleValue()
   322  	a.AssignString("meow")
   323  	a = ma.AssembleKey()
   324  	err = a.AssignString("cat")
   325  
   326  	qt.Check(t, err, qt.ErrorMatches, `cannot repeat map key "cat"`)
   327  }