aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/cli/internal/cache/cache_fs_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'cli/internal/cache/cache_fs_test.go')
-rw-r--r--cli/internal/cache/cache_fs_test.go253
1 files changed, 253 insertions, 0 deletions
diff --git a/cli/internal/cache/cache_fs_test.go b/cli/internal/cache/cache_fs_test.go
new file mode 100644
index 0000000..614ad86
--- /dev/null
+++ b/cli/internal/cache/cache_fs_test.go
@@ -0,0 +1,253 @@
+package cache
+
+import (
+ "path/filepath"
+ "testing"
+
+ "github.com/vercel/turbo/cli/internal/analytics"
+ "github.com/vercel/turbo/cli/internal/cacheitem"
+ "github.com/vercel/turbo/cli/internal/turbopath"
+ "gotest.tools/v3/assert"
+)
+
+type dummyRecorder struct{}
+
+func (dr *dummyRecorder) LogEvent(payload analytics.EventPayload) {}
+
+func TestPut(t *testing.T) {
+ // Set up a test source and cache directory
+ // The "source" directory simulates a package
+ //
+ // <src>/
+ // b
+ // child/
+ // a
+ // link -> ../b
+ // broken -> missing
+ //
+ // Ensure we end up with a matching directory under a
+ // "cache" directory:
+ //
+ // <dst>/the-hash/<src>/...
+
+ src := turbopath.AbsoluteSystemPath(t.TempDir())
+ childDir := src.UntypedJoin("child")
+ err := childDir.MkdirAll(0775)
+ assert.NilError(t, err, "Mkdir")
+ aPath := childDir.UntypedJoin("a")
+ aFile, err := aPath.Create()
+ assert.NilError(t, err, "Create")
+ _, err = aFile.WriteString("hello")
+ assert.NilError(t, err, "WriteString")
+ assert.NilError(t, aFile.Close(), "Close")
+
+ bPath := src.UntypedJoin("b")
+ bFile, err := bPath.Create()
+ assert.NilError(t, err, "Create")
+ _, err = bFile.WriteString("bFile")
+ assert.NilError(t, err, "WriteString")
+ assert.NilError(t, bFile.Close(), "Close")
+
+ srcLinkPath := childDir.UntypedJoin("link")
+ linkTarget := filepath.FromSlash("../b")
+ assert.NilError(t, srcLinkPath.Symlink(linkTarget), "Symlink")
+
+ srcBrokenLinkPath := childDir.Join("broken")
+ assert.NilError(t, srcBrokenLinkPath.Symlink("missing"), "Symlink")
+ circlePath := childDir.Join("circle")
+ assert.NilError(t, circlePath.Symlink(filepath.FromSlash("../child")), "Symlink")
+
+ files := []turbopath.AnchoredSystemPath{
+ turbopath.AnchoredUnixPath("child/").ToSystemPath(), // childDir
+ turbopath.AnchoredUnixPath("child/a").ToSystemPath(), // aPath,
+ turbopath.AnchoredUnixPath("b").ToSystemPath(), // bPath,
+ turbopath.AnchoredUnixPath("child/link").ToSystemPath(), // srcLinkPath,
+ turbopath.AnchoredUnixPath("child/broken").ToSystemPath(), // srcBrokenLinkPath,
+ turbopath.AnchoredUnixPath("child/circle").ToSystemPath(), // circlePath
+ }
+
+ dst := turbopath.AbsoluteSystemPath(t.TempDir())
+ dr := &dummyRecorder{}
+
+ cache := &fsCache{
+ cacheDirectory: dst,
+ recorder: dr,
+ }
+
+ hash := "the-hash"
+ duration := 0
+ putErr := cache.Put(src, hash, duration, files)
+ assert.NilError(t, putErr, "Put")
+
+ // Verify that we got the files that we're expecting
+ dstCachePath := dst.UntypedJoin(hash)
+
+ // This test checks outputs, so we go ahead and pull things back out.
+ // Attempting to satisfy our beliefs that the change is viable with
+ // as few changes to the tests as possible.
+ cacheItem, openErr := cacheitem.Open(dst.UntypedJoin(hash + ".tar.zst"))
+ assert.NilError(t, openErr, "Open")
+
+ _, restoreErr := cacheItem.Restore(dstCachePath)
+ assert.NilError(t, restoreErr, "Restore")
+
+ dstAPath := dstCachePath.UntypedJoin("child", "a")
+ assertFileMatches(t, aPath, dstAPath)
+
+ dstBPath := dstCachePath.UntypedJoin("b")
+ assertFileMatches(t, bPath, dstBPath)
+
+ dstLinkPath := dstCachePath.UntypedJoin("child", "link")
+ target, err := dstLinkPath.Readlink()
+ assert.NilError(t, err, "Readlink")
+ if target != linkTarget {
+ t.Errorf("Readlink got %v, want %v", target, linkTarget)
+ }
+
+ dstBrokenLinkPath := dstCachePath.UntypedJoin("child", "broken")
+ target, err = dstBrokenLinkPath.Readlink()
+ assert.NilError(t, err, "Readlink")
+ if target != "missing" {
+ t.Errorf("Readlink got %v, want missing", target)
+ }
+
+ dstCirclePath := dstCachePath.UntypedJoin("child", "circle")
+ circleLinkDest, err := dstCirclePath.Readlink()
+ assert.NilError(t, err, "Readlink")
+ expectedCircleLinkDest := filepath.FromSlash("../child")
+ if circleLinkDest != expectedCircleLinkDest {
+ t.Errorf("Cache link got %v, want %v", circleLinkDest, expectedCircleLinkDest)
+ }
+
+ assert.NilError(t, cacheItem.Close(), "Close")
+}
+
+func assertFileMatches(t *testing.T, orig turbopath.AbsoluteSystemPath, copy turbopath.AbsoluteSystemPath) {
+ t.Helper()
+ origBytes, err := orig.ReadFile()
+ assert.NilError(t, err, "ReadFile")
+ copyBytes, err := copy.ReadFile()
+ assert.NilError(t, err, "ReadFile")
+ assert.DeepEqual(t, origBytes, copyBytes)
+ origStat, err := orig.Lstat()
+ assert.NilError(t, err, "Lstat")
+ copyStat, err := copy.Lstat()
+ assert.NilError(t, err, "Lstat")
+ assert.Equal(t, origStat.Mode(), copyStat.Mode())
+}
+
+func TestFetch(t *testing.T) {
+ // Set up a test cache directory and target output directory
+ // The "cacheDir" directory simulates a cached package
+ //
+ // <cacheDir>/
+ // the-hash-meta.json
+ // the-hash/
+ // some-package/
+ // b
+ // child/
+ // a
+ // link -> ../b
+ // broken -> missing
+ // circle -> ../child
+ //
+ // Ensure we end up with a matching directory under a
+ // "some-package" directory:
+ //
+ // "some-package"/...
+
+ cacheDir := turbopath.AbsoluteSystemPath(t.TempDir())
+ hash := "the-hash"
+ src := cacheDir.UntypedJoin(hash, "some-package")
+ err := src.MkdirAll(0775)
+ assert.NilError(t, err, "mkdirAll")
+
+ childDir := src.UntypedJoin("child")
+ err = childDir.MkdirAll(0775)
+ assert.NilError(t, err, "Mkdir")
+ aPath := childDir.UntypedJoin("a")
+ aFile, err := aPath.Create()
+ assert.NilError(t, err, "Create")
+ _, err = aFile.WriteString("hello")
+ assert.NilError(t, err, "WriteString")
+ assert.NilError(t, aFile.Close(), "Close")
+
+ bPath := src.UntypedJoin("b")
+ bFile, err := bPath.Create()
+ assert.NilError(t, err, "Create")
+ _, err = bFile.WriteString("bFile")
+ assert.NilError(t, err, "WriteString")
+ assert.NilError(t, bFile.Close(), "Close")
+
+ srcLinkPath := childDir.UntypedJoin("link")
+ linkTarget := filepath.FromSlash("../b")
+ assert.NilError(t, srcLinkPath.Symlink(linkTarget), "Symlink")
+
+ srcBrokenLinkPath := childDir.UntypedJoin("broken")
+ srcBrokenLinkTarget := turbopath.AnchoredUnixPath("missing").ToSystemPath()
+ assert.NilError(t, srcBrokenLinkPath.Symlink(srcBrokenLinkTarget.ToString()), "Symlink")
+
+ circlePath := childDir.Join("circle")
+ srcCircleLinkTarget := turbopath.AnchoredUnixPath("../child").ToSystemPath()
+ assert.NilError(t, circlePath.Symlink(srcCircleLinkTarget.ToString()), "Symlink")
+
+ metadataPath := cacheDir.UntypedJoin("the-hash-meta.json")
+ err = metadataPath.WriteFile([]byte(`{"hash":"the-hash","duration":0}`), 0777)
+ assert.NilError(t, err, "WriteFile")
+
+ dr := &dummyRecorder{}
+
+ cache := &fsCache{
+ cacheDirectory: cacheDir,
+ recorder: dr,
+ }
+
+ inputFiles := []turbopath.AnchoredSystemPath{
+ turbopath.AnchoredUnixPath("some-package/child/").ToSystemPath(), // childDir
+ turbopath.AnchoredUnixPath("some-package/child/a").ToSystemPath(), // aPath,
+ turbopath.AnchoredUnixPath("some-package/b").ToSystemPath(), // bPath,
+ turbopath.AnchoredUnixPath("some-package/child/link").ToSystemPath(), // srcLinkPath,
+ turbopath.AnchoredUnixPath("some-package/child/broken").ToSystemPath(), // srcBrokenLinkPath,
+ turbopath.AnchoredUnixPath("some-package/child/circle").ToSystemPath(), // circlePath
+ }
+
+ putErr := cache.Put(cacheDir.UntypedJoin(hash), hash, 0, inputFiles)
+ assert.NilError(t, putErr, "Put")
+
+ outputDir := turbopath.AbsoluteSystemPath(t.TempDir())
+ dstOutputPath := "some-package"
+ cacheStatus, files, _, err := cache.Fetch(outputDir, "the-hash", []string{})
+ assert.NilError(t, err, "Fetch")
+ hit := cacheStatus.Local || cacheStatus.Remote
+ if !hit {
+ t.Error("Fetch got false, want true")
+ }
+ if len(files) != len(inputFiles) {
+ t.Errorf("len(files) got %v, want %v", len(files), len(inputFiles))
+ }
+
+ dstAPath := outputDir.UntypedJoin(dstOutputPath, "child", "a")
+ assertFileMatches(t, aPath, dstAPath)
+
+ dstBPath := outputDir.UntypedJoin(dstOutputPath, "b")
+ assertFileMatches(t, bPath, dstBPath)
+
+ dstLinkPath := outputDir.UntypedJoin(dstOutputPath, "child", "link")
+ target, err := dstLinkPath.Readlink()
+ assert.NilError(t, err, "Readlink")
+ if target != linkTarget {
+ t.Errorf("Readlink got %v, want %v", target, linkTarget)
+ }
+
+ // Assert that we restore broken symlinks correctly
+ dstBrokenLinkPath := outputDir.UntypedJoin(dstOutputPath, "child", "broken")
+ target, readlinkErr := dstBrokenLinkPath.Readlink()
+ assert.NilError(t, readlinkErr, "Readlink")
+ assert.Equal(t, target, srcBrokenLinkTarget.ToString())
+
+ // Assert that we restore symlinks to directories correctly
+ dstCirclePath := outputDir.UntypedJoin(dstOutputPath, "child", "circle")
+ circleTarget, circleReadlinkErr := dstCirclePath.Readlink()
+ assert.NilError(t, circleReadlinkErr, "Circle Readlink")
+ assert.Equal(t, circleTarget, srcCircleLinkTarget.ToString())
+}