mirror of
https://github.com/dolthub/dolt.git
synced 2026-02-09 18:59:12 -06:00
stretchr has fixed a bug with the -count flag. I could merge these changes into attic-labs, but it's easier to just use strechr. We forked stretchr a long time ago so that we didn't link in the HTTP testing libraries into the noms binaries (because we were using d.Chk in production code). The HTTP issue doesn't seem to happen anymore, even though we're still using d.Chk.
570 lines
17 KiB
Go
570 lines
17 KiB
Go
// Copyright 2016 Attic Labs, Inc. All rights reserved.
|
|
// Licensed under the Apache License, version 2.0:
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
package datas
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/attic-labs/noms/go/chunks"
|
|
"github.com/attic-labs/noms/go/hash"
|
|
"github.com/attic-labs/noms/go/merge"
|
|
"github.com/attic-labs/noms/go/types"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/suite"
|
|
)
|
|
|
|
func TestLocalDatabase(t *testing.T) {
|
|
suite.Run(t, &LocalDatabaseSuite{})
|
|
}
|
|
|
|
func TestRemoteDatabase(t *testing.T) {
|
|
suite.Run(t, &RemoteDatabaseSuite{})
|
|
}
|
|
|
|
func TestValidateRef(t *testing.T) {
|
|
st := &chunks.TestStorage{}
|
|
db := NewDatabase(st.NewView()).(*database)
|
|
defer db.Close()
|
|
b := types.Bool(true)
|
|
r := db.WriteValue(b)
|
|
|
|
assert.Panics(t, func() { db.validateRefAsCommit(r) })
|
|
assert.Panics(t, func() { db.validateRefAsCommit(types.NewRef(b)) })
|
|
}
|
|
|
|
type DatabaseSuite struct {
|
|
suite.Suite
|
|
storage *chunks.TestStorage
|
|
db Database
|
|
makeDb func(chunks.ChunkStore) Database
|
|
}
|
|
|
|
type LocalDatabaseSuite struct {
|
|
DatabaseSuite
|
|
}
|
|
|
|
func (suite *LocalDatabaseSuite) SetupTest() {
|
|
suite.storage = &chunks.TestStorage{}
|
|
suite.makeDb = NewDatabase
|
|
suite.db = suite.makeDb(suite.storage.NewView())
|
|
}
|
|
|
|
type RemoteDatabaseSuite struct {
|
|
DatabaseSuite
|
|
}
|
|
|
|
func (suite *RemoteDatabaseSuite) SetupTest() {
|
|
suite.storage = &chunks.TestStorage{}
|
|
suite.makeDb = func(cs chunks.ChunkStore) Database {
|
|
return NewDatabase(newHTTPChunkStoreForTest(cs))
|
|
}
|
|
suite.db = suite.makeDb(suite.storage.NewView())
|
|
}
|
|
|
|
func (suite *DatabaseSuite) TearDownTest() {
|
|
suite.db.Close()
|
|
}
|
|
|
|
func (suite *RemoteDatabaseSuite) TestWriteRefToNonexistentValue() {
|
|
ds := suite.db.GetDataset("foo")
|
|
r := types.NewRef(types.Bool(true))
|
|
suite.Panics(func() { suite.db.CommitValue(ds, r) })
|
|
}
|
|
|
|
func (suite *DatabaseSuite) TestTolerateUngettableRefs() {
|
|
suite.Nil(suite.db.ReadValue(hash.Hash{}))
|
|
}
|
|
|
|
func (suite *DatabaseSuite) TestCompletenessCheck() {
|
|
datasetID := "ds1"
|
|
ds1 := suite.db.GetDataset(datasetID)
|
|
|
|
se := types.NewSet(suite.db).Edit()
|
|
for i := 0; i < 100; i++ {
|
|
se.Insert(suite.db.WriteValue(types.Number(100)))
|
|
}
|
|
s := se.Set()
|
|
|
|
ds1, err := suite.db.CommitValue(ds1, s)
|
|
suite.NoError(err)
|
|
|
|
s = ds1.HeadValue().(types.Set)
|
|
s = s.Edit().Insert(types.NewRef(types.Number(1000))).Set() // danging ref
|
|
suite.Panics(func() {
|
|
ds1, err = suite.db.CommitValue(ds1, s)
|
|
})
|
|
}
|
|
|
|
func (suite *DatabaseSuite) TestRebase() {
|
|
datasetID := "ds1"
|
|
ds1 := suite.db.GetDataset(datasetID)
|
|
var err error
|
|
|
|
// Setup:
|
|
// ds1: |a| <- |b|
|
|
ds1, err = suite.db.CommitValue(ds1, types.String("a"))
|
|
b := types.String("b")
|
|
ds1, err = suite.db.CommitValue(ds1, b)
|
|
suite.NoError(err)
|
|
suite.True(ds1.HeadValue().Equals(b))
|
|
|
|
interloper := suite.makeDb(suite.storage.NewView())
|
|
defer interloper.Close()
|
|
|
|
// Concurrent change, to move root out from under my feet:
|
|
// ds1: |a| <- |b| <- |e|
|
|
e := types.String("e")
|
|
iDS, concErr := interloper.CommitValue(interloper.GetDataset(datasetID), e)
|
|
suite.NoError(concErr)
|
|
suite.True(iDS.HeadValue().Equals(e))
|
|
|
|
// suite.ds shouldn't see the above change yet
|
|
suite.True(suite.db.GetDataset(datasetID).HeadValue().Equals(b))
|
|
|
|
suite.db.Rebase()
|
|
suite.True(suite.db.GetDataset(datasetID).HeadValue().Equals(e))
|
|
|
|
cs := suite.storage.NewView()
|
|
noChangeDB := suite.makeDb(cs)
|
|
noChangeDB.Datasets()
|
|
cs.Reads = 0 // New baseline
|
|
|
|
noChangeDB.Rebase()
|
|
suite.Zero(cs.Reads)
|
|
}
|
|
|
|
func (suite *DatabaseSuite) TestCommitProperlyTracksRoot() {
|
|
id1, id2 := "testdataset", "othertestdataset"
|
|
|
|
db1 := suite.makeDb(suite.storage.NewView())
|
|
defer db1.Close()
|
|
ds1 := db1.GetDataset(id1)
|
|
ds1HeadVal := types.String("Commit value for " + id1)
|
|
ds1, err := db1.CommitValue(ds1, ds1HeadVal)
|
|
suite.NoError(err)
|
|
|
|
db2 := suite.makeDb(suite.storage.NewView())
|
|
defer db2.Close()
|
|
ds2 := db2.GetDataset(id2)
|
|
ds2HeadVal := types.String("Commit value for " + id2)
|
|
ds2, err = db2.CommitValue(ds2, ds2HeadVal)
|
|
suite.NoError(err)
|
|
|
|
suite.EqualValues(ds1HeadVal, ds1.HeadValue())
|
|
suite.EqualValues(ds2HeadVal, ds2.HeadValue())
|
|
suite.False(ds2.HeadValue().Equals(ds1HeadVal))
|
|
suite.False(ds1.HeadValue().Equals(ds2HeadVal))
|
|
}
|
|
|
|
func (suite *DatabaseSuite) TestDatabaseCommit() {
|
|
datasetID := "ds1"
|
|
datasets := suite.db.Datasets()
|
|
suite.Zero(datasets.Len())
|
|
|
|
// |a|
|
|
ds := suite.db.GetDataset(datasetID)
|
|
a := types.String("a")
|
|
ds2, err := suite.db.CommitValue(ds, a)
|
|
suite.NoError(err)
|
|
|
|
// ds2 matches the Datasets Map in suite.db
|
|
suite.True(ds2.HeadRef().Equals(suite.db.GetDataset(datasetID).HeadRef()))
|
|
|
|
// ds2 has |a| at its head
|
|
h, ok := ds2.MaybeHeadValue()
|
|
suite.True(ok)
|
|
suite.True(h.Equals(a))
|
|
suite.Equal(uint64(1), ds2.HeadRef().Height())
|
|
|
|
ds = ds2
|
|
aCommitRef := ds.HeadRef() // to be used to test disallowing of non-fastforward commits below
|
|
|
|
// |a| <- |b|
|
|
b := types.String("b")
|
|
ds, err = suite.db.CommitValue(ds, b)
|
|
suite.NoError(err)
|
|
suite.True(ds.HeadValue().Equals(b))
|
|
suite.Equal(uint64(2), ds.HeadRef().Height())
|
|
|
|
// |a| <- |b|
|
|
// \----|c|
|
|
// Should be disallowed.
|
|
c := types.String("c")
|
|
ds, err = suite.db.Commit(ds, c, newOpts(suite.db, aCommitRef))
|
|
suite.Error(err)
|
|
suite.True(ds.HeadValue().Equals(b))
|
|
|
|
// |a| <- |b| <- |d|
|
|
d := types.String("d")
|
|
ds, err = suite.db.CommitValue(ds, d)
|
|
suite.NoError(err)
|
|
suite.True(ds.HeadValue().Equals(d))
|
|
suite.Equal(uint64(3), ds.HeadRef().Height())
|
|
|
|
// Attempt to recommit |b| with |a| as parent.
|
|
// Should be disallowed.
|
|
ds, err = suite.db.Commit(ds, b, newOpts(suite.db, aCommitRef))
|
|
suite.Error(err)
|
|
suite.True(ds.HeadValue().Equals(d))
|
|
|
|
// Add a commit to a different datasetId
|
|
_, err = suite.db.CommitValue(suite.db.GetDataset("otherDS"), a)
|
|
suite.NoError(err)
|
|
|
|
// Get a fresh database, and verify that both datasets are present
|
|
newDB := suite.makeDb(suite.storage.NewView())
|
|
defer newDB.Close()
|
|
datasets2 := newDB.Datasets()
|
|
suite.Equal(uint64(2), datasets2.Len())
|
|
}
|
|
|
|
func (suite *DatabaseSuite) TestDatasetsMapType() {
|
|
dsID1, dsID2 := "ds1", "ds2"
|
|
|
|
datasets := suite.db.Datasets()
|
|
ds, err := suite.db.CommitValue(suite.db.GetDataset(dsID1), types.String("a"))
|
|
suite.NoError(err)
|
|
suite.NotPanics(func() { assertMapOfStringToRefOfCommit(suite.db.Datasets(), datasets, suite.db) })
|
|
|
|
datasets = suite.db.Datasets()
|
|
_, err = suite.db.CommitValue(suite.db.GetDataset(dsID2), types.Number(42))
|
|
suite.NoError(err)
|
|
suite.NotPanics(func() { assertMapOfStringToRefOfCommit(suite.db.Datasets(), datasets, suite.db) })
|
|
|
|
datasets = suite.db.Datasets()
|
|
_, err = suite.db.Delete(ds)
|
|
suite.NoError(err)
|
|
suite.NotPanics(func() { assertMapOfStringToRefOfCommit(suite.db.Datasets(), datasets, suite.db) })
|
|
}
|
|
|
|
func newOpts(vrw types.ValueReadWriter, parents ...types.Value) CommitOptions {
|
|
return CommitOptions{Parents: types.NewSet(vrw, parents...)}
|
|
}
|
|
|
|
func (suite *DatabaseSuite) TestDatabaseDuplicateCommit() {
|
|
datasetID := "ds1"
|
|
ds := suite.db.GetDataset(datasetID)
|
|
datasets := suite.db.Datasets()
|
|
suite.Zero(datasets.Len())
|
|
|
|
v := types.String("Hello")
|
|
_, err := suite.db.CommitValue(ds, v)
|
|
suite.NoError(err)
|
|
|
|
_, err = suite.db.CommitValue(ds, v)
|
|
suite.IsType(ErrMergeNeeded, err)
|
|
}
|
|
|
|
func (suite *DatabaseSuite) TestDatabaseCommitMerge() {
|
|
datasetID1, datasetID2 := "ds1", "ds2"
|
|
ds1, ds2 := suite.db.GetDataset(datasetID1), suite.db.GetDataset(datasetID2)
|
|
|
|
var err error
|
|
v := types.NewMap(suite.db, types.String("Hello"), types.Number(42))
|
|
ds1, err = suite.db.CommitValue(ds1, v)
|
|
ds1First := ds1
|
|
suite.NoError(err)
|
|
ds1, err = suite.db.CommitValue(ds1, v.Edit().Set(types.String("Friends"), types.Bool(true)).Map())
|
|
suite.NoError(err)
|
|
|
|
ds2, err = suite.db.CommitValue(ds2, types.String("Goodbye"))
|
|
suite.NoError(err)
|
|
|
|
// No common ancestor
|
|
_, err = suite.db.Commit(ds1, types.Number(47), newOpts(suite.db, ds2.HeadRef()))
|
|
suite.IsType(ErrMergeNeeded, err, "%s", err)
|
|
|
|
// Unmergeable
|
|
_, err = suite.db.Commit(ds1, types.Number(47), newOptsWithMerge(suite.db, merge.None, ds1First.HeadRef()))
|
|
suite.IsType(&merge.ErrMergeConflict{}, err, "%s", err)
|
|
|
|
// Merge policies
|
|
newV := v.Edit().Set(types.String("Friends"), types.Bool(false)).Map()
|
|
_, err = suite.db.Commit(ds1, newV, newOptsWithMerge(suite.db, merge.None, ds1First.HeadRef()))
|
|
suite.IsType(&merge.ErrMergeConflict{}, err, "%s", err)
|
|
|
|
theirs, err := suite.db.Commit(ds1, newV, newOptsWithMerge(suite.db, merge.Theirs, ds1First.HeadRef()))
|
|
suite.NoError(err)
|
|
suite.True(types.Bool(true).Equals(theirs.HeadValue().(types.Map).Get(types.String("Friends"))))
|
|
|
|
newV = v.Edit().Set(types.String("Friends"), types.Number(47)).Map()
|
|
ours, err := suite.db.Commit(ds1First, newV, newOptsWithMerge(suite.db, merge.Ours, ds1First.HeadRef()))
|
|
suite.NoError(err)
|
|
suite.True(types.Number(47).Equals(ours.HeadValue().(types.Map).Get(types.String("Friends"))))
|
|
}
|
|
|
|
func newOptsWithMerge(vrw types.ValueReadWriter, policy merge.ResolveFunc, parents ...types.Value) CommitOptions {
|
|
return CommitOptions{Parents: types.NewSet(vrw, parents...), Policy: merge.NewThreeWay(policy)}
|
|
}
|
|
|
|
func (suite *DatabaseSuite) TestDatabaseDelete() {
|
|
datasetID1, datasetID2 := "ds1", "ds2"
|
|
ds1, ds2 := suite.db.GetDataset(datasetID1), suite.db.GetDataset(datasetID2)
|
|
datasets := suite.db.Datasets()
|
|
suite.Zero(datasets.Len())
|
|
|
|
// ds1: |a|
|
|
var err error
|
|
a := types.String("a")
|
|
ds1, err = suite.db.CommitValue(ds1, a)
|
|
suite.NoError(err)
|
|
suite.True(ds1.HeadValue().Equals(a))
|
|
|
|
// ds1: |a|, ds2: |b|
|
|
b := types.String("b")
|
|
ds2, err = suite.db.CommitValue(ds2, b)
|
|
suite.NoError(err)
|
|
suite.True(ds2.HeadValue().Equals(b))
|
|
|
|
ds1, err = suite.db.Delete(ds1)
|
|
suite.NoError(err)
|
|
suite.True(suite.db.GetDataset(datasetID2).HeadValue().Equals(b))
|
|
_, present := suite.db.GetDataset(datasetID1).MaybeHead()
|
|
suite.False(present, "Dataset %s should not be present", datasetID1)
|
|
|
|
// Get a fresh database, and verify that only ds2 is present
|
|
newDB := suite.makeDb(suite.storage.NewView())
|
|
defer newDB.Close()
|
|
datasets = newDB.Datasets()
|
|
suite.Equal(uint64(1), datasets.Len())
|
|
_, present = newDB.GetDataset(datasetID2).MaybeHeadRef()
|
|
suite.True(present, "Dataset %s should be present", datasetID2)
|
|
}
|
|
|
|
type waitDuringUpdateRootChunkStore struct {
|
|
chunks.ChunkStore
|
|
preUpdateRootHook func()
|
|
}
|
|
|
|
func (w *waitDuringUpdateRootChunkStore) Commit(current, last hash.Hash) bool {
|
|
if w.preUpdateRootHook != nil {
|
|
w.preUpdateRootHook()
|
|
}
|
|
return w.ChunkStore.Commit(current, last)
|
|
}
|
|
|
|
func (suite *DatabaseSuite) TestCommitWithConcurrentChunkStoreUse() {
|
|
datasetID := "ds1"
|
|
ds1 := suite.db.GetDataset(datasetID)
|
|
var err error
|
|
|
|
// Setup:
|
|
// ds1: |a| <- |b|
|
|
ds1, err = suite.db.CommitValue(ds1, types.String("a"))
|
|
b := types.String("b")
|
|
ds1, err = suite.db.CommitValue(ds1, b)
|
|
suite.NoError(err)
|
|
suite.True(ds1.HeadValue().Equals(b))
|
|
|
|
// Craft DB that will allow me to move the backing ChunkStore while suite.db isn't looking
|
|
interloper := suite.makeDb(suite.storage.NewView())
|
|
defer interloper.Close()
|
|
|
|
// Change ds2 behind suite.db's back. This shouldn't block changes to ds1 via suite.db below.
|
|
// ds1: |a| <- |b|
|
|
// ds2: |stuff|
|
|
stf := types.String("stuff")
|
|
ds2, concErr := interloper.CommitValue(interloper.GetDataset("ds2"), stf)
|
|
suite.NoError(concErr)
|
|
suite.True(ds2.HeadValue().Equals(stf))
|
|
|
|
// Change ds1 via suite.db, which should proceed without a problem
|
|
c := types.String("c")
|
|
ds1, err = suite.db.CommitValue(ds1, c)
|
|
suite.NoError(err)
|
|
suite.True(ds1.HeadValue().Equals(c))
|
|
|
|
// Change ds1 behind suite.db's back. Will block changes to ds1 below.
|
|
// ds1: |a| <- |b| <- |c| <- |e|
|
|
e := types.String("e")
|
|
interloper.Rebase()
|
|
iDS, concErr := interloper.CommitValue(interloper.GetDataset("ds1"), e)
|
|
suite.NoError(concErr)
|
|
suite.True(iDS.HeadValue().Equals(e))
|
|
|
|
// Attempted Concurrent change, which should fail due to the above
|
|
nope := types.String("nope")
|
|
ds1, err = suite.db.CommitValue(ds1, nope)
|
|
suite.Error(err)
|
|
v := ds1.HeadValue()
|
|
suite.True(v.Equals(e), "%s", v.(types.String))
|
|
}
|
|
|
|
func (suite *DatabaseSuite) TestDeleteWithConcurrentChunkStoreUse() {
|
|
datasetID := "ds1"
|
|
ds1 := suite.db.GetDataset(datasetID)
|
|
var err error
|
|
|
|
// Setup:
|
|
// ds1: |a| <- |b|
|
|
ds1, err = suite.db.CommitValue(ds1, types.String("a"))
|
|
b := types.String("b")
|
|
ds1, err = suite.db.CommitValue(ds1, b)
|
|
suite.NoError(err)
|
|
suite.True(ds1.HeadValue().Equals(b))
|
|
|
|
// Craft DB that will allow me to move the backing ChunkStore while suite.db isn't looking
|
|
interloper := suite.makeDb(suite.storage.NewView())
|
|
defer interloper.Close()
|
|
|
|
// Concurrent change, to move root out from under my feet:
|
|
// ds1: |a| <- |b| <- |e|
|
|
e := types.String("e")
|
|
iDS, concErr := interloper.CommitValue(interloper.GetDataset(datasetID), e)
|
|
suite.NoError(concErr)
|
|
suite.True(iDS.HeadValue().Equals(e))
|
|
|
|
// Attempt to delete ds1 via suite.db, which should fail due to the above
|
|
ds1, err = suite.db.Delete(ds1)
|
|
suite.Error(err)
|
|
suite.True(ds1.HeadValue().Equals(e))
|
|
|
|
// Concurrent change, but to some other dataset. This shouldn't stop changes to ds1.
|
|
// ds1: |a| <- |b| <- |e|
|
|
// ds2: |stuff|
|
|
stf := types.String("stuff")
|
|
iDS, concErr = interloper.CommitValue(suite.db.GetDataset("other"), stf)
|
|
suite.NoError(concErr)
|
|
suite.True(iDS.HeadValue().Equals(stf))
|
|
|
|
// Attempted concurrent delete, which should proceed without a problem
|
|
ds1, err = suite.db.Delete(ds1)
|
|
suite.NoError(err)
|
|
_, present := ds1.MaybeHeadRef()
|
|
suite.False(present, "Dataset %s should not be present", datasetID)
|
|
}
|
|
|
|
func (suite *DatabaseSuite) TestSetHead() {
|
|
var err error
|
|
datasetID := "ds1"
|
|
|
|
// |a| <- |b|
|
|
ds := suite.db.GetDataset(datasetID)
|
|
a := types.String("a")
|
|
ds, err = suite.db.CommitValue(ds, a)
|
|
suite.NoError(err)
|
|
aCommitRef := ds.HeadRef() // To use in non-FF SetHead() below.
|
|
|
|
b := types.String("b")
|
|
ds, err = suite.db.CommitValue(ds, b)
|
|
suite.NoError(err)
|
|
suite.True(ds.HeadValue().Equals(b))
|
|
bCommitRef := ds.HeadRef() // To use in FF SetHead() below.
|
|
|
|
ds, err = suite.db.SetHead(ds, aCommitRef)
|
|
suite.NoError(err)
|
|
suite.True(ds.HeadValue().Equals(a))
|
|
|
|
ds, err = suite.db.SetHead(ds, bCommitRef)
|
|
suite.NoError(err)
|
|
suite.True(ds.HeadValue().Equals(b))
|
|
}
|
|
|
|
func (suite *DatabaseSuite) TestFastForward() {
|
|
var err error
|
|
datasetID := "ds1"
|
|
|
|
// |a| <- |b| <- |c|
|
|
ds := suite.db.GetDataset(datasetID)
|
|
a := types.String("a")
|
|
ds, err = suite.db.CommitValue(ds, a)
|
|
suite.NoError(err)
|
|
aCommitRef := ds.HeadRef() // To use in non-FF cases below.
|
|
|
|
b := types.String("b")
|
|
ds, err = suite.db.CommitValue(ds, b)
|
|
suite.NoError(err)
|
|
suite.True(ds.HeadValue().Equals(b))
|
|
|
|
c := types.String("c")
|
|
ds, err = suite.db.CommitValue(ds, c)
|
|
suite.NoError(err)
|
|
suite.True(ds.HeadValue().Equals(c))
|
|
cCommitRef := ds.HeadRef() // To use in FastForward() below.
|
|
|
|
// FastForward should disallow this, as |a| is not a descendant of |c|
|
|
ds, err = suite.db.FastForward(ds, aCommitRef)
|
|
suite.Error(err)
|
|
suite.True(ds.HeadValue().Equals(c))
|
|
|
|
// Move Head back to something earlier in the lineage, so we can test FastForward
|
|
ds, err = suite.db.SetHead(ds, aCommitRef)
|
|
suite.NoError(err)
|
|
suite.True(ds.HeadValue().Equals(a))
|
|
|
|
// This should succeed, because while |a| is not a direct parent of |c|, it is an ancestor.
|
|
ds, err = suite.db.FastForward(ds, cCommitRef)
|
|
suite.NoError(err)
|
|
suite.True(ds.HeadValue().Equals(c))
|
|
}
|
|
|
|
func (suite *DatabaseSuite) TestDatabaseHeightOfRefs() {
|
|
r1 := suite.db.WriteValue(types.String("hello"))
|
|
suite.Equal(uint64(1), r1.Height())
|
|
|
|
r2 := suite.db.WriteValue(r1)
|
|
suite.Equal(uint64(2), r2.Height())
|
|
suite.Equal(uint64(3), suite.db.WriteValue(r2).Height())
|
|
}
|
|
|
|
func (suite *DatabaseSuite) TestDatabaseHeightOfCollections() {
|
|
setOfStringType := types.MakeSetType(types.StringType)
|
|
setOfRefOfStringType := types.MakeSetType(types.MakeRefType(types.StringType))
|
|
|
|
// Set<String>
|
|
v1 := types.String("hello")
|
|
v2 := types.String("world")
|
|
s1 := types.NewSet(suite.db, v1, v2)
|
|
suite.Equal(uint64(1), suite.db.WriteValue(s1).Height())
|
|
|
|
// Set<Ref<String>>
|
|
s2 := types.NewSet(suite.db, suite.db.WriteValue(v1), suite.db.WriteValue(v2))
|
|
suite.Equal(uint64(2), suite.db.WriteValue(s2).Height())
|
|
|
|
// List<Set<String>>
|
|
v3 := types.String("foo")
|
|
v4 := types.String("bar")
|
|
s3 := types.NewSet(suite.db, v3, v4)
|
|
l1 := types.NewList(suite.db, s1, s3)
|
|
suite.Equal(uint64(1), suite.db.WriteValue(l1).Height())
|
|
|
|
// List<Ref<Set<String>>
|
|
l2 := types.NewList(suite.db, suite.db.WriteValue(s1), suite.db.WriteValue(s3))
|
|
suite.Equal(uint64(2), suite.db.WriteValue(l2).Height())
|
|
|
|
// List<Ref<Set<Ref<String>>>
|
|
s4 := types.NewSet(suite.db, suite.db.WriteValue(v3), suite.db.WriteValue(v4))
|
|
l3 := types.NewList(suite.db, suite.db.WriteValue(s4))
|
|
suite.Equal(uint64(3), suite.db.WriteValue(l3).Height())
|
|
|
|
// List<Set<String> | RefValue<Set<String>>>
|
|
l4 := types.NewList(suite.db, s1, suite.db.WriteValue(s3))
|
|
suite.Equal(uint64(2), suite.db.WriteValue(l4).Height())
|
|
l5 := types.NewList(suite.db, suite.db.WriteValue(s1), s3)
|
|
suite.Equal(uint64(2), suite.db.WriteValue(l5).Height())
|
|
|
|
// Familiar with the "New Jersey Turnpike" drink? Here's the noms version of that...
|
|
everything := []types.Value{v1, v2, s1, s2, v3, v4, s3, l1, l2, s4, l3, l4, l5}
|
|
andMore := make([]types.Value, 0, len(everything)*3+2)
|
|
for _, v := range everything {
|
|
andMore = append(andMore, v, types.TypeOf(v), suite.db.WriteValue(v))
|
|
}
|
|
andMore = append(andMore, setOfStringType, setOfRefOfStringType)
|
|
|
|
suite.db.WriteValue(types.NewList(suite.db, andMore...))
|
|
}
|
|
|
|
func (suite *DatabaseSuite) TestMetaOption() {
|
|
ds := suite.db.GetDataset("ds1")
|
|
m := types.NewStruct("M", types.StructData{
|
|
"author": types.String("arv"),
|
|
})
|
|
|
|
ds, err := suite.db.Commit(ds, types.String("a"), CommitOptions{Meta: m})
|
|
suite.NoError(err)
|
|
c := ds.Head()
|
|
suite.Equal(types.String("arv"), c.Get("meta").(types.Struct).Get("author"))
|
|
}
|