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 }