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