github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/store/chunks/chunk_store_common_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 chunks
    23  
    24  import (
    25  	"context"
    26  
    27  	"github.com/stretchr/testify/assert"
    28  	"github.com/stretchr/testify/suite"
    29  
    30  	"github.com/dolthub/dolt/go/store/constants"
    31  	"github.com/dolthub/dolt/go/store/hash"
    32  )
    33  
    34  type ChunkStoreTestSuite struct {
    35  	suite.Suite
    36  	Factory *memoryStoreFactory
    37  }
    38  
    39  func noopGetAddrs(c Chunk) GetAddrsCb {
    40  	return func(ctx context.Context, addrs hash.HashSet, _ PendingRefExists) error {
    41  		return nil
    42  	}
    43  }
    44  
    45  func (suite *ChunkStoreTestSuite) TestChunkStorePut() {
    46  	ctx := context.Background()
    47  	store := suite.Factory.CreateStore(ctx, "ns")
    48  	input := "abc"
    49  	c := NewChunk([]byte(input))
    50  	err := store.Put(ctx, c, noopGetAddrs)
    51  	suite.NoError(err)
    52  	h := c.Hash()
    53  
    54  	// Reading it via the API should work.
    55  	assertInputInStore(input, h, store, suite.Assert())
    56  
    57  	// Put chunk with dangling ref should error on Commit
    58  	data := []byte("bcd")
    59  	nc := NewChunk(data)
    60  	err = store.Put(ctx, nc, func(c Chunk) GetAddrsCb {
    61  		return func(ctx context.Context, addrs hash.HashSet, _ PendingRefExists) error {
    62  			addrs.Insert(hash.Of([]byte("nonsense")))
    63  			return nil
    64  		}
    65  	})
    66  	suite.NoError(err)
    67  	root, err := store.Root(ctx)
    68  	suite.NoError(err)
    69  
    70  	_, err = store.Commit(ctx, root, root)
    71  	suite.Error(err)
    72  }
    73  
    74  func (suite *ChunkStoreTestSuite) TestChunkStoreRoot() {
    75  	store := suite.Factory.CreateStore(context.Background(), "ns")
    76  	oldRoot, err := store.Root(context.Background())
    77  	suite.NoError(err)
    78  	suite.True(oldRoot.IsEmpty())
    79  
    80  	bogusRoot := hash.Parse("8habda5skfek1265pc5d5l1orptn5dr0")
    81  	newRoot := hash.Parse("8la6qjbh81v85r6q67lqbfrkmpds14lg")
    82  
    83  	// Try to update root with bogus oldRoot
    84  	result, err := store.Commit(context.Background(), newRoot, bogusRoot)
    85  	suite.NoError(err)
    86  	suite.False(result)
    87  
    88  	// Now do a valid root update
    89  	result, err = store.Commit(context.Background(), newRoot, oldRoot)
    90  	suite.NoError(err)
    91  	suite.True(result)
    92  }
    93  
    94  func (suite *ChunkStoreTestSuite) TestChunkStoreCommitPut() {
    95  	name := "ns"
    96  	store := suite.Factory.CreateStore(context.Background(), name)
    97  	input := "abc"
    98  	c := NewChunk([]byte(input))
    99  	err := store.Put(context.Background(), c, noopGetAddrs)
   100  	suite.NoError(err)
   101  	h := c.Hash()
   102  
   103  	// Reading it via the API should work...
   104  	assertInputInStore(input, h, store, suite.Assert())
   105  	// ...but it shouldn't be persisted yet
   106  	assertInputNotInStore(input, h, suite.Factory.CreateStore(context.Background(), name), suite.Assert())
   107  
   108  	r, err := store.Root(context.Background())
   109  	suite.NoError(err)
   110  	_, err = store.Commit(context.Background(), h, r) // Commit persists Chunks
   111  	suite.NoError(err)
   112  	assertInputInStore(input, h, store, suite.Assert())
   113  	assertInputInStore(input, h, suite.Factory.CreateStore(context.Background(), name), suite.Assert())
   114  }
   115  
   116  func (suite *ChunkStoreTestSuite) TestChunkStoreGetNonExisting() {
   117  	store := suite.Factory.CreateStore(context.Background(), "ns")
   118  	h := hash.Parse("11111111111111111111111111111111")
   119  	c, err := store.Get(context.Background(), h)
   120  	suite.NoError(err)
   121  	suite.True(c.IsEmpty())
   122  }
   123  
   124  func (suite *ChunkStoreTestSuite) TestChunkStoreVersion() {
   125  	store := suite.Factory.CreateStore(context.Background(), "ns")
   126  	oldRoot, err := store.Root(context.Background())
   127  	suite.NoError(err)
   128  	suite.True(oldRoot.IsEmpty())
   129  	newRoot := hash.Parse("11111222223333344444555556666677")
   130  	success, err := store.Commit(context.Background(), newRoot, oldRoot)
   131  	suite.NoError(err)
   132  	suite.True(success)
   133  
   134  	suite.Equal(constants.FormatLD1String, store.Version())
   135  }
   136  
   137  func (suite *ChunkStoreTestSuite) TestChunkStoreCommitUnchangedRoot() {
   138  	store1, store2 := suite.Factory.CreateStore(context.Background(), "ns"), suite.Factory.CreateStore(context.Background(), "ns")
   139  	input := "abc"
   140  	c := NewChunk([]byte(input))
   141  	err := store1.Put(context.Background(), c, noopGetAddrs)
   142  	suite.NoError(err)
   143  	h := c.Hash()
   144  
   145  	// Reading c from store1 via the API should work...
   146  	assertInputInStore(input, h, store1, suite.Assert())
   147  	// ...but not store2.
   148  	assertInputNotInStore(input, h, store2, suite.Assert())
   149  
   150  	newRoot, err := store1.Root(context.Background())
   151  	suite.NoError(err)
   152  	oldRoot, err := store1.Root(context.Background())
   153  	suite.NoError(err)
   154  	_, err = store1.Commit(context.Background(), newRoot, oldRoot)
   155  	suite.NoError(err)
   156  
   157  	err = store2.Rebase(context.Background())
   158  	suite.NoError(err)
   159  
   160  	// Now, reading c from store2 via the API should work...
   161  	assertInputInStore(input, h, store2, suite.Assert())
   162  }
   163  
   164  func assertInputInStore(input string, h hash.Hash, s ChunkStore, assert *assert.Assertions) {
   165  	chunk, err := s.Get(context.Background(), h)
   166  	assert.NoError(err)
   167  	assert.False(chunk.IsEmpty(), "Shouldn't get empty chunk for %s", h.String())
   168  	assert.Equal(input, string(chunk.Data()))
   169  }
   170  
   171  func assertInputNotInStore(input string, h hash.Hash, s ChunkStore, assert *assert.Assertions) {
   172  	chunk, err := s.Get(context.Background(), h)
   173  	assert.NoError(err)
   174  	assert.True(chunk.IsEmpty(), "Shouldn't get non-empty chunk for %s: %v", h.String(), chunk)
   175  }