github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/folder_branch_status_test.go (about) 1 // Copyright 2016 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package libkbfs 6 7 import ( 8 "errors" 9 "fmt" 10 "reflect" 11 "sort" 12 "testing" 13 "time" 14 15 "github.com/golang/mock/gomock" 16 "github.com/keybase/client/go/kbfs/data" 17 "github.com/keybase/client/go/kbfs/idutil" 18 "github.com/keybase/client/go/kbfs/kbfsblock" 19 "github.com/keybase/client/go/kbfs/kbfscodec" 20 "github.com/keybase/client/go/kbfs/kbfscrypto" 21 "github.com/keybase/client/go/kbfs/kbfsmd" 22 "github.com/keybase/client/go/kbfs/tlf" 23 "github.com/stretchr/testify/require" 24 "golang.org/x/net/context" 25 ) 26 27 func fbStatusTestInit(t *testing.T) (*gomock.Controller, *ConfigMock, 28 *folderBranchStatusKeeper, *MockNodeCache) { 29 ctr := NewSafeTestReporter(t) 30 mockCtrl := gomock.NewController(ctr) 31 config := NewConfigMock(mockCtrl, ctr) 32 config.mockKbpki.EXPECT().ResolveImplicitTeam( 33 gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). 34 AnyTimes().Return(idutil.ImplicitTeamInfo{}, errors.New("No such team")) 35 nodeCache := NewMockNodeCache(mockCtrl) 36 fbsk := newFolderBranchStatusKeeper(config, nodeCache, nil) 37 interposeDaemonKBPKI(config, "alice", "bob") 38 return mockCtrl, config, fbsk, nodeCache 39 } 40 41 func fbStatusTestShutdown(mockCtrl *gomock.Controller, config *ConfigMock) { 42 config.ctr.CheckForFailures() 43 mockCtrl.Finish() 44 } 45 46 func newMockNode(mockCtrl *gomock.Controller) *MockNode { 47 n := NewMockNode(mockCtrl) 48 id := NewMockNodeID(mockCtrl) 49 n.EXPECT().GetID().AnyTimes().Return(id) 50 return n 51 } 52 53 func TestFBStatusSignal(t *testing.T) { 54 mockCtrl, config, fbsk, nodeCache := fbStatusTestInit(t) 55 defer fbStatusTestShutdown(mockCtrl, config) 56 ctx := context.Background() 57 58 _, c, err := fbsk.getStatus(ctx, nil) 59 if err != nil { 60 t.Fatalf("Couldn't get status: %v", err) 61 } 62 63 n := newMockNode(mockCtrl) 64 p1 := data.Path{ 65 Path: []data.PathNode{{Name: testPPS("a1")}, {Name: testPPS("b1")}}} 66 nodeCache.EXPECT().PathFromNode(mockNodeMatcher{n}).AnyTimes().Return(p1) 67 68 fbsk.addDirtyNode(n) 69 <-c 70 71 _, c, err = fbsk.getStatus(ctx, nil) 72 if err != nil { 73 t.Fatalf("Couldn't get status: %v", err) 74 } 75 76 // no change should result in no signal 77 fbsk.addDirtyNode(n) 78 select { 79 case <-c: 80 t.Fatalf("Status should not have signalled a change") 81 default: 82 } 83 } 84 85 // mockNodeMatcher is needed to compare mock nodes -- for some reason 86 // the default equality comparison doesn't work in gomock. 87 type mockNodeMatcher struct { 88 node *MockNode 89 } 90 91 func (m mockNodeMatcher) Matches(x interface{}) bool { 92 n, ok := x.(*MockNode) 93 if !ok { 94 return false 95 } 96 return n == m.node 97 } 98 99 func (m mockNodeMatcher) String() string { 100 return fmt.Sprintf("Matches node %v", m.node) 101 } 102 103 func checkStringSlices(t *testing.T, expected, got []string) { 104 sort.Strings(expected) 105 sort.Strings(got) 106 if !reflect.DeepEqual(expected, got) { 107 t.Errorf("Expected %v; got %v", expected, got) 108 } 109 } 110 111 func TestFBStatusAllFields(t *testing.T) { 112 mockCtrl, config, fbsk, nodeCache := fbStatusTestInit(t) 113 defer fbStatusTestShutdown(mockCtrl, config) 114 ctx := context.Background() 115 116 // make a new root metadata 117 id := tlf.FakeID(1, tlf.Private) 118 h := parseTlfHandleOrBust(t, config, "alice", tlf.Private, id) 119 u := h.FirstResolvedWriter() 120 rmd, err := makeInitialRootMetadata(config.MetadataVersion(), id, h) 121 require.NoError(t, err) 122 rmd.SetUnmerged() 123 rmd.SetLastModifyingWriter(u.AsUserOrBust()) 124 125 signingKey := kbfscrypto.MakeFakeSigningKeyOrBust("fake seed") 126 signer := kbfscrypto.SigningKeySigner{Key: signingKey} 127 err = rmd.bareMd.SignWriterMetadataInternally( 128 ctx, kbfscodec.NewMsgpack(), signer) 129 require.NoError(t, err) 130 131 // make two nodes with expected PathFromNode calls 132 n1 := newMockNode(mockCtrl) 133 p1 := data.Path{ 134 Path: []data.PathNode{{Name: testPPS("a1")}, {Name: testPPS("b1")}}} 135 nodeCache.EXPECT().PathFromNode(mockNodeMatcher{n1}).AnyTimes().Return(p1) 136 n2 := newMockNode(mockCtrl) 137 p2 := data.Path{ 138 Path: []data.PathNode{{Name: testPPS("a2")}, {Name: testPPS("b2")}}} 139 nodeCache.EXPECT().PathFromNode(mockNodeMatcher{n2}).AnyTimes().Return(p2) 140 141 fbsk.setRootMetadata( 142 MakeImmutableRootMetadata(rmd, signingKey.GetVerifyingKey(), 143 kbfsmd.FakeID(1), time.Now(), true)) 144 fbsk.addDirtyNode(n1) 145 fbsk.addDirtyNode(n2) 146 147 config.mockRekeyQueue.EXPECT().IsRekeyPending(id) 148 149 config.mockClock.EXPECT().Now().AnyTimes().Return(time.Now()) 150 config.mockBserv.EXPECT().GetUserQuotaInfo(gomock.Any()).AnyTimes().Return( 151 &kbfsblock.QuotaInfo{ 152 Total: &kbfsblock.UsageStat{ 153 Bytes: map[kbfsblock.UsageType]int64{ 154 kbfsblock.UsageWrite: 10, 155 kbfsblock.UsageGitWrite: 20, 156 }, 157 }, 158 Limit: 1000, 159 GitLimit: 2000, 160 }, nil) 161 162 // check the returned status for accuracy 163 status, _, err := fbsk.getStatus(ctx, nil) 164 if err != nil { 165 t.Fatalf("Couldn't get status: %v", err) 166 } 167 168 if !status.Staged { 169 t.Errorf("Status does not show staged changes") 170 } 171 if string(status.HeadWriter) != "alice" { 172 t.Errorf("Unexpected head writer in status: %s", status.HeadWriter) 173 } 174 expectedDirtyPaths := []string{p1.String(), p2.String()} 175 checkStringSlices(t, expectedDirtyPaths, status.DirtyPaths) 176 177 require.Equal(t, int64(10), status.UsageBytes) 178 require.Equal(t, int64(1000), status.LimitBytes) 179 require.Equal(t, int64(20), status.GitUsageBytes) 180 require.Equal(t, int64(2000), status.GitLimitBytes) 181 }