github.com/jfrog/jfrog-cli-core/v2@v2.51.0/utils/reposnapshot/snapshotmanager_test.go (about) 1 package reposnapshot 2 3 import ( 4 "encoding/json" 5 "os" 6 "path" 7 "path/filepath" 8 "testing" 9 10 clientutils "github.com/jfrog/jfrog-client-go/utils" 11 "github.com/jfrog/jfrog-client-go/utils/io/fileutils" 12 "github.com/stretchr/testify/assert" 13 ) 14 15 const dummyRepoKey = "dummy-repo-local" 16 17 var expectedFile = filepath.Join("testdata", dummyRepoKey) 18 19 func TestLoadDoesNotExist(t *testing.T) { 20 _, exists, err := LoadRepoSnapshotManager(dummyRepoKey, "/path/to/file") 21 assert.NoError(t, err) 22 assert.False(t, exists) 23 } 24 25 func TestLoad(t *testing.T) { 26 sm, exists, err := LoadRepoSnapshotManager(dummyRepoKey, expectedFile) 27 assert.NoError(t, err) 28 assert.True(t, exists) 29 // Convert to wrapper in order to compare 30 expectedRoot := createTestSnapshotTree(t) 31 expectedWrapper, err := expectedRoot.convertToWrapper() 32 assert.NoError(t, err) 33 rootWrapper, err := sm.root.convertToWrapper() 34 assert.NoError(t, err) 35 36 // Marshal json to compare strings 37 expected, err := json.Marshal(expectedWrapper) 38 assert.NoError(t, err) 39 actual, err := json.Marshal(rootWrapper) 40 assert.NoError(t, err) 41 42 // Compare 43 assert.Equal(t, clientutils.IndentJson(expected), clientutils.IndentJson(actual)) 44 assert.Equal(t, expectedFile, sm.snapshotFilePath) 45 assert.Equal(t, dummyRepoKey, sm.repoKey) 46 } 47 48 func TestSaveToFile(t *testing.T) { 49 manager := initSnapshotManagerTest(t) 50 assert.NoError(t, manager.root.convertAndSaveToFile(manager.snapshotFilePath)) 51 52 // Assert file written as expected. 53 expected, err := os.ReadFile(expectedFile) 54 assert.NoError(t, err) 55 actual, err := os.ReadFile(manager.snapshotFilePath) 56 assert.NoError(t, err) 57 assert.Equal(t, clientutils.IndentJson(expected), clientutils.IndentJson(actual)) 58 } 59 60 func TestNodeCompletedAndTreeCollapsing(t *testing.T) { 61 manager := initSnapshotManagerTest(t) 62 path0 := "0" 63 node0, err := manager.LookUpNode(path0) 64 assert.NoError(t, err) 65 assert.NotNil(t, node0) 66 actualPath, err := node0.getActualPath() 67 assert.NoError(t, err) 68 assert.Equal(t, path.Join(".", path0), actualPath) 69 70 // Try setting a dir with no files as completed. Should not succeed as children are not completed. 71 assert.NoError(t, node0.CheckCompleted()) 72 assertNotCompleted(t, node0) 73 74 if len(node0.children) != 1 { 75 assert.Len(t, node0.children, 1) 76 return 77 } 78 node0a := getChild(node0, "a") 79 80 // Try setting a dir with no children as completed. Should not succeed as it still contains files. 81 assert.NoError(t, node0a.CheckCompleted()) 82 assertNotCompleted(t, node0a) 83 84 setAllNodeFilesCompleted(node0a) 85 assert.NoError(t, node0a.CheckCompleted()) 86 // Node should be completed as all files completed. 87 assertCompleted(t, node0a) 88 // Tree collapsing expected - parent should also be completed as it has no files and all children are completed. 89 assertCompleted(t, node0) 90 // root should not be completed as it still has uncompleted children. 91 assertNotCompleted(t, manager.root) 92 } 93 94 func TestNodeCompletedWhileExploring(t *testing.T) { 95 manager := initSnapshotManagerTest(t) 96 // Mark a node without files or children as unexplored and try to set it as completed. Should not succeed. 97 path2 := "2" 98 node2, err := manager.LookUpNode(path2) 99 assert.NoError(t, err) 100 assert.NotNil(t, node2) 101 node2.NodeStatus = Exploring 102 assert.NoError(t, node2.CheckCompleted()) 103 assertNotCompleted(t, node2) 104 105 // Mark it as explored and try again. 106 node2.NodeStatus = DoneExploring 107 assert.NoError(t, node2.CheckCompleted()) 108 assertCompleted(t, node2) 109 } 110 111 func assertCompleted(t *testing.T, node *Node) { 112 assert.Equal(t, Completed, node.NodeStatus) 113 assert.Nil(t, node.parent) 114 assert.Len(t, node.children, 0) 115 } 116 117 func assertNotCompleted(t *testing.T, node *Node) { 118 assert.Less(t, node.NodeStatus, Completed) 119 } 120 121 type lookUpAndPathTestSuite struct { 122 testName string 123 path string 124 errorExpected bool 125 } 126 127 func TestLookUpNodeAndActualPath(t *testing.T) { 128 manager := initSnapshotManagerTest(t) 129 130 tests := []lookUpAndPathTestSuite{ 131 {"root", ".", false}, 132 {"dir on root", "2", false}, 133 {"complex path with separator suffix", "1/a/", false}, 134 {"complex path with no separator suffix", "1/a", false}, 135 {"empty path", "", true}, 136 } 137 138 for _, test := range tests { 139 t.Run(test.testName, func(t *testing.T) { 140 node, err := manager.LookUpNode(test.path) 141 if test.errorExpected { 142 assert.ErrorContains(t, err, getLookUpNodeError(test.path)) 143 return 144 } else { 145 assert.NoError(t, err) 146 } 147 if node == nil { 148 assert.NotNil(t, node) 149 return 150 } 151 actualPath, err := node.getActualPath() 152 assert.NoError(t, err) 153 assert.Equal(t, path.Join(".", test.path), actualPath) 154 }) 155 } 156 } 157 158 func setAllNodeFilesCompleted(node *Node) { 159 node.filesCount = 0 160 } 161 162 // Tree dirs representation: 163 // root -> 0 -> a + 3 files 164 // ------> 1 -> a + 1 file 165 // -----------> b + 2 files 166 // ---------- + 1 file 167 // ------> 2 168 // ----- + 1 file 169 func createTestSnapshotTree(t *testing.T) *Node { 170 root := createNodeBase(t, ".", 1, nil) 171 dir0 := createNodeBase(t, "0", 0, root) 172 dir1 := createNodeBase(t, "1", 1, root) 173 dir2 := createNodeBase(t, "2", 0, root) 174 175 dir0a := createNodeBase(t, "a", 3, dir0) 176 dir1a := createNodeBase(t, "a", 1, dir1) 177 dir1b := createNodeBase(t, "b", 2, dir1) 178 179 addChildren(root, dir0, dir1, dir2) 180 addChildren(dir0, dir0a) 181 addChildren(dir1, dir1a, dir1b) 182 return root 183 } 184 185 func addChildren(node *Node, children ...*Node) { 186 node.children = append(node.children, children...) 187 } 188 189 func createNodeBase(t *testing.T, name string, filesCount int, parent *Node) *Node { 190 node := CreateNewNode(name, parent) 191 node.NodeStatus = DoneExploring 192 for i := 0; i < filesCount; i++ { 193 assert.NoError(t, node.IncrementFilesCount(uint64(i))) 194 } 195 return node 196 } 197 198 func getChild(node *Node, childName string) *Node { 199 for _, child := range node.children { 200 if child.name == childName { 201 return child 202 } 203 } 204 return nil 205 } 206 207 func initSnapshotManagerTest(t *testing.T) RepoSnapshotManager { 208 file, err := fileutils.CreateTempFile() 209 assert.NoError(t, err) 210 assert.NoError(t, file.Close()) 211 return newRepoSnapshotManager(createTestSnapshotTree(t), dummyRepoKey, file.Name()) 212 } 213 214 func TestGetDirectorySnapshotNodeWithLruLRU(t *testing.T) { 215 originalCacheSize := cacheSize 216 cacheSize = 3 217 defer func() { 218 cacheSize = originalCacheSize 219 }() 220 manager := initSnapshotManagerTest(t) 221 222 // Assert lru cache is empty before getting nodes. 223 assert.Zero(t, manager.lruCache.Len()) 224 225 // Get 3 nodes which will cause the cache to reach its cache size. 226 _ = getNodeAndAssert(t, manager, "1/b/", 1) 227 _ = getNodeAndAssert(t, manager, "./", 2) 228 _ = getNodeAndAssert(t, manager, "2/", 3) 229 230 // Get another node that exceeds the cache size and assert the LRU node was removed. 231 _ = getNodeAndAssert(t, manager, "0/a/", 3) 232 _, exists := manager.lruCache.Get("1/b/") 233 assert.False(t, exists) 234 } 235 236 func assertReturnedNode(t *testing.T, manager RepoSnapshotManager, node *Node, relativePath string, expectedLen int) { 237 if !assert.NotNil(t, node) { 238 return 239 } 240 actualPath, err := node.getActualPath() 241 assert.NoError(t, err) 242 assert.Equal(t, relativePath, actualPath) 243 assert.Equal(t, expectedLen, manager.lruCache.Len()) 244 } 245 246 func getNodeAndAssert(t *testing.T, manager RepoSnapshotManager, relativePath string, expectedLen int) *Node { 247 node, err := manager.GetDirectorySnapshotNodeWithLru(relativePath) 248 assert.NoError(t, err) 249 assertReturnedNode(t, manager, node, path.Dir(relativePath), expectedLen) 250 return node 251 }