github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+incompatible/core/chaincode/persistence/persistence_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package persistence_test 8 9 import ( 10 "fmt" 11 "io/ioutil" 12 "os" 13 "path/filepath" 14 15 "github.com/hyperledger/fabric/common/chaincode" 16 "github.com/hyperledger/fabric/core/chaincode/persistence" 17 "github.com/hyperledger/fabric/core/chaincode/persistence/mock" 18 . "github.com/onsi/ginkgo" 19 . "github.com/onsi/gomega" 20 "github.com/pkg/errors" 21 ) 22 23 var _ = Describe("Persistence", func() { 24 Describe("FilesystemWriter", func() { 25 var ( 26 filesystemIO *persistence.FilesystemIO 27 testDir string 28 ) 29 30 BeforeEach(func() { 31 filesystemIO = &persistence.FilesystemIO{} 32 33 var err error 34 testDir, err = ioutil.TempDir("", "persistence-test") 35 Expect(err).NotTo(HaveOccurred()) 36 }) 37 38 AfterEach(func() { 39 os.RemoveAll(testDir) 40 }) 41 42 It("writes a file", func() { 43 path := filepath.Join(testDir, "write") 44 err := filesystemIO.WriteFile(testDir, "write", []byte("test")) 45 Expect(err).NotTo(HaveOccurred()) 46 47 _, err = os.Stat(path) 48 Expect(err).NotTo(HaveOccurred()) 49 }) 50 51 When("an empty path is supplied to WriteFile", func() { 52 It("returns error", func() { 53 err := filesystemIO.WriteFile("", "write", []byte("test")) 54 Expect(err.Error()).To(Equal("empty path not allowed")) 55 }) 56 }) 57 58 It("stats a file", func() { 59 path := filepath.Join(testDir, "stat") 60 err := ioutil.WriteFile(path, []byte("test"), 0600) 61 Expect(err).NotTo(HaveOccurred()) 62 63 exists, err := filesystemIO.Exists(path) 64 Expect(err).NotTo(HaveOccurred()) 65 Expect(exists).To(BeTrue()) 66 }) 67 68 It("stats a non-existent file", func() { 69 exists, err := filesystemIO.Exists("not quite") 70 Expect(err).NotTo(HaveOccurred()) 71 Expect(exists).To(BeFalse()) 72 }) 73 74 It("removes a file", func() { 75 path := filepath.Join(testDir, "remove") 76 err := ioutil.WriteFile(path, []byte("test"), 0600) 77 Expect(err).NotTo(HaveOccurred()) 78 79 _, err = os.Stat(path) 80 Expect(err).NotTo(HaveOccurred()) 81 82 err = filesystemIO.Remove(path) 83 Expect(err).NotTo(HaveOccurred()) 84 85 _, err = os.Stat(path) 86 Expect(err).To(HaveOccurred()) 87 }) 88 89 It("reads a file", func() { 90 path := filepath.Join(testDir, "readfile") 91 err := ioutil.WriteFile(path, []byte("test"), 0600) 92 Expect(err).NotTo(HaveOccurred()) 93 94 _, err = os.Stat(path) 95 Expect(err).NotTo(HaveOccurred()) 96 97 fileBytes, err := filesystemIO.ReadFile(path) 98 Expect(err).NotTo(HaveOccurred()) 99 Expect(fileBytes).To(Equal([]byte("test"))) 100 }) 101 102 It("reads a directory", func() { 103 path := filepath.Join(testDir, "readdir") 104 err := ioutil.WriteFile(path, []byte("test"), 0600) 105 Expect(err).NotTo(HaveOccurred()) 106 107 _, err = os.Stat(path) 108 Expect(err).NotTo(HaveOccurred()) 109 110 files, err := filesystemIO.ReadDir(testDir) 111 Expect(err).NotTo(HaveOccurred()) 112 Expect(files).To(HaveLen(1)) 113 }) 114 115 It("makes a directory (and any necessary parent directories)", func() { 116 path := filepath.Join(testDir, "make", "dir") 117 err := filesystemIO.MakeDir(path, 0755) 118 Expect(err).NotTo(HaveOccurred()) 119 120 _, err = os.Stat(path) 121 Expect(err).NotTo(HaveOccurred()) 122 }) 123 }) 124 125 Describe("NewStore", func() { 126 var ( 127 err error 128 tempDir string 129 store *persistence.Store 130 ) 131 132 BeforeEach(func() { 133 tempDir, err = ioutil.TempDir("", "NewStore") 134 Expect(err).NotTo(HaveOccurred()) 135 }) 136 137 AfterEach(func() { 138 os.RemoveAll(tempDir) 139 }) 140 141 It("creates a persistence store with the specified path and creates the directory on the filesystem", func() { 142 store = persistence.NewStore(tempDir) 143 Expect(store.Path).To(Equal(tempDir)) 144 _, err = os.Stat(tempDir) 145 Expect(err).NotTo(HaveOccurred()) 146 }) 147 }) 148 149 Describe("Initialize", func() { 150 var ( 151 mockReadWriter *mock.IOReadWriter 152 store *persistence.Store 153 ) 154 155 BeforeEach(func() { 156 mockReadWriter = &mock.IOReadWriter{} 157 mockReadWriter.ExistsReturns(false, nil) 158 mockReadWriter.MakeDirReturns(nil) 159 160 store = &persistence.Store{ 161 ReadWriter: mockReadWriter, 162 } 163 }) 164 165 It("creates the directory for the persistence store", func() { 166 store.Initialize() 167 Expect(mockReadWriter.ExistsCallCount()).To(Equal(1)) 168 Expect(mockReadWriter.MakeDirCallCount()).To(Equal(1)) 169 }) 170 171 Context("when the directory already exists", func() { 172 BeforeEach(func() { 173 mockReadWriter.ExistsReturns(true, nil) 174 }) 175 176 It("returns without creating the directory", func() { 177 store.Initialize() 178 Expect(mockReadWriter.ExistsCallCount()).To(Equal(1)) 179 Expect(mockReadWriter.MakeDirCallCount()).To(Equal(0)) 180 }) 181 }) 182 183 Context("when the existence of the directory cannot be determined", func() { 184 BeforeEach(func() { 185 mockReadWriter.ExistsReturns(false, errors.New("blurg")) 186 }) 187 188 It("returns without creating the directory", func() { 189 Expect(store.Initialize).Should(Panic()) 190 Expect(mockReadWriter.ExistsCallCount()).To(Equal(1)) 191 Expect(mockReadWriter.MakeDirCallCount()).To(Equal(0)) 192 }) 193 }) 194 195 Context("when the directory cannot be created", func() { 196 BeforeEach(func() { 197 mockReadWriter.MakeDirReturns(errors.New("blarg")) 198 }) 199 200 It("returns without creating the directory", func() { 201 Expect(store.Initialize).Should(Panic()) 202 Expect(mockReadWriter.ExistsCallCount()).To(Equal(1)) 203 Expect(mockReadWriter.MakeDirCallCount()).To(Equal(1)) 204 }) 205 }) 206 }) 207 208 Describe("Save", func() { 209 var ( 210 mockReadWriter *mock.IOReadWriter 211 store *persistence.Store 212 pkgBytes []byte 213 ) 214 215 BeforeEach(func() { 216 mockReadWriter = &mock.IOReadWriter{} 217 mockReadWriter.ExistsReturns(false, nil) 218 mockReadWriter.WriteFileReturns(nil) 219 220 store = &persistence.Store{ 221 ReadWriter: mockReadWriter, 222 } 223 224 pkgBytes = []byte("testpkg") 225 }) 226 227 It("saves a new code package successfully", func() { 228 packageID, err := store.Save("testcc", pkgBytes) 229 Expect(err).NotTo(HaveOccurred()) 230 Expect(packageID).To(Equal("testcc:3fec0187440286d404241e871b44725310b11aaf43d100b053eae712fcabc66d")) 231 Expect(mockReadWriter.WriteFileCallCount()).To(Equal(1)) 232 pkgDataFilePath, pkgDataFileName, pkgData := mockReadWriter.WriteFileArgsForCall(0) 233 Expect(pkgDataFilePath).To(Equal("")) 234 Expect(pkgDataFileName).To(Equal("testcc:3fec0187440286d404241e871b44725310b11aaf43d100b053eae712fcabc66d.tar.gz")) 235 Expect(pkgData).To(Equal([]byte("testpkg"))) 236 }) 237 238 Context("when the code package was previously installed successfully", func() { 239 BeforeEach(func() { 240 mockReadWriter.ExistsReturns(true, nil) 241 }) 242 243 It("does nothing and returns the packageID", func() { 244 packageID, err := store.Save("testcc", pkgBytes) 245 Expect(err).NotTo(HaveOccurred()) 246 Expect(packageID).To(Equal("testcc:3fec0187440286d404241e871b44725310b11aaf43d100b053eae712fcabc66d")) 247 Expect(mockReadWriter.WriteFileCallCount()).To(Equal(0)) 248 }) 249 }) 250 251 Context("when writing the package fails", func() { 252 BeforeEach(func() { 253 mockReadWriter.WriteFileReturns(errors.New("soccer")) 254 }) 255 256 It("returns an error", func() { 257 packageID, err := store.Save("testcc", pkgBytes) 258 Expect(packageID).To(Equal("")) 259 Expect(err).To(MatchError(ContainSubstring("error writing chaincode install package to testcc:3fec0187440286d404241e871b44725310b11aaf43d100b053eae712fcabc66d.tar.gz: soccer"))) 260 }) 261 }) 262 }) 263 264 Describe("Delete", func() { 265 var ( 266 mockReadWriter *mock.IOReadWriter 267 store *persistence.Store 268 ) 269 270 BeforeEach(func() { 271 mockReadWriter = &mock.IOReadWriter{} 272 store = &persistence.Store{ 273 ReadWriter: mockReadWriter, 274 Path: "foo", 275 } 276 }) 277 278 It("removes the chaincode from the filesystem", func() { 279 err := store.Delete("hash") 280 Expect(err).NotTo(HaveOccurred()) 281 282 Expect(mockReadWriter.RemoveCallCount()).To(Equal(1)) 283 Expect(mockReadWriter.RemoveArgsForCall(0)).To(Equal("foo/hash.tar.gz")) 284 }) 285 286 When("remove returns an error", func() { 287 BeforeEach(func() { 288 mockReadWriter.RemoveReturns(fmt.Errorf("fake-remove-error")) 289 }) 290 291 It("returns the error", func() { 292 err := store.Delete("hash") 293 Expect(err).To(MatchError("fake-remove-error")) 294 }) 295 }) 296 }) 297 298 Describe("Load", func() { 299 var ( 300 mockReadWriter *mock.IOReadWriter 301 store *persistence.Store 302 ) 303 304 BeforeEach(func() { 305 mockReadWriter = &mock.IOReadWriter{} 306 mockReadWriter.ReadFileReturnsOnCall(0, []byte("cornerkick"), nil) 307 mockReadWriter.ExistsReturns(true, nil) 308 store = &persistence.Store{ 309 ReadWriter: mockReadWriter, 310 } 311 }) 312 313 It("loads successfully and returns the chaincode names/versions", func() { 314 ccInstallPkgBytes, err := store.Load("hash") 315 Expect(err).NotTo(HaveOccurred()) 316 Expect(ccInstallPkgBytes).To(Equal([]byte("cornerkick"))) 317 }) 318 319 Context("when the package isn't there", func() { 320 BeforeEach(func() { 321 mockReadWriter.ExistsReturns(false, nil) 322 }) 323 324 It("returns an error", func() { 325 ccInstallPkgBytes, err := store.Load("hash") 326 Expect(err).To(Equal(&persistence.CodePackageNotFoundErr{PackageID: "hash"})) 327 Expect(err).To(MatchError("chaincode install package 'hash' not found")) 328 Expect(ccInstallPkgBytes).To(HaveLen(0)) 329 }) 330 }) 331 332 Context("when an IO error occurred during stat", func() { 333 BeforeEach(func() { 334 mockReadWriter.ExistsReturns(false, errors.New("goodness me!")) 335 }) 336 337 It("returns an error", func() { 338 ccInstallPkgBytes, err := store.Load("hash") 339 Expect(err).To(MatchError("could not determine whether chaincode install package 'hash' exists: goodness me!")) 340 Expect(ccInstallPkgBytes).To(HaveLen(0)) 341 }) 342 }) 343 344 Context("when reading the chaincode install package fails", func() { 345 BeforeEach(func() { 346 mockReadWriter.ReadFileReturnsOnCall(0, nil, errors.New("redcard")) 347 }) 348 349 It("returns an error", func() { 350 ccInstallPkgBytes, err := store.Load("hash") 351 Expect(err).To(MatchError(ContainSubstring("error reading chaincode install package"))) 352 Expect(ccInstallPkgBytes).To(HaveLen(0)) 353 }) 354 }) 355 }) 356 357 Describe("ListInstalledChaincodes", func() { 358 var ( 359 mockReadWriter *mock.IOReadWriter 360 store *persistence.Store 361 ) 362 363 BeforeEach(func() { 364 mockReadWriter = &mock.IOReadWriter{} 365 mockFileInfo := &mock.OSFileInfo{} 366 mockFileInfo.NameReturns(fmt.Sprintf("%s:%x.tar.gz", "label1", []byte("hash1"))) 367 mockFileInfo2 := &mock.OSFileInfo{} 368 mockFileInfo2.NameReturns(fmt.Sprintf("%s:%x.tar.gz", "label2", []byte("hash2"))) 369 mockReadWriter.ReadDirReturns([]os.FileInfo{mockFileInfo, mockFileInfo2}, nil) 370 store = &persistence.Store{ 371 ReadWriter: mockReadWriter, 372 } 373 }) 374 375 It("returns the list of installed chaincodes", func() { 376 installedChaincodes, err := store.ListInstalledChaincodes() 377 Expect(err).NotTo(HaveOccurred()) 378 Expect(installedChaincodes).To(HaveLen(2)) 379 Expect(installedChaincodes[0]).To(Equal(chaincode.InstalledChaincode{ 380 Hash: []byte("hash1"), 381 Label: "label1", 382 PackageID: "label1:6861736831", 383 })) 384 Expect(installedChaincodes[1]).To(Equal(chaincode.InstalledChaincode{ 385 Hash: []byte("hash2"), 386 Label: "label2", 387 PackageID: "label2:6861736832", 388 })) 389 }) 390 391 Context("when extraneous files are present", func() { 392 BeforeEach(func() { 393 mockFileInfo := &mock.OSFileInfo{} 394 mockFileInfo.NameReturns(fmt.Sprintf("%s:%x.tar.gz", "label1", []byte("hash1"))) 395 mockFileInfo2 := &mock.OSFileInfo{} 396 mockFileInfo2.NameReturns(fmt.Sprintf("%s:%x.tar.gz", "label2", []byte("hash2"))) 397 mockFileInfo3 := &mock.OSFileInfo{} 398 mockFileInfo3.NameReturns(fmt.Sprintf("%s:%x.tar.gz", "", "Musha rain dum a doo, dum a da")) 399 mockFileInfo4 := &mock.OSFileInfo{} 400 mockFileInfo4.NameReturns(fmt.Sprintf("%s:%x.tar.gz", "", "barfity:barf.tar.gz")) 401 mockReadWriter.ReadDirReturns([]os.FileInfo{mockFileInfo, mockFileInfo2, mockFileInfo3}, nil) 402 }) 403 404 It("returns the list of installed chaincodes", func() { 405 installedChaincodes, err := store.ListInstalledChaincodes() 406 Expect(err).NotTo(HaveOccurred()) 407 Expect(installedChaincodes).To(HaveLen(2)) 408 Expect(installedChaincodes[0]).To(Equal(chaincode.InstalledChaincode{ 409 Hash: []byte("hash1"), 410 Label: "label1", 411 PackageID: "label1:6861736831", 412 })) 413 Expect(installedChaincodes[1]).To(Equal(chaincode.InstalledChaincode{ 414 Hash: []byte("hash2"), 415 Label: "label2", 416 PackageID: "label2:6861736832", 417 })) 418 }) 419 }) 420 421 Context("when the directory can't be read", func() { 422 BeforeEach(func() { 423 mockReadWriter.ReadDirReturns([]os.FileInfo{}, errors.New("I'm illiterate and so obviously I can't read")) 424 }) 425 426 It("returns an error", func() { 427 installedChaincodes, err := store.ListInstalledChaincodes() 428 Expect(err).To(HaveOccurred()) 429 Expect(installedChaincodes).To(HaveLen(0)) 430 }) 431 }) 432 }) 433 434 Describe("GetChaincodeInstallPath", func() { 435 var ( 436 store *persistence.Store 437 ) 438 439 BeforeEach(func() { 440 store = &persistence.Store{ 441 Path: "testPath", 442 } 443 }) 444 445 It("returns the path where chaincodes are installed", func() { 446 path := store.GetChaincodeInstallPath() 447 Expect(path).To(Equal("testPath")) 448 }) 449 }) 450 })