github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/updater/util/file_test.go (about)

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package util
     5  
     6  import (
     7  	"fmt"
     8  	"net/url"
     9  	"os"
    10  	"os/exec"
    11  	"path/filepath"
    12  	"runtime"
    13  	"strings"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/stretchr/testify/assert"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  func TestNewFile(t *testing.T) {
    22  	filename := filepath.Join(os.TempDir(), "TestNewFile")
    23  	defer RemoveFileAtPath(filename)
    24  
    25  	f := NewFile(filename, []byte("somedata"), 0600)
    26  	err := f.Save(testLog)
    27  	assert.NoError(t, err)
    28  
    29  	fileInfo, err := os.Stat(filename)
    30  	assert.NoError(t, err)
    31  	assert.False(t, fileInfo.IsDir())
    32  
    33  	if runtime.GOOS != "windows" {
    34  		assert.EqualValues(t, 0600, fileInfo.Mode().Perm())
    35  	}
    36  }
    37  
    38  func TestMakeParentDirs(t *testing.T) {
    39  	dir := filepath.Join(os.TempDir(), "TestMakeParentDirs", "TestMakeParentDirs2", "TestMakeParentDirs3")
    40  	defer RemoveFileAtPath(dir)
    41  
    42  	file := filepath.Join(dir, "testfile")
    43  	defer RemoveFileAtPath(file)
    44  
    45  	err := MakeParentDirs(file, 0700, testLog)
    46  	assert.NoError(t, err)
    47  
    48  	exists, err := FileExists(dir)
    49  	assert.NoError(t, err)
    50  	assert.True(t, exists, "File doesn't exist")
    51  
    52  	fileInfo, err := os.Stat(dir)
    53  	assert.NoError(t, err)
    54  	assert.True(t, fileInfo.IsDir())
    55  	if runtime.GOOS != "windows" {
    56  		assert.EqualValues(t, 0700, fileInfo.Mode().Perm())
    57  	}
    58  
    59  	// Test making dir that already exists
    60  	err = MakeParentDirs(file, 0700, testLog)
    61  	assert.NoError(t, err)
    62  }
    63  
    64  func TestMakeParentDirsInvalid(t *testing.T) {
    65  	err := MakeParentDirs("\\\\invalid", 0700, testLog)
    66  	if runtime.GOOS != "windows" {
    67  		assert.EqualError(t, err, "No base directory")
    68  	} else {
    69  		assert.Error(t, err)
    70  	}
    71  }
    72  
    73  func TestTempPathValid(t *testing.T) {
    74  	tempPath := TempPath("", "TempPrefix.")
    75  	t.Logf("Temp path: %s", tempPath)
    76  	assert.True(t, strings.HasPrefix(filepath.Base(tempPath), "TempPrefix."))
    77  	assert.Equal(t, len(filepath.Base(tempPath)), 63)
    78  }
    79  
    80  func TestTempPathRandFail(t *testing.T) {
    81  	// Replace rand.Read with a failing read
    82  	defaultRandRead := randRead
    83  	defer func() { randRead = defaultRandRead }()
    84  	randRead = func(b []byte) (int, error) {
    85  		return 0, fmt.Errorf("Test rand failure")
    86  	}
    87  
    88  	tempPath := TempPath("", "TempPrefix.")
    89  	t.Logf("Temp path: %s", tempPath)
    90  	assert.True(t, strings.HasPrefix(filepath.Base(tempPath), "TempPrefix."))
    91  	assert.Equal(t, len(filepath.Base(tempPath)), 30)
    92  }
    93  
    94  func TestIsDirReal(t *testing.T) {
    95  	ok, err := IsDirReal("/invalid")
    96  	assert.Error(t, err)
    97  	assert.False(t, ok)
    98  
    99  	path := os.Getenv("GOPATH")
   100  	ok, err = IsDirReal(path)
   101  	assert.NoError(t, err)
   102  	assert.True(t, ok)
   103  
   104  	_, filename, _, _ := runtime.Caller(0)
   105  	testFile := filepath.Join(filepath.Dir(filename), "../test/test.zip")
   106  	ok, err = IsDirReal(testFile)
   107  	assert.Error(t, err)
   108  	assert.Equal(t, "Path is not a directory", err.Error())
   109  	assert.False(t, ok)
   110  
   111  	// Windows requires privileges to create symbolic links
   112  	symLinkPath := TempPath("", "TestIsDirReal")
   113  	defer RemoveFileAtPath(symLinkPath)
   114  	target := os.TempDir()
   115  	if runtime.GOOS == "windows" {
   116  		err = exec.Command("cmd", "/C", "mklink", "/J", symLinkPath, target).Run()
   117  		assert.NoError(t, err)
   118  	} else {
   119  		err = os.Symlink(target, symLinkPath)
   120  		assert.NoError(t, err)
   121  	}
   122  	ok, err = IsDirReal(symLinkPath)
   123  	assert.Error(t, err)
   124  	assert.Equal(t, "Path is a symlink", err.Error())
   125  	assert.False(t, ok)
   126  }
   127  
   128  func TestMoveFileValid(t *testing.T) {
   129  	destinationPath := filepath.Join(TempPath("", "TestMoveFileDestination"), "TestMoveFileDestinationSubdir")
   130  	defer RemoveFileAtPath(destinationPath)
   131  
   132  	sourcePath, err := WriteTempFile("TestMoveFile", []byte("test"), 0600)
   133  	defer RemoveFileAtPath(sourcePath)
   134  	assert.NoError(t, err)
   135  
   136  	err = MoveFile(sourcePath, destinationPath, "", testLog)
   137  	assert.NoError(t, err)
   138  	exists, err := FileExists(destinationPath)
   139  	assert.NoError(t, err)
   140  	assert.True(t, exists)
   141  	data, err := os.ReadFile(destinationPath)
   142  	assert.NoError(t, err)
   143  	assert.Equal(t, []byte("test"), data)
   144  	srcExists, err := FileExists(sourcePath)
   145  	assert.NoError(t, err)
   146  	assert.False(t, srcExists)
   147  
   148  	// Move again with different source data, and overwrite
   149  	sourcePath2, err := WriteTempFile("TestMoveFile", []byte("test2"), 0600)
   150  	assert.NoError(t, err)
   151  	err = MoveFile(sourcePath2, destinationPath, "", testLog)
   152  	assert.NoError(t, err)
   153  	exists, err = FileExists(destinationPath)
   154  	assert.NoError(t, err)
   155  	assert.True(t, exists)
   156  	data2, err := os.ReadFile(destinationPath)
   157  	assert.NoError(t, err)
   158  	assert.Equal(t, []byte("test2"), data2)
   159  	srcExists2, err := FileExists(sourcePath2)
   160  	assert.NoError(t, err)
   161  	assert.False(t, srcExists2)
   162  }
   163  
   164  func TestMoveFileDirValid(t *testing.T) {
   165  	destinationPath := filepath.Join(TempPath("", "TestMoveFileDestination"), "TestMoveFileDestinationSubdir")
   166  	defer RemoveFileAtPath(destinationPath)
   167  
   168  	sourcePath, err := MakeTempDir("TestMoveDir", 0700)
   169  	defer RemoveFileAtPath(sourcePath)
   170  	assert.NoError(t, err)
   171  
   172  	err = MoveFile(sourcePath, destinationPath, "", testLog)
   173  	assert.NoError(t, err)
   174  	exists, err := FileExists(destinationPath)
   175  	assert.NoError(t, err)
   176  	assert.True(t, exists)
   177  
   178  	// Move again with different source data, and overwrite
   179  	sourcePath2, err := MakeTempDir("TestMoveDir2", 0700)
   180  	assert.NoError(t, err)
   181  	defer RemoveFileAtPath(sourcePath2)
   182  	err = MoveFile(sourcePath2, destinationPath, "", testLog)
   183  	assert.NoError(t, err)
   184  	exists, err = FileExists(destinationPath)
   185  	assert.NoError(t, err)
   186  	assert.True(t, exists)
   187  }
   188  
   189  func TestMoveFileInvalidSource(t *testing.T) {
   190  	sourcePath := "/invalid"
   191  	destinationPath := TempPath("", "TestMoveFileDestination")
   192  	err := MoveFile(sourcePath, destinationPath, "", testLog)
   193  	assert.Error(t, err)
   194  
   195  	exists, err := FileExists(destinationPath)
   196  	assert.NoError(t, err)
   197  	assert.False(t, exists)
   198  }
   199  
   200  func TestMoveFileInvalidDest(t *testing.T) {
   201  	sourcePath := "/invalid"
   202  	destinationPath := TempPath("", "TestMoveFileDestination")
   203  	err := MoveFile(sourcePath, destinationPath, "", testLog)
   204  	assert.Error(t, err)
   205  
   206  	exists, err := FileExists(destinationPath)
   207  	assert.NoError(t, err)
   208  	assert.False(t, exists)
   209  }
   210  
   211  func TestCopyFileValid(t *testing.T) {
   212  	destinationPath := filepath.Join(TempPath("", "TestCopyFileDestination"), "TestCopyFileDestinationSubdir")
   213  	defer RemoveFileAtPath(destinationPath)
   214  
   215  	sourcePath, err := WriteTempFile("TestCopyFile", []byte("test"), 0600)
   216  	defer RemoveFileAtPath(sourcePath)
   217  	assert.NoError(t, err)
   218  
   219  	err = CopyFile(sourcePath, destinationPath, testLog)
   220  	assert.NoError(t, err)
   221  	exists, err := FileExists(destinationPath)
   222  	assert.NoError(t, err)
   223  	assert.True(t, exists)
   224  	data, err := os.ReadFile(destinationPath)
   225  	assert.NoError(t, err)
   226  	assert.Equal(t, []byte("test"), data)
   227  
   228  	// Move again with different source data, and overwrite
   229  	sourcePath2, err := WriteTempFile("TestCopyFile", []byte("test2"), 0600)
   230  	assert.NoError(t, err)
   231  	err = CopyFile(sourcePath2, destinationPath, testLog)
   232  	assert.NoError(t, err)
   233  	exists, err = FileExists(destinationPath)
   234  	assert.NoError(t, err)
   235  	assert.True(t, exists)
   236  	data2, err := os.ReadFile(destinationPath)
   237  	assert.NoError(t, err)
   238  	assert.Equal(t, []byte("test2"), data2)
   239  }
   240  
   241  func TestCopyFileInvalidSource(t *testing.T) {
   242  	sourcePath := "/invalid"
   243  	destinationPath := TempPath("", "TestCopyFileDestination")
   244  	err := CopyFile(sourcePath, destinationPath, testLog)
   245  	assert.Error(t, err)
   246  
   247  	exists, err := FileExists(destinationPath)
   248  	assert.NoError(t, err)
   249  	assert.False(t, exists)
   250  }
   251  
   252  func TestCopyFileInvalidDest(t *testing.T) {
   253  	sourcePath := "/invalid"
   254  	destinationPath := TempPath("", "TestCopyFileDestination")
   255  	err := CopyFile(sourcePath, destinationPath, testLog)
   256  	assert.Error(t, err)
   257  
   258  	exists, err := FileExists(destinationPath)
   259  	assert.NoError(t, err)
   260  	assert.False(t, exists)
   261  }
   262  
   263  func TestCloseNil(t *testing.T) {
   264  	Close(nil)
   265  }
   266  
   267  func TestOpenTempFile(t *testing.T) {
   268  	path, tempFile, err := openTempFile("prefix", "suffix", 0)
   269  	defer Close(tempFile)
   270  	defer RemoveFileAtPath(path)
   271  	require.NoError(t, err)
   272  	require.NotNil(t, tempFile)
   273  
   274  	basePath := filepath.Base(path)
   275  	assert.True(t, strings.HasPrefix(basePath, "prefix"))
   276  	assert.True(t, strings.HasSuffix(basePath, "suffix"))
   277  }
   278  
   279  func TestFileExists(t *testing.T) {
   280  	exists, err := FileExists("/nope")
   281  	assert.NoError(t, err)
   282  	assert.False(t, exists)
   283  }
   284  
   285  func TestReadFile(t *testing.T) {
   286  	dataIn := []byte("test")
   287  	sourcePath, err := WriteTempFile("TestReadFile", dataIn, 0600)
   288  	require.NoError(t, err)
   289  
   290  	dataOut, err := ReadFile(sourcePath)
   291  	require.NoError(t, err)
   292  	assert.Equal(t, dataIn, dataOut)
   293  
   294  	_, err = ReadFile("/invalid")
   295  	assert.Error(t, err)
   296  	require.True(t, strings.HasPrefix(err.Error(), "open /invalid: "))
   297  }
   298  
   299  func TestURLStringForPathWindows(t *testing.T) {
   300  	if runtime.GOOS != "windows" {
   301  		t.Skip("Windows only test")
   302  	}
   303  	assert.Equal(t, "file:///C:/Go/bin", URLStringForPath(`C:\Go\bin`))
   304  	assert.Equal(t, "file:///C:/Program%20Files", URLStringForPath(`C:\Program Files`))
   305  	assert.Equal(t, "file:///C:/test%20%E2%9C%93%E2%9C%93", URLStringForPath(`C:\test ✓✓`))
   306  }
   307  
   308  func TestURLStringForPath(t *testing.T) {
   309  	if runtime.GOOS == "windows" {
   310  		t.Skip("See TestURLStringForPathWindows")
   311  	}
   312  	assert.Equal(t, "file:///usr/local/go/bin", URLStringForPath("/usr/local/go/bin"))
   313  	assert.Equal(t, "file:///Applications/System%20Preferences.app", URLStringForPath("/Applications/System Preferences.app"))
   314  	assert.Equal(t, "file:///test%20%E2%9C%93%E2%9C%93", URLStringForPath("/test ✓✓"))
   315  }
   316  
   317  func TestPathFromURLWindows(t *testing.T) {
   318  	if runtime.GOOS != "windows" {
   319  		t.Skip("Windows only test")
   320  	}
   321  	url, err := url.Parse("file:///C:/Go/bin")
   322  	require.NoError(t, err)
   323  	assert.Equal(t, `C:\Go\bin`, PathFromURL(url))
   324  }
   325  
   326  func TestPathFromURL(t *testing.T) {
   327  	if runtime.GOOS == "windows" {
   328  		t.Skip("See TestPathFromURLWindows")
   329  	}
   330  	url, err := url.Parse("file:///usr/local/go/bin")
   331  	require.NoError(t, err)
   332  	assert.Equal(t, "/usr/local/go/bin", PathFromURL(url))
   333  
   334  	url, err = url.Parse("file:///Applications/System%20Preferences.app")
   335  	require.NoError(t, err)
   336  	assert.Equal(t, "/Applications/System Preferences.app", PathFromURL(url))
   337  }
   338  
   339  func TestTouchModTime(t *testing.T) {
   340  	path, err := RandomID("TestTouchModTime")
   341  	defer RemoveFileAtPath(path)
   342  	require.NoError(t, err)
   343  	now := time.Now()
   344  	err = Touch(path)
   345  	require.NoError(t, err)
   346  	ti, err := FileModTime(path)
   347  	require.NoError(t, err)
   348  	assert.WithinDuration(t, now, ti, time.Second)
   349  	time.Sleep(1 * time.Second)
   350  
   351  	// Touch same path, ensure it updates mod time
   352  	err = Touch(path)
   353  	require.NoError(t, err)
   354  	ti2, err := FileModTime(path)
   355  	require.NoError(t, err)
   356  	assert.NotEqual(t, ti, ti2)
   357  }