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  }