github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/graveler/ref/resolve_ref_test.go (about)

     1  package ref_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/go-test/deep"
    10  	"github.com/treeverse/lakefs/pkg/graveler"
    11  	"github.com/treeverse/lakefs/pkg/graveler/ref"
    12  	"github.com/treeverse/lakefs/pkg/ident"
    13  	"github.com/treeverse/lakefs/pkg/testutil"
    14  )
    15  
    16  func TestResolveRawRef(t *testing.T) {
    17  	r, _ := testRefManager(t)
    18  
    19  	ctx := context.Background()
    20  	repository, err := r.CreateRepository(ctx, "repo1", graveler.Repository{
    21  		StorageNamespace: "s3://",
    22  		CreationDate:     time.Now(),
    23  		DefaultBranchID:  "main",
    24  	})
    25  	testutil.Must(t, err)
    26  
    27  	mainBranch, err := r.GetBranch(ctx, repository, "main")
    28  	if err != nil {
    29  		t.Fatalf("Failed to get main branch information: %s", err)
    30  	}
    31  
    32  	ts, _ := time.Parse(time.RFC3339, "2020-12-01T15:00:00Z")
    33  	var previous graveler.CommitID
    34  	for i := 0; i < 20; i++ {
    35  		c := graveler.Commit{
    36  			Committer:    "user1",
    37  			Message:      "message1",
    38  			MetaRangeID:  "deadbeef123",
    39  			CreationDate: ts,
    40  			Parents:      graveler.CommitParents{},
    41  			Metadata:     graveler.Metadata{"foo": "bar"},
    42  		}
    43  		if previous != "" {
    44  			c.Parents = append(c.Parents, previous)
    45  		}
    46  		cid, err := r.AddCommit(ctx, repository, c)
    47  		if err != nil {
    48  			t.Fatalf("unexpected error: %v", err)
    49  		}
    50  		previous = cid
    51  		ts = ts.Add(time.Minute)
    52  	}
    53  
    54  	iter, err := r.Log(ctx, repository, previous, false, nil)
    55  	if err != nil {
    56  		t.Fatalf("unexpected error: %v", err)
    57  	}
    58  
    59  	var commitIDs []graveler.CommitID
    60  	for iter.Next() {
    61  		commit := iter.Value()
    62  		if commit == nil {
    63  			t.Fatal("Log iterator returned nil value after Next")
    64  		}
    65  		commitIDs = append(commitIDs, commit.CommitID)
    66  	}
    67  	if err := iter.Err(); err != nil {
    68  		t.Fatalf("unexpected error: %v", err)
    69  	}
    70  	//  commit log:
    71  	commitLog := []graveler.CommitID{
    72  		"de85cde9a3871433ed0afad67d6505aef3b3a779223916be237b44bbdc1b65ed",
    73  		"abd533122a68be2fe2fea4a6a5c48229560acb98331754f87d69d93fade7a497",
    74  		"f5d6eb69609e31016f8c89c983a0b59ee4daca07434c89ecabf927a265feb497",
    75  		"5376fcefd52e7a49ea24d88af06fd3f600210abd3c9fc52fadbcc2ba36b05588",
    76  		"0cb1b3655d48b0b068951fac1cfc276ca01623000b7aabc53f16076a8c91725d",
    77  		"74f7a8402e808b8f621a4f46c2138fbf51f242273f24982d235fe3ab67db4e3b",
    78  		"c2b8083b2ec9d30a9e6d09d4d0938ff48a67ad7bedd532ebf17808f5f199f0b0",
    79  		"6564419ee5290a114a353dc1bc0c4828f46eaeddfde2d6e1b9612fbf1098203d",
    80  		"2f9ac33733f0a0b284d97194711b3b46de9b4ff723545d0a95951668a34cff04",
    81  		"1c6977726cabb5549f1ff9c8ab7ee7672033c8b5b47427b4099f26103e7fb4bc",
    82  		"155a755e8956eebdb31d20dace8b8fd7e8eaa4f84a58f1af701757f9a8bddc4c",
    83  		"504956bd68f17e1a2fec4fc9e2ada276a885236dcc730d9dc99187a4bbe73900",
    84  		"5ed05bd6c8ee3e1fd4d1009b19c5d74b0ef918d5e2c597a4d7d88ba36f4075d2",
    85  		"eb0751d2da465e3f78e90b328630c360085fa8f129351379fa3b8a0ad3fc4424",
    86  		"daf38e9d2a32b43a07a0768e8223d06d2f3122fcf600b2eabde613c077508c73",
    87  		"4f4cc1feb893ed337608cff0962bc1bcde08d3bde9752b1309eea692d87c4470",
    88  		"b12888687d6d3956fd40767f2697f4a3cdf7230e3b86ce63711200e420041b59",
    89  		"2c910787d475900509fef33de2c20700baf33dae8c0abdd6ad416e73b651ab24",
    90  		"c634a9bdf2d2e4d819cadaeb62a0b43ced0b0398aa513ac3cf218b4dca05dcd7",
    91  		"fac53a04432b2e6e7185f3ac8314a874c556b2557adf3aa8d5b1df985cf96566",
    92  	}
    93  
    94  	if diff := deep.Equal(commitIDs, commitLog); diff != nil {
    95  		t.Error("Difference found on commit log", diff)
    96  	}
    97  
    98  	branch1CommitID := commitLog[3]
    99  	testutil.Must(t, r.SetBranch(ctx, repository, "branch1", graveler.Branch{
   100  		CommitID:     branch1CommitID,
   101  		StagingToken: "token1",
   102  	}))
   103  
   104  	branch2CommitID := commitLog[16]
   105  	testutil.Must(t, r.SetBranch(ctx, repository, "branch2", graveler.Branch{
   106  		CommitID:     branch2CommitID,
   107  		StagingToken: "token2",
   108  	}))
   109  
   110  	tagCommitID := commitLog[9]
   111  	testutil.Must(t, r.CreateTag(ctx, repository, "v1.0", tagCommitID))
   112  
   113  	commitCommitID := commitLog[11]
   114  
   115  	branch3Name := string(commitLog[10])[:6]
   116  	branch3CommitID := commitLog[14]
   117  
   118  	testutil.Must(t, r.SetBranch(ctx, repository, graveler.BranchID(branch3Name), graveler.Branch{
   119  		CommitID:     branch3CommitID,
   120  		StagingToken: "token3",
   121  	}))
   122  
   123  	tag2Name := string(commitLog[6])[:10]
   124  	tag2CommitID := commitLog[8]
   125  
   126  	testutil.Must(t, r.CreateTag(ctx, repository, graveler.TagID(tag2Name), tag2CommitID))
   127  
   128  	table := []struct {
   129  		Name                   string
   130  		Ref                    graveler.Ref
   131  		ExpectedCommitID       graveler.CommitID
   132  		ExpectedBranchModifier graveler.ResolvedBranchModifier
   133  		ExpectedToken          graveler.StagingToken
   134  		ExpectedErr            error
   135  	}{
   136  		{
   137  			Name:                   "branch_exist",
   138  			Ref:                    graveler.Ref("branch1"),
   139  			ExpectedBranchModifier: graveler.ResolvedBranchModifierNone,
   140  			ExpectedToken:          "token1",
   141  			ExpectedCommitID:       branch1CommitID,
   142  		},
   143  		{
   144  			Name:        "branch_doesnt_exist",
   145  			Ref:         graveler.Ref("branch3"),
   146  			ExpectedErr: graveler.ErrNotFound,
   147  		},
   148  		{
   149  			Name:                   "branch_head",
   150  			Ref:                    graveler.Ref("branch1@"),
   151  			ExpectedBranchModifier: graveler.ResolvedBranchModifierCommitted,
   152  			ExpectedCommitID:       branch1CommitID,
   153  		},
   154  		{
   155  			Name:        "branch_invalid_head",
   156  			Ref:         graveler.Ref("branch1@1"),
   157  			ExpectedErr: graveler.ErrInvalidRef,
   158  		},
   159  		{
   160  			Name:        "branch_invalid_head_caret1",
   161  			Ref:         graveler.Ref("branch1@^"),
   162  			ExpectedErr: graveler.ErrInvalidRef,
   163  		},
   164  		{
   165  			Name:        "branch_invalid_head_caret2",
   166  			Ref:         graveler.Ref("branch1^@"),
   167  			ExpectedErr: graveler.ErrInvalidRef,
   168  		},
   169  		{
   170  			Name:                   "main_staging",
   171  			Ref:                    graveler.Ref("main$"),
   172  			ExpectedBranchModifier: graveler.ResolvedBranchModifierStaging,
   173  			ExpectedCommitID:       mainBranch.CommitID,
   174  			ExpectedToken:          mainBranch.StagingToken,
   175  		},
   176  		{
   177  			Name:                   "main_committed",
   178  			Ref:                    graveler.Ref("main@"),
   179  			ExpectedBranchModifier: graveler.ResolvedBranchModifierCommitted,
   180  			ExpectedCommitID:       mainBranch.CommitID,
   181  		},
   182  		{
   183  			Name:             "tag_exist",
   184  			Ref:              graveler.Ref("v1.0"),
   185  			ExpectedCommitID: tagCommitID,
   186  		},
   187  		{
   188  			Name:        "tag_doesnt_exist",
   189  			Ref:         graveler.Ref("v1.bad"),
   190  			ExpectedErr: graveler.ErrNotFound,
   191  		},
   192  		{
   193  			Name:             "commit",
   194  			Ref:              graveler.Ref(commitCommitID),
   195  			ExpectedCommitID: commitCommitID,
   196  		},
   197  		{
   198  			Name:             "commit_prefix_good",
   199  			Ref:              graveler.Ref(commitCommitID[:5]),
   200  			ExpectedCommitID: commitCommitID,
   201  		},
   202  		{
   203  			Name:                   "branch_precedes_commit_prefix",
   204  			Ref:                    graveler.Ref(branch3Name),
   205  			ExpectedBranchModifier: graveler.ResolvedBranchModifierNone,
   206  			ExpectedToken:          "token3",
   207  			ExpectedCommitID:       branch3CommitID,
   208  		},
   209  		{
   210  			Name:             "tag_precedes_commit_prefix",
   211  			Ref:              graveler.Ref(tag2Name),
   212  			ExpectedCommitID: tag2CommitID,
   213  		},
   214  		{
   215  			Name:        "commit_prefix_ambiguous",
   216  			Ref:         graveler.Ref("5"),
   217  			ExpectedErr: graveler.ErrNotFound,
   218  		},
   219  		{
   220  			Name:        "commit_prefix_missing",
   221  			Ref:         graveler.Ref("66666"),
   222  			ExpectedErr: graveler.ErrNotFound,
   223  		},
   224  		{
   225  			Name:             "branch_with_modifier",
   226  			Ref:              graveler.Ref(branch1CommitID + "~2"),
   227  			ExpectedCommitID: commitLog[5],
   228  		},
   229  		{
   230  			Name:             "commit_with_modifier",
   231  			Ref:              graveler.Ref(commitCommitID + "~2"),
   232  			ExpectedCommitID: commitLog[13],
   233  		},
   234  		{
   235  			Name:             "commit_prefix_with_modifier",
   236  			Ref:              graveler.Ref(commitCommitID[:5] + "~2"),
   237  			ExpectedCommitID: commitLog[13],
   238  		},
   239  		{
   240  			Name:        "commit_prefix_with_modifier_too_big",
   241  			Ref:         graveler.Ref(commitCommitID + "~200"),
   242  			ExpectedErr: graveler.ErrNotFound,
   243  		},
   244  	}
   245  
   246  	for _, cas := range table {
   247  		t.Run(cas.Name, func(t *testing.T) {
   248  			rawRef, err := r.ParseRef(cas.Ref)
   249  			if err != nil {
   250  				if cas.ExpectedErr == nil || !errors.Is(err, cas.ExpectedErr) {
   251  					t.Fatalf("unexpected error while parse '%s': %v, expected: %v", cas.Ref, err, cas.ExpectedErr)
   252  				}
   253  				return
   254  			}
   255  			resolvedRef, err := r.ResolveRawRef(ctx, repository, rawRef)
   256  			if err != nil {
   257  				if cas.ExpectedErr == nil || !errors.Is(err, cas.ExpectedErr) {
   258  					t.Fatalf("unexpected error while resolve '%s': %v, expected: %v", cas.Ref, err, cas.ExpectedErr)
   259  				}
   260  				return
   261  			}
   262  			if cas.ExpectedCommitID != resolvedRef.CommitID {
   263  				t.Fatalf("got unexpected commit ID: '%s', expected: '%s'",
   264  					resolvedRef.CommitID, cas.ExpectedCommitID)
   265  			}
   266  			if cas.ExpectedToken != resolvedRef.StagingToken {
   267  				t.Fatalf("got unexpected staging token: '%s', expected: '%s'",
   268  					resolvedRef.StagingToken, cas.ExpectedToken)
   269  			}
   270  			if cas.ExpectedBranchModifier != resolvedRef.ResolvedBranchModifier {
   271  				t.Fatalf("got unexpected branch modifier: %d, expected: %d",
   272  					resolvedRef.ResolvedBranchModifier, cas.ExpectedBranchModifier)
   273  			}
   274  		})
   275  	}
   276  }
   277  
   278  func TestResolveRef_SameDate(t *testing.T) {
   279  	r, _ := testRefManager(t)
   280  	ctx := context.Background()
   281  	repository, err := r.CreateRepository(ctx, "repo1", graveler.Repository{
   282  		StorageNamespace: "s3://",
   283  		CreationDate:     time.Now(),
   284  		DefaultBranchID:  "main",
   285  	})
   286  	testutil.Must(t, err)
   287  
   288  	ts, _ := time.Parse(time.RFC3339, "2020-12-01T15:00:00Z")
   289  	addCommit := func(message string, parents ...graveler.CommitID) graveler.CommitID {
   290  		c := graveler.Commit{
   291  			Message:      message,
   292  			Committer:    "tester",
   293  			MetaRangeID:  "deadbeef1",
   294  			CreationDate: ts,
   295  			Parents:      graveler.CommitParents{},
   296  		}
   297  		for _, p := range parents {
   298  			c.Parents = append(c.Parents, p)
   299  		}
   300  		cid, err := r.AddCommit(ctx, repository, c)
   301  		testutil.MustDo(t, "add commit", err)
   302  		return cid
   303  	}
   304  	c1 := addCommit("c1")
   305  	c2 := addCommit("c2", c1)
   306  	c3 := addCommit("c3", c1)
   307  	c4 := addCommit("c4", c3)
   308  	c5 := addCommit("c5", c4, c2)
   309  
   310  	it, err := r.Log(ctx, repository, c5, false, nil)
   311  	testutil.MustDo(t, "Log request", err)
   312  	var commitIDs []graveler.CommitID
   313  	for it.Next() {
   314  		commit := it.Value()
   315  		commitIDs = append(commitIDs, commit.CommitID)
   316  	}
   317  	testutil.MustDo(t, "Log complete iteration", it.Err())
   318  
   319  	expected := []graveler.CommitID{c5, c4, c3, c2, c1}
   320  	if diff := deep.Equal(commitIDs, expected); diff != nil {
   321  		t.Fatal("Iterator over commits found diff:", diff)
   322  	}
   323  }
   324  
   325  func TestResolveRef_DereferenceWithGraph(t *testing.T) {
   326  	/*
   327  		This is taken from `git help rev-parse` - let's run these tests
   328  
   329  		           G   H   I   J
   330  		            \ /     \ /
   331  		             D   E   F
   332  		              \  |  / \
   333  		               \ | /   |
   334  		                \|/    |
   335  		                 B     C
   336  		                  \   /
   337  		                   \ /
   338  		                    A
   339  
   340  		           A =      = A^0
   341  		           B = A^   = A^1     = A~1
   342  		           C =      = A^2
   343  		           D = A^^  = A^1^1   = A~2
   344  		           E = B^2  = A^^2
   345  		           F = B^3  = A^^3
   346  		           G = A^^^ = A^1^1^1 = A~3
   347  		           H = D^2  = B^^2    = A^^^2  = A~2^2
   348  		           I = F^   = B^3^    = A^^3^
   349  		           J = F^2  = B^3^2   = A^^3^2
   350  	*/
   351  	r, _ := testRefManager(t)
   352  	repository, err := r.CreateRepository(context.Background(), "repo1", graveler.Repository{
   353  		StorageNamespace: "s3://",
   354  		CreationDate:     time.Now(),
   355  		DefaultBranchID:  "main",
   356  	})
   357  	testutil.Must(t, err)
   358  
   359  	ts, _ := time.Parse(time.RFC3339, "2020-12-01T15:00:00Z")
   360  	addCommit := func(parents ...graveler.CommitID) graveler.CommitID {
   361  		commit := graveler.Commit{
   362  			CreationDate: ts,
   363  		}
   364  		for _, p := range parents {
   365  			commit.Parents = append(commit.Parents, p)
   366  		}
   367  		cid, err := r.AddCommit(context.Background(), repository, commit)
   368  		testutil.MustDo(t, "add commit", err)
   369  		ts = ts.Add(time.Second)
   370  		return cid
   371  	}
   372  
   373  	G := addCommit()
   374  	H := addCommit()
   375  	I := addCommit()
   376  	J := addCommit()
   377  	D := addCommit(G, H)
   378  	E := addCommit()
   379  	F := addCommit(I, J)
   380  	B := addCommit(D, E, F)
   381  	C := addCommit(F)
   382  	A := addCommit(B, C)
   383  
   384  	resolve := func(base graveler.CommitID, mod string, expected graveler.CommitID) {
   385  		t.Helper()
   386  		reference := string(base) + mod
   387  		resolved, err := resolveRef(context.Background(), r, ident.NewHexAddressProvider(), repository, graveler.Ref(reference))
   388  		if err != nil {
   389  			t.Fatalf("unexpected error: %v", err)
   390  		}
   391  		if resolved.CommitID != expected {
   392  			t.Fatalf("Ref %s got %s, expected %s", reference, resolved.CommitID, expected)
   393  		}
   394  	}
   395  
   396  	// now the tests:
   397  	resolve(A, "^0", A)
   398  	resolve(A, "^", B)
   399  	resolve(A, "^1", B)
   400  	resolve(A, "~1", B)
   401  	resolve(A, "^2", C)
   402  	resolve(A, "^^", D)
   403  	resolve(A, "^1^1", D)
   404  	resolve(A, "~2", D)
   405  	resolve(B, "^2", E)
   406  	resolve(A, "^^2", E)
   407  	resolve(B, "^2", E)
   408  	resolve(A, "^^2", E)
   409  	resolve(B, "^3", F)
   410  	resolve(A, "^^3", F)
   411  	resolve(A, "^^^", G)
   412  	resolve(A, "^1^1^1", G)
   413  	resolve(A, "~3", G)
   414  	resolve(D, "^2", H)
   415  	resolve(B, "^^2", H)
   416  	resolve(A, "^^^2", H)
   417  	resolve(A, "~2^2", H)
   418  	resolve(F, "^", I)
   419  	resolve(B, "^3^", I)
   420  	resolve(A, "^^3^", I)
   421  	resolve(F, "^2", J)
   422  	resolve(B, "^3^2", J)
   423  	resolve(A, "^^3^2", J)
   424  }
   425  
   426  func resolveRef(ctx context.Context, store ref.Store, addressProvider ident.AddressProvider, repository *graveler.RepositoryRecord, reference graveler.Ref) (*graveler.ResolvedRef, error) {
   427  	rawRef, err := ref.ParseRef(reference)
   428  	if err != nil {
   429  		return nil, err
   430  	}
   431  	return ref.ResolveRawRef(ctx, store, addressProvider, repository, rawRef)
   432  }