github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/doltdb/doltdb_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  package doltdb
    16  
    17  import (
    18  	"context"
    19  	"io/ioutil"
    20  	"path/filepath"
    21  	"testing"
    22  
    23  	"github.com/google/uuid"
    24  	"github.com/stretchr/testify/assert"
    25  	"github.com/stretchr/testify/require"
    26  
    27  	"github.com/dolthub/dolt/go/libraries/doltcore/dbfactory"
    28  	"github.com/dolthub/dolt/go/libraries/doltcore/ref"
    29  	"github.com/dolthub/dolt/go/libraries/doltcore/row"
    30  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    31  	"github.com/dolthub/dolt/go/libraries/doltcore/schema/encoding"
    32  	"github.com/dolthub/dolt/go/libraries/utils/filesys"
    33  	"github.com/dolthub/dolt/go/libraries/utils/test"
    34  	"github.com/dolthub/dolt/go/store/hash"
    35  	"github.com/dolthub/dolt/go/store/types"
    36  )
    37  
    38  const (
    39  	idTag        = 0
    40  	firstTag     = 1
    41  	lastTag      = 2
    42  	isMarriedTag = 3
    43  	ageTag       = 4
    44  	emptyTag     = 5
    45  )
    46  const testSchemaIndexName = "idx_name"
    47  const testSchemaIndexAge = "idx_age"
    48  
    49  var id0, _ = uuid.NewRandom()
    50  var id1, _ = uuid.NewRandom()
    51  var id2, _ = uuid.NewRandom()
    52  var id3, _ = uuid.NewRandom()
    53  
    54  func createTestSchema(t *testing.T) schema.Schema {
    55  	colColl := schema.NewColCollection(
    56  		schema.NewColumn("id", idTag, types.UUIDKind, true, schema.NotNullConstraint{}),
    57  		schema.NewColumn("first", firstTag, types.StringKind, false, schema.NotNullConstraint{}),
    58  		schema.NewColumn("last", lastTag, types.StringKind, false, schema.NotNullConstraint{}),
    59  		schema.NewColumn("is_married", isMarriedTag, types.BoolKind, false),
    60  		schema.NewColumn("age", ageTag, types.UintKind, false),
    61  		schema.NewColumn("empty", emptyTag, types.IntKind, false),
    62  	)
    63  	sch, err := schema.SchemaFromCols(colColl)
    64  	require.NoError(t, err)
    65  	_, err = sch.Indexes().AddIndexByColTags(testSchemaIndexName, []uint64{firstTag, lastTag}, schema.IndexProperties{IsUnique: false, Comment: ""})
    66  	require.NoError(t, err)
    67  	_, err = sch.Indexes().AddIndexByColTags(testSchemaIndexAge, []uint64{ageTag}, schema.IndexProperties{IsUnique: false, Comment: ""})
    68  	require.NoError(t, err)
    69  	return sch
    70  }
    71  
    72  func CreateTestTable(vrw types.ValueReadWriter, tSchema schema.Schema, rowData types.Map) (*Table, error) {
    73  	schemaVal, err := encoding.MarshalSchemaAsNomsValue(context.Background(), vrw, tSchema)
    74  
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	empty, _ := types.NewMap(context.Background(), vrw)
    80  	tbl, err := NewTable(context.Background(), vrw, schemaVal, rowData, empty, nil)
    81  
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	return tbl, nil
    87  }
    88  
    89  func createTestRowData(t *testing.T, vrw types.ValueReadWriter, sch schema.Schema) (types.Map, []row.Row) {
    90  	return createTestRowDataFromTaggedValues(t, vrw, sch,
    91  		row.TaggedValues{
    92  			idTag: types.UUID(id0), firstTag: types.String("bill"), lastTag: types.String("billerson"), ageTag: types.Uint(53)},
    93  		row.TaggedValues{
    94  			idTag: types.UUID(id1), firstTag: types.String("eric"), lastTag: types.String("ericson"), isMarriedTag: types.Bool(true), ageTag: types.Uint(21)},
    95  		row.TaggedValues{
    96  			idTag: types.UUID(id2), firstTag: types.String("john"), lastTag: types.String("johnson"), isMarriedTag: types.Bool(false), ageTag: types.Uint(53)},
    97  		row.TaggedValues{
    98  			idTag: types.UUID(id3), firstTag: types.String("robert"), lastTag: types.String("robertson"), ageTag: types.Uint(36)},
    99  	)
   100  }
   101  
   102  func createTestRowDataFromTaggedValues(t *testing.T, vrw types.ValueReadWriter, sch schema.Schema, vals ...row.TaggedValues) (types.Map, []row.Row) {
   103  	var err error
   104  	rows := make([]row.Row, len(vals))
   105  
   106  	m, err := types.NewMap(context.Background(), vrw)
   107  	assert.NoError(t, err)
   108  	ed := m.Edit()
   109  
   110  	for i, val := range vals {
   111  		r, err := row.New(types.Format_Default, sch, val)
   112  		require.NoError(t, err)
   113  		rows[i] = r
   114  		ed = ed.Set(r.NomsMapKey(sch), r.NomsMapValue(sch))
   115  	}
   116  
   117  	m, err = ed.Map(context.Background())
   118  	assert.NoError(t, err)
   119  
   120  	return m, rows
   121  }
   122  
   123  func TestIsValidTableName(t *testing.T) {
   124  	assert.True(t, IsValidTableName("a"))
   125  	assert.True(t, IsValidTableName("a1"))
   126  	assert.True(t, IsValidTableName("_a1"))
   127  	assert.True(t, IsValidTableName("a1_b_c------1"))
   128  	assert.True(t, IsValidTableName("Add-098234_lkjasdf0p98"))
   129  	assert.False(t, IsValidTableName("1"))
   130  	assert.False(t, IsValidTableName("-"))
   131  	assert.False(t, IsValidTableName("-a"))
   132  	assert.False(t, IsValidTableName(""))
   133  	assert.False(t, IsValidTableName("1a"))
   134  	assert.False(t, IsValidTableName("a1-"))
   135  	assert.False(t, IsValidTableName("ab!!c"))
   136  }
   137  
   138  // DO NOT CHANGE THIS TEST
   139  // It is necessary to ensure consistent system table definitions
   140  // for more info: https://github.com/dolthub/dolt/pull/663
   141  func TestSystemTableTags(t *testing.T) {
   142  	var sysTableMin uint64 = 1 << 51
   143  
   144  	t.Run("asdf", func(t *testing.T) {
   145  		assert.Equal(t, sysTableMin, schema.SystemTableReservedMin)
   146  	})
   147  	t.Run("dolt_doc tags", func(t *testing.T) {
   148  		docTableMin := sysTableMin + uint64(5)
   149  		assert.Equal(t, docTableMin+0, schema.DocNameTag)
   150  		assert.Equal(t, docTableMin+1, schema.DocTextTag)
   151  	})
   152  	t.Run("dolt_history_ tags", func(t *testing.T) {
   153  		doltHistoryMin := sysTableMin + uint64(1000)
   154  		assert.Equal(t, doltHistoryMin+0, schema.HistoryCommitterTag)
   155  		assert.Equal(t, doltHistoryMin+1, schema.HistoryCommitHashTag)
   156  		assert.Equal(t, doltHistoryMin+2, schema.HistoryCommitDateTag)
   157  	})
   158  	t.Run("dolt_diff_ tags", func(t *testing.T) {
   159  		diffTableMin := sysTableMin + uint64(2000)
   160  		assert.Equal(t, diffTableMin+0, schema.DiffCommitTag)
   161  	})
   162  	t.Run("dolt_query_catalog tags", func(t *testing.T) {
   163  		queryCatalogMin := sysTableMin + uint64(3005)
   164  		assert.Equal(t, queryCatalogMin+0, schema.QueryCatalogIdTag)
   165  		assert.Equal(t, queryCatalogMin+1, schema.QueryCatalogOrderTag)
   166  		assert.Equal(t, queryCatalogMin+2, schema.QueryCatalogNameTag)
   167  		assert.Equal(t, queryCatalogMin+3, schema.QueryCatalogQueryTag)
   168  		assert.Equal(t, queryCatalogMin+4, schema.QueryCatalogDescriptionTag)
   169  	})
   170  	t.Run("dolt_schemas tags", func(t *testing.T) {
   171  		doltSchemasMin := sysTableMin + uint64(4007)
   172  		assert.Equal(t, doltSchemasMin+0, schema.DoltSchemasIdTag)
   173  		assert.Equal(t, doltSchemasMin+1, schema.DoltSchemasTypeTag)
   174  		assert.Equal(t, doltSchemasMin+2, schema.DoltSchemasNameTag)
   175  		assert.Equal(t, doltSchemasMin+3, schema.DoltSchemasFragmentTag)
   176  	})
   177  }
   178  
   179  func TestEmptyInMemoryRepoCreation(t *testing.T) {
   180  	ddb, err := LoadDoltDB(context.Background(), types.Format_Default, InMemDoltDB)
   181  
   182  	if err != nil {
   183  		t.Fatal("Failed to load db")
   184  	}
   185  
   186  	err = ddb.WriteEmptyRepo(context.Background(), "Bill Billerson", "bigbillieb@fake.horse")
   187  
   188  	if err != nil {
   189  		t.Fatal("Unexpected error creating empty repo", err)
   190  	}
   191  
   192  	cs, _ := NewCommitSpec("master")
   193  	commit, err := ddb.Resolve(context.Background(), cs, nil)
   194  
   195  	if err != nil {
   196  		t.Fatal("Could not find commit")
   197  	}
   198  
   199  	h, err := commit.HashOf()
   200  	assert.NoError(t, err)
   201  	cs2, _ := NewCommitSpec(h.String())
   202  	_, err = ddb.Resolve(context.Background(), cs2, nil)
   203  
   204  	if err != nil {
   205  		t.Fatal("Failed to get commit by hash")
   206  	}
   207  }
   208  
   209  func TestLoadNonExistentLocalFSRepo(t *testing.T) {
   210  	_, err := test.ChangeToTestDir("TestLoadRepo")
   211  
   212  	if err != nil {
   213  		panic("Couldn't change the working directory to the test directory.")
   214  	}
   215  
   216  	ddb, err := LoadDoltDB(context.Background(), types.Format_Default, LocalDirDoltDB)
   217  	assert.Nil(t, ddb, "Should return nil when loading a non-existent data dir")
   218  	assert.Error(t, err, "Should see an error here")
   219  }
   220  
   221  func TestLoadBadLocalFSRepo(t *testing.T) {
   222  	testDir, err := test.ChangeToTestDir("TestLoadRepo")
   223  
   224  	if err != nil {
   225  		panic("Couldn't change the working directory to the test directory.")
   226  	}
   227  
   228  	contents := []byte("not a directory")
   229  	ioutil.WriteFile(filepath.Join(testDir, dbfactory.DoltDataDir), contents, 0644)
   230  
   231  	ddb, err := LoadDoltDB(context.Background(), types.Format_Default, LocalDirDoltDB)
   232  	assert.Nil(t, ddb, "Should return nil when loading a non-directory data dir file")
   233  	assert.Error(t, err, "Should see an error here")
   234  }
   235  
   236  func TestLDNoms(t *testing.T) {
   237  	testDir, err := test.ChangeToTestDir("TestLoadRepo")
   238  
   239  	if err != nil {
   240  		panic("Couldn't change the working directory to the test directory.")
   241  	}
   242  
   243  	committerName := "Bill Billerson"
   244  	committerEmail := "bigbillieb@fake.horse"
   245  
   246  	// Create an empty repo in a temp dir on the filesys
   247  	{
   248  		err := filesys.LocalFS.MkDirs(filepath.Join(testDir, dbfactory.DoltDataDir))
   249  
   250  		if err != nil {
   251  			t.Fatal("Failed to create noms directory")
   252  		}
   253  
   254  		ddb, _ := LoadDoltDB(context.Background(), types.Format_Default, LocalDirDoltDB)
   255  		err = ddb.WriteEmptyRepo(context.Background(), committerName, committerEmail)
   256  
   257  		if err != nil {
   258  			t.Fatal("Unexpected error creating empty repo", err)
   259  		}
   260  	}
   261  
   262  	//read the empty repo back and add a new table.  Write the value, but don't commit
   263  	var valHash hash.Hash
   264  	var tbl *Table
   265  	{
   266  		ddb, _ := LoadDoltDB(context.Background(), types.Format_Default, LocalDirDoltDB)
   267  		cs, _ := NewCommitSpec("master")
   268  		commit, err := ddb.Resolve(context.Background(), cs, nil)
   269  
   270  		if err != nil {
   271  			t.Fatal("Couldn't find commit")
   272  		}
   273  
   274  		meta, err := commit.GetCommitMeta()
   275  		assert.NoError(t, err)
   276  
   277  		if meta.Name != committerName || meta.Email != committerEmail {
   278  			t.Error("Unexpected metadata")
   279  		}
   280  
   281  		root, err := commit.GetRootValue()
   282  
   283  		assert.NoError(t, err)
   284  
   285  		names, err := root.GetTableNames(context.Background())
   286  		assert.NoError(t, err)
   287  		if len(names) != 0 {
   288  			t.Fatal("There should be no tables in empty db")
   289  		}
   290  
   291  		tSchema := createTestSchema(t)
   292  		rowData, _ := createTestRowData(t, ddb.db, tSchema)
   293  		tbl, err = CreateTestTable(ddb.db, tSchema, rowData)
   294  
   295  		if err != nil {
   296  			t.Fatal("Failed to create test table with data")
   297  		}
   298  
   299  		root, err = root.PutTable(context.Background(), "test", tbl)
   300  		assert.NoError(t, err)
   301  
   302  		valHash, err = ddb.WriteRootValue(context.Background(), root)
   303  		assert.NoError(t, err)
   304  	}
   305  
   306  	// reopen the db and commit the value.  Perform a couple checks for
   307  	{
   308  		ddb, _ := LoadDoltDB(context.Background(), types.Format_Default, LocalDirDoltDB)
   309  		meta, err := NewCommitMeta(committerName, committerEmail, "Sample data")
   310  		if err != nil {
   311  			t.Error("Failed to commit")
   312  		}
   313  
   314  		commit, err := ddb.Commit(context.Background(), valHash, ref.NewBranchRef("master"), meta)
   315  		if err != nil {
   316  			t.Error("Failed to commit")
   317  		}
   318  
   319  		numParents, err := commit.NumParents()
   320  		assert.NoError(t, err)
   321  
   322  		if numParents != 1 {
   323  			t.Error("Unexpected ancestry")
   324  		}
   325  
   326  		root, err := commit.GetRootValue()
   327  		assert.NoError(t, err)
   328  
   329  		readTable, ok, err := root.GetTable(context.Background(), "test")
   330  		assert.NoError(t, err)
   331  
   332  		if !ok {
   333  			t.Error("Could not retrieve test table")
   334  		}
   335  
   336  		has, err := readTable.HasTheSameSchema(tbl)
   337  		assert.NoError(t, err)
   338  
   339  		if !has {
   340  			t.Error("Unexpected schema")
   341  		}
   342  	}
   343  }