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

     1  package ref_test
     2  
     3  import (
     4  	"context"
     5  	"encoding/hex"
     6  	"errors"
     7  	"fmt"
     8  	"reflect"
     9  	"sort"
    10  	"strconv"
    11  	"strings"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/go-test/deep"
    16  	"github.com/golang/mock/gomock"
    17  	"github.com/stretchr/testify/assert"
    18  	"github.com/stretchr/testify/require"
    19  	"github.com/treeverse/lakefs/pkg/batch"
    20  	"github.com/treeverse/lakefs/pkg/graveler"
    21  	"github.com/treeverse/lakefs/pkg/graveler/ref"
    22  	"github.com/treeverse/lakefs/pkg/ident"
    23  	"github.com/treeverse/lakefs/pkg/kv"
    24  	"github.com/treeverse/lakefs/pkg/kv/mock"
    25  	"github.com/treeverse/lakefs/pkg/testutil"
    26  	"google.golang.org/protobuf/proto"
    27  	"google.golang.org/protobuf/types/known/timestamppb"
    28  )
    29  
    30  // TestManager_GetRepositoryCache test get repository information while using cache. Match the number of times we
    31  // call get repository vs number of times we fetch the data.
    32  func TestManager_GetRepositoryCache(t *testing.T) {
    33  	const (
    34  		times = 1
    35  		calls = 3
    36  	)
    37  	ctrl := gomock.NewController(t)
    38  	mockStore := mock.NewMockStore(ctrl)
    39  	ctx := context.Background()
    40  	mockStore.EXPECT().Get(ctx, []byte("graveler"), []byte("repos/repo1")).Times(times).Return(&kv.ValueWithPredicate{}, nil)
    41  	cacheConfig := ref.CacheConfig{
    42  		Size:   100,
    43  		Expiry: 20 * time.Millisecond,
    44  		Jitter: 0,
    45  	}
    46  	cfg := ref.ManagerConfig{
    47  		Executor:              batch.NopExecutor(),
    48  		KVStore:               mockStore,
    49  		AddressProvider:       ident.NewHexAddressProvider(),
    50  		RepositoryCacheConfig: cacheConfig,
    51  		CommitCacheConfig:     cacheConfig,
    52  	}
    53  	refManager := ref.NewRefManager(cfg)
    54  	for i := 0; i < calls; i++ {
    55  		_, err := refManager.GetRepository(ctx, "repo1")
    56  		if err != nil {
    57  			t.Fatalf("Failed to get repository (iteration %d): %s", i, err)
    58  		}
    59  	}
    60  
    61  	// wait for cache to expire and call again
    62  	time.Sleep(cacheConfig.Expiry + cacheConfig.Jitter + time.Second)
    63  	mockStore.EXPECT().Get(ctx, []byte("graveler"), []byte("repos/repo1")).Times(1).Return(&kv.ValueWithPredicate{}, nil)
    64  	_, err := refManager.GetRepository(ctx, "repo1")
    65  	if err != nil {
    66  		t.Fatalf("Failed to get repository: %s", err)
    67  	}
    68  }
    69  
    70  // TestManager_GetCommitCache test gets commit record while using cache. We match the number of times we call get repository vs number of times we fetch the data.
    71  func TestManager_GetCommitCache(t *testing.T) {
    72  	const (
    73  		times = 1
    74  		calls = 3
    75  	)
    76  	ctrl := gomock.NewController(t)
    77  	mockStore := mock.NewMockStore(ctrl)
    78  	ctx := context.Background()
    79  
    80  	const commitID = "8a3e3f677ed588ab1e19b6cdb050cbce383f9f1166200e7b7252932ceb61189c"
    81  	const repoID = "repo2"
    82  	const repoInstanceID = "iuid"
    83  	mockStore.EXPECT().
    84  		Get(ctx, []byte(repoID+"-"+repoInstanceID), []byte("commits/"+commitID)).
    85  		Times(times).
    86  		Return(&kv.ValueWithPredicate{}, nil)
    87  
    88  	cacheConfig := ref.CacheConfig{
    89  		Size:   100,
    90  		Expiry: 20 * time.Millisecond,
    91  	}
    92  	cfg := ref.ManagerConfig{
    93  		Executor:              batch.NopExecutor(),
    94  		KVStore:               mockStore,
    95  		AddressProvider:       ident.NewHexAddressProvider(),
    96  		RepositoryCacheConfig: cacheConfig,
    97  		CommitCacheConfig:     cacheConfig,
    98  	}
    99  	refManager := ref.NewRefManager(cfg)
   100  	for i := 0; i < calls; i++ {
   101  		_, err := refManager.GetCommit(ctx, &graveler.RepositoryRecord{
   102  			RepositoryID: repoID,
   103  			Repository:   &graveler.Repository{InstanceUID: repoInstanceID},
   104  		}, commitID)
   105  		if err != nil {
   106  			t.Fatalf("Failed to get commit (iteration %d): %s", i, err)
   107  		}
   108  	}
   109  
   110  	// wait for cache to expire and call again
   111  	time.Sleep(cacheConfig.Expiry + cacheConfig.Jitter + time.Second)
   112  	mockStore.EXPECT().
   113  		Get(ctx, []byte(repoID+"-"+repoInstanceID), []byte("commits/"+commitID)).
   114  		Times(times).
   115  		Return(&kv.ValueWithPredicate{}, nil)
   116  	_, err := refManager.GetCommit(ctx, &graveler.RepositoryRecord{
   117  		RepositoryID: repoID,
   118  		Repository:   &graveler.Repository{InstanceUID: repoInstanceID},
   119  	}, commitID)
   120  	if err != nil {
   121  		t.Fatalf("Failed to get repository: %s", err)
   122  	}
   123  }
   124  
   125  func TestManager_GetRepository(t *testing.T) {
   126  	r, _ := testRefManager(t)
   127  	t.Run("repo_doesnt_exist", func(t *testing.T) {
   128  		_, err := r.GetRepository(context.Background(), "example-repo")
   129  		if !errors.Is(err, graveler.ErrRepositoryNotFound) {
   130  			t.Fatalf("expected ErrRepositoryNotFound got error: %v", err)
   131  		}
   132  	})
   133  	t.Run("repo_exists", func(t *testing.T) {
   134  		repoID := graveler.RepositoryID("example-repo")
   135  		branchID := graveler.BranchID("weird-branch")
   136  
   137  		repository, err := r.CreateRepository(context.Background(), repoID, graveler.Repository{
   138  			StorageNamespace: "s3://foo",
   139  			CreationDate:     time.Now(),
   140  			DefaultBranchID:  branchID,
   141  		})
   142  		testutil.Must(t, err)
   143  
   144  		repo, err := r.GetRepository(context.Background(), repoID)
   145  		if err != nil {
   146  			t.Fatalf("unexpected error: %v", err)
   147  		}
   148  
   149  		if repo.DefaultBranchID != repository.DefaultBranchID {
   150  			t.Fatalf("got '%s' branch ID, expected '%s'", repo.DefaultBranchID, repository.DefaultBranchID)
   151  		}
   152  		branch, err := r.GetBranch(context.Background(), repo, branchID)
   153  		if err != nil {
   154  			t.Fatalf("unexpected error: %v", err)
   155  		}
   156  		if branch.CommitID == "" {
   157  			t.Fatal("empty first commit - first commit wasn't created")
   158  		}
   159  
   160  		commit, err := r.GetCommit(context.Background(), repo, branch.CommitID)
   161  		if err != nil {
   162  			t.Fatalf("unexpected error: %v", err)
   163  		}
   164  		if len(commit.Parents) != 0 {
   165  			t.Fatalf("first commit parents should be empty: %v", commit.Parents)
   166  		}
   167  		if commit.MetaRangeID != "" {
   168  			t.Fatalf("first commit metarange should be empty: %v", commit.MetaRangeID)
   169  		}
   170  	})
   171  }
   172  
   173  func TestManager_ListRepositories(t *testing.T) {
   174  	r, _ := testRefManager(t)
   175  	repoIDs := []graveler.RepositoryID{"a", "aa", "b", "c", "e", "d"}
   176  	for _, repoId := range repoIDs {
   177  		_, err := r.CreateRepository(context.Background(), repoId, graveler.Repository{
   178  			StorageNamespace: "s3://foo",
   179  			CreationDate:     time.Now(),
   180  			DefaultBranchID:  "main",
   181  		})
   182  		testutil.Must(t, err)
   183  	}
   184  
   185  	t.Run("listing all repos", func(t *testing.T) {
   186  		iter, err := r.ListRepositories(context.Background())
   187  		if err != nil {
   188  			t.Fatalf("unexpected error: %v", err)
   189  		}
   190  		defer iter.Close()
   191  
   192  		repoIds := make([]graveler.RepositoryID, 0)
   193  		for iter.Next() {
   194  			repo := iter.Value()
   195  			repoIds = append(repoIds, repo.RepositoryID)
   196  		}
   197  		if iter.Err() != nil {
   198  			t.Fatalf("unexpected error: %v", iter.Err())
   199  		}
   200  
   201  		if !reflect.DeepEqual(repoIds, []graveler.RepositoryID{"a", "aa", "b", "c", "d", "e"}) {
   202  			t.Fatalf("got wrong list of repo IDs")
   203  		}
   204  	})
   205  
   206  	t.Run("listing repos from prefix", func(t *testing.T) {
   207  		iter, err := r.ListRepositories(context.Background())
   208  		if err != nil {
   209  			t.Fatalf("unexpected error: %v", err)
   210  		}
   211  		defer iter.Close()
   212  		iter.SeekGE("aaa")
   213  
   214  		repoIds := make([]graveler.RepositoryID, 0)
   215  		for iter.Next() {
   216  			repo := iter.Value()
   217  			repoIds = append(repoIds, repo.RepositoryID)
   218  		}
   219  		if iter.Err() != nil {
   220  			t.Fatalf("unexpected error: %v", iter.Err())
   221  		}
   222  
   223  		if !reflect.DeepEqual(repoIds, []graveler.RepositoryID{"b", "c", "d", "e"}) {
   224  			t.Fatalf("got wrong list of repo IDs")
   225  		}
   226  	})
   227  }
   228  
   229  func TestManager_DeleteRepository(t *testing.T) {
   230  	r, store := testRefManager(t)
   231  	ctx := context.Background()
   232  	repoID := graveler.RepositoryID("example-repo")
   233  
   234  	t.Run("repo_exists", func(t *testing.T) {
   235  		repository, err := r.CreateRepository(ctx, repoID, graveler.Repository{
   236  			StorageNamespace: "s3://foo",
   237  			CreationDate:     time.Now(),
   238  			DefaultBranchID:  "weird-branch",
   239  		})
   240  		testutil.Must(t, err)
   241  
   242  		_, err = r.GetRepository(context.Background(), "example-repo")
   243  		if err != nil {
   244  			t.Fatalf("unexpected error: %v", err)
   245  		}
   246  
   247  		// Create repository entities and ensure their deletion afterwards
   248  		testutil.Must(t, r.CreateTag(ctx, repository, "v1.0", "c1"))
   249  		testutil.Must(t, r.CreateBranch(ctx, repository, "f1", graveler.Branch{CommitID: "c1", StagingToken: "s1"}))
   250  		c := graveler.Commit{
   251  			Committer:    "user1",
   252  			Message:      "message1",
   253  			MetaRangeID:  "deadbeef123",
   254  			CreationDate: time.Now(),
   255  			Parents:      graveler.CommitParents{"deadbeef1", "deadbeef12"},
   256  			Metadata:     graveler.Metadata{"foo": "bar"},
   257  		}
   258  		_, err = r.AddCommit(ctx, repository, c)
   259  		testutil.Must(t, err)
   260  
   261  		err = r.DeleteRepository(context.Background(), "example-repo")
   262  		if err != nil {
   263  			t.Fatalf("unexpected error: %v", err)
   264  		}
   265  
   266  		// wait for cache expiry
   267  		time.Sleep(testRepoCacheConfig.Expiry + testRepoCacheConfig.Jitter + time.Second)
   268  
   269  		_, err = r.GetRepository(context.Background(), "example-repo")
   270  		if !errors.Is(err, graveler.ErrRepositoryNotFound) {
   271  			t.Fatalf("expected ErrRepositoryNotFound, got: %v", err)
   272  		}
   273  
   274  		// Verify no keys on repo partition
   275  		itr := kv.NewPartitionIterator(ctx, store, (&graveler.RepoMetadata{}).ProtoReflect().Type(), graveler.RepoPartition(repository), 10)
   276  		defer itr.Close()
   277  		for itr.Next() {
   278  			entry := itr.Entry()
   279  			t.Fatalf("partition expected empty: %s", string(entry.Key))
   280  		}
   281  		// Check itr.Next() not false on an error
   282  		require.NoError(t, itr.Err())
   283  
   284  		// Create after delete
   285  		_, err = r.CreateRepository(ctx, repoID, graveler.Repository{
   286  			StorageNamespace: "s3://foo",
   287  			CreationDate:     time.Now(),
   288  			DefaultBranchID:  "weird-branch",
   289  		})
   290  		testutil.Must(t, err)
   291  		_, err = r.GetRepository(context.Background(), "example-repo")
   292  		if err != nil {
   293  			t.Fatalf("unexpected error: %v", err)
   294  		}
   295  	})
   296  
   297  	t.Run("repo_does_not_exist", func(t *testing.T) {
   298  		err := r.DeleteRepository(context.Background(), "example-repo11111")
   299  		if !errors.Is(err, graveler.ErrRepositoryNotFound) {
   300  			t.Fatalf("unexpected error: %v", err)
   301  		}
   302  	})
   303  }
   304  
   305  func TestManager_GetBranch(t *testing.T) {
   306  	r, _ := testRefManager(t)
   307  	repository, err := r.CreateRepository(context.Background(), "repo1", graveler.Repository{
   308  		StorageNamespace: "s3://",
   309  		CreationDate:     time.Now(),
   310  		DefaultBranchID:  "main",
   311  	})
   312  	testutil.Must(t, err)
   313  
   314  	t.Run("get_branch_exists", func(t *testing.T) {
   315  		branch, err := r.GetBranch(context.Background(), repository, "main")
   316  		if err != nil {
   317  			t.Fatalf("unexpected error: %v", err)
   318  		}
   319  		if branch.CommitID == "" {
   320  			t.Fatal("unexpected empty branch commit received")
   321  		}
   322  	})
   323  
   324  	t.Run("get_branch_doesnt_exists", func(t *testing.T) {
   325  		_, err := r.GetBranch(context.Background(), repository, "mainnnnn")
   326  		if !errors.Is(err, graveler.ErrBranchNotFound) {
   327  			t.Fatalf("expected ErrBranchNotFound, got error: %v", err)
   328  		}
   329  	})
   330  }
   331  
   332  func TestManager_CreateBranch(t *testing.T) {
   333  	r, _ := testRefManager(t)
   334  	ctx := context.Background()
   335  	repository, err := r.CreateRepository(ctx, "repo1", graveler.Repository{
   336  		StorageNamespace: "s3://",
   337  		CreationDate:     time.Now(),
   338  		DefaultBranchID:  "main",
   339  	})
   340  	testutil.Must(t, err)
   341  
   342  	err = r.CreateBranch(ctx, repository, "f1", graveler.Branch{CommitID: "c1", StagingToken: "s1"})
   343  	testutil.MustDo(t, "create branch f1", err)
   344  
   345  	br, err := r.GetBranch(ctx, repository, "f1")
   346  	testutil.MustDo(t, "get f1 branch", err)
   347  	if br == nil {
   348  		t.Fatal("get branch got nil")
   349  	}
   350  	if br.CommitID != "c1" {
   351  		t.Fatalf("unexpected commit for branch f1: %s - expected: c1", br.CommitID)
   352  	}
   353  
   354  	// check we can't create existing
   355  	err = r.CreateBranch(ctx, repository, "f1", graveler.Branch{CommitID: "c2", StagingToken: "s2"})
   356  	if !errors.Is(err, graveler.ErrBranchExists) {
   357  		t.Fatalf("CreateBranch() err = %s, expected already exists", err)
   358  	}
   359  	// overwrite by delete and create
   360  	err = r.DeleteBranch(ctx, repository, "f1")
   361  	testutil.MustDo(t, "delete branch f1", err)
   362  
   363  	err = r.CreateBranch(ctx, repository, "f1", graveler.Branch{CommitID: "c2", StagingToken: "s2"})
   364  	testutil.MustDo(t, "create branch f1", err)
   365  
   366  	br, err = r.GetBranch(ctx, repository, "f1")
   367  	testutil.MustDo(t, "get f1 branch", err)
   368  
   369  	if br == nil {
   370  		t.Fatal("get branch got nil")
   371  	}
   372  	if br.CommitID != "c2" {
   373  		t.Fatalf("unexpected commit for branch f1: %s - expected: c2", br.CommitID)
   374  	}
   375  }
   376  
   377  func TestManager_SetBranch(t *testing.T) {
   378  	r, _ := testRefManager(t)
   379  	repository, err := r.CreateRepository(context.Background(), "repo1", graveler.Repository{
   380  		StorageNamespace: "s3://",
   381  		CreationDate:     time.Now(),
   382  		DefaultBranchID:  "main",
   383  	})
   384  	testutil.Must(t, err)
   385  
   386  	testutil.Must(t, r.SetBranch(context.Background(), repository, "branch2", graveler.Branch{
   387  		CommitID: "c2",
   388  	}))
   389  
   390  	b, err := r.GetBranch(context.Background(), repository, "branch2")
   391  	if err != nil {
   392  		t.Fatalf("unexpected error: %v", err)
   393  	}
   394  
   395  	if b.CommitID != "c2" {
   396  		t.Fatalf("unexpected commit for branch2: %s - expected: c2", b.CommitID)
   397  	}
   398  
   399  	// overwrite
   400  	testutil.Must(t, r.SetBranch(context.Background(), repository, "branch2", graveler.Branch{
   401  		CommitID: "c3",
   402  	}))
   403  
   404  	b, err = r.GetBranch(context.Background(), repository, "branch2")
   405  	if err != nil {
   406  		t.Fatalf("unexpected error: %v", err)
   407  	}
   408  
   409  	if b.CommitID != "c3" {
   410  		t.Fatalf("unexpected commit for branch2: %s - expected: c3", b.CommitID)
   411  	}
   412  }
   413  
   414  func TestManager_BranchUpdate(t *testing.T) {
   415  	ctx := context.Background()
   416  	r, _ := testRefManager(t)
   417  	const (
   418  		repoID    = "repo1"
   419  		branchID  = "branch1"
   420  		commitID1 = "c1"
   421  		commitID2 = "c2"
   422  	)
   423  	repository, err := r.CreateRepository(context.Background(), repoID, graveler.Repository{
   424  		StorageNamespace: "s3://",
   425  		CreationDate:     time.Now(),
   426  		DefaultBranchID:  "main",
   427  	})
   428  	testutil.Must(t, err)
   429  	tests := []struct {
   430  		name           string
   431  		f              graveler.BranchUpdateFunc
   432  		err            error
   433  		expectedCommit string
   434  	}{
   435  		{
   436  			name: "success_branch_update",
   437  			f: func(*graveler.Branch) (*graveler.Branch, error) {
   438  				newBranch := &graveler.Branch{
   439  					CommitID:     commitID2,
   440  					StagingToken: "",
   441  					SealedTokens: nil,
   442  				}
   443  				return newBranch, nil
   444  			},
   445  			expectedCommit: commitID2,
   446  		},
   447  		{
   448  			name: "failed_branch_update_due_to_branch_change",
   449  			f: func(*graveler.Branch) (*graveler.Branch, error) {
   450  				b := graveler.Branch{
   451  					CommitID: "Another commit during validation",
   452  				}
   453  				_ = r.SetBranch(ctx, repository, branchID, b)
   454  				return &b, nil
   455  			},
   456  			err:            kv.ErrPredicateFailed,
   457  			expectedCommit: "Another commit during validation",
   458  		},
   459  		{
   460  			name: "failed_branch_update_on_validation",
   461  			f: func(*graveler.Branch) (*graveler.Branch, error) {
   462  				return nil, graveler.ErrInvalid
   463  			},
   464  			err:            graveler.ErrInvalid,
   465  			expectedCommit: commitID1,
   466  		},
   467  	}
   468  	for _, tt := range tests {
   469  		t.Run(tt.name, func(t *testing.T) {
   470  			testutil.Must(t, r.SetBranch(context.Background(), repository, branchID, graveler.Branch{
   471  				CommitID: commitID1,
   472  			}))
   473  
   474  			err := r.BranchUpdate(ctx, repository, branchID, tt.f)
   475  			require.ErrorIs(t, err, tt.err)
   476  
   477  			b, err := r.GetBranch(context.Background(), repository, branchID)
   478  			require.NoError(t, err)
   479  			require.Equal(t, tt.expectedCommit, b.CommitID.String())
   480  		})
   481  	}
   482  }
   483  
   484  func TestManager_DeleteBranch(t *testing.T) {
   485  	r, _ := testRefManager(t)
   486  	ctx := context.Background()
   487  	repository, err := r.CreateRepository(ctx, "repo1", graveler.Repository{
   488  		StorageNamespace: "s3://",
   489  		CreationDate:     time.Now(),
   490  		DefaultBranchID:  "main",
   491  	})
   492  	testutil.Must(t, err)
   493  
   494  	testutil.Must(t, r.SetBranch(ctx, repository, "branch2", graveler.Branch{
   495  		CommitID: "c2",
   496  	}))
   497  
   498  	testutil.Must(t, r.DeleteBranch(ctx, repository, "branch2"))
   499  
   500  	_, err = r.GetBranch(ctx, repository, "branch2")
   501  	if !errors.Is(err, graveler.ErrBranchNotFound) {
   502  		t.Fatalf("Expected ErrBranchNotFound, got error: %v", err)
   503  	}
   504  }
   505  
   506  func TestManager_ListBranches(t *testing.T) {
   507  	r, _ := testRefManager(t)
   508  	repository, err := r.CreateRepository(context.Background(), "repo1", graveler.Repository{
   509  		StorageNamespace: "s3://",
   510  		CreationDate:     time.Now(),
   511  		DefaultBranchID:  "main",
   512  	})
   513  	testutil.Must(t, err)
   514  
   515  	for _, b := range []graveler.BranchID{"a", "aa", "c", "b", "z", "f"} {
   516  		testutil.Must(t, r.SetBranch(context.Background(), repository, b, graveler.Branch{
   517  			CommitID: "c2",
   518  		}))
   519  	}
   520  
   521  	iter, err := r.ListBranches(context.Background(), repository)
   522  	if err != nil {
   523  		t.Fatalf("unexpected error: %v", err)
   524  	}
   525  	defer iter.Close()
   526  
   527  	var bs []graveler.BranchID
   528  	for iter.Next() {
   529  		b := iter.Value()
   530  		bs = append(bs, b.BranchID)
   531  	}
   532  	if iter.Err() != nil {
   533  		t.Fatalf("unexpected error: %v", iter.Err())
   534  	}
   535  	if !reflect.DeepEqual(bs, []graveler.BranchID{"a", "aa", "b", "c", "f", "main", "z"}) {
   536  		t.Fatalf("unexpected branch list: %v", bs)
   537  	}
   538  }
   539  
   540  func TestManager_GetTag(t *testing.T) {
   541  	r, _ := testRefManager(t)
   542  	ctx := context.Background()
   543  	repository, err := r.CreateRepository(ctx, "repo1", graveler.Repository{
   544  		StorageNamespace: "s3://",
   545  		CreationDate:     time.Now(),
   546  		DefaultBranchID:  "main",
   547  	})
   548  	testutil.MustDo(t, "create repo", err)
   549  
   550  	t.Run("exists", func(t *testing.T) {
   551  		err := r.CreateTag(ctx, repository, "v1.0", "c1")
   552  		testutil.MustDo(t, "set tag", err)
   553  		commitID, err := r.GetTag(context.Background(), repository, "v1.0")
   554  		testutil.MustDo(t, "get existing tag", err)
   555  		if commitID == nil {
   556  			t.Fatal("get tag, missing commit id")
   557  		}
   558  		if *commitID != "c1" {
   559  			t.Fatalf("get tag, commit id: %s, expected c1", *commitID)
   560  		}
   561  	})
   562  
   563  	t.Run("not_exists", func(t *testing.T) {
   564  		commitID, err := r.GetTag(context.Background(), repository, "v1.bad")
   565  		if !errors.Is(err, graveler.ErrNotFound) {
   566  			t.Fatalf("expected ErrNotFound, got error: %v", err)
   567  		}
   568  		if commitID != nil {
   569  			t.Fatalf("get not existing commitID: %s, expected nil", *commitID)
   570  		}
   571  	})
   572  }
   573  
   574  func TestManager_CreateTag(t *testing.T) {
   575  	r, _ := testRefManager(t)
   576  	ctx := context.Background()
   577  	repository, err := r.CreateRepository(ctx, "repo1", graveler.Repository{
   578  		StorageNamespace: "s3://",
   579  		CreationDate:     time.Now(),
   580  		DefaultBranchID:  "main",
   581  	})
   582  	testutil.Must(t, err)
   583  
   584  	err = r.CreateTag(ctx, repository, "v2", "c2")
   585  	testutil.MustDo(t, "create tag v2", err)
   586  
   587  	commit, err := r.GetTag(ctx, repository, "v2")
   588  	testutil.MustDo(t, "get v2 tag", err)
   589  	if commit == nil {
   590  		t.Fatal("get tag got nil")
   591  	}
   592  	if *commit != "c2" {
   593  		t.Fatalf("unexpected commit for tag v2: %s - expected: c2", *commit)
   594  	}
   595  
   596  	// check we can't create existing
   597  	err = r.CreateTag(ctx, repository, "v2", "c5")
   598  	if !errors.Is(err, graveler.ErrTagAlreadyExists) {
   599  		t.Fatalf("CreateTag() err = %s, expected already exists", err)
   600  	}
   601  	// overwrite by delete and create
   602  	err = r.DeleteTag(ctx, repository, "v2")
   603  	testutil.MustDo(t, "delete tag v2", err)
   604  
   605  	err = r.CreateTag(ctx, repository, "v2", "c3")
   606  	testutil.MustDo(t, "re-create tag v2", err)
   607  
   608  	commit, err = r.GetTag(ctx, repository, "v2")
   609  	testutil.MustDo(t, "get tag v2", err)
   610  	if commit == nil {
   611  		t.Fatal("get tag got nil")
   612  	}
   613  	if *commit != "c3" {
   614  		t.Fatalf("unexpected commit for v2: %s - expected: c3", *commit)
   615  	}
   616  }
   617  
   618  func TestManager_DeleteTag(t *testing.T) {
   619  	r, _ := testRefManager(t)
   620  	ctx := context.Background()
   621  	repository, err := r.CreateRepository(ctx, "repo1", graveler.Repository{
   622  		StorageNamespace: "s3://",
   623  		CreationDate:     time.Now(),
   624  		DefaultBranchID:  "main",
   625  	})
   626  	testutil.Must(t, err)
   627  	testutil.Must(t, r.CreateTag(ctx, repository, "v1", "c2"))
   628  	testutil.Must(t, r.DeleteTag(ctx, repository, "v1"))
   629  	commitID, err := r.GetTag(ctx, repository, "v1")
   630  	if !errors.Is(err, graveler.ErrNotFound) {
   631  		t.Fatal("unexpected error:", err)
   632  	}
   633  	if commitID != nil {
   634  		t.Fatal("expected commit ID:", *commitID)
   635  	}
   636  }
   637  
   638  func TestManager_ListTags(t *testing.T) {
   639  	r, _ := testRefManager(t)
   640  	ctx := context.Background()
   641  	repository, err := r.CreateRepository(ctx, "repo1", graveler.Repository{
   642  		StorageNamespace: "s3://",
   643  		CreationDate:     time.Now(),
   644  		DefaultBranchID:  "main",
   645  	})
   646  	testutil.Must(t, err)
   647  
   648  	var commitsTagged []graveler.CommitID
   649  	tags := []string{"tag-a", "tag-b", "the-end", "v1", "v1.1"}
   650  	sort.Strings(tags)
   651  	for i, tag := range tags {
   652  		commitID := graveler.CommitID(fmt.Sprintf("c%d", i))
   653  		commitsTagged = append(commitsTagged, commitID)
   654  		err := r.CreateTag(ctx, repository, graveler.TagID(tag), commitID)
   655  		testutil.MustDo(t, "set tag "+tag, err)
   656  	}
   657  
   658  	iter, err := r.ListTags(ctx, repository)
   659  	if err != nil {
   660  		t.Fatal("unexpected error:", err)
   661  	}
   662  	defer iter.Close()
   663  	var commits []graveler.CommitID
   664  	for iter.Next() {
   665  		commits = append(commits, iter.Value().CommitID)
   666  	}
   667  	testutil.MustDo(t, "list tags completed", iter.Err())
   668  
   669  	if diff := deep.Equal(commits, commitsTagged); diff != nil {
   670  		t.Fatal("ListTags found mismatch:", diff)
   671  	}
   672  }
   673  
   674  func TestManager_AddCommit(t *testing.T) {
   675  	r, _ := testRefManager(t)
   676  	ctx := context.Background()
   677  	repository, err := r.CreateRepository(ctx, "repo1", graveler.Repository{
   678  		StorageNamespace: "s3://",
   679  		CreationDate:     time.Now(),
   680  		DefaultBranchID:  "main",
   681  	})
   682  	testutil.Must(t, err)
   683  
   684  	ts, _ := time.Parse(time.RFC3339, "2020-12-01T15:00:00Z00:00")
   685  	c := graveler.Commit{
   686  		Committer:    "user1",
   687  		Message:      "message1",
   688  		MetaRangeID:  "deadbeef123",
   689  		CreationDate: ts,
   690  		Parents:      graveler.CommitParents{"deadbeef1", "deadbeef12"},
   691  		Metadata:     graveler.Metadata{"foo": "bar"},
   692  	}
   693  
   694  	cid, err := r.AddCommit(ctx, repository, c)
   695  	if err != nil {
   696  		t.Fatalf("unexpected error: %v", err)
   697  	}
   698  
   699  	const expectedCommitID = "2277b5abd2d3ba6b4d35c48a0e358b0c4bcf5cd6d891c67437fb4c4af0d2fd4b"
   700  	if cid != expectedCommitID {
   701  		t.Fatalf("Commit ID '%s', expected '%s'", cid, expectedCommitID)
   702  	}
   703  
   704  	commit, err := r.GetCommit(ctx, repository, cid)
   705  	if err != nil {
   706  		t.Fatalf("unexpected error: %v", err)
   707  	}
   708  
   709  	if commit.Parents[0] != "deadbeef1" {
   710  		t.Fatalf("expected parent1 to be deadbeef1, got %v", commit.Parents)
   711  	}
   712  
   713  	if commit.Metadata["foo"] != "bar" {
   714  		t.Fatalf("unexpected metadata value for foo: %v", commit.Metadata["foo"])
   715  	}
   716  }
   717  
   718  func TestManager_Log(t *testing.T) {
   719  	r, _ := testRefManager(t)
   720  	ctx := context.Background()
   721  	repository, err := r.CreateRepository(ctx, "repo1", graveler.Repository{
   722  		StorageNamespace: "s3://",
   723  		CreationDate:     time.Now(),
   724  		DefaultBranchID:  "main",
   725  	})
   726  	testutil.Must(t, err)
   727  
   728  	ts, _ := time.Parse(time.RFC3339, "2020-12-01T15:00:00Z")
   729  	var previous graveler.CommitID
   730  	for i := 0; i < 20; i++ {
   731  		c := graveler.Commit{
   732  			Committer:    "user1",
   733  			Message:      "message1",
   734  			MetaRangeID:  "deadbeef123",
   735  			CreationDate: ts,
   736  			Parents:      graveler.CommitParents{},
   737  			Metadata:     graveler.Metadata{"foo": "bar"},
   738  		}
   739  		if previous != "" {
   740  			c.Parents = append(c.Parents, previous)
   741  		}
   742  		cid, err := r.AddCommit(ctx, repository, c)
   743  		if err != nil {
   744  			t.Fatalf("unexpected error: %v", err)
   745  		}
   746  		previous = cid
   747  		ts = ts.Add(time.Second)
   748  	}
   749  
   750  	iter, err := r.Log(ctx, repository, previous, false, nil)
   751  	if err != nil {
   752  		t.Fatalf("unexpected error: %v", err)
   753  	}
   754  	defer iter.Close()
   755  
   756  	ids := make([]graveler.CommitID, 0)
   757  	for iter.Next() {
   758  		c := iter.Value()
   759  		ids = append(ids, c.CommitID)
   760  	}
   761  	if iter.Err() != nil {
   762  		t.Fatalf("unexpected error: %v", iter.Err())
   763  	}
   764  
   765  	expected := []graveler.CommitID{
   766  		"663b7520a2a05aaeed17de6136fa80eb5cd8417982011eb551230571ee412f2f",
   767  		"87856d024fbe092852118edd958717d905019fa4eae40bac18a719e2f869e0f7",
   768  		"25e51c6a8675c52558f8e303757624fca629bbc81f53afffa71a560df1c03948",
   769  		"7653a24da53a43b229a64c7fec4a3259ed2cd3cba8b0021650dd54ea286a06cd",
   770  		"d19e92e3b0717236255b529b35a7f1ec33e716be58af01c0f2fda80f4ded5a7a",
   771  		"ac2f92fbefff7914f82c148a391c4705555aacb4ef9fe2c43c21e88f92e459ec",
   772  		"fecd3e1f97cc1e54df6a06737d98931a8298c7ab1c870042666a10b42f7f7c0a",
   773  		"1d6e9e55a600eceead14e70a903cea94df5a7b74a6ca4de6f8206ab45d60a5bd",
   774  		"1f147e667ad0db53c2e9392d4bd35cb649269762f1da19e9e4d2e7b444dfd875",
   775  		"61d527f08cc67522728f8ffc93bcb91cc789de80beb9c43a41ee225fb9c446b2",
   776  		"32700dc4b5355be186976745fbef029f8ef7533170c0766ed77c9e3f574178c5",
   777  		"4d82f11b02d6cb609d2bc1007620f578733ffff971a749ff4462dc69b834c20a",
   778  		"2b4bb867adb1ac94c2f569dca92b9156a40ba0cd22a4bdc63cac1eb6b21b6f63",
   779  		"72ee57f5cb8dddb264624f9ac7266c6ebc82af509e69f84d592a6732e74af06c",
   780  		"3bbd01827326eba3f2a60e2ec98573cff1d2ead6c53336a5796ccf9d6401b052",
   781  		"8107b1d0a1ce6f75a1848f31ab3261eb86acdfe9b4e84b7eaf329b3904179de9",
   782  		"988c38b9f7d9b5df7242c3e837dac93e91dc0ff73da7dae1e010bcf18e3e0fa6",
   783  		"cb5dca579b23b81f8148fc9153a4c9c733d830c26be1d5f8d12496300c02dd89",
   784  		"ebbd689937253304ae29a541a727bfd11ab59a5659bb293e8ab2ed407c9a74c1",
   785  		"fac53a04432b2e6e7185f3ac8314a874c556b2557adf3aa8d5b1df985cf96566",
   786  	}
   787  	if diff := deep.Equal(ids, expected); diff != nil {
   788  		t.Fatal("Commits log wrong result:", diff)
   789  	}
   790  }
   791  
   792  func TestManager_LogGraph(t *testing.T) {
   793  	r, _ := testRefManager(t)
   794  	ctx := context.Background()
   795  	repository, err := r.CreateRepository(ctx, "repo1", graveler.Repository{
   796  		StorageNamespace: "s3://",
   797  		CreationDate:     time.Now(),
   798  		DefaultBranchID:  "main",
   799  	})
   800  	testutil.MustDo(t, "Create repository", err)
   801  	dag := map[string][]string{
   802  		"c1": {},
   803  		"c2": {"c1"},
   804  		"c3": {"c1"},
   805  		"c4": {"c2"},
   806  		"c5": {"c3"},
   807  		"c6": {"c5"},
   808  		"c7": {"c4"},
   809  		"c8": {"c6", "c7"},
   810  	}
   811  	tests := map[string]struct {
   812  		firstParent bool
   813  		seek        string
   814  		start       string
   815  		since       time.Time
   816  		expected    []string
   817  	}{
   818  		/*
   819  			---1----2----4----7
   820  			    \	           \
   821  				 3----5----6----8---
   822  		*/
   823  		"full_graph": {
   824  			start:    "c8",
   825  			expected: []string{"c8", "c7", "c6", "c5", "c4", "c3", "c2", "c1"},
   826  		},
   827  		"full_graph_first_parent": {
   828  			start:       "c8",
   829  			firstParent: true,
   830  			expected:    []string{"c8", "c6", "c5", "c3", "c1"},
   831  		},
   832  		"with_seek": {
   833  			start:    "c8",
   834  			seek:     "c4",
   835  			expected: []string{"c4", "c3", "c2", "c1"},
   836  		},
   837  		"with_seek_first_parent": {
   838  			start:       "c8",
   839  			seek:        "c5",
   840  			firstParent: true,
   841  			expected:    []string{"c5", "c3", "c1"},
   842  		},
   843  		"start_from": {
   844  			start:    "c7",
   845  			expected: []string{"c7", "c4", "c2", "c1"},
   846  		},
   847  		"since": {
   848  			start:    "c8",
   849  			since:    time.Date(2020, time.December, 1, 15, 5, 0, 0, time.UTC),
   850  			expected: []string{"c8", "c7", "c6", "c5"},
   851  		},
   852  	}
   853  	for name, tst := range tests {
   854  		t.Run(name, func(t *testing.T) {
   855  			nextCommitTS := time.Date(2020, time.December, 1, 15, 0, 0, 0, time.UTC)
   856  			commitNameToID := map[string]graveler.CommitID{}
   857  			addCommit := func(commitName string, parentNames ...string) graveler.CommitID {
   858  				nextCommitTS = nextCommitTS.Add(time.Minute)
   859  				parentIDs := make([]graveler.CommitID, 0, len(parentNames))
   860  				for _, parentName := range parentNames {
   861  					parentIDs = append(parentIDs, commitNameToID[parentName])
   862  				}
   863  				c := graveler.Commit{
   864  					Committer:    "user1",
   865  					Message:      commitName,
   866  					MetaRangeID:  "fefe1221",
   867  					CreationDate: nextCommitTS,
   868  					Parents:      parentIDs,
   869  					Metadata:     graveler.Metadata{"foo": "bar"},
   870  				}
   871  				cid, err := r.AddCommit(ctx, repository, c)
   872  				commitNameToID[commitName] = cid
   873  				testutil.MustDo(t, "Add commit "+commitName, err)
   874  				return cid
   875  			}
   876  			commitNames := make([]string, 0, len(dag))
   877  			for commitName := range dag {
   878  				commitNames = append(commitNames, commitName)
   879  			}
   880  			sort.Strings(commitNames)
   881  			for _, commitName := range commitNames {
   882  				addCommit(commitName, dag[commitName]...)
   883  			}
   884  
   885  			// setup time since
   886  			var since *time.Time
   887  			if !tst.since.IsZero() {
   888  				since = &tst.since
   889  			}
   890  
   891  			it, err := r.Log(ctx, repository, commitNameToID[tst.start], tst.firstParent, since)
   892  			if err != nil {
   893  				t.Fatal("Error during create Log iterator", err)
   894  			}
   895  			defer it.Close()
   896  			if tst.seek != "" {
   897  				it.SeekGE(commitNameToID[tst.seek])
   898  			}
   899  			var commits []string
   900  			for it.Next() {
   901  				c := it.Value()
   902  				commits = append(commits, c.Message)
   903  			}
   904  			if err := it.Err(); err != nil {
   905  				t.Fatal("Iteration ended with error", err)
   906  			}
   907  			if diff := deep.Equal(commits, tst.expected); diff != nil {
   908  				t.Fatal("Found diff between expected commits:", diff)
   909  			}
   910  		})
   911  	}
   912  }
   913  
   914  func TestConsistentCommitIdentity(t *testing.T) {
   915  	addressProvider := ident.NewHexAddressProvider()
   916  	commit := graveler.Commit{
   917  		Committer:    "some-committer",
   918  		Message:      "I just committed",
   919  		MetaRangeID:  "123456789987654321",
   920  		CreationDate: time.Date(2021, time.January, 24, 15, 10, 11, 1564956600, time.UTC),
   921  		Parents: graveler.CommitParents{
   922  			graveler.CommitID("132456987153687sdfsdf"),
   923  			graveler.CommitID("1324569csfvdkjhcsdkjc"),
   924  		},
   925  		Metadata: map[string]string{
   926  			"sdkafjnb":       "1234",
   927  			"sdkjvcnbkjndsc": "asnjkdl",
   928  		},
   929  	}
   930  
   931  	// Should NOT be changed (unless you really know what you're doing):
   932  	// If this is failing, and you're tempted to change this value,
   933  	// then you are probably introducing a breaking change to commits identity.
   934  	// All previous references to commits (if not migrated) may be lost.
   935  	const expected = "f1a106bbeb12d3eb54418d6000f4507501d289d0d0879dcce6f4d31425587df1"
   936  
   937  	// Running many times to check that it's actually consistent (see issue #1291)
   938  	const iterations = 50
   939  
   940  	for i := 0; i < iterations; i++ {
   941  		res := addressProvider.ContentAddress(commit)
   942  		assert.Equalf(t, expected, res, "iteration %d content mismatch", i+1)
   943  	}
   944  }
   945  
   946  func TestManager_GetCommitByPrefix(t *testing.T) {
   947  	commitIDs := []string{"c1234", "d1", "b1", "c1245", "a1"}
   948  	identityToFakeIdentity := make(map[string]string)
   949  
   950  	provider := &fakeAddressProvider{identityToFakeIdentity: identityToFakeIdentity}
   951  	r, _ := testRefManagerWithAddressProvider(t, provider)
   952  	ctx := context.Background()
   953  	repository, err := r.CreateRepository(ctx, "repo1", graveler.Repository{
   954  		StorageNamespace: "s3://",
   955  		CreationDate:     time.Now(),
   956  		DefaultBranchID:  "main",
   957  	})
   958  	testutil.MustDo(t, "Create repository", err)
   959  	for _, commitID := range commitIDs {
   960  		c := graveler.Commit{
   961  			Committer:    "user1",
   962  			Message:      fmt.Sprintf("id_%s", commitID),
   963  			MetaRangeID:  "deadbeef123",
   964  			CreationDate: time.Now(),
   965  			Parents:      graveler.CommitParents{"deadbeef1"},
   966  			Metadata:     graveler.Metadata{"foo": "bar"},
   967  		}
   968  		identityToFakeIdentity[hex.EncodeToString(c.Identity())] = commitID
   969  		_, err := r.AddCommit(ctx, repository, c)
   970  		testutil.MustDo(t, "add commit", err)
   971  		if err != nil {
   972  			t.Fatalf("unexpected error on adding commit: %v", err)
   973  		}
   974  	}
   975  	tests := []struct {
   976  		Prefix                string
   977  		ExpectedCommitMessage string
   978  		ExpectedErr           error
   979  	}{
   980  		{
   981  			Prefix:                "a",
   982  			ExpectedCommitMessage: "id_a1",
   983  		},
   984  		{
   985  			Prefix:                "c123",
   986  			ExpectedCommitMessage: "id_c1234",
   987  		},
   988  		{
   989  			Prefix:      "c1",
   990  			ExpectedErr: graveler.ErrCommitNotFound,
   991  		},
   992  		{
   993  			Prefix:      "e",
   994  			ExpectedErr: graveler.ErrCommitNotFound,
   995  		},
   996  	}
   997  	for _, tst := range tests {
   998  		t.Run(tst.Prefix, func(t *testing.T) {
   999  			c, err := r.GetCommitByPrefix(ctx, repository, graveler.CommitID(tst.Prefix))
  1000  			if !errors.Is(err, tst.ExpectedErr) {
  1001  				t.Fatalf("expected error %v, got=%v", tst.ExpectedErr, err)
  1002  			}
  1003  			if tst.ExpectedErr != nil {
  1004  				return
  1005  			}
  1006  			if c.Message != tst.ExpectedCommitMessage {
  1007  				t.Fatalf("got commit different than expected. expected=%s, got=%s", tst.ExpectedCommitMessage, c.Message)
  1008  			}
  1009  		})
  1010  	}
  1011  }
  1012  
  1013  func TestManager_ListCommits(t *testing.T) {
  1014  	r, _ := testRefManager(t)
  1015  	ctx := context.Background()
  1016  	repository, err := r.CreateRepository(ctx, "repo1", graveler.Repository{
  1017  		StorageNamespace: "s3://",
  1018  		CreationDate:     time.Now(),
  1019  		DefaultBranchID:  "main",
  1020  	})
  1021  	testutil.Must(t, err)
  1022  	nextCommitNumber := 0
  1023  	addNextCommit := func(parents ...graveler.CommitID) graveler.CommitID {
  1024  		nextCommitNumber++
  1025  		id := "c" + strconv.Itoa(nextCommitNumber)
  1026  		c := graveler.Commit{
  1027  			Message: id,
  1028  			Parents: parents,
  1029  		}
  1030  		cid, err := r.AddCommit(ctx, repository, c)
  1031  		testutil.MustDo(t, "Add commit "+id, err)
  1032  		return cid
  1033  	}
  1034  	c1 := addNextCommit()
  1035  	c2 := addNextCommit(c1)
  1036  	c3 := addNextCommit(c1)
  1037  	c4 := addNextCommit(c2)
  1038  	c5 := addNextCommit(c3)
  1039  	c6 := addNextCommit(c5)
  1040  	addNextCommit(c4)
  1041  	addNextCommit(c6, c1)
  1042  	/*
  1043  	 1----2----4---7
  1044  	 | \
  1045  	 |  3----5----6
  1046  	 |             \
  1047  	 ---------------8
  1048  	*/
  1049  
  1050  	iter, err := r.ListCommits(ctx, repository)
  1051  	testutil.MustDo(t, "fill generations", err)
  1052  	defer iter.Close()
  1053  	var lastCommit string
  1054  	var i int
  1055  	for iter.Next() {
  1056  		commit := iter.Value()
  1057  		if i == 0 {
  1058  			gravelerCommitReflection := reflect.Indirect(reflect.ValueOf(graveler.Commit{}))
  1059  			listCommitReflection := reflect.Indirect(reflect.ValueOf(*commit.Commit))
  1060  			listCommitFields := make(map[string]struct{})
  1061  			for i := 0; i < listCommitReflection.NumField(); i++ {
  1062  				listCommitFields[listCommitReflection.Type().Field(i).Name] = struct{}{}
  1063  			}
  1064  			for i := 0; i < gravelerCommitReflection.NumField(); i++ {
  1065  				fieldName := gravelerCommitReflection.Type().Field(i).Name
  1066  				_, exists := listCommitFields[fieldName]
  1067  				if !exists {
  1068  					t.Errorf("missing field: %s in commit response from list commits.", fieldName)
  1069  				}
  1070  			}
  1071  		} else if string(commit.CommitID) < lastCommit {
  1072  			t.Errorf("wrong commitId order for commit number%d in ListCommits response. commitId: %s came after commitId: %s", i, string(commit.CommitID), lastCommit)
  1073  		}
  1074  		lastCommit = string(commit.CommitID)
  1075  		i++
  1076  	}
  1077  }
  1078  
  1079  func TestManager_DeleteExpiredImports(t *testing.T) {
  1080  	r, store := testRefManager(t)
  1081  	ctx := context.Background()
  1082  	repository, err := r.CreateRepository(ctx, "repo1", graveler.Repository{
  1083  		StorageNamespace: "s3://",
  1084  		CreationDate:     time.Now(),
  1085  		DefaultBranchID:  "main",
  1086  	})
  1087  	testutil.Must(t, err)
  1088  
  1089  	imports := []*graveler.ImportStatusData{
  1090  		{
  1091  			Id:        "not_expired1",
  1092  			Completed: false,
  1093  			UpdatedAt: timestamppb.New(time.Now()),
  1094  			Error:     "An error",
  1095  		},
  1096  		{
  1097  			Id:        "not_expired2",
  1098  			Completed: false,
  1099  			UpdatedAt: timestamppb.New(time.Now().Add(-ref.ImportExpiryTime + time.Hour)),
  1100  			Error:     "An error",
  1101  		},
  1102  		{
  1103  			Id:        "expired",
  1104  			Completed: true,
  1105  			UpdatedAt: timestamppb.New(time.Now().Add(-ref.ImportExpiryTime - time.Hour)),
  1106  			Error:     "",
  1107  		},
  1108  		{
  1109  			Id:        "stale",
  1110  			Completed: false,
  1111  			UpdatedAt: timestamppb.New(time.Now().Add(-ref.ImportExpiryTime - time.Hour)),
  1112  			Error:     "",
  1113  		},
  1114  	}
  1115  
  1116  	repoPartition := graveler.RepoPartition(repository)
  1117  	for _, i := range imports {
  1118  		data, err := proto.Marshal(i)
  1119  		require.NoError(t, err)
  1120  		err = store.Set(ctx, []byte(repoPartition), []byte(graveler.ImportsPath(i.Id)), data)
  1121  		require.NoError(t, err)
  1122  	}
  1123  
  1124  	err = r.DeleteExpiredImports(context.Background(), repository)
  1125  	require.NoError(t, err)
  1126  
  1127  	it, err := kv.NewPrimaryIterator(ctx, store, (&graveler.ImportStatusData{}).ProtoReflect().Type(), repoPartition, []byte(graveler.ImportsPath("")), kv.IteratorOptionsFrom([]byte("")))
  1128  	require.NoError(t, err)
  1129  	defer it.Close()
  1130  
  1131  	count := 0
  1132  	for it.Next() {
  1133  		entry := it.Entry()
  1134  		count += 1
  1135  		id := string(entry.Key)
  1136  		require.True(t, strings.HasPrefix(id, "imports/not_expired"), id)
  1137  	}
  1138  	require.NoError(t, it.Err())
  1139  	require.Equal(t, 2, count)
  1140  }
  1141  
  1142  func TestManager_GetRepositoryMetadata(t *testing.T) {
  1143  	ctx := context.Background()
  1144  	r, _ := testRefManager(t)
  1145  	const (
  1146  		repoID = "repo1"
  1147  	)
  1148  	repository, err := r.CreateRepository(context.Background(), repoID, graveler.Repository{
  1149  		StorageNamespace: "s3://",
  1150  		CreationDate:     time.Now(),
  1151  		DefaultBranchID:  "main",
  1152  	})
  1153  	testutil.Must(t, err)
  1154  
  1155  	t.Run("get_on_non_existing_repo", func(t *testing.T) {
  1156  		_, err := r.GetRepositoryMetadata(ctx, "not_exist")
  1157  		require.ErrorIs(t, err, graveler.ErrNotFound)
  1158  	})
  1159  
  1160  	t.Run("basic", func(t *testing.T) {
  1161  		metadata, err := r.GetRepositoryMetadata(ctx, repository.RepositoryID)
  1162  		require.NoError(t, err)
  1163  		require.Nil(t, metadata)
  1164  	})
  1165  }
  1166  
  1167  func TestManager_SetRepositoryMetadata(t *testing.T) {
  1168  	ctx := context.Background()
  1169  	r, store := testRefManager(t)
  1170  	const (
  1171  		repoID = "repo1"
  1172  		key    = "test_key"
  1173  	)
  1174  	repository, err := r.CreateRepository(context.Background(), repoID, graveler.Repository{
  1175  		StorageNamespace: "s3://",
  1176  		CreationDate:     time.Now(),
  1177  		DefaultBranchID:  "main",
  1178  	})
  1179  	testutil.Must(t, err)
  1180  	tests := []struct {
  1181  		name             string
  1182  		f                graveler.RepoMetadataUpdateFunc
  1183  		err              error
  1184  		expectedMetadata graveler.RepositoryMetadata
  1185  	}{
  1186  		{
  1187  			name: "success_branch_update",
  1188  			f: func(metadata graveler.RepositoryMetadata) (graveler.RepositoryMetadata, error) {
  1189  				metadata[key] = "success"
  1190  				return metadata, nil
  1191  			},
  1192  			expectedMetadata: graveler.RepositoryMetadata{key: "success"},
  1193  		},
  1194  		{
  1195  			name: "failed_metadata_update_due_to_changes",
  1196  			f: func(metadata graveler.RepositoryMetadata) (graveler.RepositoryMetadata, error) {
  1197  				m := graveler.RepoMetadata{
  1198  					Metadata: graveler.RepositoryMetadata{
  1199  						key: "failed",
  1200  					},
  1201  				}
  1202  				_ = kv.SetMsg(ctx, store, graveler.RepoPartition(repository), []byte(graveler.RepoMetadataPath()), &m)
  1203  				metadata[key] = "not_expected"
  1204  				return metadata, nil
  1205  			},
  1206  			err:              kv.ErrPredicateFailed,
  1207  			expectedMetadata: graveler.RepositoryMetadata{key: "failed"},
  1208  		},
  1209  		{
  1210  			name: "failed_update_on_validation",
  1211  			f: func(metadata graveler.RepositoryMetadata) (graveler.RepositoryMetadata, error) {
  1212  				return nil, graveler.ErrInvalid
  1213  			},
  1214  			err:              graveler.ErrInvalid,
  1215  			expectedMetadata: graveler.RepositoryMetadata{key: "failed"},
  1216  		},
  1217  	}
  1218  	for _, tt := range tests {
  1219  		t.Run(tt.name, func(t *testing.T) {
  1220  			err = r.SetRepositoryMetadata(ctx, repository, tt.f)
  1221  			require.ErrorIs(t, err, tt.err)
  1222  
  1223  			metadata, err := r.GetRepositoryMetadata(ctx, repository.RepositoryID)
  1224  			require.NoError(t, err)
  1225  			require.Equal(t, tt.expectedMetadata, metadata)
  1226  		})
  1227  	}
  1228  }