github.com/jbendotnet/noms@v0.0.0-20190904222105-c43e4293ea92/cmd/noms/splore/noms_splore_test.go (about)

     1  // Copyright 2017 Attic Labs, Inc. All rights reserved.
     2  // Licensed under the Apache License, version 2.0:
     3  // http://www.apache.org/licenses/LICENSE-2.0
     4  
     5  package splore
     6  
     7  import (
     8  	"fmt"
     9  	"io/ioutil"
    10  	"net"
    11  	"net/http"
    12  	"os"
    13  	"strings"
    14  	"testing"
    15  
    16  	"github.com/attic-labs/noms/go/chunks"
    17  	"github.com/attic-labs/noms/go/d"
    18  	"github.com/attic-labs/noms/go/datas"
    19  	"github.com/attic-labs/noms/go/spec"
    20  	"github.com/attic-labs/noms/go/types"
    21  	"github.com/attic-labs/noms/go/util/verbose"
    22  	"github.com/stretchr/testify/assert"
    23  )
    24  
    25  func TestNomsSplore(t *testing.T) {
    26  	assert := assert.New(t)
    27  
    28  	dir, err := ioutil.TempDir("", "TestNomsSplore")
    29  	d.PanicIfError(err)
    30  	defer os.RemoveAll(dir)
    31  
    32  	quiet := verbose.Quiet()
    33  	defer verbose.SetQuiet(quiet)
    34  	verbose.SetQuiet(true)
    35  
    36  	getNode := func(id string) string {
    37  		lchan := make(chan net.Listener)
    38  		httpServe = func(l net.Listener, h http.Handler) error {
    39  			lchan <- l
    40  			http.Serve(l, h) // this will error because of the l.Close() below
    41  			return nil
    42  		}
    43  
    44  		go func() { run(&http.ServeMux{}, 0, false, "nbs:"+dir) }()
    45  		l := <-lchan
    46  		defer l.Close()
    47  
    48  		r, err := http.Get(fmt.Sprintf("http://%s/getNode?id=%s", l.Addr().String(), id))
    49  		assert.NoError(err)
    50  		defer r.Body.Close()
    51  		body, err := ioutil.ReadAll(r.Body)
    52  		return string(body)
    53  	}
    54  
    55  	// No data yet:
    56  	assert.JSONEq(`{
    57  		"children": [],
    58  		"hasChildren": false,
    59  		"id": "",
    60  		"name": "Map(0)"
    61  	}`, getNode(""))
    62  
    63  	// Path not found:
    64  	assert.JSONEq(`{"error": "not found"}`, getNode(".notfound"))
    65  
    66  	// Test with real data:
    67  	sp, err := spec.ForDataset(fmt.Sprintf("nbs:%s::ds", dir))
    68  	d.PanicIfError(err)
    69  	defer sp.Close()
    70  	db := sp.GetDatabase()
    71  	strct := types.NewStruct("StructName", types.StructData{
    72  		"blob":          types.NewBlob(db),
    73  		"bool":          types.Bool(true),
    74  		"list":          types.NewList(db, types.Number(1), types.Number(2)),
    75  		"map":           types.NewMap(db, types.String("a"), types.String("b"), types.String("c"), types.String("d")),
    76  		"number":        types.Number(42),
    77  		"ref":           db.WriteValue(types.Bool(true)),
    78  		"set":           types.NewSet(db, types.Number(3), types.Number(4)),
    79  		"string":        types.String("hello world"),
    80  		"typeCompound":  types.MakeMapType(types.StringType, types.MakeListType(types.BoolType)),
    81  		"typePrimitive": types.NumberType,
    82  		"typeStruct":    types.MakeStructType("StructType", types.StructField{Name: "x", Type: types.StringType}, types.StructField{Name: "y", Type: types.MakeStructType("")}),
    83  	})
    84  	sp.GetDatabase().CommitValue(sp.GetDataset(), strct)
    85  
    86  	// The dataset head hash changes whenever the test data changes, so instead
    87  	// of updating it all the time, use string replacement.
    88  	dsHash := sp.GetDataset().HeadRef().TargetHash().String()
    89  	test := func(expectJSON string, id string) {
    90  		expectJSON = strings.Replace(expectJSON, "{{dsHash}}", dsHash, -1)
    91  		assert.JSONEq(expectJSON, getNode(id))
    92  	}
    93  
    94  	// Root => datasets:
    95  	test(`{
    96  		"children": [
    97  		{
    98  			"key": {
    99  				"hasChildren": false,
   100  				"id": "@at(0)@key",
   101  				"name": "\"ds\""
   102  			},
   103  			"label": "",
   104  			"value": {
   105  				"hasChildren": true,
   106  				"id": "@at(0)",
   107  				"name": "Value#{{dsHash}}"
   108  			}
   109  		}
   110  		],
   111  		"hasChildren": true,
   112  		"id": "",
   113  		"name": "Map(1)"
   114  	}`, "")
   115  
   116  	// Dataset 0 (ds) => dataset head ref.
   117  	test(`{
   118  		"children": [
   119  		{
   120  			"key": {
   121  				"hasChildren": false,
   122  				"id": "",
   123  				"name": ""
   124  			},
   125  			"label": "",
   126  			"value": {
   127  				"hasChildren": true,
   128  				"id": "@at(0)@target",
   129  				"name": "Value#{{dsHash}}"
   130  			}
   131  		}
   132  		],
   133  		"hasChildren": true,
   134  		"id": "@at(0)",
   135  		"name": "Value#{{dsHash}}"
   136  	}`, "@at(0)")
   137  
   138  	// ds head ref => ds head:
   139  	// There is a %s replacement here for the ID root, because this expectation
   140  	// is used both for fetching via @at(0)@target and as an absolute ref.
   141  	expectDSHeadFmt := `{
   142  		"children": [
   143  		{
   144  			"key": {
   145  				"hasChildren": false,
   146  				"id": "",
   147  				"name": ""
   148  			},
   149  			"label": "meta",
   150  			"value": {
   151  				"hasChildren": false,
   152  				"id": "%[1]s.meta",
   153  				"name": "{}"
   154  			}
   155  		},
   156  		{
   157  			"key": {
   158  				"hasChildren": false,
   159  				"id": "",
   160  				"name": ""
   161  			},
   162  			"label": "parents",
   163  			"value": {
   164  				"hasChildren": false,
   165  				"id": "%[1]s.parents",
   166  				"name": "Set(0)"
   167  			}
   168  		},
   169  		{
   170  			"key": {
   171  				"hasChildren": false,
   172  				"id": "",
   173  				"name": ""
   174  			},
   175  			"label": "value",
   176  			"value": {
   177  				"hasChildren": true,
   178  				"id": "%[1]s.value",
   179  				"name": "StructName"
   180  			}
   181  		}
   182  		],
   183  		"hasChildren": true,
   184  		"id": "%[1]s",
   185  		"name": "Commit"
   186  	}`
   187  
   188  	test(fmt.Sprintf(expectDSHeadFmt, "@at(0)@target"), "@at(0)@target")
   189  	test(fmt.Sprintf(expectDSHeadFmt, "#"+dsHash), "%23"+dsHash)
   190  
   191  	// ds head value => strct.
   192  	test(`{
   193  		"children": [
   194  		{
   195  			"key": {
   196  				"hasChildren": false,
   197  				"id": "",
   198  				"name": ""
   199  			},
   200  			"label": "blob",
   201  			"value": {
   202  				"hasChildren": false,
   203  				"id": "@at(0)@target.value.blob",
   204  				"name": "Blob(0 B)"
   205  			}
   206  		},
   207  		{
   208  			"key": {
   209  				"hasChildren": false,
   210  				"id": "",
   211  				"name": ""
   212  			},
   213  			"label": "bool",
   214  			"value": {
   215  				"hasChildren": false,
   216  				"id": "@at(0)@target.value.bool",
   217  				"name": "true"
   218  			}
   219  		},
   220  		{
   221  			"key": {
   222  				"hasChildren": false,
   223  				"id": "",
   224  				"name": ""
   225  			},
   226  			"label": "list",
   227  			"value": {
   228  				"hasChildren": true,
   229  				"id": "@at(0)@target.value.list",
   230  				"name": "List(2)"
   231  			}
   232  		},
   233  		{
   234  			"key": {
   235  				"hasChildren": false,
   236  				"id": "",
   237  				"name": ""
   238  			},
   239  			"label": "map",
   240  			"value": {
   241  				"hasChildren": true,
   242  				"id": "@at(0)@target.value.map",
   243  				"name": "Map(2)"
   244  			}
   245  		},
   246  		{
   247  			"key": {
   248  				"hasChildren": false,
   249  				"id": "",
   250  				"name": ""
   251  			},
   252  			"label": "number",
   253  			"value": {
   254  				"hasChildren": false,
   255  				"id": "@at(0)@target.value.number",
   256  				"name": "42"
   257  			}
   258  		},
   259  		{
   260  			"key": {
   261  				"hasChildren": false,
   262  				"id": "",
   263  				"name": ""
   264  			},
   265  			"label": "ref",
   266  			"value": {
   267  				"hasChildren": true,
   268  				"id": "@at(0)@target.value.ref",
   269  				"name": "Bool#g19moobgrm32dn083bokhksuobulq28c"
   270  			}
   271  		},
   272  		{
   273  			"key": {
   274  				"hasChildren": false,
   275  				"id": "",
   276  				"name": ""
   277  			},
   278  			"label": "set",
   279  			"value": {
   280  				"hasChildren": true,
   281  				"id": "@at(0)@target.value.set",
   282  				"name": "Set(2)"
   283  			}
   284  		},
   285  		{
   286  			"key": {
   287  				"hasChildren": false,
   288  				"id": "",
   289  				"name": ""
   290  			},
   291  			"label": "string",
   292  			"value": {
   293  				"hasChildren": false,
   294  				"id": "@at(0)@target.value.string",
   295  				"name": "\"hello world\""
   296  			}
   297  		},
   298  		{
   299  			"key": {
   300  				"hasChildren": false,
   301  				"id": "",
   302  				"name": ""
   303  			},
   304  			"label": "typeCompound",
   305  			"value": {
   306  				"hasChildren": true,
   307  				"id": "@at(0)@target.value.typeCompound",
   308  				"name": "Map"
   309  			}
   310  		},
   311  		{
   312  			"key": {
   313  				"hasChildren": false,
   314  				"id": "",
   315  				"name": ""
   316  			},
   317  			"label": "typePrimitive",
   318  			"value": {
   319  				"hasChildren": false,
   320  				"id": "@at(0)@target.value.typePrimitive",
   321  				"name": "Number"
   322  			}
   323  		},
   324  		{
   325  			"key": {
   326  				"hasChildren": false,
   327  				"id": "",
   328  				"name": ""
   329  			},
   330  			"label": "typeStruct",
   331  			"value": {
   332  				"hasChildren": true,
   333  				"id": "@at(0)@target.value.typeStruct",
   334  				"name": "struct StructType"
   335  			}
   336  		}
   337  		],
   338  		"hasChildren": true,
   339  		"id": "@at(0)@target.value",
   340  		"name": "StructName"
   341  	}`, "@at(0)@target.value")
   342  
   343  	// strct.blob:
   344  	test(`{
   345  		"children": [],
   346  		"hasChildren": false,
   347  		"id": "@at(0)@target.value.blob",
   348  		"name": "Blob(0 B)"
   349  	}`, "@at(0)@target.value.blob")
   350  
   351  	// strct.bool:
   352  	test(`{
   353  		"children": [],
   354  		"hasChildren": false,
   355  		"id": "@at(0)@target.value.bool",
   356  		"name": "true"
   357  	}`, "@at(0)@target.value.bool")
   358  
   359  	// strct.list:
   360  	test(`{
   361  		"children": [
   362  		{
   363  			"key": {
   364  				"hasChildren": false,
   365  				"id": "",
   366  				"name": ""
   367  			},
   368  			"label": "",
   369  			"value": {
   370  				"hasChildren": false,
   371  				"id": "@at(0)@target.value.list[0]",
   372  				"name": "1"
   373  			}
   374  		},
   375  		{
   376  			"key": {
   377  				"hasChildren": false,
   378  				"id": "",
   379  				"name": ""
   380  			},
   381  			"label": "",
   382  			"value": {
   383  				"hasChildren": false,
   384  				"id": "@at(0)@target.value.list[1]",
   385  				"name": "2"
   386  			}
   387  		}
   388  		],
   389  		"hasChildren": true,
   390  		"id": "@at(0)@target.value.list",
   391  		"name": "List(2)"
   392  	}`, "@at(0)@target.value.list")
   393  
   394  	// strct.map:
   395  	test(`{
   396  		"children": [
   397  		{
   398  			"key": {
   399  				"hasChildren": false,
   400  				"id": "@at(0)@target.value.map@at(0)@key",
   401  				"name": "\"a\""
   402  			},
   403  			"label": "",
   404  			"value": {
   405  				"hasChildren": false,
   406  				"id": "@at(0)@target.value.map@at(0)",
   407  				"name": "\"b\""
   408  			}
   409  		},
   410  		{
   411  			"key": {
   412  				"hasChildren": false,
   413  				"id": "@at(0)@target.value.map@at(1)@key",
   414  				"name": "\"c\""
   415  			},
   416  			"label": "",
   417  			"value": {
   418  				"hasChildren": false,
   419  				"id": "@at(0)@target.value.map@at(1)",
   420  				"name": "\"d\""
   421  			}
   422  		}
   423  		],
   424  		"hasChildren": true,
   425  		"id": "@at(0)@target.value.map",
   426  		"name": "Map(2)"
   427  	}`, "@at(0)@target.value.map")
   428  
   429  	// strct.number:
   430  	test(`{
   431  		"children": [],
   432  		"hasChildren": false,
   433  		"id": "@at(0)@target.value.number",
   434  		"name": "42"
   435  	}`, "@at(0)@target.value.number")
   436  
   437  	// strct.ref:
   438  	test(`{
   439  		"children": [
   440  		{
   441  			"key": {
   442  				"hasChildren": false,
   443  				"id": "",
   444  				"name": ""
   445  			},
   446  			"label": "",
   447  			"value": {
   448  				"hasChildren": true,
   449  				"id": "@at(0)@target.value.ref@target",
   450  				"name": "Bool#g19moobgrm32dn083bokhksuobulq28c"
   451  			}
   452  		}
   453  		],
   454  		"hasChildren": true,
   455  		"id": "@at(0)@target.value.ref",
   456  		"name": "Bool#g19moobgrm32dn083bokhksuobulq28c"
   457  	}`, "@at(0)@target.value.ref")
   458  
   459  	// strct.set:
   460  	test(`{
   461  		"children": [
   462  		{
   463  			"key": {
   464  				"hasChildren": false,
   465  				"id": "",
   466  				"name": ""
   467  			},
   468  			"label": "",
   469  			"value": {
   470  				"hasChildren": false,
   471  				"id": "@at(0)@target.value.set@at(0)",
   472  				"name": "3"
   473  			}
   474  		},
   475  		{
   476  			"key": {
   477  				"hasChildren": false,
   478  				"id": "",
   479  				"name": ""
   480  			},
   481  			"label": "",
   482  			"value": {
   483  				"hasChildren": false,
   484  				"id": "@at(0)@target.value.set@at(1)",
   485  				"name": "4"
   486  			}
   487  		}
   488  		],
   489  		"hasChildren": true,
   490  		"id": "@at(0)@target.value.set",
   491  		"name": "Set(2)"
   492  	}`, "@at(0)@target.value.set")
   493  
   494  	// strct.string:
   495  	test(`{
   496  		"children": [],
   497  		"hasChildren": false,
   498  		"id": "@at(0)@target.value.string",
   499  		"name": "\"hello world\""
   500  	}`, "@at(0)@target.value.string")
   501  
   502  	// strct.typeCompound:
   503  	test(`{
   504  		"children": [
   505  		{
   506  			"key": {
   507  				"hasChildren": false,
   508  				"id": "",
   509  				"name": ""
   510  			},
   511  			"label": "",
   512  			"value": {
   513  				"hasChildren": false,
   514  				"id": "@at(0)@target.value.typeCompound[0]",
   515  				"name": "String"
   516  			}
   517  		},
   518  		{
   519  			"key": {
   520  				"hasChildren": false,
   521  				"id": "",
   522  				"name": ""
   523  			},
   524  			"label": "",
   525  			"value": {
   526  				"hasChildren": true,
   527  				"id": "@at(0)@target.value.typeCompound[1]",
   528  				"name": "List"
   529  			}
   530  		}
   531  		],
   532  		"hasChildren": true,
   533  		"id": "@at(0)@target.value.typeCompound",
   534  		"name": "Map"
   535  	}`, "@at(0)@target.value.typeCompound")
   536  
   537  	// strct.typePrimitive:
   538  	test(`{
   539  		"children": [],
   540  		"hasChildren": false,
   541  		"id": "@at(0)@target.value.typePrimitive",
   542  		"name": "Number"
   543  	}`, "@at(0)@target.value.typePrimitive")
   544  
   545  	// strct.typeStruct:
   546  	test(`{
   547  		"children": [
   548  		{
   549  			"key": {
   550  				"hasChildren": false,
   551  				"id": "",
   552  				"name": ""
   553  			},
   554  			"label": "x",
   555  			"value": {
   556  				"hasChildren": false,
   557  				"id": "@at(0)@target.value.typeStruct.x",
   558  				"name": "String"
   559  			}
   560  		},
   561  		{
   562  			"key": {
   563  				"hasChildren": false,
   564  				"id": "",
   565  				"name": ""
   566  			},
   567  			"label": "y",
   568  			"value": {
   569  				"hasChildren": false,
   570  				"id": "@at(0)@target.value.typeStruct.y",
   571  				"name": "struct {}"
   572  			}
   573  		}
   574  		],
   575  		"hasChildren": true,
   576  		"id": "@at(0)@target.value.typeStruct",
   577  		"name": "struct StructType"
   578  	}`, "@at(0)@target.value.typeStruct")
   579  }
   580  
   581  func TestNomsSploreGetMetaChildren(t *testing.T) {
   582  	assert := assert.New(t)
   583  
   584  	storage := &chunks.TestStorage{}
   585  	db := datas.NewDatabase(storage.NewView())
   586  	defer db.Close()
   587  
   588  	// A bunch of lists with just numbers or ref<number>s in them. None of these
   589  	// should be detected as meta sequences:
   590  
   591  	l1 := types.NewList(db)
   592  	assert.Nil(getMetaChildren(l1))
   593  
   594  	l2 := types.NewList(db, types.Number(1))
   595  	assert.Nil(getMetaChildren(l2))
   596  
   597  	l3 := types.NewList(db, types.Number(1), types.Number(2))
   598  	assert.Nil(getMetaChildren(l3))
   599  
   600  	l4 := types.NewList(db, db.WriteValue(types.Number(1)))
   601  	assert.Nil(getMetaChildren(l4))
   602  
   603  	l5 := types.NewList(db, db.WriteValue(types.Number(1)), types.Number(2))
   604  	assert.Nil(getMetaChildren(l5))
   605  
   606  	l6 := types.NewList(db, db.WriteValue(types.Number(1)), db.WriteValue(types.Number(2)))
   607  	assert.Nil(getMetaChildren(l6))
   608  
   609  	l7 := types.NewList(db, l1)
   610  	assert.Nil(getMetaChildren(l7))
   611  
   612  	l8 := types.NewList(db, l4)
   613  	assert.Nil(getMetaChildren(l8))
   614  
   615  	// List with more or equal ref<list> than elements. This can't possibly be a meta
   616  	// sequence, because there are no empty leaf sequences:
   617  
   618  	l1Ref := db.WriteValue(l1)
   619  	l2Ref := db.WriteValue(l2)
   620  	l3Ref := db.WriteValue(l3)
   621  	listRefList := types.NewList(db, l1Ref, l2Ref, l3Ref)
   622  
   623  	l9 := types.NewList(db, listRefList)
   624  	assert.Nil(getMetaChildren(l9))
   625  
   626  	l10 := types.NewList(db, types.Number(1), listRefList)
   627  	assert.Nil(getMetaChildren(l10))
   628  
   629  	l11 := listRefList
   630  	assert.Nil(getMetaChildren(l11))
   631  
   632  	l12 := types.NewList(db, types.Number(1), types.Number(2), listRefList)
   633  	assert.Nil(getMetaChildren(l12))
   634  
   635  	l13 := types.NewList(db, types.Number(1), db.WriteValue(types.Number(2)), listRefList)
   636  	assert.Nil(getMetaChildren(l13))
   637  
   638  	// List with fewer ref<list> as children. For now this is the closet
   639  	// approximation for detecting meta sequences:
   640  
   641  	l1Hash := "#" + l1Ref.TargetHash().String()
   642  	l2Hash := "#" + l2Ref.TargetHash().String()
   643  	l3Hash := "#" + l3Ref.TargetHash().String()
   644  	expectNodeChildren := []nodeChild{
   645  		{Value: nodeInfo{HasChildren: true, ID: l1Hash, Name: "List" + l1Hash}},
   646  		{Value: nodeInfo{HasChildren: true, ID: l2Hash, Name: "List" + l2Hash}},
   647  		{Value: nodeInfo{HasChildren: true, ID: l3Hash, Name: "List" + l3Hash}},
   648  	}
   649  
   650  	l14 := types.NewList(db, types.Number(1), types.Number(2), types.Number(3), listRefList)
   651  	assert.Equal(expectNodeChildren, getMetaChildren(l14))
   652  
   653  	l15 := types.NewList(db, types.Number(1), types.Number(2), db.WriteValue(types.Number(3)), listRefList)
   654  	assert.Equal(expectNodeChildren, getMetaChildren(l15))
   655  
   656  	l16 := types.NewList(db, types.Number(1), types.Number(2), types.Number(3), types.Number(4), listRefList)
   657  	assert.Equal(expectNodeChildren, getMetaChildren(l16))
   658  
   659  	l17 := types.NewList(db, types.Number(1), types.Number(2), db.WriteValue(types.Number(3)), db.WriteValue(types.Number(4)), listRefList)
   660  	assert.Equal(expectNodeChildren, getMetaChildren(l17))
   661  }