package mock import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "os" "path/filepath" "testing" ) func TestFilesMockFileExists(t *testing.T) { t.Parallel() t.Run("no init", func(t *testing.T) { files := FilesMock{} path := filepath.Join("some", "path") exists, err := files.FileExists(path) assert.NoError(t, err) assert.False(t, exists) }) t.Run("file exists after AddFile()", func(t *testing.T) { files := FilesMock{} path := filepath.Join("some", "path") files.AddFile(path, []byte("dummy content")) exists, err := files.FileExists(path) assert.NoError(t, err) assert.True(t, exists) }) t.Run("path is a directory after AddDir()", func(t *testing.T) { files := FilesMock{} path := filepath.Join("some", "path") files.AddDir(path) exists, err := files.FileExists(path) assert.NoError(t, err) assert.False(t, exists) }) t.Run("file exists after changing current dir", func(t *testing.T) { files := FilesMock{} path := filepath.Join("some", "path") files.AddFile(path, []byte("dummy content")) err := files.Chdir("some") assert.NoError(t, err) exists, err := files.FileExists("path") assert.NoError(t, err) assert.True(t, exists) }) t.Run("file does not exist after changing current dir", func(t *testing.T) { files := FilesMock{} path := filepath.Join("some", "path") files.AddFile(path, []byte("dummy content")) err := files.Chdir("some") assert.NoError(t, err) exists, err := files.FileExists(path) assert.NoError(t, err) assert.False(t, exists) }) } func TestFilesMockDirExists(t *testing.T) { t.Parallel() t.Run("no init", func(t *testing.T) { files := FilesMock{} path := filepath.Join("some", "path") exists, err := files.DirExists(path) assert.NoError(t, err) assert.False(t, exists) }) t.Run("root folder always exists", func(t *testing.T) { files := FilesMock{} exists, err := files.DirExists(string(os.PathSeparator)) assert.NoError(t, err) assert.False(t, exists) }) t.Run("dir exists after AddDir()", func(t *testing.T) { files := FilesMock{} path := filepath.Join("some", "path") files.AddDir(path) exists, err := files.DirExists(path) assert.NoError(t, err) assert.True(t, exists) }) t.Run("absolute dir exists after AddDir()", func(t *testing.T) { files := FilesMock{} path := filepath.Join("some", "path") files.AddDir(path) err := files.Chdir("some") assert.NoError(t, err) exists, err := files.DirExists(string(os.PathSeparator) + path) assert.NoError(t, err) assert.True(t, exists) }) t.Run("parent dirs exists after AddFile()", func(t *testing.T) { files := FilesMock{} path := filepath.Join("path", "to", "some", "file") files.AddFile(path, []byte("dummy content")) testDirs := []string{ "path", filepath.Join("path", "to"), filepath.Join("path", "to", "some"), } for _, dir := range testDirs { exists, err := files.DirExists(dir) assert.NoError(t, err) assert.True(t, exists, "Should exist: '%s'", dir) } }) t.Run("invalid sub-folders do not exist after AddFile()", func(t *testing.T) { files := FilesMock{} path := filepath.Join("path", "to", "some", "file") files.AddFile(path, []byte("dummy content")) testDirs := []string{ "to", filepath.Join("to", "some"), "some", filepath.Join("path", "to", "so"), } for _, dir := range testDirs { exists, err := files.DirExists(dir) assert.NoError(t, err) assert.False(t, exists, "Should not exist: '%s'", dir) } }) t.Run("dir still exists after removing last file", func(t *testing.T) { files := FilesMock{} dir := filepath.Join("path", "to") file := filepath.Join(dir, "file") files.AddFile(file, []byte("dummy content")) err := files.FileRemove(file) assert.NoError(t, err) exists, err := files.DirExists(dir) assert.NoError(t, err) assert.True(t, exists) }) } func TestFilesMockCopy(t *testing.T) { t.Parallel() t.Run("copy a previously added file successfully", func(t *testing.T) { files := FilesMock{} src := filepath.Join("some", "file") content := []byte("dummy content") files.AddFile(src, content) dst := filepath.Join("another", "file") length, err := files.Copy(src, dst) assert.NoError(t, err) assert.Equal(t, length, int64(len(content))) }) t.Run("fail to copy non-existing file", func(t *testing.T) { files := FilesMock{} src := filepath.Join("some", "file") dst := filepath.Join("another", "file") length, err := files.Copy(src, dst) assert.EqualError(t, err, "cannot copy '"+src+"': file does not exist") assert.Equal(t, length, int64(0)) }) t.Run("fail to copy folder", func(t *testing.T) { files := FilesMock{} src := filepath.Join("some", "file") files.AddDir(src) dst := filepath.Join("another", "file") length, err := files.Copy(src, dst) assert.EqualError(t, err, "cannot copy '"+src+"': file does not exist") assert.Equal(t, length, int64(0)) }) } func TestFilesMockFileRemove(t *testing.T) { t.Parallel() t.Run("fail to remove non-existing file", func(t *testing.T) { files := FilesMock{} path := filepath.Join("some", "file") err := files.FileRemove(path) assert.EqualError(t, err, "the file '"+path+"' does not exist: file does not exist") assert.False(t, files.HasRemovedFile(path)) }) t.Run("fail to remove non-empty directory", func(t *testing.T) { files := FilesMock{} path := filepath.Join("dir", "file") files.AddFile(path, []byte("dummy content")) err := files.FileRemove("dir") assert.Error(t, err) }) t.Run("fail to remove non-empty directory also when it was explicitly added", func(t *testing.T) { files := FilesMock{} path := filepath.Join("dir", "file") files.AddFile(path, []byte("dummy content")) files.AddDir("dir") err := files.FileRemove("dir") assert.Error(t, err) }) t.Run("succeed to remove empty directory when it was explicitly added", func(t *testing.T) { files := FilesMock{} files.AddDir("dir") err := files.FileRemove("dir") assert.NoError(t, err) }) t.Run("removing chain of entries works", func(t *testing.T) { files := FilesMock{} path := filepath.Join("path", "to", "file") files.AddFile(path, []byte("dummy content")) assert.NoError(t, files.FileRemove(filepath.Join("path", "to", "file"))) assert.NoError(t, files.FileRemove(filepath.Join("path", "to"))) assert.NoError(t, files.FileRemove(filepath.Join("path"))) assert.Len(t, files.files, 0) }) t.Run("removing entry from current dir works", func(t *testing.T) { files := FilesMock{} path := filepath.Join("path", "to", "file") files.AddFile(path, []byte("dummy content")) err := files.Chdir("path") assert.NoError(t, err) assert.NoError(t, files.FileRemove(filepath.Join("to", "file"))) assert.NoError(t, files.FileRemove(filepath.Join("to"))) err = files.Chdir("/") assert.NoError(t, err) assert.NoError(t, files.FileRemove(filepath.Join("path"))) assert.Len(t, files.files, 0) }) t.Run("track removing a file", func(t *testing.T) { files := FilesMock{} path := filepath.Join("some", "file") files.AddFile(path, []byte("dummy content")) assert.True(t, files.HasFile(path)) err := files.FileRemove(path) assert.NoError(t, err) assert.False(t, files.HasFile(path)) assert.True(t, files.HasRemovedFile(path)) }) } func TestFilesMockFileRename(t *testing.T) { t.Parallel() t.Run("fail to rename non-existing file (no init)", func(t *testing.T) { files := FilesMock{} oldPath := filepath.Join("foo", "bar") newPath := filepath.Join("foo", "baz") err := files.FileRename(oldPath, newPath) assert.EqualError(t, err, "the file '"+oldPath+"' does not exist: file does not exist") }) t.Run("fail to rename non-existing file", func(t *testing.T) { files := FilesMock{} files.AddDir("triggers/initialization") oldPath := filepath.Join("foo", "bar") newPath := filepath.Join("foo", "baz") err := files.FileRename(oldPath, newPath) assert.EqualError(t, err, "renaming file '"+oldPath+"' is not supported, since it does not exist, or is not a leaf-entry") }) t.Run("fail to rename non-existing file, even no-op", func(t *testing.T) { files := FilesMock{} files.AddDir("triggers/initialization") oldPath := filepath.Join("foo", "bar") newPath := oldPath err := files.FileRename(oldPath, newPath) assert.EqualError(t, err, "renaming file '"+oldPath+"' is not supported, since it does not exist, or is not a leaf-entry") }) t.Run("success to rename dir (no-op)", func(t *testing.T) { files := FilesMock{} oldPath := filepath.Join("foo", "bar") files.AddDir(oldPath) newPath := oldPath err := files.FileRename(oldPath, newPath) assert.NoError(t, err) assert.True(t, files.HasFile(newPath)) }) t.Run("success to rename dir", func(t *testing.T) { files := FilesMock{} oldPath := filepath.Join("foo", "bar") files.AddDir(oldPath) newPath := filepath.Join("foo", "baz") err := files.FileRename(oldPath, newPath) assert.NoError(t, err) assert.True(t, files.HasFile(newPath)) assert.False(t, files.HasFile(oldPath)) }) t.Run("success to rename file", func(t *testing.T) { files := FilesMock{} oldPath := filepath.Join("foo", "bar") files.AddFile(oldPath, []byte("dummy contents")) newPath := filepath.Join("foo", "baz") err := files.FileRename(oldPath, newPath) assert.NoError(t, err) assert.True(t, files.HasFile(newPath)) assert.False(t, files.HasFile(oldPath)) }) t.Run("fail to rename file, already exists", func(t *testing.T) { files := FilesMock{} oldPath := filepath.Join("foo", "bar") newPath := filepath.Join("foo", "baz") files.AddFile(oldPath, []byte("dummy contents")) files.AddFile(newPath, []byte("dummy contents")) err := files.FileRename(oldPath, newPath) assert.EqualError(t, err, "cannot rename '"+oldPath+"', target path '"+newPath+"' already exists") assert.True(t, files.HasFile(newPath)) assert.True(t, files.HasFile(oldPath)) }) } func TestFilesMockGetwd(t *testing.T) { t.Parallel() t.Run("test root", func(t *testing.T) { files := FilesMock{} dir, err := files.Getwd() assert.NoError(t, err) assert.Equal(t, string(os.PathSeparator), dir) }) t.Run("test sub folder", func(t *testing.T) { files := FilesMock{} dirChain := []string{"some", "deep", "folder"} files.AddDir(filepath.Join(dirChain...)) for _, element := range dirChain { err := files.Chdir(element) assert.NoError(t, err) } dir, err := files.Getwd() assert.NoError(t, err) assert.Equal(t, string(os.PathSeparator)+filepath.Join(dirChain...), dir) }) } func TestFilesMockGlob(t *testing.T) { t.Parallel() files := FilesMock{} content := []byte("dummy content") files.AddFile(filepath.Join("dir", "foo.xml"), content) files.AddFile(filepath.Join("dir", "another", "foo.xml"), content) files.AddFile(filepath.Join("dir", "baz"), content) files.AddFile(filepath.Join("baz.xml"), content) t.Run("one match in root-dir", func(t *testing.T) { matches, err := files.Glob("*.xml") assert.NoError(t, err) if assert.Len(t, matches, 1) { assert.Equal(t, matches[0], "baz.xml") } }) t.Run("three matches in all levels", func(t *testing.T) { matches, err := files.Glob("**/*.xml") assert.NoError(t, err) if assert.Len(t, matches, 3) { assert.Equal(t, matches[0], "baz.xml") assert.Equal(t, matches[1], filepath.Join("dir", "another", "foo.xml")) assert.Equal(t, matches[2], filepath.Join("dir", "foo.xml")) } }) t.Run("match only in sub-dir", func(t *testing.T) { matches, err := files.Glob("*/*.xml") assert.NoError(t, err) if assert.Len(t, matches, 1) { assert.Equal(t, matches[0], filepath.Join("dir", "foo.xml")) } }) t.Run("match for two levels", func(t *testing.T) { matches, err := files.Glob("*/*/*.xml") assert.NoError(t, err) if assert.Len(t, matches, 1) { assert.Equal(t, matches[0], filepath.Join("dir", "another", "foo.xml")) } }) t.Run("match prefix", func(t *testing.T) { matches, err := files.Glob("**/baz*") assert.NoError(t, err) if assert.Len(t, matches, 2) { assert.Equal(t, matches[0], filepath.Join("baz.xml")) assert.Equal(t, matches[1], filepath.Join("dir", "baz")) } }) t.Run("no matches", func(t *testing.T) { matches, err := files.Glob("**/*bar*") assert.NoError(t, err) assert.Len(t, matches, 0) }) } func TestStat(t *testing.T) { t.Parallel() t.Run("non existing file (no init)", func(t *testing.T) { // init files := FilesMock{} // test _, err := files.Stat("doesNotExist.txt") assert.EqualError(t, err, "stat doesNotExist.txt: no such file or directory") }) t.Run("non existing file", func(t *testing.T) { // init files := FilesMock{} files.AddFile("tmp/dummy.txt", []byte("Hello SAP")) // test _, err := files.Stat("doesNotExist.txt") assert.EqualError(t, err, "stat doesNotExist.txt: no such file or directory") }) t.Run("check file info", func(t *testing.T) { // init files := FilesMock{} files.AddFile("tmp/dummy.txt", []byte("Hello SAP")) // test info, err := files.Stat("tmp/dummy.txt") // assert if assert.NoError(t, err) { // only the base name is returned. assert.Equal(t, "dummy.txt", info.Name()) assert.False(t, info.IsDir()) // if not specified otherwise the 644 file mode is used. assert.Equal(t, defaultFileMode, info.Mode()) } }) t.Run("check implicit dir", func(t *testing.T) { // init files := FilesMock{} files.AddFile("tmp/dummy.txt", []byte("Hello SAP")) // test info, err := files.Stat("tmp") // assert if assert.NoError(t, err) { assert.True(t, info.IsDir()) assert.Equal(t, defaultDirMode, info.Mode()) } }) t.Run("check explicit dir", func(t *testing.T) { // init files := FilesMock{} explicitMode := os.FileMode(0700) files.AddDirWithMode("bin", explicitMode) // test info, err := files.Stat("bin") // assert if assert.NoError(t, err) { assert.True(t, info.IsDir()) assert.Equal(t, explicitMode, info.Mode()) } }) } func TestChmod(t *testing.T) { files := FilesMock{} files.AddFileWithMode("tmp/log.txt", []byte("build failed"), 0777) t.Run("non existing file", func(t *testing.T) { err := files.Chmod("does/not/exist", 0400) assert.EqualError(t, err, "chmod: does/not/exist: No such file or directory") }) t.Run("chmod for file", func(t *testing.T) { err := files.Chmod("tmp/log.txt", 0645) if assert.NoError(t, err) { info, e := files.Stat("tmp/log.txt") if assert.NoError(t, e) { assert.Equal(t, os.FileMode(0645), info.Mode()) } } }) t.Run("chmod for directory", func(t *testing.T) { err := files.Chmod("tmp", 0766) if assert.NoError(t, err) { info, e := files.Stat("tmp") if assert.NoError(t, e) { assert.Equal(t, os.FileMode(0766), info.Mode()) } } }) } func TestRelativePaths(t *testing.T) { t.Parallel() t.Run("files are not mixed up", func(t *testing.T) { files := FilesMock{} files.AddFile("path/to/file", []byte("content")) files.AddFile("file", []byte("root-content")) err := files.Chdir("path") if assert.NoError(t, err) { exists, _ := files.FileExists("file") assert.False(t, exists) err := files.Chdir("to") if assert.NoError(t, err) { content, err := files.FileRead("file") if assert.NoError(t, err) { assert.Equal(t, []byte("content"), content, "should not read root file") } } } }) t.Run("root folder exists after change dir", func(t *testing.T) { files := FilesMock{} files.AddFile("path/to/file", []byte("content")) errChdirInto := files.Chdir("path") assert.NoError(t, errChdirInto) exists, err := files.DirExists("/") assert.NoError(t, err) assert.True(t, exists, "the root folder should exist no matter the current dir") }) t.Run("current folder always exists", func(t *testing.T) { files := FilesMock{} files.AddFile("path/to/file", []byte("content")) exists, err := files.DirExists(".") assert.NoError(t, err) assert.True(t, exists, "the current folder should exist") errChdirInto := files.Chdir("path") assert.NoError(t, errChdirInto) exists, err = files.DirExists("./") assert.NoError(t, err) assert.True(t, exists, "the current folder should exist after changing into it") }) t.Run("chmod works on implicit, relative dir", func(t *testing.T) { files := FilesMock{} files.AddFile("path/to/file", []byte("content")) errChdirInto := files.Chdir("path") errChmod := files.Chmod("to", 0700) errChdirBack := files.Chdir("/") assert.NoError(t, errChdirInto) assert.NoError(t, errChmod) assert.NoError(t, errChdirBack) fileInfo, err := files.Stat("path/to") if assert.NoError(t, err) { assert.Equal(t, os.FileMode(0700), fileInfo.Mode()) } }) } func TestOpen(t *testing.T) { filePath := filepath.Join("some", "file") t.Parallel() t.Run("no init without O_CREATE", func(t *testing.T) { // init files := FilesMock{} // test file, err := files.Open(filePath, 0, 0) // assert if assert.Error(t, err) { assert.Contains(t, err.Error(), "does not exist") assert.Nil(t, file) } }) t.Run("no init with O_CREATE", func(t *testing.T) { // init files := FilesMock{} // test file, err := files.Open(filePath, os.O_CREATE, 0644) // assert if assert.NoError(t, err) && assert.NotNil(t, file) { assert.Equal(t, &files, file.files) assert.Equal(t, files.Separator+filePath, file.absPath) assert.NotNil(t, file.content) } }) t.Run("content is replaced without O_APPEND", func(t *testing.T) { // init files := FilesMock{} files.AddFile(filePath, []byte("initial-content")) // test file, _ := files.Open(filePath, os.O_CREATE, 0644) written, err := file.WriteString("hello") if assert.NoError(t, err) { assert.Equal(t, written, len("hello")) content, err := files.FileRead(filePath) if assert.NoError(t, err) { assert.Equal(t, []byte("hello"), content) } } }) t.Run("content is truncated with O_TRUNC an nothing written", func(t *testing.T) { // init files := FilesMock{} files.AddFile(filePath, []byte("initial-content")) // test file, err := files.Open(filePath, os.O_CREATE|os.O_TRUNC, 0644) require.NoError(t, err) err = file.Close() assert.NoError(t, err) content, err := files.FileRead(filePath) if assert.NoError(t, err) { assert.Len(t, content, 0) } }) t.Run("content is appended", func(t *testing.T) { // init files := FilesMock{} files.AddFile(filePath, []byte("initial-content")) // test file, _ := files.Open(filePath, os.O_APPEND, 0644) written1, err1 := file.WriteString("-hel") written2, err2 := file.WriteString("lo") if assert.NoError(t, err1) && assert.NoError(t, err2) { assert.Equal(t, written1+written2, len("-hello")) content, err := files.FileRead(filePath) if assert.NoError(t, err) { assert.Equal(t, []byte("initial-content-hello"), content) } } }) t.Run("cannot write to closed file", func(t *testing.T) { // init files := FilesMock{} files.AddFile(filePath, []byte("initial-content")) // test file, _ := files.Open(filePath, os.O_APPEND, 0644) _, err := file.WriteString("-hello") assert.NoError(t, err) err = file.Close() assert.NoError(t, err) _, err = file.WriteString("-more") assert.Error(t, err) content, err := files.FileRead(filePath) if assert.NoError(t, err) { assert.Equal(t, []byte("initial-content-hello"), content) } }) }