github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/graveler/branch/protection_manager_test.go (about) 1 package branch_test 2 3 import ( 4 "context" 5 "encoding/base64" 6 "errors" 7 "testing" 8 9 "github.com/go-openapi/swag" 10 "github.com/go-test/deep" 11 "github.com/golang/mock/gomock" 12 "github.com/stretchr/testify/require" 13 "github.com/treeverse/lakefs/pkg/graveler" 14 "github.com/treeverse/lakefs/pkg/graveler/branch" 15 "github.com/treeverse/lakefs/pkg/graveler/mock" 16 "github.com/treeverse/lakefs/pkg/graveler/settings" 17 "github.com/treeverse/lakefs/pkg/kv/kvtest" 18 "github.com/treeverse/lakefs/pkg/testutil" 19 ) 20 21 var repository = &graveler.RepositoryRecord{ 22 RepositoryID: "example-repo", 23 Repository: &graveler.Repository{ 24 StorageNamespace: "mem://my-storage", 25 DefaultBranchID: "main", 26 }, 27 } 28 29 func TestSetAndGet(t *testing.T) { 30 ctx := context.Background() 31 bpm := prepareTest(t, ctx) 32 _, eTag, err := bpm.GetRules(ctx, repository) 33 require.NoError(t, err) 34 require.NotNil(t, eTag) 35 require.Equal(t, "", *eTag) 36 37 err = bpm.SetRules(ctx, repository, &graveler.BranchProtectionRules{ 38 BranchPatternToBlockedActions: map[string]*graveler.BranchProtectionBlockedActions{ 39 "main*": { 40 Value: []graveler.BranchProtectionBlockedAction{ 41 graveler.BranchProtectionBlockedAction_STAGING_WRITE, 42 }, 43 }, 44 }, 45 }, eTag) 46 testutil.Must(t, err) 47 48 rules, _, err := bpm.GetRules(ctx, repository) 49 testutil.Must(t, err) 50 51 if len(rules.BranchPatternToBlockedActions) != 1 { 52 t.Fatalf("expected 1 rule, got %d", len(rules.BranchPatternToBlockedActions)) 53 } 54 expectedActions := &graveler.BranchProtectionBlockedActions{Value: []graveler.BranchProtectionBlockedAction{graveler.BranchProtectionBlockedAction_STAGING_WRITE}} 55 if diff := deep.Equal(expectedActions, rules.BranchPatternToBlockedActions["main*"]); diff != nil { 56 t.Fatalf("got unexpected blocked actions. diff=%s", diff) 57 } 58 } 59 60 func TestSetWrongETag(t *testing.T) { 61 ctx := context.Background() 62 bpm := prepareTest(t, ctx) 63 err := bpm.SetRules(ctx, repository, &graveler.BranchProtectionRules{ 64 BranchPatternToBlockedActions: map[string]*graveler.BranchProtectionBlockedActions{ 65 "main*": { 66 Value: []graveler.BranchProtectionBlockedAction{ 67 graveler.BranchProtectionBlockedAction_STAGING_WRITE, 68 }, 69 }, 70 }, 71 }, swag.String(base64.StdEncoding.EncodeToString([]byte("WRONG_ETAG")))) 72 if !errors.Is(err, graveler.ErrPreconditionFailed) { 73 t.Fatalf("expected ErrPreconditionFailed, got %v", err) 74 } 75 } 76 77 func TestIsBlocked(t *testing.T) { 78 ctx := context.Background() 79 var ( 80 action1 = graveler.BranchProtectionBlockedAction_STAGING_WRITE 81 action2 = graveler.BranchProtectionBlockedAction_COMMIT 82 action3 = graveler.BranchProtectionBlockedAction(2) 83 action4 = graveler.BranchProtectionBlockedAction(3) 84 ) 85 tests := map[string]struct { 86 patternToBlockedActions map[string]*graveler.BranchProtectionBlockedActions 87 expectedBlockedActions map[string]*graveler.BranchProtectionBlockedActions 88 expectedAllowedActions map[string]*graveler.BranchProtectionBlockedActions 89 }{ 90 "two_rules": { 91 patternToBlockedActions: map[string]*graveler.BranchProtectionBlockedActions{"main*": {Value: []graveler.BranchProtectionBlockedAction{action1}}, "dev": {Value: []graveler.BranchProtectionBlockedAction{action2}}}, 92 expectedBlockedActions: map[string]*graveler.BranchProtectionBlockedActions{"main": {Value: []graveler.BranchProtectionBlockedAction{action1}}, "main2": {Value: []graveler.BranchProtectionBlockedAction{action1}}, "dev": {Value: []graveler.BranchProtectionBlockedAction{action2}}}, 93 expectedAllowedActions: map[string]*graveler.BranchProtectionBlockedActions{"main": {Value: []graveler.BranchProtectionBlockedAction{action2}}, "main2": {Value: []graveler.BranchProtectionBlockedAction{action2}}, "dev": {Value: []graveler.BranchProtectionBlockedAction{action1}}, "dev1": {Value: []graveler.BranchProtectionBlockedAction{action1, action2}}}, 94 }, 95 "multiple_blocked": { 96 patternToBlockedActions: map[string]*graveler.BranchProtectionBlockedActions{"main*": {Value: []graveler.BranchProtectionBlockedAction{action1, action2, action3}}, "stable_*": {Value: []graveler.BranchProtectionBlockedAction{action3, action4}}}, 97 expectedBlockedActions: map[string]*graveler.BranchProtectionBlockedActions{"main": {Value: []graveler.BranchProtectionBlockedAction{action1, action2, action3}}, "main2": {Value: []graveler.BranchProtectionBlockedAction{action1, action2, action3}}, "stable_branch": {Value: []graveler.BranchProtectionBlockedAction{action3, action4}}}, 98 expectedAllowedActions: map[string]*graveler.BranchProtectionBlockedActions{"main": {Value: []graveler.BranchProtectionBlockedAction{action4}}, "main2": {Value: []graveler.BranchProtectionBlockedAction{action4}}, "stable_branch": {Value: []graveler.BranchProtectionBlockedAction{action1, action2}}}, 99 }, 100 "overlapping_patterns": { 101 patternToBlockedActions: map[string]*graveler.BranchProtectionBlockedActions{"main*": {Value: []graveler.BranchProtectionBlockedAction{action1}}, "mai*": {Value: []graveler.BranchProtectionBlockedAction{action2}}, "ma*": {Value: []graveler.BranchProtectionBlockedAction{action2, action3}}}, 102 expectedBlockedActions: map[string]*graveler.BranchProtectionBlockedActions{"main": {Value: []graveler.BranchProtectionBlockedAction{action1, action2, action3}}}, 103 expectedAllowedActions: map[string]*graveler.BranchProtectionBlockedActions{"main": {Value: []graveler.BranchProtectionBlockedAction{action4}}}, 104 }, 105 "no_rules": { 106 expectedAllowedActions: map[string]*graveler.BranchProtectionBlockedActions{"main": {Value: []graveler.BranchProtectionBlockedAction{action1, action2}}}, 107 }, 108 } 109 for name, tst := range tests { 110 t.Run(name, func(t *testing.T) { 111 bpm := prepareTest(t, ctx) 112 testutil.Must(t, bpm.SetRules(ctx, repository, &graveler.BranchProtectionRules{ 113 BranchPatternToBlockedActions: tst.patternToBlockedActions, 114 }, nil)) 115 116 for branchID, expectedBlockedActions := range tst.expectedBlockedActions { 117 for _, action := range expectedBlockedActions.Value { 118 res, err := bpm.IsBlocked(ctx, repository, graveler.BranchID(branchID), action) 119 testutil.Must(t, err) 120 if !res { 121 t.Fatalf("branch %s action %s expected to be blocked, but was allowed", branchID, action) 122 } 123 } 124 } 125 for branchID, expectedAllowedActions := range tst.expectedAllowedActions { 126 for _, action := range expectedAllowedActions.Value { 127 res, err := bpm.IsBlocked(ctx, repository, graveler.BranchID(branchID), action) 128 testutil.Must(t, err) 129 if res { 130 t.Fatalf("branch %s action %s expected to be allowed, but was blocked", branchID, action) 131 } 132 } 133 } 134 }) 135 } 136 } 137 138 func prepareTest(t *testing.T, ctx context.Context) *branch.ProtectionManager { 139 ctrl := gomock.NewController(t) 140 refManager := mock.NewMockRefManager(ctrl) 141 branchLock := mock.NewMockBranchLocker(ctrl) 142 cb := func(_ context.Context, _ *graveler.RepositoryRecord, _ graveler.BranchID, f func() (interface{}, error)) (interface{}, error) { 143 return f() 144 } 145 branchLock.EXPECT().MetadataUpdater(ctx, gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(cb).AnyTimes() 146 refManager.EXPECT().GetRepository(ctx, gomock.Any()).AnyTimes().Return(repository, nil) 147 kvStore := kvtest.GetStore(ctx, t) 148 m := settings.NewManager(refManager, kvStore) 149 150 return branch.NewProtectionManager(m) 151 }