Files
dolt/go/store/datas/database_test.go

658 lines
23 KiB
Go

// Copyright 2019 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// This file incorporates work covered by the following copyright and
// permission notice:
//
// 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 (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/dolthub/dolt/go/store/chunks"
"github.com/dolthub/dolt/go/store/d"
"github.com/dolthub/dolt/go/store/hash"
"github.com/dolthub/dolt/go/store/types"
)
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.NewViewWithDefaultFormat()).(*database)
defer db.Close()
b := types.Bool(true)
r, err := db.WriteValue(context.Background(), b)
assert.NoError(t, err)
_, err = db.validateRefAsCommit(context.Background(), r)
assert.Error(t, err)
_, err = db.validateRefAsCommit(context.Background(), mustRef(types.NewRef(b, db.Format())))
}
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.NewViewWithDefaultFormat()).(*database)
}
type RemoteDatabaseSuite struct {
DatabaseSuite
}
func (suite *RemoteDatabaseSuite) SetupTest() {
suite.storage = &chunks.TestStorage{}
suite.makeDb = func(cs chunks.ChunkStore) Database {
return NewDatabase(cs)
}
suite.db = suite.makeDb(suite.storage.NewViewWithDefaultFormat()).(*database)
}
func (suite *DatabaseSuite) TearDownTest() {
suite.db.Close()
}
func (suite *RemoteDatabaseSuite) TestWriteRefToNonexistentValue() {
ds, err := suite.db.GetDataset(context.Background(), "foo")
suite.NoError(err)
r, err := types.NewRef(types.Bool(true), suite.db.Format())
suite.NoError(err)
suite.Panics(func() { CommitValue(context.Background(), suite.db, ds, r) })
}
func (suite *DatabaseSuite) TestTolerateUngettableRefs() {
suite.Nil(suite.db.ReadValue(context.Background(), hash.Hash{}))
}
func (suite *DatabaseSuite) TestCompletenessCheck() {
datasetID := "ds1"
ds1, err := suite.db.GetDataset(context.Background(), datasetID)
suite.NoError(err)
s, err := types.NewSet(context.Background(), suite.db)
suite.NoError(err)
se := s.Edit()
for i := 0; i < 100; i++ {
ref, err := suite.db.WriteValue(context.Background(), types.Float(100))
suite.NoError(err)
se.Insert(ref)
}
s, err = se.Set(context.Background())
suite.NoError(err)
ds1, err = CommitValue(context.Background(), suite.db, ds1, s)
suite.NoError(err)
s = mustHeadValue(ds1).(types.Set)
ref, err := types.NewRef(types.Float(1000), suite.db.Format())
suite.NoError(err)
se, err = s.Edit().Insert(ref)
suite.NoError(err)
s, err = se.Set(context.Background()) // danging ref
suite.NoError(err)
suite.Panics(func() {
ds1, err = CommitValue(context.Background(), suite.db, ds1, s)
})
}
func (suite *DatabaseSuite) TestRebase() {
datasetID := "ds1"
ds1, err := suite.db.GetDataset(context.Background(), datasetID)
suite.NoError(err)
// Setup:
// ds1: |a| <- |b|
ds1, _ = CommitValue(context.Background(), suite.db, ds1, types.String("a"))
b := types.String("b")
ds1, err = CommitValue(context.Background(), suite.db, ds1, b)
suite.NoError(err)
suite.True(mustHeadValue(ds1).Equals(b))
interloper := suite.makeDb(suite.storage.NewViewWithDefaultFormat())
defer interloper.Close()
// Concurrent change, to move root out from under my feet:
// ds1: |a| <- |b| <- |e|
e := types.String("e")
ds, err := interloper.GetDataset(context.Background(), datasetID)
suite.NoError(err)
iDS, concErr := CommitValue(context.Background(), interloper, ds, e)
suite.NoError(concErr)
suite.True(mustHeadValue(iDS).Equals(e))
// suite.ds shouldn't see the above change yet
ds, err = suite.db.GetDataset(context.Background(), datasetID)
suite.NoError(err)
suite.True(mustHeadValue(ds).Equals(b))
err = ChunkStoreFromDatabase(suite.db).Rebase(context.Background())
suite.NoError(err)
ds, err = suite.db.GetDataset(context.Background(), datasetID)
suite.NoError(err)
suite.True(mustHeadValue(ds).Equals(e))
}
func (suite *DatabaseSuite) TestCommitProperlyTracksRoot() {
id1, id2 := "testdataset", "othertestdataset"
db1 := suite.makeDb(suite.storage.NewViewWithDefaultFormat())
defer db1.Close()
ds1, err := db1.GetDataset(context.Background(), id1)
suite.NoError(err)
ds1HeadVal := types.String("Commit value for " + id1)
ds1, err = CommitValue(context.Background(), db1, ds1, ds1HeadVal)
suite.NoError(err)
db2 := suite.makeDb(suite.storage.NewViewWithDefaultFormat())
defer db2.Close()
ds2, err := db2.GetDataset(context.Background(), id2)
suite.NoError(err)
ds2HeadVal := types.String("Commit value for " + id2)
ds2, err = CommitValue(context.Background(), db2, ds2, ds2HeadVal)
suite.NoError(err)
suite.EqualValues(ds1HeadVal, mustHeadValue(ds1))
suite.EqualValues(ds2HeadVal, mustHeadValue(ds2))
suite.False(mustHeadValue(ds2).Equals(ds1HeadVal))
suite.False(mustHeadValue(ds1).Equals(ds2HeadVal))
}
func (suite *DatabaseSuite) TestDatabaseCommit() {
datasetID := "ds1"
datasets, err := suite.db.Datasets(context.Background())
suite.NoError(err)
suite.Zero(datasets.Len())
// |a|
ds, err := suite.db.GetDataset(context.Background(), datasetID)
suite.NoError(err)
a := types.String("a")
ds2, err := CommitValue(context.Background(), suite.db, ds, a)
suite.NoError(err)
// ds2 matches the Datasets Map in suite.db
suiteDS, err := suite.db.GetDataset(context.Background(), datasetID)
suite.NoError(err)
headAddr := mustHeadAddr(suiteDS)
suite.True(mustHeadAddr(ds2) == headAddr)
// ds2 has |a| at its head
h, ok, err := ds2.MaybeHeadValue()
suite.NoError(err)
suite.True(ok)
suite.True(h.Equals(a))
comm, err := commitFromValue(suite.db.Format(), mustHead(ds2))
suite.NoError(err)
suite.Equal(uint64(1), comm.Height())
ds = ds2
aCommitAddr := mustHeadAddr(ds) // to be used to test disallowing of non-fastforward commits below
// |a| <- |b|
b := types.String("b")
ds, err = CommitValue(context.Background(), suite.db, ds, b)
suite.NoError(err)
suite.True(mustHeadValue(ds).Equals(b))
comm, err = commitFromValue(suite.db.Format(), mustHead(ds))
suite.NoError(err)
suite.Equal(uint64(2), comm.Height())
// |a| <- |b|
// \----|c|
// Should be disallowed.
c := types.String("c")
_, err = suite.db.Commit(context.Background(), ds, c, newOpts(suite.db, aCommitAddr))
suite.Error(err)
suite.True(mustHeadValue(ds).Equals(b))
// |a| <- |b| <- |d|
d := types.String("d")
ds, err = CommitValue(context.Background(), suite.db, ds, d)
suite.NoError(err)
suite.True(mustHeadValue(ds).Equals(d))
comm, err = commitFromValue(suite.db.Format(), mustHead(ds))
suite.NoError(err)
suite.Equal(uint64(3), comm.Height())
// Attempt to recommit |b| with |a| as parent.
// Should be disallowed.
_, err = suite.db.Commit(context.Background(), ds, b, newOpts(suite.db, aCommitAddr))
suite.Error(err)
suite.True(mustHeadValue(ds).Equals(d))
// Add a commit to a different datasetId
ds, err = suite.db.GetDataset(context.Background(), "otherDS")
suite.NoError(err)
_, err = CommitValue(context.Background(), suite.db, ds, a)
suite.NoError(err)
// Get a fresh database, and verify that both datasets are present
newDB := suite.makeDb(suite.storage.NewViewWithDefaultFormat())
defer newDB.Close()
datasets2, err := newDB.Datasets(context.Background())
suite.NoError(err)
l, err := datasets2.Len()
suite.NoError(err)
suite.Equal(uint64(2), l)
}
func mustNomsMap(t *testing.T, dsm DatasetsMap) types.Map {
m, ok := dsm.(nomsDatasetsMap)
require.True(t, ok)
return m.m
}
func (suite *DatabaseSuite) TestDatasetsMapType() {
if suite.db.Format().UsesFlatbuffers() {
suite.T().Skip()
}
dsID1, dsID2 := "ds1", "ds2"
datasets, err := suite.db.Datasets(context.Background())
suite.NoError(err)
ds, err := suite.db.GetDataset(context.Background(), dsID1)
suite.NoError(err)
ds, err = CommitValue(context.Background(), suite.db, ds, types.String("a"))
suite.NoError(err)
dss, err := suite.db.Datasets(context.Background())
suite.NoError(err)
assertMapOfStringToRefOfCommit(context.Background(), mustNomsMap(suite.T(), dss), mustNomsMap(suite.T(), datasets), suite.db)
datasets, err = suite.db.Datasets(context.Background())
suite.NoError(err)
ds2, err := suite.db.GetDataset(context.Background(), dsID2)
suite.NoError(err)
_, err = CommitValue(context.Background(), suite.db, ds2, types.Float(42))
suite.NoError(err)
dss, err = suite.db.Datasets(context.Background())
suite.NoError(err)
assertMapOfStringToRefOfCommit(context.Background(), mustNomsMap(suite.T(), dss), mustNomsMap(suite.T(), datasets), suite.db)
datasets, err = suite.db.Datasets(context.Background())
suite.NoError(err)
_, err = suite.db.Delete(context.Background(), ds)
suite.NoError(err)
dss, err = suite.db.Datasets(context.Background())
suite.NoError(err)
assertMapOfStringToRefOfCommit(context.Background(), mustNomsMap(suite.T(), dss), mustNomsMap(suite.T(), datasets), suite.db)
}
func assertMapOfStringToRefOfCommit(ctx context.Context, proposed, datasets types.Map, vr types.ValueReader) {
var derr error
changes := make(chan types.ValueChanged)
go func() {
defer close(changes)
derr = proposed.Diff(ctx, datasets, changes)
}()
for change := range changes {
switch change.ChangeType {
case types.DiffChangeAdded, types.DiffChangeModified:
// Since this is a Map Diff, change.V is the key at which a change was detected.
// Go get the Value there, which should be a Ref<Value>, deref it, and then ensure the target is a Commit.
val := change.NewValue
ref, ok := val.(types.Ref)
if !ok {
d.Panic("Root of a Database must be a Map<String, Ref<Commit>>, but key %s maps to a %s", change.Key.(types.String), mustString(mustType(types.TypeOf(val)).Describe(ctx)))
}
if targetValue, err := ref.TargetValue(ctx, vr); err != nil {
d.PanicIfError(err)
} else if is, err := IsCommit(targetValue); err != nil {
d.PanicIfError(err)
} else if !is {
d.Panic("Root of a Database must be a Map<String, Ref<Commit>>, but the ref at key %s points to a %s", change.Key.(types.String), mustString(mustType(types.TypeOf(targetValue)).Describe(ctx)))
}
}
}
d.PanicIfError(derr)
}
func newOpts(vrw types.ValueReadWriter, parent hash.Hash) CommitOptions {
return CommitOptions{Parents: []hash.Hash{parent}}
}
func (suite *DatabaseSuite) TestDatabaseDuplicateCommit() {
datasetID := "ds1"
ds, err := suite.db.GetDataset(context.Background(), datasetID)
suite.NoError(err)
datasets, err := suite.db.Datasets(context.Background())
suite.NoError(err)
suite.Zero(datasets.Len())
v := types.String("Hello")
_, err = CommitValue(context.Background(), suite.db, ds, v)
suite.NoError(err)
_, err = CommitValue(context.Background(), suite.db, ds, v)
suite.IsType(ErrMergeNeeded, err)
}
func (suite *DatabaseSuite) TestDatabaseDelete() {
datasetID1, datasetID2 := "ds1", "ds2"
ds1, err := suite.db.GetDataset(context.Background(), datasetID1)
suite.NoError(err)
ds2, err := suite.db.GetDataset(context.Background(), datasetID2)
suite.NoError(err)
datasets, err := suite.db.Datasets(context.Background())
suite.NoError(err)
suite.Zero(datasets.Len())
// ds1: |a|
a := types.String("a")
ds1, err = CommitValue(context.Background(), suite.db, ds1, a)
suite.NoError(err)
suite.True(mustHeadValue(ds1).Equals(a))
// ds1: |a|, ds2: |b|
b := types.String("b")
ds2, err = CommitValue(context.Background(), suite.db, ds2, b)
suite.NoError(err)
suite.True(mustHeadValue(ds2).Equals(b))
ds1, err = suite.db.Delete(context.Background(), ds1)
suite.NoError(err)
currDS2, err := suite.db.GetDataset(context.Background(), datasetID2)
suite.NoError(err)
suite.True(mustHeadValue(currDS2).Equals(b))
currDS1, err := suite.db.GetDataset(context.Background(), datasetID1)
suite.NoError(err)
_, present := currDS1.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.NewViewWithDefaultFormat())
defer newDB.Close()
datasets, err = newDB.Datasets(context.Background())
suite.NoError(err)
l, err := datasets.Len()
suite.NoError(err)
suite.Equal(uint64(1), l)
newDS, err := newDB.GetDataset(context.Background(), datasetID2)
suite.NoError(err)
present = newDS.HasHead()
suite.True(present, "Dataset %s should be present", datasetID2)
}
func (suite *DatabaseSuite) TestCommitWithConcurrentChunkStoreUse() {
datasetID := "ds1"
ds1, err := suite.db.GetDataset(context.Background(), datasetID)
suite.NoError(err)
// Setup:
// ds1: |a| <- |b|
ds1, _ = CommitValue(context.Background(), suite.db, ds1, types.String("a"))
b := types.String("b")
ds1, err = CommitValue(context.Background(), suite.db, ds1, b)
suite.NoError(err)
suite.True(mustHeadValue(ds1).Equals(b))
// Craft DB that will allow me to move the backing ChunkStore while suite.db isn't looking
interloperCS := suite.storage.NewViewWithDefaultFormat()
interloper := suite.makeDb(interloperCS)
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, err := interloper.GetDataset(context.Background(), "ds2")
suite.NoError(err)
ds2, concErr := CommitValue(context.Background(), interloper, ds2, stf)
suite.NoError(concErr)
suite.True(mustHeadValue(ds2).Equals(stf))
// Change ds1 via suite.db, which should proceed without a problem
c := types.String("c")
ds1, err = CommitValue(context.Background(), suite.db, ds1, c)
suite.NoError(err)
suite.True(mustHeadValue(ds1).Equals(c))
// Change ds1 behind suite.db's back. Will block changes to ds1 below.
// ds1: |a| <- |b| <- |c| <- |e|
e := types.String("e")
interloperCS.Rebase(context.Background())
iDS, err := interloper.GetDataset(context.Background(), "ds1")
suite.NoError(err)
iDS, concErr = CommitValue(context.Background(), interloper, iDS, e)
suite.NoError(concErr)
suite.True(mustHeadValue(iDS).Equals(e))
v := mustHeadValue(iDS)
suite.True(v.Equals(e), "%s", v.(types.String))
// Attempted Concurrent change, which should fail due to the above
nope := types.String("nope")
_, err = CommitValue(context.Background(), suite.db, ds1, nope)
suite.Error(err)
}
func (suite *DatabaseSuite) TestDeleteWithConcurrentChunkStoreUse() {
datasetID := "ds1"
ds1, err := suite.db.GetDataset(context.Background(), datasetID)
suite.Require().NoError(err)
// Setup:
// ds1: |a| <- |b|
ds1, _ = CommitValue(context.Background(), suite.db, ds1, types.String("a"))
b := types.String("b")
ds1, err = CommitValue(context.Background(), suite.db, ds1, b)
suite.Require().NoError(err)
suite.Require().True(mustHeadValue(ds1).Equals(b))
// Craft DB that will allow me to move the backing ChunkStore while suite.db isn't looking
interloper := suite.makeDb(suite.storage.NewViewWithDefaultFormat())
defer interloper.Close()
// Concurrent change, to move root out from under my feet:
// ds1: |a| <- |b| <- |e|
e := types.String("e")
iDS, err := interloper.GetDataset(context.Background(), datasetID)
suite.Require().NoError(err)
iDS, concErr := CommitValue(context.Background(), interloper, iDS, e)
suite.Require().NoError(concErr)
suite.Require().True(mustHeadValue(iDS).Equals(e))
// Attempt to delete ds1 via suite.db, which should fail due to the above
_, err = suite.db.Delete(context.Background(), ds1)
suite.Require().Error(err)
// Concurrent change, but to some other dataset. This shouldn't stop changes to ds1.
// ds1: |a| <- |b| <- |e|
// ds2: |stuff|
stf := types.String("stuff")
otherDS, err := suite.db.GetDataset(context.Background(), "other")
suite.Require().NoError(err)
iDS, concErr = CommitValue(context.Background(), suite.db, otherDS, stf)
suite.Require().NoError(concErr)
suite.Require().True(mustHeadValue(iDS).Equals(stf))
// Attempted concurrent delete, which should proceed without a problem
ds1, err = suite.db.Delete(context.Background(), ds1)
suite.Require().NoError(err)
present := ds1.HasHead()
suite.False(present, "Dataset %s should not be present", datasetID)
}
func (suite *DatabaseSuite) TestSetHead() {
var err error
datasetID := "ds1"
// |a| <- |b|
ds, err := suite.db.GetDataset(context.Background(), datasetID)
suite.NoError(err)
a := types.String("a")
ds, err = CommitValue(context.Background(), suite.db, ds, a)
suite.NoError(err)
aCommitAddr := mustHeadAddr(ds) // To use in non-FF SetHeadToCommit() below.
b := types.String("b")
ds, err = CommitValue(context.Background(), suite.db, ds, b)
suite.NoError(err)
suite.True(mustHeadValue(ds).Equals(b))
bCommitAddr := mustHeadAddr(ds) // To use in FF SetHeadToCommit() below.
ds, err = suite.db.SetHead(context.Background(), ds, aCommitAddr)
suite.NoError(err)
suite.True(mustHeadValue(ds).Equals(a))
ds, err = suite.db.SetHead(context.Background(), ds, bCommitAddr)
suite.NoError(err)
suite.True(mustHeadValue(ds).Equals(b))
}
func (suite *DatabaseSuite) TestFastForward() {
datasetID := "ds1"
// |a| <- |b| <- |c|
ds, err := suite.db.GetDataset(context.Background(), datasetID)
suite.NoError(err)
a := types.String("a")
ds, err = CommitValue(context.Background(), suite.db, ds, a)
suite.NoError(err)
aCommitAddr := mustHeadAddr(ds) // To use in non-FF cases below.
b := types.String("b")
ds, err = CommitValue(context.Background(), suite.db, ds, b)
suite.NoError(err)
suite.True(mustHeadValue(ds).Equals(b))
c := types.String("c")
ds, err = CommitValue(context.Background(), suite.db, ds, c)
suite.NoError(err)
suite.True(mustHeadValue(ds).Equals(c))
cCommitAddr := mustHeadAddr(ds) // To use in FastForward() below.
// FastForward should disallow this, as |a| is not a descendant of |c|
_, err = suite.db.FastForward(context.Background(), ds, aCommitAddr)
suite.Error(err)
// Move Head back to something earlier in the lineage, so we can test FastForward
ds, err = suite.db.SetHead(context.Background(), ds, aCommitAddr)
suite.NoError(err)
suite.True(mustHeadValue(ds).Equals(a))
// This should succeed, because while |a| is not a direct parent of |c|, it is an ancestor.
ds, err = suite.db.FastForward(context.Background(), ds, cCommitAddr)
suite.Require().NoError(err)
suite.True(mustHeadValue(ds).Equals(c))
}
func (suite *DatabaseSuite) TestDatabaseHeightOfRefs() {
r1, err := suite.db.WriteValue(context.Background(), types.String("hello"))
suite.NoError(err)
suite.Equal(uint64(1), r1.Height())
r2, err := suite.db.WriteValue(context.Background(), r1)
suite.NoError(err)
suite.Equal(uint64(2), r2.Height())
suite.Equal(uint64(3), mustRef(suite.db.WriteValue(context.Background(), r2)).Height())
}
func (suite *DatabaseSuite) TestDatabaseHeightOfCollections() {
setOfStringType, err := types.MakeSetType(types.PrimitiveTypeMap[types.StringKind])
suite.NoError(err)
setOfRefOfStringType, err := types.MakeSetType(mustType(types.MakeRefType(types.PrimitiveTypeMap[types.StringKind])))
suite.NoError(err)
// Set<String>
v1 := types.String("hello")
v2 := types.String("world")
s1, err := types.NewSet(context.Background(), suite.db, v1, v2)
suite.NoError(err)
ref, err := suite.db.WriteValue(context.Background(), s1)
suite.NoError(err)
suite.Equal(uint64(1), ref.Height())
// Set<Ref<String>>
s2, err := types.NewSet(context.Background(), suite.db, mustRef(suite.db.WriteValue(context.Background(), v1)), mustRef(suite.db.WriteValue(context.Background(), v2)))
suite.NoError(err)
suite.Equal(uint64(2), mustRef(suite.db.WriteValue(context.Background(), s2)).Height())
// List<Set<String>>
v3 := types.String("foo")
v4 := types.String("bar")
s3, err := types.NewSet(context.Background(), suite.db, v3, v4)
suite.NoError(err)
l1, err := types.NewList(context.Background(), suite.db, s1, s3)
suite.NoError(err)
suite.Equal(uint64(1), mustRef(suite.db.WriteValue(context.Background(), l1)).Height())
// List<Ref<Set<String>>
l2, err := types.NewList(context.Background(), suite.db, mustRef(suite.db.WriteValue(context.Background(), s1)), mustRef(suite.db.WriteValue(context.Background(), s3)))
suite.NoError(err)
suite.Equal(uint64(2), mustRef(suite.db.WriteValue(context.Background(), l2)).Height())
// List<Ref<Set<Ref<String>>>
s4, err := types.NewSet(context.Background(), suite.db, mustRef(suite.db.WriteValue(context.Background(), v3)), mustRef(suite.db.WriteValue(context.Background(), v4)))
suite.NoError(err)
l3, err := types.NewList(context.Background(), suite.db, mustRef(suite.db.WriteValue(context.Background(), s4)))
suite.NoError(err)
suite.Equal(uint64(3), mustRef(suite.db.WriteValue(context.Background(), l3)).Height())
// List<Set<String> | RefValue<Set<String>>>
l4, err := types.NewList(context.Background(), suite.db, s1, mustRef(suite.db.WriteValue(context.Background(), s3)))
suite.NoError(err)
suite.Equal(uint64(2), mustRef(suite.db.WriteValue(context.Background(), l4)).Height())
l5, err := types.NewList(context.Background(), suite.db, mustRef(suite.db.WriteValue(context.Background(), s1)), s3)
suite.NoError(err)
suite.Equal(uint64(2), mustRef(suite.db.WriteValue(context.Background(), 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, mustType(types.TypeOf(v)), mustRef(suite.db.WriteValue(context.Background(), v)))
}
andMore = append(andMore, setOfStringType, setOfRefOfStringType)
_, err = suite.db.WriteValue(context.Background(), mustValue(types.NewList(context.Background(), suite.db, andMore...)))
suite.NoError(err)
}
func (suite *DatabaseSuite) TestMetaOption() {
ctx := context.Background()
ds, err := suite.db.GetDataset(ctx, "ds1")
suite.NoError(err)
ds, err = suite.db.Commit(ctx, ds, types.String("a"), CommitOptions{Meta: &CommitMeta{Name: "arv"}})
suite.NoError(err)
meta, err := GetCommitMeta(ctx, mustHead(ds))
suite.Equal("arv", meta.Name)
}