github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/store/datas/database_test.go (about)

     1  // Copyright 2019 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // This file incorporates work covered by the following copyright and
    16  // permission notice:
    17  //
    18  // Copyright 2016 Attic Labs, Inc. All rights reserved.
    19  // Licensed under the Apache License, version 2.0:
    20  // http://www.apache.org/licenses/LICENSE-2.0
    21  
    22  package datas
    23  
    24  import (
    25  	"context"
    26  	"testing"
    27  
    28  	"github.com/stretchr/testify/assert"
    29  	"github.com/stretchr/testify/suite"
    30  
    31  	"github.com/dolthub/dolt/go/store/chunks"
    32  	"github.com/dolthub/dolt/go/store/d"
    33  	"github.com/dolthub/dolt/go/store/hash"
    34  	"github.com/dolthub/dolt/go/store/merge"
    35  	"github.com/dolthub/dolt/go/store/types"
    36  )
    37  
    38  func TestLocalDatabase(t *testing.T) {
    39  	suite.Run(t, &LocalDatabaseSuite{})
    40  }
    41  
    42  func TestRemoteDatabase(t *testing.T) {
    43  	suite.Run(t, &RemoteDatabaseSuite{})
    44  }
    45  
    46  func TestValidateRef(t *testing.T) {
    47  	st := &chunks.TestStorage{}
    48  	db := NewDatabase(st.NewView()).(*database)
    49  	defer db.Close()
    50  	b := types.Bool(true)
    51  	r, err := db.WriteValue(context.Background(), b)
    52  	assert.NoError(t, err)
    53  
    54  	assert.Panics(t, func() { db.validateRefAsCommit(context.Background(), r) })
    55  	assert.Panics(t, func() { db.validateRefAsCommit(context.Background(), mustRef(types.NewRef(b, types.Format_7_18))) })
    56  }
    57  
    58  type DatabaseSuite struct {
    59  	suite.Suite
    60  	storage *chunks.TestStorage
    61  	db      Database
    62  	makeDb  func(chunks.ChunkStore) Database
    63  }
    64  
    65  type LocalDatabaseSuite struct {
    66  	DatabaseSuite
    67  }
    68  
    69  func (suite *LocalDatabaseSuite) SetupTest() {
    70  	suite.storage = &chunks.TestStorage{}
    71  	suite.makeDb = NewDatabase
    72  	suite.db = suite.makeDb(suite.storage.NewView())
    73  }
    74  
    75  type RemoteDatabaseSuite struct {
    76  	DatabaseSuite
    77  }
    78  
    79  func (suite *RemoteDatabaseSuite) SetupTest() {
    80  	suite.storage = &chunks.TestStorage{}
    81  	suite.makeDb = func(cs chunks.ChunkStore) Database {
    82  		return NewDatabase(cs)
    83  	}
    84  	suite.db = suite.makeDb(suite.storage.NewView())
    85  }
    86  
    87  func (suite *DatabaseSuite) TearDownTest() {
    88  	suite.db.Close()
    89  }
    90  
    91  func (suite *RemoteDatabaseSuite) TestWriteRefToNonexistentValue() {
    92  	ds, err := suite.db.GetDataset(context.Background(), "foo")
    93  	suite.NoError(err)
    94  	r, err := types.NewRef(types.Bool(true), types.Format_7_18)
    95  	suite.NoError(err)
    96  	suite.Panics(func() { suite.db.CommitValue(context.Background(), ds, r) })
    97  }
    98  
    99  func (suite *DatabaseSuite) TestTolerateUngettableRefs() {
   100  	suite.Nil(suite.db.ReadValue(context.Background(), hash.Hash{}))
   101  }
   102  
   103  func (suite *DatabaseSuite) TestCompletenessCheck() {
   104  	datasetID := "ds1"
   105  	ds1, err := suite.db.GetDataset(context.Background(), datasetID)
   106  	suite.NoError(err)
   107  
   108  	s, err := types.NewSet(context.Background(), suite.db)
   109  	suite.NoError(err)
   110  	se := s.Edit()
   111  	for i := 0; i < 100; i++ {
   112  		ref, err := suite.db.WriteValue(context.Background(), types.Float(100))
   113  		suite.NoError(err)
   114  		se.Insert(ref)
   115  	}
   116  	s, err = se.Set(context.Background())
   117  	suite.NoError(err)
   118  
   119  	ds1, err = suite.db.CommitValue(context.Background(), ds1, s)
   120  	suite.NoError(err)
   121  
   122  	s = mustHeadValue(ds1).(types.Set)
   123  	ref, err := types.NewRef(types.Float(1000), types.Format_7_18)
   124  	suite.NoError(err)
   125  	se, err = s.Edit().Insert(ref)
   126  	suite.NoError(err)
   127  	s, err = se.Set(context.Background()) // danging ref
   128  	suite.NoError(err)
   129  	suite.Panics(func() {
   130  		ds1, err = suite.db.CommitValue(context.Background(), ds1, s)
   131  	})
   132  }
   133  
   134  func (suite *DatabaseSuite) TestRebase() {
   135  	datasetID := "ds1"
   136  	ds1, err := suite.db.GetDataset(context.Background(), datasetID)
   137  	suite.NoError(err)
   138  
   139  	// Setup:
   140  	// ds1: |a| <- |b|
   141  	ds1, _ = suite.db.CommitValue(context.Background(), ds1, types.String("a"))
   142  	b := types.String("b")
   143  	ds1, err = suite.db.CommitValue(context.Background(), ds1, b)
   144  	suite.NoError(err)
   145  	suite.True(mustHeadValue(ds1).Equals(b))
   146  
   147  	interloper := suite.makeDb(suite.storage.NewView())
   148  	defer interloper.Close()
   149  
   150  	// Concurrent change, to move root out from under my feet:
   151  	// ds1: |a| <- |b| <- |e|
   152  	e := types.String("e")
   153  	ds, err := interloper.GetDataset(context.Background(), datasetID)
   154  	suite.NoError(err)
   155  	iDS, concErr := interloper.CommitValue(context.Background(), ds, e)
   156  	suite.NoError(concErr)
   157  	suite.True(mustHeadValue(iDS).Equals(e))
   158  
   159  	// suite.ds shouldn't see the above change yet
   160  	ds, err = suite.db.GetDataset(context.Background(), datasetID)
   161  	suite.NoError(err)
   162  	suite.True(mustHeadValue(ds).Equals(b))
   163  
   164  	err = suite.db.Rebase(context.Background())
   165  	suite.NoError(err)
   166  	ds, err = suite.db.GetDataset(context.Background(), datasetID)
   167  	suite.NoError(err)
   168  	suite.True(mustHeadValue(ds).Equals(e))
   169  
   170  	cs := suite.storage.NewView()
   171  	noChangeDB := suite.makeDb(cs)
   172  	_, err = noChangeDB.Datasets(context.Background())
   173  	suite.NoError(err)
   174  	n := cs.Reads()
   175  
   176  	err = noChangeDB.Rebase(context.Background())
   177  	suite.NoError(err)
   178  	suite.Equal(n, cs.Reads())
   179  }
   180  
   181  func (suite *DatabaseSuite) TestCommitProperlyTracksRoot() {
   182  	id1, id2 := "testdataset", "othertestdataset"
   183  
   184  	db1 := suite.makeDb(suite.storage.NewView())
   185  	defer db1.Close()
   186  	ds1, err := db1.GetDataset(context.Background(), id1)
   187  	suite.NoError(err)
   188  	ds1HeadVal := types.String("Commit value for " + id1)
   189  	ds1, err = db1.CommitValue(context.Background(), ds1, ds1HeadVal)
   190  	suite.NoError(err)
   191  
   192  	db2 := suite.makeDb(suite.storage.NewView())
   193  	defer db2.Close()
   194  	ds2, err := db2.GetDataset(context.Background(), id2)
   195  	suite.NoError(err)
   196  	ds2HeadVal := types.String("Commit value for " + id2)
   197  	ds2, err = db2.CommitValue(context.Background(), ds2, ds2HeadVal)
   198  	suite.NoError(err)
   199  
   200  	suite.EqualValues(ds1HeadVal, mustHeadValue(ds1))
   201  	suite.EqualValues(ds2HeadVal, mustHeadValue(ds2))
   202  	suite.False(mustHeadValue(ds2).Equals(ds1HeadVal))
   203  	suite.False(mustHeadValue(ds1).Equals(ds2HeadVal))
   204  }
   205  
   206  func (suite *DatabaseSuite) TestDatabaseCommit() {
   207  	datasetID := "ds1"
   208  	datasets, err := suite.db.Datasets(context.Background())
   209  	suite.NoError(err)
   210  	suite.Zero(datasets.Len())
   211  
   212  	// |a|
   213  	ds, err := suite.db.GetDataset(context.Background(), datasetID)
   214  	suite.NoError(err)
   215  	a := types.String("a")
   216  	ds2, err := suite.db.CommitValue(context.Background(), ds, a)
   217  	suite.NoError(err)
   218  
   219  	// ds2 matches the Datasets Map in suite.db
   220  	suiteDS, err := suite.db.GetDataset(context.Background(), datasetID)
   221  	suite.NoError(err)
   222  	headRef := mustHeadRef(suiteDS)
   223  	suite.True(mustHeadRef(ds2).Equals(headRef))
   224  
   225  	// ds2 has |a| at its head
   226  	h, ok, err := ds2.MaybeHeadValue()
   227  	suite.NoError(err)
   228  	suite.True(ok)
   229  	suite.True(h.Equals(a))
   230  	suite.Equal(uint64(1), mustHeadRef(ds2).Height())
   231  
   232  	ds = ds2
   233  	aCommitRef := mustHeadRef(ds) // to be used to test disallowing of non-fastforward commits below
   234  
   235  	// |a| <- |b|
   236  	b := types.String("b")
   237  	ds, err = suite.db.CommitValue(context.Background(), ds, b)
   238  	suite.NoError(err)
   239  	suite.True(mustHeadValue(ds).Equals(b))
   240  	suite.Equal(uint64(2), mustHeadRef(ds).Height())
   241  
   242  	// |a| <- |b|
   243  	//   \----|c|
   244  	// Should be disallowed.
   245  	c := types.String("c")
   246  	_, err = suite.db.Commit(context.Background(), ds, c, newOpts(suite.db, aCommitRef))
   247  	suite.Error(err)
   248  	suite.True(mustHeadValue(ds).Equals(b))
   249  
   250  	// |a| <- |b| <- |d|
   251  	d := types.String("d")
   252  	ds, err = suite.db.CommitValue(context.Background(), ds, d)
   253  	suite.NoError(err)
   254  	suite.True(mustHeadValue(ds).Equals(d))
   255  	suite.Equal(uint64(3), mustHeadRef(ds).Height())
   256  
   257  	// Attempt to recommit |b| with |a| as parent.
   258  	// Should be disallowed.
   259  	_, err = suite.db.Commit(context.Background(), ds, b, newOpts(suite.db, aCommitRef))
   260  	suite.Error(err)
   261  	suite.True(mustHeadValue(ds).Equals(d))
   262  
   263  	// Add a commit to a different datasetId
   264  	ds, err = suite.db.GetDataset(context.Background(), "otherDS")
   265  	suite.NoError(err)
   266  	_, err = suite.db.CommitValue(context.Background(), ds, a)
   267  	suite.NoError(err)
   268  
   269  	// Get a fresh database, and verify that both datasets are present
   270  	newDB := suite.makeDb(suite.storage.NewView())
   271  	defer newDB.Close()
   272  	datasets2, err := newDB.Datasets(context.Background())
   273  	suite.NoError(err)
   274  	suite.Equal(uint64(2), datasets2.Len())
   275  }
   276  
   277  func (suite *DatabaseSuite) TestDatasetsMapType() {
   278  	dsID1, dsID2 := "ds1", "ds2"
   279  
   280  	datasets, err := suite.db.Datasets(context.Background())
   281  	suite.NoError(err)
   282  	ds, err := suite.db.GetDataset(context.Background(), dsID1)
   283  	suite.NoError(err)
   284  	ds, err = suite.db.CommitValue(context.Background(), ds, types.String("a"))
   285  	suite.NoError(err)
   286  	dss, err := suite.db.Datasets(context.Background())
   287  	suite.NoError(err)
   288  	assertMapOfStringToRefOfCommit(context.Background(), dss, datasets, suite.db)
   289  
   290  	datasets, err = suite.db.Datasets(context.Background())
   291  	suite.NoError(err)
   292  	ds2, err := suite.db.GetDataset(context.Background(), dsID2)
   293  	suite.NoError(err)
   294  	_, err = suite.db.CommitValue(context.Background(), ds2, types.Float(42))
   295  	suite.NoError(err)
   296  	dss, err = suite.db.Datasets(context.Background())
   297  	suite.NoError(err)
   298  	assertMapOfStringToRefOfCommit(context.Background(), dss, datasets, suite.db)
   299  
   300  	datasets, err = suite.db.Datasets(context.Background())
   301  	suite.NoError(err)
   302  	_, err = suite.db.Delete(context.Background(), ds)
   303  	suite.NoError(err)
   304  	dss, err = suite.db.Datasets(context.Background())
   305  	suite.NoError(err)
   306  	assertMapOfStringToRefOfCommit(context.Background(), dss, datasets, suite.db)
   307  }
   308  
   309  func assertMapOfStringToRefOfCommit(ctx context.Context, proposed, datasets types.Map, vr types.ValueReader) {
   310  	var derr error
   311  	changes := make(chan types.ValueChanged)
   312  	go func() {
   313  		defer close(changes)
   314  		derr = proposed.Diff(ctx, datasets, changes)
   315  	}()
   316  	for change := range changes {
   317  		switch change.ChangeType {
   318  		case types.DiffChangeAdded, types.DiffChangeModified:
   319  			// Since this is a Map Diff, change.V is the key at which a change was detected.
   320  			// Go get the Value there, which should be a Ref<Value>, deref it, and then ensure the target is a Commit.
   321  			val := change.NewValue
   322  			ref, ok := val.(types.Ref)
   323  			if !ok {
   324  				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)))
   325  			}
   326  			if targetValue, err := ref.TargetValue(ctx, vr); err != nil {
   327  				d.PanicIfError(err)
   328  			} else if is, err := IsCommit(targetValue); err != nil {
   329  				d.PanicIfError(err)
   330  			} else if !is {
   331  				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)))
   332  			}
   333  		}
   334  	}
   335  	d.PanicIfError(derr)
   336  }
   337  
   338  func newOpts(vrw types.ValueReadWriter, parents ...types.Value) CommitOptions {
   339  	pList, err := types.NewList(context.Background(), vrw, parents...)
   340  	d.PanicIfError(err)
   341  
   342  	return CommitOptions{ParentsList: pList}
   343  }
   344  
   345  func (suite *DatabaseSuite) TestDatabaseDuplicateCommit() {
   346  	datasetID := "ds1"
   347  	ds, err := suite.db.GetDataset(context.Background(), datasetID)
   348  	suite.NoError(err)
   349  	datasets, err := suite.db.Datasets(context.Background())
   350  	suite.NoError(err)
   351  	suite.Zero(datasets.Len())
   352  
   353  	v := types.String("Hello")
   354  	_, err = suite.db.CommitValue(context.Background(), ds, v)
   355  	suite.NoError(err)
   356  
   357  	_, err = suite.db.CommitValue(context.Background(), ds, v)
   358  	suite.IsType(ErrMergeNeeded, err)
   359  }
   360  
   361  func (suite *DatabaseSuite) TestDatabaseCommitMerge() {
   362  	datasetID1, datasetID2 := "ds1", "ds2"
   363  	ds1, err := suite.db.GetDataset(context.Background(), datasetID1)
   364  	suite.NoError(err)
   365  	ds2, err := suite.db.GetDataset(context.Background(), datasetID2)
   366  	suite.NoError(err)
   367  
   368  	v, err := types.NewMap(context.Background(), suite.db, types.String("Hello"), types.Float(42))
   369  	suite.NoError(err)
   370  	ds1, err = suite.db.CommitValue(context.Background(), ds1, v)
   371  	ds1First := ds1
   372  	suite.NoError(err)
   373  	s, err := v.Edit().Set(types.String("Friends"), types.Bool(true)).Map(context.Background())
   374  	suite.NoError(err)
   375  	ds1, err = suite.db.CommitValue(context.Background(), ds1, s)
   376  	suite.NoError(err)
   377  
   378  	ds2, err = suite.db.CommitValue(context.Background(), ds2, types.String("Goodbye"))
   379  	suite.NoError(err)
   380  
   381  	// No common ancestor
   382  	_, err = suite.db.Commit(context.Background(), ds1, types.Float(47), newOpts(suite.db, mustHeadRef(ds2)))
   383  	suite.IsType(ErrMergeNeeded, err, "%s", err)
   384  
   385  	// Unmergeable
   386  	_, err = suite.db.Commit(context.Background(), ds1, types.Float(47), newOptsWithMerge(suite.db, merge.None, mustHeadRef(ds1First)))
   387  	suite.IsType(&merge.ErrMergeConflict{}, err, "%s", err)
   388  
   389  	// Merge policies
   390  	newV, err := v.Edit().Set(types.String("Friends"), types.Bool(false)).Map(context.Background())
   391  	suite.NoError(err)
   392  	_, err = suite.db.Commit(context.Background(), ds1, newV, newOptsWithMerge(suite.db, merge.None, mustHeadRef(ds1First)))
   393  	suite.IsType(&merge.ErrMergeConflict{}, err, "%s", err)
   394  
   395  	theirs, err := suite.db.Commit(context.Background(), ds1, newV, newOptsWithMerge(suite.db, merge.Theirs, mustHeadRef(ds1First)))
   396  	suite.NoError(err)
   397  	suite.True(types.Bool(true).Equals(mustGetValue(mustHeadValue(theirs).(types.Map).MaybeGet(context.Background(), types.String("Friends")))))
   398  
   399  	newV, err = v.Edit().Set(types.String("Friends"), types.Float(47)).Map(context.Background())
   400  	suite.NoError(err)
   401  	ours, err := suite.db.Commit(context.Background(), ds1First, newV, newOptsWithMerge(suite.db, merge.Ours, mustHeadRef(ds1First)))
   402  	suite.NoError(err)
   403  	suite.True(types.Float(47).Equals(mustGetValue(mustHeadValue(ours).(types.Map).MaybeGet(context.Background(), types.String("Friends")))))
   404  }
   405  
   406  func newOptsWithMerge(vrw types.ValueReadWriter, policy merge.ResolveFunc, parents ...types.Value) CommitOptions {
   407  	plist, err := types.NewList(context.Background(), vrw, parents...)
   408  	d.PanicIfError(err)
   409  	return CommitOptions{ParentsList: plist, Policy: merge.NewThreeWay(policy)}
   410  }
   411  
   412  func (suite *DatabaseSuite) TestDatabaseDelete() {
   413  	datasetID1, datasetID2 := "ds1", "ds2"
   414  	ds1, err := suite.db.GetDataset(context.Background(), datasetID1)
   415  	suite.NoError(err)
   416  	ds2, err := suite.db.GetDataset(context.Background(), datasetID2)
   417  	suite.NoError(err)
   418  	datasets, err := suite.db.Datasets(context.Background())
   419  	suite.NoError(err)
   420  	suite.Zero(datasets.Len())
   421  
   422  	// ds1: |a|
   423  	a := types.String("a")
   424  	ds1, err = suite.db.CommitValue(context.Background(), ds1, a)
   425  	suite.NoError(err)
   426  	suite.True(mustHeadValue(ds1).Equals(a))
   427  
   428  	// ds1: |a|, ds2: |b|
   429  	b := types.String("b")
   430  	ds2, err = suite.db.CommitValue(context.Background(), ds2, b)
   431  	suite.NoError(err)
   432  	suite.True(mustHeadValue(ds2).Equals(b))
   433  
   434  	ds1, err = suite.db.Delete(context.Background(), ds1)
   435  	suite.NoError(err)
   436  	currDS2, err := suite.db.GetDataset(context.Background(), datasetID2)
   437  	suite.NoError(err)
   438  	suite.True(mustHeadValue(currDS2).Equals(b))
   439  	currDS1, err := suite.db.GetDataset(context.Background(), datasetID1)
   440  	suite.NoError(err)
   441  	_, present := currDS1.MaybeHead()
   442  	suite.False(present, "Dataset %s should not be present", datasetID1)
   443  
   444  	// Get a fresh database, and verify that only ds2 is present
   445  	newDB := suite.makeDb(suite.storage.NewView())
   446  	defer newDB.Close()
   447  	datasets, err = newDB.Datasets(context.Background())
   448  	suite.NoError(err)
   449  	suite.Equal(uint64(1), datasets.Len())
   450  	newDS, err := newDB.GetDataset(context.Background(), datasetID2)
   451  	suite.NoError(err)
   452  	_, present, err = newDS.MaybeHeadRef()
   453  	suite.NoError(err)
   454  	suite.True(present, "Dataset %s should be present", datasetID2)
   455  }
   456  
   457  func (suite *DatabaseSuite) TestCommitWithConcurrentChunkStoreUse() {
   458  	datasetID := "ds1"
   459  	ds1, err := suite.db.GetDataset(context.Background(), datasetID)
   460  	suite.NoError(err)
   461  
   462  	// Setup:
   463  	// ds1: |a| <- |b|
   464  	ds1, _ = suite.db.CommitValue(context.Background(), ds1, types.String("a"))
   465  	b := types.String("b")
   466  	ds1, err = suite.db.CommitValue(context.Background(), ds1, b)
   467  	suite.NoError(err)
   468  	suite.True(mustHeadValue(ds1).Equals(b))
   469  
   470  	// Craft DB that will allow me to move the backing ChunkStore while suite.db isn't looking
   471  	interloper := suite.makeDb(suite.storage.NewView())
   472  	defer interloper.Close()
   473  
   474  	// Change ds2 behind suite.db's back. This shouldn't block changes to ds1 via suite.db below.
   475  	// ds1: |a| <- |b|
   476  	// ds2: |stuff|
   477  	stf := types.String("stuff")
   478  	ds2, err := interloper.GetDataset(context.Background(), "ds2")
   479  	suite.NoError(err)
   480  	ds2, concErr := interloper.CommitValue(context.Background(), ds2, stf)
   481  	suite.NoError(concErr)
   482  	suite.True(mustHeadValue(ds2).Equals(stf))
   483  
   484  	// Change ds1 via suite.db, which should proceed without a problem
   485  	c := types.String("c")
   486  	ds1, err = suite.db.CommitValue(context.Background(), ds1, c)
   487  	suite.NoError(err)
   488  	suite.True(mustHeadValue(ds1).Equals(c))
   489  
   490  	// Change ds1 behind suite.db's back. Will block changes to ds1 below.
   491  	// ds1: |a| <- |b| <- |c| <- |e|
   492  	e := types.String("e")
   493  	interloper.Rebase(context.Background())
   494  	iDS, err := interloper.GetDataset(context.Background(), "ds1")
   495  	suite.NoError(err)
   496  	iDS, concErr = interloper.CommitValue(context.Background(), iDS, e)
   497  	suite.NoError(concErr)
   498  	suite.True(mustHeadValue(iDS).Equals(e))
   499  	v := mustHeadValue(iDS)
   500  	suite.True(v.Equals(e), "%s", v.(types.String))
   501  
   502  	// Attempted Concurrent change, which should fail due to the above
   503  	nope := types.String("nope")
   504  	_, err = suite.db.CommitValue(context.Background(), ds1, nope)
   505  	suite.Error(err)
   506  }
   507  
   508  func (suite *DatabaseSuite) TestDeleteWithConcurrentChunkStoreUse() {
   509  	datasetID := "ds1"
   510  	ds1, err := suite.db.GetDataset(context.Background(), datasetID)
   511  	suite.NoError(err)
   512  
   513  	// Setup:
   514  	// ds1: |a| <- |b|
   515  	ds1, _ = suite.db.CommitValue(context.Background(), ds1, types.String("a"))
   516  	b := types.String("b")
   517  	ds1, err = suite.db.CommitValue(context.Background(), ds1, b)
   518  	suite.NoError(err)
   519  	suite.True(mustHeadValue(ds1).Equals(b))
   520  
   521  	// Craft DB that will allow me to move the backing ChunkStore while suite.db isn't looking
   522  	interloper := suite.makeDb(suite.storage.NewView())
   523  	defer interloper.Close()
   524  
   525  	// Concurrent change, to move root out from under my feet:
   526  	// ds1: |a| <- |b| <- |e|
   527  	e := types.String("e")
   528  	iDS, err := interloper.GetDataset(context.Background(), datasetID)
   529  	suite.NoError(err)
   530  	iDS, concErr := interloper.CommitValue(context.Background(), iDS, e)
   531  	suite.NoError(concErr)
   532  	suite.True(mustHeadValue(iDS).Equals(e))
   533  
   534  	// Attempt to delete ds1 via suite.db, which should fail due to the above
   535  	_, err = suite.db.Delete(context.Background(), ds1)
   536  	suite.Error(err)
   537  
   538  	// Concurrent change, but to some other dataset. This shouldn't stop changes to ds1.
   539  	// ds1: |a| <- |b| <- |e|
   540  	// ds2: |stuff|
   541  	stf := types.String("stuff")
   542  	otherDS, err := suite.db.GetDataset(context.Background(), "other")
   543  	suite.NoError(err)
   544  	iDS, concErr = interloper.CommitValue(context.Background(), otherDS, stf)
   545  	suite.NoError(concErr)
   546  	suite.True(mustHeadValue(iDS).Equals(stf))
   547  
   548  	// Attempted concurrent delete, which should proceed without a problem
   549  	ds1, err = suite.db.Delete(context.Background(), ds1)
   550  	suite.NoError(err)
   551  	_, present, err := ds1.MaybeHeadRef()
   552  	suite.NoError(err)
   553  	suite.False(present, "Dataset %s should not be present", datasetID)
   554  }
   555  
   556  func (suite *DatabaseSuite) TestSetHead() {
   557  	var err error
   558  	datasetID := "ds1"
   559  
   560  	// |a| <- |b|
   561  	ds, err := suite.db.GetDataset(context.Background(), datasetID)
   562  	suite.NoError(err)
   563  	a := types.String("a")
   564  	ds, err = suite.db.CommitValue(context.Background(), ds, a)
   565  	suite.NoError(err)
   566  	aCommitRef := mustHeadRef(ds) // To use in non-FF SetHeadToCommit() below.
   567  
   568  	b := types.String("b")
   569  	ds, err = suite.db.CommitValue(context.Background(), ds, b)
   570  	suite.NoError(err)
   571  	suite.True(mustHeadValue(ds).Equals(b))
   572  	bCommitRef := mustHeadRef(ds) // To use in FF SetHeadToCommit() below.
   573  
   574  	ds, err = suite.db.SetHead(context.Background(), ds, aCommitRef)
   575  	suite.NoError(err)
   576  	suite.True(mustHeadValue(ds).Equals(a))
   577  
   578  	ds, err = suite.db.SetHead(context.Background(), ds, bCommitRef)
   579  	suite.NoError(err)
   580  	suite.True(mustHeadValue(ds).Equals(b))
   581  }
   582  
   583  func (suite *DatabaseSuite) TestFastForward() {
   584  	datasetID := "ds1"
   585  
   586  	// |a| <- |b| <- |c|
   587  	ds, err := suite.db.GetDataset(context.Background(), datasetID)
   588  	suite.NoError(err)
   589  	a := types.String("a")
   590  	ds, err = suite.db.CommitValue(context.Background(), ds, a)
   591  	suite.NoError(err)
   592  	aCommitRef := mustHeadRef(ds) // To use in non-FF cases below.
   593  
   594  	b := types.String("b")
   595  	ds, err = suite.db.CommitValue(context.Background(), ds, b)
   596  	suite.NoError(err)
   597  	suite.True(mustHeadValue(ds).Equals(b))
   598  
   599  	c := types.String("c")
   600  	ds, err = suite.db.CommitValue(context.Background(), ds, c)
   601  	suite.NoError(err)
   602  	suite.True(mustHeadValue(ds).Equals(c))
   603  	cCommitRef := mustHeadRef(ds) // To use in FastForward() below.
   604  
   605  	// FastForward should disallow this, as |a| is not a descendant of |c|
   606  	_, err = suite.db.FastForward(context.Background(), ds, aCommitRef)
   607  	suite.Error(err)
   608  
   609  	// Move Head back to something earlier in the lineage, so we can test FastForward
   610  	ds, err = suite.db.SetHead(context.Background(), ds, aCommitRef)
   611  	suite.NoError(err)
   612  	suite.True(mustHeadValue(ds).Equals(a))
   613  
   614  	// This should succeed, because while |a| is not a direct parent of |c|, it is an ancestor.
   615  	ds, err = suite.db.FastForward(context.Background(), ds, cCommitRef)
   616  	suite.NoError(err)
   617  	suite.True(mustHeadValue(ds).Equals(c))
   618  }
   619  
   620  func (suite *DatabaseSuite) TestDatabaseHeightOfRefs() {
   621  	r1, err := suite.db.WriteValue(context.Background(), types.String("hello"))
   622  	suite.NoError(err)
   623  	suite.Equal(uint64(1), r1.Height())
   624  
   625  	r2, err := suite.db.WriteValue(context.Background(), r1)
   626  	suite.NoError(err)
   627  	suite.Equal(uint64(2), r2.Height())
   628  	suite.Equal(uint64(3), mustRef(suite.db.WriteValue(context.Background(), r2)).Height())
   629  }
   630  
   631  func (suite *DatabaseSuite) TestDatabaseHeightOfCollections() {
   632  	setOfStringType, err := types.MakeSetType(types.PrimitiveTypeMap[types.StringKind])
   633  	suite.NoError(err)
   634  	setOfRefOfStringType, err := types.MakeSetType(mustType(types.MakeRefType(types.PrimitiveTypeMap[types.StringKind])))
   635  	suite.NoError(err)
   636  
   637  	// Set<String>
   638  	v1 := types.String("hello")
   639  	v2 := types.String("world")
   640  	s1, err := types.NewSet(context.Background(), suite.db, v1, v2)
   641  	suite.NoError(err)
   642  	ref, err := suite.db.WriteValue(context.Background(), s1)
   643  	suite.NoError(err)
   644  	suite.Equal(uint64(1), ref.Height())
   645  
   646  	// Set<Ref<String>>
   647  	s2, err := types.NewSet(context.Background(), suite.db, mustRef(suite.db.WriteValue(context.Background(), v1)), mustRef(suite.db.WriteValue(context.Background(), v2)))
   648  	suite.NoError(err)
   649  	suite.Equal(uint64(2), mustRef(suite.db.WriteValue(context.Background(), s2)).Height())
   650  
   651  	// List<Set<String>>
   652  	v3 := types.String("foo")
   653  	v4 := types.String("bar")
   654  	s3, err := types.NewSet(context.Background(), suite.db, v3, v4)
   655  	suite.NoError(err)
   656  	l1, err := types.NewList(context.Background(), suite.db, s1, s3)
   657  	suite.NoError(err)
   658  	suite.Equal(uint64(1), mustRef(suite.db.WriteValue(context.Background(), l1)).Height())
   659  
   660  	// List<Ref<Set<String>>
   661  	l2, err := types.NewList(context.Background(), suite.db, mustRef(suite.db.WriteValue(context.Background(), s1)), mustRef(suite.db.WriteValue(context.Background(), s3)))
   662  	suite.NoError(err)
   663  	suite.Equal(uint64(2), mustRef(suite.db.WriteValue(context.Background(), l2)).Height())
   664  
   665  	// List<Ref<Set<Ref<String>>>
   666  	s4, err := types.NewSet(context.Background(), suite.db, mustRef(suite.db.WriteValue(context.Background(), v3)), mustRef(suite.db.WriteValue(context.Background(), v4)))
   667  	suite.NoError(err)
   668  	l3, err := types.NewList(context.Background(), suite.db, mustRef(suite.db.WriteValue(context.Background(), s4)))
   669  	suite.NoError(err)
   670  	suite.Equal(uint64(3), mustRef(suite.db.WriteValue(context.Background(), l3)).Height())
   671  
   672  	// List<Set<String> | RefValue<Set<String>>>
   673  	l4, err := types.NewList(context.Background(), suite.db, s1, mustRef(suite.db.WriteValue(context.Background(), s3)))
   674  	suite.NoError(err)
   675  	suite.Equal(uint64(2), mustRef(suite.db.WriteValue(context.Background(), l4)).Height())
   676  	l5, err := types.NewList(context.Background(), suite.db, mustRef(suite.db.WriteValue(context.Background(), s1)), s3)
   677  	suite.NoError(err)
   678  	suite.Equal(uint64(2), mustRef(suite.db.WriteValue(context.Background(), l5)).Height())
   679  
   680  	// Familiar with the "New Jersey Turnpike" drink? Here's the noms version of that...
   681  	everything := []types.Value{v1, v2, s1, s2, v3, v4, s3, l1, l2, s4, l3, l4, l5}
   682  	andMore := make([]types.Value, 0, len(everything)*3+2)
   683  	for _, v := range everything {
   684  		andMore = append(andMore, v, mustType(types.TypeOf(v)), mustRef(suite.db.WriteValue(context.Background(), v)))
   685  	}
   686  	andMore = append(andMore, setOfStringType, setOfRefOfStringType)
   687  
   688  	_, err = suite.db.WriteValue(context.Background(), mustValue(types.NewList(context.Background(), suite.db, andMore...)))
   689  	suite.NoError(err)
   690  }
   691  
   692  func (suite *DatabaseSuite) TestMetaOption() {
   693  	ds, err := suite.db.GetDataset(context.Background(), "ds1")
   694  	suite.NoError(err)
   695  
   696  	m, err := types.NewStruct(types.Format_7_18, "M", types.StructData{
   697  		"author": types.String("arv"),
   698  	})
   699  
   700  	suite.NoError(err)
   701  	ds, err = suite.db.Commit(context.Background(), ds, types.String("a"), CommitOptions{Meta: m})
   702  	suite.NoError(err)
   703  	c := mustHead(ds)
   704  	suite.Equal(types.String("arv"), mustGetValue(mustGetValue(c.MaybeGet("meta")).(types.Struct).MaybeGet("author")))
   705  }