github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/chaincode/platforms/util/writer_test.go (about) 1 /* 2 Copyright London Stock Exchange 2016 All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package util 8 9 import ( 10 "archive/tar" 11 "bytes" 12 "compress/gzip" 13 "errors" 14 "fmt" 15 "io" 16 "io/ioutil" 17 "os" 18 "path/filepath" 19 "runtime" 20 "testing" 21 "time" 22 23 "github.com/stretchr/testify/require" 24 ) 25 26 func TestWriteFileToPackage(t *testing.T) { 27 tempDir, err := ioutil.TempDir("", "utiltest") 28 require.NoError(t, err) 29 defer os.RemoveAll(tempDir) 30 31 buf := bytes.NewBuffer(nil) 32 gw := gzip.NewWriter(buf) 33 tw := tar.NewWriter(gw) 34 35 // Create a file and write it to tar writer 36 filename := "test.txt" 37 filecontent := "hello" 38 filePath := filepath.Join(tempDir, filename) 39 err = ioutil.WriteFile(filePath, bytes.NewBufferString(filecontent).Bytes(), 0o600) 40 require.NoError(t, err, "Error creating file %s", filePath) 41 42 err = WriteFileToPackage(filePath, filename, tw) 43 require.NoError(t, err, "Error returned by WriteFileToPackage while writing existing file") 44 tw.Close() 45 gw.Close() 46 47 // Read the file from the archive and check the name and file content 48 r := bytes.NewReader(buf.Bytes()) 49 gr, err := gzip.NewReader(r) 50 require.NoError(t, err, "Error creating a gzip reader") 51 defer gr.Close() 52 53 tr := tar.NewReader(gr) 54 header, err := tr.Next() 55 require.NoError(t, err, "Error getting the file from the tar") 56 require.Equal(t, filename, header.Name, "filename read from archive does not match what was added") 57 require.Equal(t, time.Time{}, header.AccessTime, "expected zero access time") 58 require.Equal(t, time.Unix(0, 0), header.ModTime, "expected zero modification time") 59 require.Equal(t, time.Time{}, header.ChangeTime, "expected zero change time") 60 require.Equal(t, int64(0o100644), header.Mode, "expected regular file mode") 61 require.Equal(t, 500, header.Uid, "expected 500 uid") 62 require.Equal(t, 500, header.Gid, "expected 500 gid") 63 require.Equal(t, "", header.Uname, "expected empty user name") 64 require.Equal(t, "", header.Gname, "expected empty group name") 65 66 b := make([]byte, 5) 67 n, err := tr.Read(b) 68 require.Equal(t, 5, n) 69 require.True(t, err == nil || err == io.EOF, "Error reading file from the archive") // go1.10 returns io.EOF 70 require.Equal(t, filecontent, string(b), "file content from archive does not equal original content") 71 72 t.Run("non existent file", func(t *testing.T) { 73 tw := tar.NewWriter(&bytes.Buffer{}) 74 err := WriteFileToPackage("missing-file", "", tw) 75 require.Error(t, err, "expected error writing a non existent file") 76 require.Contains(t, err.Error(), "missing-file") 77 }) 78 79 t.Run("closed tar writer", func(t *testing.T) { 80 tw := tar.NewWriter(&bytes.Buffer{}) 81 tw.Close() 82 err := WriteFileToPackage(filePath, "test.txt", tw) 83 require.EqualError(t, err, fmt.Sprintf("failed to write header for %s: archive/tar: write after close", filePath)) 84 }) 85 86 t.Run("stream write failure", func(t *testing.T) { 87 failWriter := &failingWriter{failAt: 514} 88 tw := tar.NewWriter(failWriter) 89 err := WriteFileToPackage(filePath, "test.txt", tw) 90 require.EqualError(t, err, fmt.Sprintf("failed to write %s as test.txt: failed-the-write", filePath)) 91 }) 92 } 93 94 type failingWriter struct { 95 written int 96 failAt int 97 } 98 99 func (f *failingWriter) Write(b []byte) (int, error) { 100 f.written += len(b) 101 if f.written < f.failAt { 102 return len(b), nil 103 } 104 return 0, errors.New("failed-the-write") 105 } 106 107 // Success case 1: with include file types and without exclude dir 108 func TestWriteFolderToTarPackage1(t *testing.T) { 109 srcPath := filepath.Join("testdata", "sourcefiles") 110 filePath := "src/src/Hello.java" 111 includeFileTypes := map[string]bool{ 112 ".java": true, 113 } 114 excludeFileTypes := map[string]bool{} 115 116 tarBytes := createTestTar(t, srcPath, []string{}, includeFileTypes, excludeFileTypes) 117 118 // Read the file from the archive and check the name 119 entries := tarContents(t, tarBytes) 120 require.ElementsMatch(t, []string{filePath}, entries, "archive should only contain one file") 121 } 122 123 // Success case 2: with exclude dir and no include file types 124 func TestWriteFolderToTarPackage2(t *testing.T) { 125 srcPath := filepath.Join("testdata", "sourcefiles") 126 tarBytes := createTestTar(t, srcPath, []string{"src"}, nil, nil) 127 128 entries := tarContents(t, tarBytes) 129 require.ElementsMatch(t, []string{"src/artifact.xml", "META-INF/statedb/couchdb/indexes/indexOwner.json"}, entries) 130 } 131 132 // Success case 3: with chaincode metadata in META-INF directory 133 func TestWriteFolderToTarPackage3(t *testing.T) { 134 srcPath := filepath.Join("testdata", "sourcefiles") 135 filePath := "META-INF/statedb/couchdb/indexes/indexOwner.json" 136 137 tarBytes := createTestTar(t, srcPath, []string{}, nil, nil) 138 139 // Read the files from the archive and check for the metadata index file 140 entries := tarContents(t, tarBytes) 141 require.Contains(t, entries, filePath, "should have found statedb index artifact in META-INF directory") 142 } 143 144 // Success case 4: with chaincode metadata in META-INF directory, pass trailing slash in srcPath 145 func TestWriteFolderToTarPackage4(t *testing.T) { 146 srcPath := filepath.Join("testdata", "sourcefiles") + string(filepath.Separator) 147 filePath := "META-INF/statedb/couchdb/indexes/indexOwner.json" 148 149 tarBytes := createTestTar(t, srcPath, []string{}, nil, nil) 150 151 // Read the files from the archive and check for the metadata index file 152 entries := tarContents(t, tarBytes) 153 require.Contains(t, entries, filePath, "should have found statedb index artifact in META-INF directory") 154 } 155 156 // Success case 5: with hidden files in META-INF directory (hidden files get ignored) 157 func TestWriteFolderToTarPackage5(t *testing.T) { 158 srcPath := filepath.Join("testdata", "sourcefiles") 159 filePath := "META-INF/.hiddenfile" 160 161 require.FileExists(t, filepath.Join(srcPath, "META-INF", ".hiddenfile")) 162 163 tarBytes := createTestTar(t, srcPath, []string{}, nil, nil) 164 165 // Read the files from the archive and check for the metadata index file 166 entries := tarContents(t, tarBytes) 167 require.NotContains(t, entries, filePath, "should not contain .hiddenfile in META-INF directory") 168 } 169 170 // Failure case 1: no files in directory 171 func TestWriteFolderToTarPackageFailure1(t *testing.T) { 172 srcPath, err := ioutil.TempDir("", "utiltest") 173 require.NoError(t, err) 174 defer os.RemoveAll(srcPath) 175 176 tw := tar.NewWriter(bytes.NewBuffer(nil)) 177 defer tw.Close() 178 179 err = WriteFolderToTarPackage(tw, srcPath, []string{}, nil, nil) 180 require.Contains(t, err.Error(), "no source files found") 181 } 182 183 // Failure case 2: with invalid chaincode metadata in META-INF directory 184 func TestWriteFolderToTarPackageFailure2(t *testing.T) { 185 srcPath := filepath.Join("testdata", "BadMetadataInvalidIndex") 186 buf := bytes.NewBuffer(nil) 187 gw := gzip.NewWriter(buf) 188 tw := tar.NewWriter(gw) 189 190 err := WriteFolderToTarPackage(tw, srcPath, []string{}, nil, nil) 191 require.Error(t, err, "Should have received error writing folder to package") 192 require.Contains(t, err.Error(), "Index metadata file [META-INF/statedb/couchdb/indexes/bad.json] is not a valid JSON") 193 194 tw.Close() 195 gw.Close() 196 } 197 198 // Failure case 3: with unexpected content in META-INF directory 199 func TestWriteFolderToTarPackageFailure3(t *testing.T) { 200 srcPath := filepath.Join("testdata", "BadMetadataUnexpectedFolderContent") 201 buf := bytes.NewBuffer(nil) 202 gw := gzip.NewWriter(buf) 203 tw := tar.NewWriter(gw) 204 205 err := WriteFolderToTarPackage(tw, srcPath, []string{}, nil, nil) 206 require.Error(t, err, "Should have received error writing folder to package") 207 require.Contains(t, err.Error(), "metadata file path must begin with META-INF/statedb") 208 209 tw.Close() 210 gw.Close() 211 } 212 213 // Failure case 4: with lstat failed 214 func Test_WriteFolderToTarPackageFailure4(t *testing.T) { 215 if runtime.GOOS == "windows" { 216 t.Skip("unable to chmod execute permission on windows directory") 217 } 218 219 tempDir, err := ioutil.TempDir("", "WriteFolderToTarPackageFailure4BadFileMode") 220 require.NoError(t, err) 221 defer os.RemoveAll(tempDir) 222 testFile := filepath.Join(tempDir, "test.java") 223 err = ioutil.WriteFile(testFile, []byte("Content"), 0o644) 224 require.NoError(t, err, "Error creating file", testFile) 225 err = os.Chmod(tempDir, 0o644) 226 require.NoError(t, err) 227 228 buf := bytes.NewBuffer(nil) 229 tw := tar.NewWriter(buf) 230 defer tw.Close() 231 232 err = WriteFolderToTarPackage(tw, tempDir, []string{}, nil, nil) 233 require.Error(t, err, "Should have received error writing folder to package") 234 require.Contains(t, err.Error(), "permission denied") 235 236 err = os.Chmod(tempDir, 0o700) 237 require.NoError(t, err) 238 } 239 240 func createTestTar(t *testing.T, srcPath string, excludeDir []string, includeFileTypeMap map[string]bool, excludeFileTypeMap map[string]bool) []byte { 241 buf := bytes.NewBuffer(nil) 242 gw := gzip.NewWriter(buf) 243 tw := tar.NewWriter(gw) 244 245 err := WriteFolderToTarPackage(tw, srcPath, excludeDir, includeFileTypeMap, excludeFileTypeMap) 246 require.NoError(t, err, "Error writing folder to package") 247 248 tw.Close() 249 gw.Close() 250 return buf.Bytes() 251 } 252 253 func tarContents(t *testing.T, buf []byte) []string { 254 br := bytes.NewReader(buf) 255 gr, err := gzip.NewReader(br) 256 require.NoError(t, err) 257 defer gr.Close() 258 259 tr := tar.NewReader(gr) 260 261 var entries []string 262 for { 263 header, err := tr.Next() 264 if err == io.EOF { // No more entries 265 break 266 } 267 require.NoError(t, err, "failed to get next entry") 268 entries = append(entries, header.Name) 269 } 270 271 return entries 272 }