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 }