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 }