github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/pkg/mock/fileUtils_test.go (about)

     1  //go:build unit
     2  // +build unit
     3  
     4  package mock
     5  
     6  import (
     7  	"archive/tar"
     8  	"bytes"
     9  	"compress/gzip"
    10  	"errors"
    11  	"io"
    12  	"os"
    13  	"path/filepath"
    14  	"testing"
    15  
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  func TestFilesMockFileExists(t *testing.T) {
    21  	t.Parallel()
    22  	t.Run("no init", func(t *testing.T) {
    23  		files := FilesMock{}
    24  		path := filepath.Join("some", "path")
    25  		exists, err := files.FileExists(path)
    26  		assert.NoError(t, err)
    27  		assert.False(t, exists)
    28  	})
    29  	t.Run("file exists after AddFile()", func(t *testing.T) {
    30  		files := FilesMock{}
    31  		path := filepath.Join("some", "path")
    32  		files.AddFile(path, []byte("dummy content"))
    33  		exists, err := files.FileExists(path)
    34  		assert.NoError(t, err)
    35  		assert.True(t, exists)
    36  	})
    37  	t.Run("path is a directory after AddDir()", func(t *testing.T) {
    38  		files := FilesMock{}
    39  		path := filepath.Join("some", "path")
    40  		files.AddDir(path)
    41  		exists, err := files.FileExists(path)
    42  		assert.NoError(t, err)
    43  		assert.False(t, exists)
    44  	})
    45  	t.Run("file exists after changing current dir", func(t *testing.T) {
    46  		files := FilesMock{}
    47  		path := filepath.Join("some", "path")
    48  		files.AddFile(path, []byte("dummy content"))
    49  		err := files.Chdir("some")
    50  		assert.NoError(t, err)
    51  		exists, err := files.FileExists("path")
    52  		assert.NoError(t, err)
    53  		assert.True(t, exists)
    54  	})
    55  	t.Run("file does not exist after changing current dir", func(t *testing.T) {
    56  		files := FilesMock{}
    57  		path := filepath.Join("some", "path")
    58  		files.AddFile(path, []byte("dummy content"))
    59  		err := files.Chdir("some")
    60  		assert.NoError(t, err)
    61  		exists, err := files.FileExists(path)
    62  		assert.NoError(t, err)
    63  		assert.False(t, exists)
    64  	})
    65  }
    66  
    67  func TestFilesMockDirExists(t *testing.T) {
    68  	t.Parallel()
    69  	t.Run("no init", func(t *testing.T) {
    70  		files := FilesMock{}
    71  		path := filepath.Join("some", "path")
    72  		exists, err := files.DirExists(path)
    73  		assert.NoError(t, err)
    74  		assert.False(t, exists)
    75  	})
    76  	t.Run("root folder always exists", func(t *testing.T) {
    77  		files := FilesMock{}
    78  		exists, err := files.DirExists(string(os.PathSeparator))
    79  		assert.NoError(t, err)
    80  		assert.False(t, exists)
    81  	})
    82  	t.Run("dir exists after AddDir()", func(t *testing.T) {
    83  		files := FilesMock{}
    84  		path := filepath.Join("some", "path")
    85  		files.AddDir(path)
    86  		exists, err := files.DirExists(path)
    87  		assert.NoError(t, err)
    88  		assert.True(t, exists)
    89  	})
    90  	t.Run("absolute dir exists after AddDir()", func(t *testing.T) {
    91  		files := FilesMock{}
    92  		path := filepath.Join("some", "path")
    93  		files.AddDir(path)
    94  		err := files.Chdir("some")
    95  		assert.NoError(t, err)
    96  		exists, err := files.DirExists(string(os.PathSeparator) + path)
    97  		assert.NoError(t, err)
    98  		assert.True(t, exists)
    99  	})
   100  	t.Run("parent dirs exists after AddFile()", func(t *testing.T) {
   101  		files := FilesMock{}
   102  		path := filepath.Join("path", "to", "some", "file")
   103  		files.AddFile(path, []byte("dummy content"))
   104  		testDirs := []string{
   105  			"path",
   106  			filepath.Join("path", "to"),
   107  			filepath.Join("path", "to", "some"),
   108  		}
   109  		for _, dir := range testDirs {
   110  			exists, err := files.DirExists(dir)
   111  			assert.NoError(t, err)
   112  			assert.True(t, exists, "Should exist: '%s'", dir)
   113  		}
   114  	})
   115  	t.Run("invalid sub-folders do not exist after AddFile()", func(t *testing.T) {
   116  		files := FilesMock{}
   117  		path := filepath.Join("path", "to", "some", "file")
   118  		files.AddFile(path, []byte("dummy content"))
   119  		testDirs := []string{
   120  			"to",
   121  			filepath.Join("to", "some"),
   122  			"some",
   123  			filepath.Join("path", "to", "so"),
   124  		}
   125  		for _, dir := range testDirs {
   126  			exists, err := files.DirExists(dir)
   127  			assert.NoError(t, err)
   128  			assert.False(t, exists, "Should not exist: '%s'", dir)
   129  		}
   130  	})
   131  	t.Run("dir still exists after removing last file", func(t *testing.T) {
   132  		files := FilesMock{}
   133  		dir := filepath.Join("path", "to")
   134  		file := filepath.Join(dir, "file")
   135  		files.AddFile(file, []byte("dummy content"))
   136  		err := files.FileRemove(file)
   137  		assert.NoError(t, err)
   138  		exists, err := files.DirExists(dir)
   139  		assert.NoError(t, err)
   140  		assert.True(t, exists)
   141  	})
   142  }
   143  
   144  func TestFilesMockCopy(t *testing.T) {
   145  	t.Parallel()
   146  	t.Run("copy a previously added file successfully", func(t *testing.T) {
   147  		files := FilesMock{}
   148  		src := filepath.Join("some", "file")
   149  		content := []byte("dummy content")
   150  		files.AddFile(src, content)
   151  		dst := filepath.Join("another", "file")
   152  		length, err := files.Copy(src, dst)
   153  		assert.NoError(t, err)
   154  		assert.Equal(t, length, int64(len(content)))
   155  	})
   156  	t.Run("fail to copy non-existing file", func(t *testing.T) {
   157  		files := FilesMock{}
   158  		src := filepath.Join("some", "file")
   159  		dst := filepath.Join("another", "file")
   160  		length, err := files.Copy(src, dst)
   161  		assert.EqualError(t, err, "cannot copy '"+src+"': file does not exist")
   162  		assert.Equal(t, length, int64(0))
   163  	})
   164  	t.Run("fail to copy folder", func(t *testing.T) {
   165  		files := FilesMock{}
   166  		src := filepath.Join("some", "file")
   167  		files.AddDir(src)
   168  		dst := filepath.Join("another", "file")
   169  		length, err := files.Copy(src, dst)
   170  		assert.EqualError(t, err, "cannot copy '"+src+"': file does not exist")
   171  		assert.Equal(t, length, int64(0))
   172  	})
   173  }
   174  
   175  func TestFilesMockFileRemove(t *testing.T) {
   176  	t.Parallel()
   177  	t.Run("fail to remove non-existing file", func(t *testing.T) {
   178  		files := FilesMock{}
   179  		path := filepath.Join("some", "file")
   180  		err := files.FileRemove(path)
   181  		assert.EqualError(t, err, "the file '"+path+"' does not exist: file does not exist")
   182  		assert.False(t, files.HasRemovedFile(path))
   183  	})
   184  	t.Run("fail to remove non-empty directory", func(t *testing.T) {
   185  		files := FilesMock{}
   186  		path := filepath.Join("dir", "file")
   187  		files.AddFile(path, []byte("dummy content"))
   188  		err := files.FileRemove("dir")
   189  		assert.Error(t, err)
   190  	})
   191  	t.Run("fail to remove non-empty directory also when it was explicitly added", func(t *testing.T) {
   192  		files := FilesMock{}
   193  		path := filepath.Join("dir", "file")
   194  		files.AddFile(path, []byte("dummy content"))
   195  		files.AddDir("dir")
   196  		err := files.FileRemove("dir")
   197  		assert.Error(t, err)
   198  	})
   199  	t.Run("succeed to remove empty directory when it was explicitly added", func(t *testing.T) {
   200  		files := FilesMock{}
   201  		files.AddDir("dir")
   202  		err := files.FileRemove("dir")
   203  		assert.NoError(t, err)
   204  	})
   205  	t.Run("removing chain of entries works", func(t *testing.T) {
   206  		files := FilesMock{}
   207  		path := filepath.Join("path", "to", "file")
   208  		files.AddFile(path, []byte("dummy content"))
   209  		assert.NoError(t, files.FileRemove(filepath.Join("path", "to", "file")))
   210  		assert.NoError(t, files.FileRemove(filepath.Join("path", "to")))
   211  		assert.NoError(t, files.FileRemove(filepath.Join("path")))
   212  		assert.Len(t, files.files, 0)
   213  	})
   214  	t.Run("removing entry from current dir works", func(t *testing.T) {
   215  		files := FilesMock{}
   216  		path := filepath.Join("path", "to", "file")
   217  		files.AddFile(path, []byte("dummy content"))
   218  
   219  		err := files.Chdir("path")
   220  		assert.NoError(t, err)
   221  
   222  		assert.NoError(t, files.FileRemove(filepath.Join("to", "file")))
   223  		assert.NoError(t, files.FileRemove(filepath.Join("to")))
   224  
   225  		err = files.Chdir("/")
   226  		assert.NoError(t, err)
   227  
   228  		assert.NoError(t, files.FileRemove(filepath.Join("path")))
   229  		assert.Len(t, files.files, 0)
   230  	})
   231  	t.Run("track removing a file", func(t *testing.T) {
   232  		files := FilesMock{}
   233  		path := filepath.Join("some", "file")
   234  		files.AddFile(path, []byte("dummy content"))
   235  		assert.True(t, files.HasFile(path))
   236  		err := files.FileRemove(path)
   237  		assert.NoError(t, err)
   238  		assert.False(t, files.HasFile(path))
   239  		assert.True(t, files.HasRemovedFile(path))
   240  	})
   241  }
   242  
   243  func TestFilesMockFileRename(t *testing.T) {
   244  	t.Parallel()
   245  	t.Run("fail to rename non-existing file (no init)", func(t *testing.T) {
   246  		files := FilesMock{}
   247  		oldPath := filepath.Join("foo", "bar")
   248  		newPath := filepath.Join("foo", "baz")
   249  		err := files.FileRename(oldPath, newPath)
   250  		assert.EqualError(t, err, "the file '"+oldPath+"' does not exist: file does not exist")
   251  	})
   252  	t.Run("fail to rename non-existing file", func(t *testing.T) {
   253  		files := FilesMock{}
   254  		files.AddDir("triggers/initialization")
   255  		oldPath := filepath.Join("foo", "bar")
   256  		newPath := filepath.Join("foo", "baz")
   257  		err := files.FileRename(oldPath, newPath)
   258  		assert.EqualError(t, err,
   259  			"renaming file '"+oldPath+"' is not supported, since it does not exist, or is not a leaf-entry")
   260  	})
   261  	t.Run("fail to rename non-existing file, even no-op", func(t *testing.T) {
   262  		files := FilesMock{}
   263  		files.AddDir("triggers/initialization")
   264  		oldPath := filepath.Join("foo", "bar")
   265  		newPath := oldPath
   266  		err := files.FileRename(oldPath, newPath)
   267  		assert.EqualError(t, err,
   268  			"renaming file '"+oldPath+"' is not supported, since it does not exist, or is not a leaf-entry")
   269  	})
   270  	t.Run("success to rename dir (no-op)", func(t *testing.T) {
   271  		files := FilesMock{}
   272  		oldPath := filepath.Join("foo", "bar")
   273  		files.AddDir(oldPath)
   274  		newPath := oldPath
   275  		err := files.FileRename(oldPath, newPath)
   276  		assert.NoError(t, err)
   277  		assert.True(t, files.HasFile(newPath))
   278  	})
   279  	t.Run("success to rename dir", func(t *testing.T) {
   280  		files := FilesMock{}
   281  		oldPath := filepath.Join("foo", "bar")
   282  		files.AddDir(oldPath)
   283  		newPath := filepath.Join("foo", "baz")
   284  		err := files.FileRename(oldPath, newPath)
   285  		assert.NoError(t, err)
   286  		assert.True(t, files.HasFile(newPath))
   287  		assert.False(t, files.HasFile(oldPath))
   288  	})
   289  	t.Run("success to rename file", func(t *testing.T) {
   290  		files := FilesMock{}
   291  		oldPath := filepath.Join("foo", "bar")
   292  		files.AddFile(oldPath, []byte("dummy contents"))
   293  		newPath := filepath.Join("foo", "baz")
   294  		err := files.FileRename(oldPath, newPath)
   295  		assert.NoError(t, err)
   296  		assert.True(t, files.HasFile(newPath))
   297  		assert.False(t, files.HasFile(oldPath))
   298  	})
   299  	t.Run("fail to rename file, already exists", func(t *testing.T) {
   300  		files := FilesMock{}
   301  		oldPath := filepath.Join("foo", "bar")
   302  		newPath := filepath.Join("foo", "baz")
   303  		files.AddFile(oldPath, []byte("dummy contents"))
   304  		files.AddFile(newPath, []byte("dummy contents"))
   305  		err := files.FileRename(oldPath, newPath)
   306  		assert.EqualError(t, err, "cannot rename '"+oldPath+"', target path '"+newPath+"' already exists")
   307  		assert.True(t, files.HasFile(newPath))
   308  		assert.True(t, files.HasFile(oldPath))
   309  	})
   310  }
   311  
   312  func TestFilesMockGetwd(t *testing.T) {
   313  	t.Parallel()
   314  	t.Run("test root", func(t *testing.T) {
   315  		files := FilesMock{}
   316  		dir, err := files.Getwd()
   317  		assert.NoError(t, err)
   318  		assert.Equal(t, string(os.PathSeparator), dir)
   319  	})
   320  	t.Run("test sub folder", func(t *testing.T) {
   321  		files := FilesMock{}
   322  		dirChain := []string{"some", "deep", "folder"}
   323  		files.AddDir(filepath.Join(dirChain...))
   324  		for _, element := range dirChain {
   325  			err := files.Chdir(element)
   326  			assert.NoError(t, err)
   327  		}
   328  		dir, err := files.Getwd()
   329  		assert.NoError(t, err)
   330  		assert.Equal(t, string(os.PathSeparator)+filepath.Join(dirChain...), dir)
   331  	})
   332  }
   333  
   334  func TestFilesMockGlob(t *testing.T) {
   335  	t.Parallel()
   336  
   337  	files := FilesMock{}
   338  	content := []byte("dummy content")
   339  	files.AddFile(filepath.Join("dir", "foo.xml"), content)
   340  	files.AddFile(filepath.Join("dir", "another", "foo.xml"), content)
   341  	files.AddFile(filepath.Join("dir", "baz"), content)
   342  	files.AddFile(filepath.Join("baz.xml"), content)
   343  
   344  	t.Run("one match in root-dir", func(t *testing.T) {
   345  		matches, err := files.Glob("*.xml")
   346  		assert.NoError(t, err)
   347  		if assert.Len(t, matches, 1) {
   348  			assert.Equal(t, matches[0], "baz.xml")
   349  		}
   350  	})
   351  	t.Run("three matches in all levels", func(t *testing.T) {
   352  		matches, err := files.Glob("**/*.xml")
   353  		assert.NoError(t, err)
   354  		if assert.Len(t, matches, 3) {
   355  			assert.Equal(t, matches[0], "baz.xml")
   356  			assert.Equal(t, matches[1], filepath.Join("dir", "another", "foo.xml"))
   357  			assert.Equal(t, matches[2], filepath.Join("dir", "foo.xml"))
   358  		}
   359  	})
   360  	t.Run("match only in sub-dir", func(t *testing.T) {
   361  		matches, err := files.Glob("*/*.xml")
   362  		assert.NoError(t, err)
   363  		if assert.Len(t, matches, 1) {
   364  			assert.Equal(t, matches[0], filepath.Join("dir", "foo.xml"))
   365  		}
   366  	})
   367  	t.Run("match for two levels", func(t *testing.T) {
   368  		matches, err := files.Glob("*/*/*.xml")
   369  		assert.NoError(t, err)
   370  		if assert.Len(t, matches, 1) {
   371  			assert.Equal(t, matches[0], filepath.Join("dir", "another", "foo.xml"))
   372  		}
   373  	})
   374  	t.Run("match prefix", func(t *testing.T) {
   375  		matches, err := files.Glob("**/baz*")
   376  		assert.NoError(t, err)
   377  		if assert.Len(t, matches, 2) {
   378  			assert.Equal(t, matches[0], filepath.Join("baz.xml"))
   379  			assert.Equal(t, matches[1], filepath.Join("dir", "baz"))
   380  		}
   381  	})
   382  	t.Run("no matches", func(t *testing.T) {
   383  		matches, err := files.Glob("**/*bar*")
   384  		assert.NoError(t, err)
   385  		assert.Len(t, matches, 0)
   386  	})
   387  }
   388  
   389  func TestStat(t *testing.T) {
   390  	t.Parallel()
   391  	t.Run("non existing file (no init)", func(t *testing.T) {
   392  		// init
   393  		files := FilesMock{}
   394  		// test
   395  		_, err := files.Stat("doesNotExist.txt")
   396  		assert.EqualError(t, err, "stat doesNotExist.txt: no such file or directory")
   397  	})
   398  	t.Run("non existing file", func(t *testing.T) {
   399  		// init
   400  		files := FilesMock{}
   401  		files.AddFile("tmp/dummy.txt", []byte("Hello SAP"))
   402  		// test
   403  		_, err := files.Stat("doesNotExist.txt")
   404  		assert.EqualError(t, err, "stat doesNotExist.txt: no such file or directory")
   405  	})
   406  	t.Run("check file info", func(t *testing.T) {
   407  		// init
   408  		files := FilesMock{}
   409  		files.AddFile("tmp/dummy.txt", []byte("Hello SAP"))
   410  		// test
   411  		info, err := files.Stat("tmp/dummy.txt")
   412  		// assert
   413  		if assert.NoError(t, err) {
   414  			// only the base name is returned.
   415  			assert.Equal(t, "dummy.txt", info.Name())
   416  			assert.False(t, info.IsDir())
   417  			// if not specified otherwise the 644 file mode is used.
   418  			assert.Equal(t, defaultFileMode, info.Mode())
   419  		}
   420  	})
   421  	t.Run("check implicit dir", func(t *testing.T) {
   422  		// init
   423  		files := FilesMock{}
   424  		files.AddFile("tmp/dummy.txt", []byte("Hello SAP"))
   425  		// test
   426  		info, err := files.Stat("tmp")
   427  		// assert
   428  		if assert.NoError(t, err) {
   429  			assert.True(t, info.IsDir())
   430  			assert.Equal(t, defaultDirMode, info.Mode())
   431  		}
   432  	})
   433  	t.Run("check explicit dir", func(t *testing.T) {
   434  		// init
   435  		files := FilesMock{}
   436  		explicitMode := os.FileMode(0700)
   437  		files.AddDirWithMode("bin", explicitMode)
   438  		// test
   439  		info, err := files.Stat("bin")
   440  		// assert
   441  		if assert.NoError(t, err) {
   442  			assert.True(t, info.IsDir())
   443  			assert.Equal(t, explicitMode, info.Mode())
   444  		}
   445  	})
   446  }
   447  
   448  func TestChmod(t *testing.T) {
   449  	files := FilesMock{}
   450  	files.AddFileWithMode("tmp/log.txt", []byte("build failed"), 0777)
   451  
   452  	t.Run("non existing file", func(t *testing.T) {
   453  		err := files.Chmod("does/not/exist", 0400)
   454  		assert.EqualError(t, err, "chmod: does/not/exist: No such file or directory")
   455  	})
   456  	t.Run("chmod for file", func(t *testing.T) {
   457  		err := files.Chmod("tmp/log.txt", 0645)
   458  		if assert.NoError(t, err) {
   459  			info, e := files.Stat("tmp/log.txt")
   460  			if assert.NoError(t, e) {
   461  				assert.Equal(t, os.FileMode(0645), info.Mode())
   462  			}
   463  		}
   464  	})
   465  	t.Run("chmod for directory", func(t *testing.T) {
   466  		err := files.Chmod("tmp", 0766)
   467  		if assert.NoError(t, err) {
   468  			info, e := files.Stat("tmp")
   469  			if assert.NoError(t, e) {
   470  				assert.Equal(t, os.FileMode(0766), info.Mode())
   471  			}
   472  		}
   473  	})
   474  }
   475  
   476  func TestRelativePaths(t *testing.T) {
   477  	t.Parallel()
   478  	t.Run("files are not mixed up", func(t *testing.T) {
   479  		files := FilesMock{}
   480  		files.AddFile("path/to/file", []byte("content"))
   481  		files.AddFile("file", []byte("root-content"))
   482  
   483  		err := files.Chdir("path")
   484  		if assert.NoError(t, err) {
   485  			exists, _ := files.FileExists("file")
   486  			assert.False(t, exists)
   487  
   488  			err := files.Chdir("to")
   489  			if assert.NoError(t, err) {
   490  				content, err := files.FileRead("file")
   491  				if assert.NoError(t, err) {
   492  					assert.Equal(t, []byte("content"), content, "should not read root file")
   493  				}
   494  			}
   495  		}
   496  	})
   497  	t.Run("root folder exists after change dir", func(t *testing.T) {
   498  		files := FilesMock{}
   499  		files.AddFile("path/to/file", []byte("content"))
   500  
   501  		errChdirInto := files.Chdir("path")
   502  		assert.NoError(t, errChdirInto)
   503  
   504  		exists, err := files.DirExists("/")
   505  		assert.NoError(t, err)
   506  		assert.True(t, exists, "the root folder should exist no matter the current dir")
   507  	})
   508  	t.Run("current folder always exists", func(t *testing.T) {
   509  		files := FilesMock{}
   510  		files.AddFile("path/to/file", []byte("content"))
   511  
   512  		exists, err := files.DirExists(".")
   513  		assert.NoError(t, err)
   514  		assert.True(t, exists, "the current folder should exist")
   515  
   516  		errChdirInto := files.Chdir("path")
   517  		assert.NoError(t, errChdirInto)
   518  
   519  		exists, err = files.DirExists("./")
   520  		assert.NoError(t, err)
   521  		assert.True(t, exists, "the current folder should exist after changing into it")
   522  	})
   523  	t.Run("chmod works on implicit, relative dir", func(t *testing.T) {
   524  		files := FilesMock{}
   525  		files.AddFile("path/to/file", []byte("content"))
   526  
   527  		errChdirInto := files.Chdir("path")
   528  		errChmod := files.Chmod("to", 0700)
   529  		errChdirBack := files.Chdir("/")
   530  
   531  		assert.NoError(t, errChdirInto)
   532  		assert.NoError(t, errChmod)
   533  		assert.NoError(t, errChdirBack)
   534  
   535  		fileInfo, err := files.Stat("path/to")
   536  		if assert.NoError(t, err) {
   537  			assert.Equal(t, os.FileMode(0700), fileInfo.Mode())
   538  		}
   539  	})
   540  }
   541  
   542  func TestOpen(t *testing.T) {
   543  	filePath := filepath.Join("some", "file")
   544  	t.Parallel()
   545  	t.Run("no init without O_CREATE", func(t *testing.T) {
   546  		// init
   547  		files := FilesMock{}
   548  		// test
   549  		file, err := files.OpenFile(filePath, 0, 0)
   550  		// assert
   551  		if assert.Error(t, err) {
   552  			assert.Contains(t, err.Error(), "does not exist")
   553  			assert.Nil(t, file)
   554  		}
   555  	})
   556  	t.Run("no init with O_CREATE", func(t *testing.T) {
   557  		// init
   558  		files := FilesMock{}
   559  		// test
   560  		file, err := files.OpenFile(filePath, os.O_CREATE, 0644)
   561  		// assert
   562  		if assert.NoError(t, err) && assert.NotNil(t, file) {
   563  			assert.Equal(t, &files, file.files)
   564  			assert.Equal(t, files.Separator+filePath, file.absPath)
   565  			assert.NotNil(t, file.content)
   566  		}
   567  	})
   568  	t.Run("content is replaced without O_APPEND", func(t *testing.T) {
   569  		// init
   570  		files := FilesMock{}
   571  		files.AddFile(filePath, []byte("initial-content"))
   572  		// test
   573  		file, _ := files.OpenFile(filePath, os.O_CREATE, 0644)
   574  		written, err := file.WriteString("hello")
   575  		if assert.NoError(t, err) {
   576  			assert.Equal(t, written, len("hello"))
   577  			content, err := files.FileRead(filePath)
   578  			if assert.NoError(t, err) {
   579  				assert.Equal(t, []byte("hello"), content)
   580  			}
   581  		}
   582  	})
   583  	t.Run("content is truncated with O_TRUNC an nothing written", func(t *testing.T) {
   584  		// init
   585  		files := FilesMock{}
   586  		files.AddFile(filePath, []byte("initial-content"))
   587  		// test
   588  		file, err := files.OpenFile(filePath, os.O_CREATE|os.O_TRUNC, 0644)
   589  		require.NoError(t, err)
   590  		err = file.Close()
   591  		assert.NoError(t, err)
   592  		content, err := files.FileRead(filePath)
   593  		if assert.NoError(t, err) {
   594  			assert.Len(t, content, 0)
   595  		}
   596  	})
   597  	t.Run("content is appended", func(t *testing.T) {
   598  		// init
   599  		files := FilesMock{}
   600  		files.AddFile(filePath, []byte("initial-content"))
   601  		// test
   602  		file, _ := files.OpenFile(filePath, os.O_APPEND, 0644)
   603  		written1, err1 := file.WriteString("-hel")
   604  		written2, err2 := file.WriteString("lo")
   605  		if assert.NoError(t, err1) && assert.NoError(t, err2) {
   606  			assert.Equal(t, written1+written2, len("-hello"))
   607  			content, err := files.FileRead(filePath)
   608  			if assert.NoError(t, err) {
   609  				assert.Equal(t, []byte("initial-content-hello"), content)
   610  			}
   611  		}
   612  	})
   613  	t.Run("cannot write to closed file", func(t *testing.T) {
   614  		// init
   615  		files := FilesMock{}
   616  		files.AddFile(filePath, []byte("initial-content"))
   617  		// test
   618  		file, _ := files.OpenFile(filePath, os.O_APPEND, 0644)
   619  		_, err := file.WriteString("-hello")
   620  		assert.NoError(t, err)
   621  		err = file.Close()
   622  		assert.NoError(t, err)
   623  		_, err = file.WriteString("-more")
   624  		assert.Error(t, err)
   625  		content, err := files.FileRead(filePath)
   626  		if assert.NoError(t, err) {
   627  			assert.Equal(t, []byte("initial-content-hello"), content)
   628  		}
   629  	})
   630  }
   631  
   632  func TestFilesMockTempDir(t *testing.T) {
   633  	t.Parallel()
   634  	t.Run("creates a temp dir without a pattern", func(t *testing.T) {
   635  		files := FilesMock{}
   636  		dir, err := files.TempDir("", "")
   637  		assert.NoError(t, err)
   638  		assert.Equal(t, "/tmp", dir)
   639  		ok, err := files.DirExists("/tmp")
   640  		assert.NoError(t, err)
   641  		assert.True(t, ok)
   642  	})
   643  	t.Run("creates a temp dir with a pattern", func(t *testing.T) {
   644  		files := FilesMock{}
   645  		dir, err := files.TempDir("", "pattern")
   646  		assert.NoError(t, err)
   647  		assert.Equal(t, "/tmp/patterntest", dir)
   648  		ok, err := files.DirExists("/tmp/patterntest")
   649  		assert.NoError(t, err)
   650  		assert.True(t, ok)
   651  	})
   652  }
   653  
   654  func TestFilesMockSymlink(t *testing.T) {
   655  	t.Parallel()
   656  	t.Run("creates a symlink", func(t *testing.T) {
   657  		files := FilesMock{}
   658  		files.AddDir("/backup")
   659  		assert.NoError(t, files.Symlink("/folder", "/backup/folder"))
   660  
   661  		assert.True(t, files.HasCreatedSymlink("/folder", "/backup/folder"))
   662  	})
   663  
   664  	t.Run("fails if parent directory doesn't exist", func(t *testing.T) {
   665  		files := FilesMock{}
   666  		err := files.Symlink("/non/existent/folder", "/symbolic/link")
   667  		assert.Error(t, err)
   668  		assert.Equal(t, "failed to create symlink: parent directory /symbolic doesn't exist", err.Error())
   669  	})
   670  
   671  	t.Run("fails if FileWriteError is specified", func(t *testing.T) {
   672  		expectedErr := errors.New("test")
   673  		files := FilesMock{
   674  			FileWriteErrors: map[string]error{
   675  				"/symbolic/link": expectedErr,
   676  			},
   677  		}
   678  		err := files.Symlink("/non/existent/folder", "/symbolic/link")
   679  		assert.Error(t, err)
   680  		assert.Equal(t, expectedErr, err)
   681  	})
   682  }
   683  
   684  func TestFilesMockCreateArchive(t *testing.T) {
   685  	t.Parallel()
   686  	t.Run("creates an archive with the provided content", func(t *testing.T) {
   687  		files := FilesMock{}
   688  		a, err := files.CreateArchive(map[string][]byte{
   689  			"filename": []byte("file content"),
   690  		})
   691  		assert.NoError(t, err)
   692  
   693  		buf := bytes.NewBuffer(a)
   694  		zr, err := gzip.NewReader(buf)
   695  		assert.NoError(t, err)
   696  		defer zr.Close()
   697  
   698  		tr := tar.NewReader(zr)
   699  
   700  		for {
   701  			f, err := tr.Next()
   702  			if err == io.EOF {
   703  				break
   704  			}
   705  			assert.NoError(t, err)
   706  			assert.Equal(t, "filename", f.Name)
   707  
   708  			content, err := io.ReadAll(tr)
   709  			assert.NoError(t, err)
   710  			assert.Equal(t, "file content", string(content))
   711  		}
   712  	})
   713  
   714  	t.Run("fails if the content is empty", func(t *testing.T) {
   715  		files := FilesMock{}
   716  		a, err := files.CreateArchive(nil)
   717  		assert.Error(t, err)
   718  		assert.Equal(t, "mock archive content must not be empty", err.Error())
   719  		assert.Nil(t, a)
   720  	})
   721  }