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 }