github.com/kaituanwang/hyperledger@v2.0.1+incompatible/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  
    22  	"github.com/stretchr/testify/assert"
    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(), 0600)
    40  	require.NoError(t, err, "Error creating file %s", filePath)
    41  
    42  	err = WriteFileToPackage(filePath, filename, tw)
    43  	assert.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  	assert.Equal(t, filename, header.Name, "filename read from archive does not match what was added")
    57  
    58  	b := make([]byte, 5)
    59  	n, err := tr.Read(b)
    60  	assert.Equal(t, 5, n)
    61  	assert.True(t, err == nil || err == io.EOF, "Error reading file from the archive") // go1.10 returns io.EOF
    62  	assert.Equal(t, filecontent, string(b), "file content from archive does not equal original content")
    63  
    64  	t.Run("non existent file", func(t *testing.T) {
    65  		tw := tar.NewWriter(&bytes.Buffer{})
    66  		err := WriteFileToPackage("missing-file", "", tw)
    67  		assert.Error(t, err, "expected error writing a non existent file")
    68  		assert.Contains(t, err.Error(), "missing-file")
    69  	})
    70  
    71  	t.Run("closed tar writer", func(t *testing.T) {
    72  		tw := tar.NewWriter(&bytes.Buffer{})
    73  		tw.Close()
    74  		err := WriteFileToPackage(filePath, "test.txt", tw)
    75  		assert.EqualError(t, err, fmt.Sprintf("failed to write header for %s: archive/tar: write after close", filePath))
    76  	})
    77  
    78  	t.Run("stream write failure", func(t *testing.T) {
    79  		failWriter := &failingWriter{failAt: 514}
    80  		tw := tar.NewWriter(failWriter)
    81  		err := WriteFileToPackage(filePath, "test.txt", tw)
    82  		assert.EqualError(t, err, fmt.Sprintf("failed to write %s as test.txt: failed-the-write", filePath))
    83  	})
    84  }
    85  
    86  type failingWriter struct {
    87  	written int
    88  	failAt  int
    89  }
    90  
    91  func (f *failingWriter) Write(b []byte) (int, error) {
    92  	f.written += len(b)
    93  	if f.written < f.failAt {
    94  		return len(b), nil
    95  	}
    96  	return 0, errors.New("failed-the-write")
    97  }
    98  
    99  // Success case 1: with include file types and without exclude dir
   100  func TestWriteFolderToTarPackage1(t *testing.T) {
   101  	srcPath := filepath.Join("testdata", "sourcefiles")
   102  	filePath := "src/src/Hello.java"
   103  	includeFileTypes := map[string]bool{
   104  		".java": true,
   105  	}
   106  	excludeFileTypes := map[string]bool{}
   107  
   108  	tarBytes := createTestTar(t, srcPath, []string{}, includeFileTypes, excludeFileTypes)
   109  
   110  	// Read the file from the archive and check the name
   111  	entries := tarContents(t, tarBytes)
   112  	assert.ElementsMatch(t, []string{filePath}, entries, "archive should only contain one file")
   113  }
   114  
   115  // Success case 2: with exclude dir and no include file types
   116  func TestWriteFolderToTarPackage2(t *testing.T) {
   117  	srcPath := filepath.Join("testdata", "sourcefiles")
   118  	tarBytes := createTestTar(t, srcPath, []string{"src"}, nil, nil)
   119  
   120  	entries := tarContents(t, tarBytes)
   121  	assert.ElementsMatch(t, []string{"src/artifact.xml", "META-INF/statedb/couchdb/indexes/indexOwner.json"}, entries)
   122  }
   123  
   124  // Success case 3: with chaincode metadata in META-INF directory
   125  func TestWriteFolderToTarPackage3(t *testing.T) {
   126  	srcPath := filepath.Join("testdata", "sourcefiles")
   127  	filePath := "META-INF/statedb/couchdb/indexes/indexOwner.json"
   128  
   129  	tarBytes := createTestTar(t, srcPath, []string{}, nil, nil)
   130  
   131  	// Read the files from the archive and check for the metadata index file
   132  	entries := tarContents(t, tarBytes)
   133  	assert.Contains(t, entries, filePath, "should have found statedb index artifact in META-INF directory")
   134  }
   135  
   136  // Success case 4: with chaincode metadata in META-INF directory, pass trailing slash in srcPath
   137  func TestWriteFolderToTarPackage4(t *testing.T) {
   138  	srcPath := filepath.Join("testdata", "sourcefiles") + string(filepath.Separator)
   139  	filePath := "META-INF/statedb/couchdb/indexes/indexOwner.json"
   140  
   141  	tarBytes := createTestTar(t, srcPath, []string{}, nil, nil)
   142  
   143  	// Read the files from the archive and check for the metadata index file
   144  	entries := tarContents(t, tarBytes)
   145  	assert.Contains(t, entries, filePath, "should have found statedb index artifact in META-INF directory")
   146  }
   147  
   148  // Success case 5: with hidden files in META-INF directory (hidden files get ignored)
   149  func TestWriteFolderToTarPackage5(t *testing.T) {
   150  	srcPath := filepath.Join("testdata", "sourcefiles")
   151  	filePath := "META-INF/.hiddenfile"
   152  
   153  	assert.FileExists(t, filepath.Join(srcPath, "META-INF", ".hiddenfile"))
   154  
   155  	tarBytes := createTestTar(t, srcPath, []string{}, nil, nil)
   156  
   157  	// Read the files from the archive and check for the metadata index file
   158  	entries := tarContents(t, tarBytes)
   159  	assert.NotContains(t, entries, filePath, "should not contain .hiddenfile in META-INF directory")
   160  }
   161  
   162  // Failure case 1: no files in directory
   163  func TestWriteFolderToTarPackageFailure1(t *testing.T) {
   164  	srcPath, err := ioutil.TempDir("", "utiltest")
   165  	require.NoError(t, err)
   166  	defer os.RemoveAll(srcPath)
   167  
   168  	tw := tar.NewWriter(bytes.NewBuffer(nil))
   169  	defer tw.Close()
   170  
   171  	err = WriteFolderToTarPackage(tw, srcPath, []string{}, nil, nil)
   172  	assert.Contains(t, err.Error(), "no source files found")
   173  }
   174  
   175  // Failure case 2: with invalid chaincode metadata in META-INF directory
   176  func TestWriteFolderToTarPackageFailure2(t *testing.T) {
   177  	srcPath := filepath.Join("testdata", "BadMetadataInvalidIndex")
   178  	buf := bytes.NewBuffer(nil)
   179  	gw := gzip.NewWriter(buf)
   180  	tw := tar.NewWriter(gw)
   181  
   182  	err := WriteFolderToTarPackage(tw, srcPath, []string{}, nil, nil)
   183  	assert.Error(t, err, "Should have received error writing folder to package")
   184  	assert.Contains(t, err.Error(), "Index metadata file [META-INF/statedb/couchdb/indexes/bad.json] is not a valid JSON")
   185  
   186  	tw.Close()
   187  	gw.Close()
   188  }
   189  
   190  // Failure case 3: with unexpected content in META-INF directory
   191  func TestWriteFolderToTarPackageFailure3(t *testing.T) {
   192  	srcPath := filepath.Join("testdata", "BadMetadataUnexpectedFolderContent")
   193  	buf := bytes.NewBuffer(nil)
   194  	gw := gzip.NewWriter(buf)
   195  	tw := tar.NewWriter(gw)
   196  
   197  	err := WriteFolderToTarPackage(tw, srcPath, []string{}, nil, nil)
   198  	assert.Error(t, err, "Should have received error writing folder to package")
   199  	assert.Contains(t, err.Error(), "metadata file path must begin with META-INF/statedb")
   200  
   201  	tw.Close()
   202  	gw.Close()
   203  }
   204  
   205  // Failure case 4: with lstat failed
   206  func Test_WriteFolderToTarPackageFailure4(t *testing.T) {
   207  	if runtime.GOOS == "windows" {
   208  		t.Skip("unable to chmod execute permission on windows directory")
   209  	}
   210  
   211  	tempDir, err := ioutil.TempDir("", "WriteFolderToTarPackageFailure4BadFileMode")
   212  	require.NoError(t, err)
   213  	defer os.RemoveAll(tempDir)
   214  	testFile := filepath.Join(tempDir, "test.java")
   215  	err = ioutil.WriteFile(testFile, []byte("Content"), 0644)
   216  	require.NoError(t, err, "Error creating file", testFile)
   217  	err = os.Chmod(tempDir, 0644)
   218  	require.NoError(t, err)
   219  
   220  	buf := bytes.NewBuffer(nil)
   221  	tw := tar.NewWriter(buf)
   222  	defer tw.Close()
   223  
   224  	err = WriteFolderToTarPackage(tw, tempDir, []string{}, nil, nil)
   225  	assert.Error(t, err, "Should have received error writing folder to package")
   226  	assert.Contains(t, err.Error(), "permission denied")
   227  
   228  	err = os.Chmod(tempDir, 0700)
   229  	require.NoError(t, err)
   230  }
   231  
   232  func createTestTar(t *testing.T, srcPath string, excludeDir []string, includeFileTypeMap map[string]bool, excludeFileTypeMap map[string]bool) []byte {
   233  	buf := bytes.NewBuffer(nil)
   234  	gw := gzip.NewWriter(buf)
   235  	tw := tar.NewWriter(gw)
   236  
   237  	err := WriteFolderToTarPackage(tw, srcPath, excludeDir, includeFileTypeMap, excludeFileTypeMap)
   238  	assert.NoError(t, err, "Error writing folder to package")
   239  
   240  	tw.Close()
   241  	gw.Close()
   242  	return buf.Bytes()
   243  }
   244  
   245  func tarContents(t *testing.T, buf []byte) []string {
   246  	br := bytes.NewReader(buf)
   247  	gr, err := gzip.NewReader(br)
   248  	require.NoError(t, err)
   249  	defer gr.Close()
   250  
   251  	tr := tar.NewReader(gr)
   252  
   253  	var entries []string
   254  	for {
   255  		header, err := tr.Next()
   256  		if err == io.EOF { // No more entries
   257  			break
   258  		}
   259  		require.NoError(t, err, "failed to get next entry")
   260  		entries = append(entries, header.Name)
   261  	}
   262  
   263  	return entries
   264  }