github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/md_util_test.go (about)

     1  // Copyright 2018 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  	"fmt"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/keybase/client/go/kbfs/data"
    13  	"github.com/keybase/client/go/kbfs/kbfsblock"
    14  	"github.com/keybase/client/go/kbfs/kbfscodec"
    15  	"github.com/keybase/client/go/kbfs/kbfsmd"
    16  	"github.com/keybase/client/go/kbfs/test/clocktest"
    17  	"github.com/keybase/client/go/kbfs/tlf"
    18  	"github.com/keybase/client/go/kbfs/tlfhandle"
    19  	kbname "github.com/keybase/client/go/kbun"
    20  	"github.com/keybase/client/go/logger"
    21  	"github.com/pkg/errors"
    22  	"github.com/stretchr/testify/require"
    23  	"golang.org/x/net/context"
    24  )
    25  
    26  type testBlockCache struct {
    27  	b data.Block
    28  }
    29  
    30  func (c testBlockCache) Get(ptr data.BlockPointer) (data.Block, error) {
    31  	return c.b, nil
    32  }
    33  
    34  func (testBlockCache) Put(ptr data.BlockPointer, tlf tlf.ID, block data.Block,
    35  	lifetime data.BlockCacheLifetime, _ data.BlockCacheHashBehavior) error {
    36  	return errors.New("Shouldn't be called")
    37  }
    38  
    39  type blockChangesNoInfo struct {
    40  	// An ordered list of operations completed in this update
    41  	Ops opsList `codec:"o,omitempty"`
    42  }
    43  
    44  func TestReembedBlockChanges(t *testing.T) {
    45  	codec := kbfscodec.NewMsgpack()
    46  	RegisterOps(codec)
    47  
    48  	oldDir := data.BlockPointer{ID: kbfsblock.FakeID(1)}
    49  	co, err := newCreateOp("file", oldDir, data.File)
    50  	require.NoError(t, err)
    51  
    52  	changes := blockChangesNoInfo{
    53  		Ops: opsList{co},
    54  	}
    55  
    56  	encodedChanges, err := codec.Encode(changes)
    57  	require.NoError(t, err)
    58  	block := &data.FileBlock{Contents: encodedChanges}
    59  
    60  	ctx := context.Background()
    61  	bcache := testBlockCache{block}
    62  	tlfID := tlf.FakeID(1, tlf.Private)
    63  	mode := modeTest{NewInitModeFromType(InitDefault)}
    64  
    65  	ptr := data.BlockPointer{ID: kbfsblock.FakeID(2)}
    66  	pmd := PrivateMetadata{
    67  		Changes: BlockChanges{
    68  			Info: data.BlockInfo{
    69  				BlockPointer: ptr,
    70  			},
    71  		},
    72  	}
    73  
    74  	// We make the cache always return a block, so we can pass in
    75  	// nil for bops and rmdWithKeys.
    76  	err = reembedBlockChanges(
    77  		ctx, codec, bcache, nil, mode, tlfID, &pmd, nil,
    78  		logger.NewTestLogger(t))
    79  	require.NoError(t, err)
    80  
    81  	// We expect to get changes back, except with the implicit ref
    82  	// block added.
    83  	expectedCO, err := newCreateOp("file", oldDir, data.File)
    84  	require.NoError(t, err)
    85  	expectedCO.AddRefBlock(ptr)
    86  	expectedChanges := BlockChanges{
    87  		Ops: opsList{expectedCO},
    88  	}
    89  
    90  	// In particular, Info should be empty.
    91  	expectedPmd := PrivateMetadata{
    92  		Changes: expectedChanges,
    93  
    94  		cachedChanges: BlockChanges{
    95  			Info: data.BlockInfo{
    96  				BlockPointer: ptr,
    97  			},
    98  		},
    99  	}
   100  	require.Equal(t, expectedPmd, pmd)
   101  }
   102  
   103  func TestGetRevisionByTime(t *testing.T) {
   104  	var u1 kbname.NormalizedUsername = "u1"
   105  	config, _, ctx, cancel := kbfsOpsInitNoMocks(t, u1)
   106  	defer kbfsTestShutdownNoMocks(ctx, t, config, cancel)
   107  
   108  	clock, t1 := clocktest.NewTestClockAndTimeNow()
   109  	config.SetClock(clock)
   110  
   111  	t.Log("Create revision 1")
   112  	h, err := tlfhandle.ParseHandle(
   113  		ctx, config.KBPKI(), config.MDOps(), nil, string(u1), tlf.Private)
   114  	require.NoError(t, err)
   115  	kbfsOps := config.KBFSOps()
   116  	rootNode, _, err := kbfsOps.GetOrCreateRootNode(ctx, h, data.MasterBranch)
   117  	require.NoError(t, err)
   118  	err = kbfsOps.SyncAll(ctx, rootNode.GetFolderBranch())
   119  	require.NoError(t, err)
   120  
   121  	t.Log("Create revision 2")
   122  	t2 := t1.Add(1 * time.Minute)
   123  	clock.Set(t2)
   124  	nodeA, _, err := kbfsOps.CreateFile(
   125  		ctx, rootNode, testPPS("a"), false, NoExcl)
   126  	require.NoError(t, err)
   127  	data := []byte{1}
   128  	err = kbfsOps.Write(ctx, nodeA, data, 0)
   129  	require.NoError(t, err)
   130  	err = kbfsOps.SyncAll(ctx, rootNode.GetFolderBranch())
   131  	require.NoError(t, err)
   132  
   133  	t.Log("Clear the MD cache, to make sure it gets repopulated")
   134  	config.ResetCaches()
   135  
   136  	t.Log(ctx, "Check exact times")
   137  	rev, err := GetMDRevisionByTime(ctx, config, h, t2)
   138  	require.NoError(t, err)
   139  	require.Equal(t, kbfsmd.Revision(2), rev)
   140  	_, err = config.MDCache().Get(h.TlfID(), rev, kbfsmd.NullBranchID)
   141  	require.NoError(t, err)
   142  	rev, err = GetMDRevisionByTime(ctx, config, h, t1)
   143  	require.NoError(t, err)
   144  	require.Equal(t, kbfsmd.Revision(1), rev)
   145  	_, err = config.MDCache().Get(h.TlfID(), rev, kbfsmd.NullBranchID)
   146  	require.NoError(t, err)
   147  
   148  	t.Log(ctx, "Check in-between times")
   149  	rev, err = GetMDRevisionByTime(ctx, config, h, t2.Add(30*time.Second))
   150  	require.NoError(t, err)
   151  	require.Equal(t, kbfsmd.Revision(2), rev)
   152  	rev, err = GetMDRevisionByTime(ctx, config, h, t1.Add(30*time.Second))
   153  	require.NoError(t, err)
   154  	require.Equal(t, kbfsmd.Revision(1), rev)
   155  
   156  	t.Log(ctx, "Check too-early time")
   157  	_, err = GetMDRevisionByTime(ctx, config, h, t1.Add(-30*time.Second))
   158  	require.Error(t, err)
   159  }
   160  
   161  func TestGetChangesBetweenRevisions(t *testing.T) {
   162  	var u1 kbname.NormalizedUsername = "u1"
   163  	config, _, ctx, cancel := kbfsOpsInitNoMocks(t, u1)
   164  	defer kbfsTestShutdownNoMocks(ctx, t, config, cancel)
   165  
   166  	t.Log("Create revision 1")
   167  	h, err := tlfhandle.ParseHandle(
   168  		ctx, config.KBPKI(), config.MDOps(), nil, string(u1), tlf.Private)
   169  	require.NoError(t, err)
   170  	kbfsOps := config.KBFSOps()
   171  	rootNode, _, err := kbfsOps.GetOrCreateRootNode(ctx, h, data.MasterBranch)
   172  	require.NoError(t, err)
   173  
   174  	t.Log("Add more revisions")
   175  	nodeA, _, err := kbfsOps.CreateDir(ctx, rootNode, testPPS("a"))
   176  	require.NoError(t, err)
   177  	// Revision 2.
   178  	err = kbfsOps.SyncAll(ctx, rootNode.GetFolderBranch())
   179  	require.NoError(t, err)
   180  
   181  	nodeB, _, err := kbfsOps.CreateFile(ctx, nodeA, testPPS("b"), false, NoExcl)
   182  	require.NoError(t, err)
   183  	// Revision 3.
   184  	err = kbfsOps.SyncAll(ctx, rootNode.GetFolderBranch())
   185  	require.NoError(t, err)
   186  
   187  	err = kbfsOps.Write(ctx, nodeB, []byte("test"), 0)
   188  	require.NoError(t, err)
   189  	// Revision 4.
   190  	err = kbfsOps.SyncAll(ctx, rootNode.GetFolderBranch())
   191  	require.NoError(t, err)
   192  
   193  	nodeC, _, err := kbfsOps.CreateDir(ctx, rootNode, testPPS("c"))
   194  	require.NoError(t, err)
   195  	// Revision 5.
   196  	err = kbfsOps.SyncAll(ctx, rootNode.GetFolderBranch())
   197  	require.NoError(t, err)
   198  
   199  	err = kbfsOps.Rename(ctx, nodeA, testPPS("b"), nodeC, testPPS("d"))
   200  	require.NoError(t, err)
   201  	// Revision 6.
   202  	err = kbfsOps.SyncAll(ctx, rootNode.GetFolderBranch())
   203  	require.NoError(t, err)
   204  
   205  	err = kbfsOps.RemoveDir(ctx, rootNode, testPPS("a"))
   206  	require.NoError(t, err)
   207  	// Revision 7.
   208  	err = kbfsOps.SyncAll(ctx, rootNode.GetFolderBranch())
   209  	require.NoError(t, err)
   210  
   211  	type e struct {
   212  		t    ChangeType
   213  		p    string
   214  		u    int // len(UnrefsForDelete)
   215  		used bool
   216  	}
   217  	checkChanges := func(changes []*ChangeItem, expectedChanges []e) {
   218  		require.Len(t, changes, len(expectedChanges))
   219  		for _, c := range changes {
   220  			found := false
   221  			for _, ec := range expectedChanges {
   222  				if !ec.used && ec.t == c.Type &&
   223  					ec.u == len(c.UnrefsForDelete) &&
   224  					ec.p == c.CurrPath.CanonicalPathPlaintext() {
   225  					found = true
   226  					ec.used = true
   227  					break
   228  				}
   229  			}
   230  			require.True(
   231  				t, found, fmt.Sprintf(
   232  					"Didn't expect change: %#v, changes=%#v, expected=%#v",
   233  					*c, changes, expectedChanges))
   234  		}
   235  	}
   236  
   237  	t.Log("Check single revision")
   238  	tlfID := rootNode.GetFolderBranch().Tlf
   239  	changes, _, err := GetChangesBetweenRevisions(
   240  		ctx, config, tlfID, kbfsmd.Revision(1), kbfsmd.Revision(2))
   241  	require.NoError(t, err)
   242  	checkChanges(
   243  		changes, []e{
   244  			{ChangeTypeWrite, "/keybase/private/u1", 0, false},
   245  			{ChangeTypeWrite, "/keybase/private/u1/a", 0, false},
   246  		})
   247  
   248  	t.Log("Check multiple revisions")
   249  	changes, _, err = GetChangesBetweenRevisions(
   250  		ctx, config, tlfID, kbfsmd.Revision(1), kbfsmd.Revision(5))
   251  	require.NoError(t, err)
   252  	checkChanges(
   253  		changes, []e{
   254  			{ChangeTypeWrite, "/keybase/private/u1", 0, false},
   255  			{ChangeTypeWrite, "/keybase/private/u1/a", 0, false},
   256  			{ChangeTypeWrite, "/keybase/private/u1/a/b", 0, false},
   257  			{ChangeTypeWrite, "/keybase/private/u1/c", 0, false},
   258  		})
   259  
   260  	t.Log("Check rename")
   261  	changes, _, err = GetChangesBetweenRevisions(
   262  		ctx, config, tlfID, kbfsmd.Revision(5), kbfsmd.Revision(6))
   263  	require.NoError(t, err)
   264  	checkChanges(
   265  		changes, []e{
   266  			{ChangeTypeWrite, "/keybase/private/u1", 0, false},
   267  			{ChangeTypeWrite, "/keybase/private/u1/c", 0, false},
   268  			{ChangeTypeRename, "/keybase/private/u1/c/d", 0, false},
   269  		})
   270  
   271  	t.Log("Check internal rename")
   272  	changes, _, err = GetChangesBetweenRevisions(
   273  		ctx, config, tlfID, kbfsmd.Revision(1), kbfsmd.Revision(6))
   274  	require.NoError(t, err)
   275  	checkChanges(
   276  		changes, []e{
   277  			{ChangeTypeWrite, "/keybase/private/u1", 0, false},
   278  			{ChangeTypeWrite, "/keybase/private/u1/a", 0, false},
   279  			{ChangeTypeWrite, "/keybase/private/u1/c", 0, false},
   280  			{ChangeTypeWrite, "/keybase/private/u1/c/d", 0, false},
   281  		})
   282  
   283  	t.Log("Check delete")
   284  	changes, _, err = GetChangesBetweenRevisions(
   285  		ctx, config, tlfID, kbfsmd.Revision(6), kbfsmd.Revision(7))
   286  	require.NoError(t, err)
   287  	checkChanges(
   288  		changes, []e{
   289  			{ChangeTypeWrite, "/keybase/private/u1", 0, false},
   290  			{ChangeTypeDelete, "/keybase/private/u1/a", 1, false},
   291  		})
   292  
   293  	t.Log("Check full sequence")
   294  	changes, _, err = GetChangesBetweenRevisions(
   295  		ctx, config, tlfID, kbfsmd.Revision(1), kbfsmd.Revision(7))
   296  	require.NoError(t, err)
   297  	checkChanges(
   298  		changes, []e{
   299  			{ChangeTypeWrite, "/keybase/private/u1", 0, false},
   300  			{ChangeTypeWrite, "/keybase/private/u1/c", 0, false},
   301  			{ChangeTypeWrite, "/keybase/private/u1/c/d", 0, false},
   302  		})
   303  }