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  }