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  })