diff options
Diffstat (limited to 'cli/internal/cacheitem/create_test.go')
| -rw-r--r-- | cli/internal/cacheitem/create_test.go | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/cli/internal/cacheitem/create_test.go b/cli/internal/cacheitem/create_test.go new file mode 100644 index 0000000..97eeb01 --- /dev/null +++ b/cli/internal/cacheitem/create_test.go @@ -0,0 +1,205 @@ +package cacheitem + +import ( + "encoding/hex" + "io/fs" + "os" + "runtime" + "testing" + + "github.com/vercel/turbo/cli/internal/turbopath" + "gotest.tools/v3/assert" +) + +type createFileDefinition struct { + Path turbopath.AnchoredSystemPath + Linkname string + fs.FileMode +} + +func createEntry(t *testing.T, anchor turbopath.AbsoluteSystemPath, fileDefinition createFileDefinition) error { + t.Helper() + if fileDefinition.FileMode.IsDir() { + return createDir(t, anchor, fileDefinition) + } else if fileDefinition.FileMode&os.ModeSymlink != 0 { + return createSymlink(t, anchor, fileDefinition) + } else if fileDefinition.FileMode&os.ModeNamedPipe != 0 { + return createFifo(t, anchor, fileDefinition) + } else { + return createFile(t, anchor, fileDefinition) + } +} + +func createDir(t *testing.T, anchor turbopath.AbsoluteSystemPath, fileDefinition createFileDefinition) error { + t.Helper() + path := fileDefinition.Path.RestoreAnchor(anchor) + mkdirAllErr := path.MkdirAllMode(fileDefinition.FileMode & 0777) + assert.NilError(t, mkdirAllErr, "MkdirAll") + return mkdirAllErr +} +func createFile(t *testing.T, anchor turbopath.AbsoluteSystemPath, fileDefinition createFileDefinition) error { + t.Helper() + path := fileDefinition.Path.RestoreAnchor(anchor) + writeErr := path.WriteFile([]byte("file contents"), fileDefinition.FileMode&0777) + assert.NilError(t, writeErr, "WriteFile") + return writeErr +} +func createSymlink(t *testing.T, anchor turbopath.AbsoluteSystemPath, fileDefinition createFileDefinition) error { + t.Helper() + path := fileDefinition.Path.RestoreAnchor(anchor) + symlinkErr := path.Symlink(fileDefinition.Linkname) + assert.NilError(t, symlinkErr, "Symlink") + lchmodErr := path.Lchmod(fileDefinition.FileMode & 0777) + assert.NilError(t, lchmodErr, "Lchmod") + return symlinkErr +} + +func TestCreate(t *testing.T) { + tests := []struct { + name string + files []createFileDefinition + wantDarwin string + wantUnix string + wantWindows string + wantErr error + }{ + { + name: "hello world", + files: []createFileDefinition{ + { + Path: turbopath.AnchoredSystemPath("hello world.txt"), + FileMode: 0 | 0644, + }, + }, + wantDarwin: "4f39f1cab23906f3b89f313392ef7c26f2586e1c15fa6b577cce640c4781d082817927b4875a5413bc23e1248f0b198218998d70e7336e8b1244542ba446ca07", + wantUnix: "4f39f1cab23906f3b89f313392ef7c26f2586e1c15fa6b577cce640c4781d082817927b4875a5413bc23e1248f0b198218998d70e7336e8b1244542ba446ca07", + wantWindows: "e304d1ba8c51209f97bd11dabf27ca06996b70a850db592343942c49480de47bcbb4b7131fb3dd4d7564021d3bc0e648919e4876572b46ac1da97fca92b009c5", + }, + { + name: "links", + files: []createFileDefinition{ + { + Path: turbopath.AnchoredSystemPath("one"), + Linkname: "two", + FileMode: 0 | os.ModeSymlink | 0777, + }, + { + Path: turbopath.AnchoredSystemPath("two"), + Linkname: "three", + FileMode: 0 | os.ModeSymlink | 0777, + }, + { + Path: turbopath.AnchoredSystemPath("three"), + Linkname: "real", + FileMode: 0 | os.ModeSymlink | 0777, + }, + { + Path: turbopath.AnchoredSystemPath("real"), + FileMode: 0 | 0644, + }, + }, + wantDarwin: "07278fdf37db4b212352367f391377bd6bac8f361dd834ae5522d809539bcf3b34d046873c1b45876d7372251446bb12c32f9fa9824914c4a1a01f6d7a206702", + wantUnix: "07278fdf37db4b212352367f391377bd6bac8f361dd834ae5522d809539bcf3b34d046873c1b45876d7372251446bb12c32f9fa9824914c4a1a01f6d7a206702", + wantWindows: "d4dac527e40860ee1ba3fdf2b9b12a1eba385050cf4f5877558dd531f0ecf2a06952fd5f88b852ad99e010943ed7b7f1437b727796369524e85f0c06f25d62c9", + }, + { + name: "subdirectory", + files: []createFileDefinition{ + { + Path: turbopath.AnchoredSystemPath("parent"), + FileMode: 0 | os.ModeDir | 0755, + }, + { + Path: turbopath.AnchoredSystemPath("parent/child"), + FileMode: 0 | 0644, + }, + }, + wantDarwin: "b513eea231daa84245d1d23d99fc398ccf17166ca49754ffbdcc1a3269cd75b7ad176a9c7095ff2481f71dca9fc350189747035f13d53b3a864e4fe35165233f", + wantUnix: "b513eea231daa84245d1d23d99fc398ccf17166ca49754ffbdcc1a3269cd75b7ad176a9c7095ff2481f71dca9fc350189747035f13d53b3a864e4fe35165233f", + wantWindows: "a8c3cba54e4dc214d3b21c3fa284d4032fe317d2f88943159efd5d16f3551ab53fae5c92ebf8acdd1bdb85d1238510b7938772cb11a0daa1b72b5e0f2700b5c7", + }, + { + name: "symlink permissions", + files: []createFileDefinition{ + { + Path: turbopath.AnchoredSystemPath("one"), + Linkname: "two", + FileMode: 0 | os.ModeSymlink | 0644, + }, + }, + wantDarwin: "3ea9d8a4581a0c2ba77557c72447b240c5ac622edcdac570a0bf597c276c2917b4ea73e6c373bbac593a480e396845651fa4b51e049531ff5d44c0adb807c2d9", + wantUnix: "99d953cbe1c0d8545e6f8382208fcefe14bcbefe39872f7b6310da14ac195b9a1b04b6d7b4b56f01a27216176193344a92488f99e124fcd68693f313f7137a1c", + wantWindows: "a4b1dc5c296f8ac4c9124727c1d84d70f72872c7bb4ced6d83ee312889e822baf1eaa72f88e624fb1aac4339d0a1f766ede77eabd2e4524eb26e89f883dc479d", + }, + { + name: "unsupported types error", + files: []createFileDefinition{ + { + Path: turbopath.AnchoredSystemPath("fifo"), + FileMode: 0 | os.ModeNamedPipe | 0644, + }, + }, + wantErr: errUnsupportedFileType, + }, + } + for _, tt := range tests { + getTestFunc := func(compressed bool) func(t *testing.T) { + return func(t *testing.T) { + inputDir := turbopath.AbsoluteSystemPath(t.TempDir()) + archiveDir := turbopath.AbsoluteSystemPath(t.TempDir()) + var archivePath turbopath.AbsoluteSystemPath + if compressed { + archivePath = turbopath.AnchoredSystemPath("out.tar.zst").RestoreAnchor(archiveDir) + } else { + archivePath = turbopath.AnchoredSystemPath("out.tar").RestoreAnchor(archiveDir) + } + + cacheItem, cacheCreateErr := Create(archivePath) + assert.NilError(t, cacheCreateErr, "Cache Create") + + for _, file := range tt.files { + createErr := createEntry(t, inputDir, file) + if createErr != nil { + assert.ErrorIs(t, createErr, tt.wantErr) + assert.NilError(t, cacheItem.Close(), "Close") + return + } + + addFileError := cacheItem.AddFile(inputDir, file.Path) + if addFileError != nil { + assert.ErrorIs(t, addFileError, tt.wantErr) + assert.NilError(t, cacheItem.Close(), "Close") + return + } + } + + assert.NilError(t, cacheItem.Close(), "Cache Close") + + // We only check for repeatability on compressed caches. + if compressed { + openedCacheItem, openedCacheItemErr := Open(archivePath) + assert.NilError(t, openedCacheItemErr, "Cache Open") + + // We actually only need to compare the generated SHA. + // That ensures we got the same output. (Effectively snapshots.) + // This must be called after `Close` because both `tar` and `gzip` have footers. + shaOne, shaOneErr := openedCacheItem.GetSha() + assert.NilError(t, shaOneErr, "GetSha") + snapshot := hex.EncodeToString(shaOne) + + switch runtime.GOOS { + case "darwin": + assert.Equal(t, snapshot, tt.wantDarwin, "Got expected hash.") + case "windows": + assert.Equal(t, snapshot, tt.wantWindows, "Got expected hash.") + default: + assert.Equal(t, snapshot, tt.wantUnix, "Got expected hash.") + } + assert.NilError(t, openedCacheItem.Close(), "Close") + } + } + } + t.Run(tt.name, getTestFunc(false)) + t.Run(tt.name+"zst", getTestFunc(true)) + } +} |
