github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/env/actions/commitwalk/commitwalk_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 commitwalk
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  	"time"
    21  
    22  	"github.com/stretchr/testify/assert"
    23  	"github.com/stretchr/testify/require"
    24  
    25  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    26  	"github.com/dolthub/dolt/go/libraries/doltcore/env"
    27  	"github.com/dolthub/dolt/go/libraries/doltcore/merge"
    28  	"github.com/dolthub/dolt/go/libraries/doltcore/ref"
    29  	"github.com/dolthub/dolt/go/libraries/utils/filesys"
    30  	"github.com/dolthub/dolt/go/store/datas"
    31  	"github.com/dolthub/dolt/go/store/datas/pull"
    32  	"github.com/dolthub/dolt/go/store/hash"
    33  	"github.com/dolthub/dolt/go/store/types"
    34  )
    35  
    36  const (
    37  	testHomeDir = "/doesnotexist/home"
    38  	workingDir  = "/doesnotexist/work"
    39  )
    40  
    41  var lastUserTSMillis int64
    42  
    43  func MonotonicNow() time.Time {
    44  	now := time.Now()
    45  	millis := now.UnixMilli()
    46  	if millis <= lastUserTSMillis {
    47  		now = time.UnixMilli(lastUserTSMillis).Add(time.Millisecond)
    48  	}
    49  	lastUserTSMillis = now.UnixMilli()
    50  	return now
    51  }
    52  
    53  func testHomeDirFunc() (string, error) {
    54  	return testHomeDir, nil
    55  }
    56  
    57  func createUninitializedEnv() *env.DoltEnv {
    58  	initialDirs := []string{testHomeDir, workingDir}
    59  	fs := filesys.NewInMemFS(initialDirs, nil, workingDir)
    60  	dEnv := env.Load(context.Background(), testHomeDirFunc, fs, doltdb.InMemDoltDB, "test")
    61  	return dEnv
    62  }
    63  
    64  func TestGetDotDotRevisions(t *testing.T) {
    65  	dEnv := createUninitializedEnv()
    66  	err := dEnv.InitRepo(context.Background(), types.Format_Default, "Bill Billerson", "bill@billerson.com", env.DefaultInitBranch)
    67  	require.NoError(t, err)
    68  
    69  	cs, err := doltdb.NewCommitSpec(env.DefaultInitBranch)
    70  	require.NoError(t, err)
    71  	opt, err := dEnv.DoltDB.Resolve(context.Background(), cs, nil)
    72  	require.NoError(t, err)
    73  	commit, ok := opt.ToCommit()
    74  	require.True(t, ok)
    75  
    76  	rv, err := commit.GetRootValue(context.Background())
    77  	require.NoError(t, err)
    78  	r, rvh, err := dEnv.DoltDB.WriteRootValue(context.Background(), rv)
    79  	require.NoError(t, err)
    80  	rv = r
    81  
    82  	// Create 5 commits on main.
    83  	mainCommits := make([]*doltdb.Commit, 6)
    84  	mainCommits[0] = commit
    85  	for i := 1; i < 6; i++ {
    86  		mainCommits[i] = mustCreateCommit(t, dEnv.DoltDB, env.DefaultInitBranch, rvh, mainCommits[i-1])
    87  	}
    88  
    89  	// Create a feature branch.
    90  	bref := ref.NewBranchRef("feature")
    91  	err = dEnv.DoltDB.NewBranchAtCommit(context.Background(), bref, mainCommits[5], nil)
    92  	require.NoError(t, err)
    93  
    94  	// Create 3 commits on feature branch.
    95  	featureCommits := []*doltdb.Commit{}
    96  	featureCommits = append(featureCommits, mainCommits[5])
    97  	for i := 1; i < 4; i++ {
    98  		featureCommits = append(featureCommits, mustCreateCommit(t, dEnv.DoltDB, "feature", rvh, featureCommits[i-1]))
    99  	}
   100  
   101  	// Create 1 commit on main.
   102  	mainCommits = append(mainCommits, mustCreateCommit(t, dEnv.DoltDB, env.DefaultInitBranch, rvh, mainCommits[5]))
   103  
   104  	// Merge main to feature branch.
   105  	featureCommits = append(featureCommits, mustCreateCommit(t, dEnv.DoltDB, "feature", rvh, featureCommits[3], mainCommits[6]))
   106  
   107  	// Create 3 commits on feature branch.
   108  	for i := 5; i < 8; i++ {
   109  		featureCommits = append(featureCommits, mustCreateCommit(t, dEnv.DoltDB, "feature", rvh, featureCommits[i-1]))
   110  	}
   111  
   112  	// Create 3 commits on main.
   113  	for i := 7; i < 10; i++ {
   114  		mainCommits = append(mainCommits, mustCreateCommit(t, dEnv.DoltDB, env.DefaultInitBranch, rvh, mainCommits[i-1]))
   115  	}
   116  
   117  	// Branches look like this:
   118  	//
   119  	//                       feature:  F1--F2--F3--F4--F5--F6--F7
   120  	//                                /            /
   121  	// main: M0--M1--M2--M3--M4--M5/F0------------M6--M7--M8--M9
   122  
   123  	featureHash := mustGetHash(t, featureCommits[7])
   124  	mainHash := mustGetHash(t, mainCommits[6])
   125  	featurePreMergeHash := mustGetHash(t, featureCommits[3])
   126  
   127  	res, err := GetDotDotRevisions(context.Background(), dEnv.DoltDB, []hash.Hash{featureHash}, dEnv.DoltDB, []hash.Hash{mainHash}, 100)
   128  	require.NoError(t, err)
   129  
   130  	assert.Len(t, res, 7)
   131  	assertEqualHashes(t, featureCommits[7], res[0])
   132  	assertEqualHashes(t, featureCommits[6], res[1])
   133  	assertEqualHashes(t, featureCommits[5], res[2])
   134  	assertEqualHashes(t, featureCommits[4], res[3])
   135  	assertEqualHashes(t, featureCommits[3], res[4])
   136  	assertEqualHashes(t, featureCommits[2], res[5])
   137  	assertEqualHashes(t, featureCommits[1], res[6])
   138  
   139  	res, err = GetDotDotRevisions(context.Background(), dEnv.DoltDB, []hash.Hash{mainHash}, dEnv.DoltDB, []hash.Hash{featureHash}, 100)
   140  	require.NoError(t, err)
   141  	assert.Len(t, res, 0)
   142  
   143  	res, err = GetDotDotRevisions(context.Background(), dEnv.DoltDB, []hash.Hash{featureHash}, dEnv.DoltDB, []hash.Hash{mainHash}, 3)
   144  	require.NoError(t, err)
   145  	assert.Len(t, res, 3)
   146  	assertEqualHashes(t, featureCommits[7], res[0])
   147  	assertEqualHashes(t, featureCommits[6], res[1])
   148  	assertEqualHashes(t, featureCommits[5], res[2])
   149  
   150  	res, err = GetDotDotRevisions(context.Background(), dEnv.DoltDB, []hash.Hash{featurePreMergeHash}, dEnv.DoltDB, []hash.Hash{mainHash}, 3)
   151  	require.NoError(t, err)
   152  	assert.Len(t, res, 3)
   153  	assertEqualHashes(t, featureCommits[3], res[0])
   154  	assertEqualHashes(t, featureCommits[2], res[1])
   155  	assertEqualHashes(t, featureCommits[1], res[2])
   156  
   157  	res, err = GetDotDotRevisions(context.Background(), dEnv.DoltDB, []hash.Hash{featurePreMergeHash}, dEnv.DoltDB, []hash.Hash{mainHash}, 3)
   158  	require.NoError(t, err)
   159  	assert.Len(t, res, 3)
   160  	assertEqualHashes(t, featureCommits[3], res[0])
   161  	assertEqualHashes(t, featureCommits[2], res[1])
   162  	assertEqualHashes(t, featureCommits[1], res[2])
   163  
   164  	// Three dot
   165  	mergeBaseHash, err := merge.MergeBase(context.Background(), mainCommits[6], featureCommits[7])
   166  	require.NoError(t, err)
   167  
   168  	res, err = GetDotDotRevisions(context.Background(), dEnv.DoltDB, []hash.Hash{featureHash, mainHash}, dEnv.DoltDB, []hash.Hash{mergeBaseHash}, -1)
   169  	require.NoError(t, err)
   170  	assert.Len(t, res, 7)
   171  	assertEqualHashes(t, featureCommits[7], res[0])
   172  	assertEqualHashes(t, featureCommits[6], res[1])
   173  	assertEqualHashes(t, featureCommits[5], res[2])
   174  	assertEqualHashes(t, featureCommits[4], res[3])
   175  	assertEqualHashes(t, featureCommits[3], res[4])
   176  	assertEqualHashes(t, featureCommits[2], res[5])
   177  	assertEqualHashes(t, featureCommits[1], res[6])
   178  
   179  	mergeBaseHash, err = merge.MergeBase(context.Background(), mainCommits[6], featureCommits[3])
   180  	require.NoError(t, err)
   181  
   182  	res, err = GetDotDotRevisions(context.Background(), dEnv.DoltDB, []hash.Hash{featurePreMergeHash, mainHash}, dEnv.DoltDB, []hash.Hash{mergeBaseHash}, -1)
   183  	require.NoError(t, err)
   184  	assert.Len(t, res, 4)
   185  	assertEqualHashes(t, featureCommits[3], res[0])
   186  	assertEqualHashes(t, featureCommits[2], res[1])
   187  	assertEqualHashes(t, mainCommits[6], res[2])
   188  	assertEqualHashes(t, featureCommits[1], res[3])
   189  
   190  	// Create a similar branch to "feature" on a forked repository and GetDotDotRevisions using that as well.
   191  	forkEnv := mustForkDB(t, dEnv.DoltDB, "feature", featureCommits[4])
   192  
   193  	// Create 3 commits on feature branch.
   194  	for i := 5; i < 8; i++ {
   195  		featureCommits[i] = mustCreateCommit(t, forkEnv.DoltDB, "feature", rvh, featureCommits[i-1])
   196  	}
   197  
   198  	featureHash = mustGetHash(t, featureCommits[7])
   199  	mainHash = mustGetHash(t, mainCommits[6])
   200  	featurePreMergeHash = mustGetHash(t, featureCommits[3])
   201  
   202  	res, err = GetDotDotRevisions(context.Background(), dEnv.DoltDB, []hash.Hash{featureHash}, dEnv.DoltDB, []hash.Hash{mainHash}, 100)
   203  	require.Error(t, err)
   204  	res, err = GetDotDotRevisions(context.Background(), forkEnv.DoltDB, []hash.Hash{featureHash}, dEnv.DoltDB, []hash.Hash{mainHash}, 100)
   205  	require.NoError(t, err)
   206  	assert.Len(t, res, 7)
   207  	assertEqualHashes(t, featureCommits[7], res[0])
   208  	assertEqualHashes(t, featureCommits[6], res[1])
   209  	assertEqualHashes(t, featureCommits[5], res[2])
   210  	assertEqualHashes(t, featureCommits[4], res[3])
   211  	assertEqualHashes(t, featureCommits[3], res[4])
   212  	assertEqualHashes(t, featureCommits[2], res[5])
   213  	assertEqualHashes(t, featureCommits[1], res[6])
   214  
   215  	res, err = GetDotDotRevisions(context.Background(), dEnv.DoltDB, []hash.Hash{mainHash}, dEnv.DoltDB, []hash.Hash{featureHash}, 100)
   216  	require.Error(t, err)
   217  	res, err = GetDotDotRevisions(context.Background(), dEnv.DoltDB, []hash.Hash{mainHash}, forkEnv.DoltDB, []hash.Hash{featureHash}, 100)
   218  	require.NoError(t, err)
   219  	assert.Len(t, res, 0)
   220  
   221  	res, err = GetDotDotRevisions(context.Background(), forkEnv.DoltDB, []hash.Hash{featureHash}, dEnv.DoltDB, []hash.Hash{mainHash}, 3)
   222  	require.NoError(t, err)
   223  	assert.Len(t, res, 3)
   224  	assertEqualHashes(t, featureCommits[7], res[0])
   225  	assertEqualHashes(t, featureCommits[6], res[1])
   226  	assertEqualHashes(t, featureCommits[5], res[2])
   227  
   228  	res, err = GetDotDotRevisions(context.Background(), dEnv.DoltDB, []hash.Hash{featurePreMergeHash}, dEnv.DoltDB, []hash.Hash{mainHash}, 3)
   229  	require.NoError(t, err)
   230  	assert.Len(t, res, 3)
   231  	assertEqualHashes(t, featureCommits[3], res[0])
   232  	assertEqualHashes(t, featureCommits[2], res[1])
   233  	assertEqualHashes(t, featureCommits[1], res[2])
   234  
   235  	res, err = GetDotDotRevisions(context.Background(), forkEnv.DoltDB, []hash.Hash{featurePreMergeHash}, dEnv.DoltDB, []hash.Hash{mainHash}, 3)
   236  	require.NoError(t, err)
   237  	assert.Len(t, res, 3)
   238  	assertEqualHashes(t, featureCommits[3], res[0])
   239  	assertEqualHashes(t, featureCommits[2], res[1])
   240  	assertEqualHashes(t, featureCommits[1], res[2])
   241  }
   242  
   243  func assertEqualHashes(t *testing.T, lc, rc interface{}) {
   244  	leftCm, ok := lc.(*doltdb.Commit)
   245  	if !ok {
   246  		opt, ok := lc.(*doltdb.OptionalCommit)
   247  		require.True(t, ok)
   248  		leftCm, ok = opt.ToCommit()
   249  		require.True(t, ok)
   250  	}
   251  
   252  	rightCm, ok := rc.(*doltdb.Commit)
   253  	if !ok {
   254  		opt, ok := rc.(*doltdb.OptionalCommit)
   255  		require.True(t, ok)
   256  		rightCm, ok = opt.ToCommit()
   257  		require.True(t, ok)
   258  	}
   259  
   260  	assert.Equal(t, mustGetHash(t, leftCm), mustGetHash(t, rightCm))
   261  }
   262  
   263  func mustCreateCommit(t *testing.T, ddb *doltdb.DoltDB, bn string, rvh hash.Hash, parents ...*doltdb.Commit) *doltdb.Commit {
   264  	cm, err := datas.NewCommitMetaWithUserTS("Bill Billerson", "bill@billerson.com", "A New Commit.", MonotonicNow())
   265  	require.NoError(t, err)
   266  	pcs := make([]*doltdb.CommitSpec, 0, len(parents))
   267  	for _, parent := range parents {
   268  		cs, err := doltdb.NewCommitSpec(mustGetHash(t, parent).String())
   269  		require.NoError(t, err)
   270  		pcs = append(pcs, cs)
   271  	}
   272  	bref := ref.NewBranchRef(bn)
   273  	commit, err := ddb.CommitWithParentSpecs(context.Background(), rvh, bref, pcs, cm)
   274  	require.NoError(t, err)
   275  	return commit
   276  }
   277  
   278  func mustForkDB(t *testing.T, fromDB *doltdb.DoltDB, bn string, cm *doltdb.Commit) *env.DoltEnv {
   279  	h, err := cm.HashOf()
   280  	require.NoError(t, err)
   281  	forkEnv := createUninitializedEnv()
   282  	err = forkEnv.InitRepo(context.Background(), types.Format_Default, "Bill Billerson", "bill@billerson.com", env.DefaultInitBranch)
   283  	require.NoError(t, err)
   284  	ps := make(chan pull.Stats)
   285  	go func() {
   286  		for range ps {
   287  		}
   288  	}()
   289  	err = forkEnv.DoltDB.PullChunks(context.Background(), "", fromDB, []hash.Hash{h}, ps, nil)
   290  	if err == pull.ErrDBUpToDate {
   291  		err = nil
   292  	}
   293  	require.NoError(t, err)
   294  	err = forkEnv.DoltDB.SetHead(context.Background(), ref.NewBranchRef(bn), h)
   295  	require.NoError(t, err)
   296  	return forkEnv
   297  }
   298  
   299  func mustGetHash(t *testing.T, c *doltdb.Commit) hash.Hash {
   300  	h, err := c.HashOf()
   301  	require.NoError(t, err)
   302  	return h
   303  }