package packagemanager import ( "reflect" "testing" "github.com/vercel/turbo/cli/internal/turbopath" "gotest.tools/v3/assert" ) func TestInferRoot(t *testing.T) { type file struct { path turbopath.AnchoredSystemPath content []byte } tests := []struct { name string fs []file executionDirectory turbopath.AnchoredSystemPath rootPath turbopath.AnchoredSystemPath packageMode PackageType }{ // Scenario 0 { name: "turbo.json at current dir, no package.json", fs: []file{ {path: turbopath.AnchoredUnixPath("turbo.json").ToSystemPath()}, }, executionDirectory: turbopath.AnchoredUnixPath("").ToSystemPath(), rootPath: turbopath.AnchoredUnixPath("").ToSystemPath(), packageMode: Multi, }, { name: "turbo.json at parent dir, no package.json", fs: []file{ {path: turbopath.AnchoredUnixPath("execution/path/subdir/.file").ToSystemPath()}, {path: turbopath.AnchoredUnixPath("turbo.json").ToSystemPath()}, }, executionDirectory: turbopath.AnchoredUnixPath("execution/path/subdir").ToSystemPath(), // This is "no inference" rootPath: turbopath.AnchoredUnixPath("execution/path/subdir").ToSystemPath(), packageMode: Multi, }, // Scenario 1A { name: "turbo.json at current dir, has package.json, has workspaces key", fs: []file{ {path: turbopath.AnchoredUnixPath("turbo.json").ToSystemPath()}, { path: turbopath.AnchoredUnixPath("package.json").ToSystemPath(), content: []byte("{ \"workspaces\": [ \"exists\" ] }"), }, }, executionDirectory: turbopath.AnchoredUnixPath("").ToSystemPath(), rootPath: turbopath.AnchoredUnixPath("").ToSystemPath(), packageMode: Multi, }, { name: "turbo.json at parent dir, has package.json, has workspaces key", fs: []file{ {path: turbopath.AnchoredUnixPath("execution/path/subdir/.file").ToSystemPath()}, {path: turbopath.AnchoredUnixPath("turbo.json").ToSystemPath()}, { path: turbopath.AnchoredUnixPath("package.json").ToSystemPath(), content: []byte("{ \"workspaces\": [ \"exists\" ] }"), }, }, executionDirectory: turbopath.AnchoredUnixPath("execution/path/subdir").ToSystemPath(), rootPath: turbopath.AnchoredUnixPath("").ToSystemPath(), packageMode: Multi, }, { name: "turbo.json at parent dir, has package.json, has pnpm workspaces", fs: []file{ {path: turbopath.AnchoredUnixPath("execution/path/subdir/.file").ToSystemPath()}, {path: turbopath.AnchoredUnixPath("turbo.json").ToSystemPath()}, { path: turbopath.AnchoredUnixPath("package.json").ToSystemPath(), content: []byte("{}"), }, { path: turbopath.AnchoredUnixPath("pnpm-workspace.yaml").ToSystemPath(), content: []byte("packages:\n - docs"), }, }, executionDirectory: turbopath.AnchoredUnixPath("execution/path/subdir").ToSystemPath(), rootPath: turbopath.AnchoredUnixPath("").ToSystemPath(), packageMode: Multi, }, // Scenario 1A aware of the weird thing we do for packages. { name: "turbo.json at current dir, has package.json, has packages key", fs: []file{ {path: turbopath.AnchoredUnixPath("turbo.json").ToSystemPath()}, { path: turbopath.AnchoredUnixPath("package.json").ToSystemPath(), content: []byte("{ \"packages\": [ \"exists\" ] }"), }, }, executionDirectory: turbopath.AnchoredUnixPath("").ToSystemPath(), rootPath: turbopath.AnchoredUnixPath("").ToSystemPath(), packageMode: Single, }, { name: "turbo.json at parent dir, has package.json, has packages key", fs: []file{ {path: turbopath.AnchoredUnixPath("execution/path/subdir/.file").ToSystemPath()}, {path: turbopath.AnchoredUnixPath("turbo.json").ToSystemPath()}, { path: turbopath.AnchoredUnixPath("package.json").ToSystemPath(), content: []byte("{ \"packages\": [ \"exists\" ] }"), }, }, executionDirectory: turbopath.AnchoredUnixPath("execution/path/subdir").ToSystemPath(), rootPath: turbopath.AnchoredUnixPath("").ToSystemPath(), packageMode: Single, }, // Scenario 1A aware of the the weird thing we do for packages when both methods of specification exist. { name: "turbo.json at current dir, has package.json, has workspace and packages key", fs: []file{ {path: turbopath.AnchoredUnixPath("turbo.json").ToSystemPath()}, { path: turbopath.AnchoredUnixPath("package.json").ToSystemPath(), content: []byte("{ \"workspaces\": [ \"clobbered\" ], \"packages\": [ \"exists\" ] }"), }, }, executionDirectory: turbopath.AnchoredUnixPath("").ToSystemPath(), rootPath: turbopath.AnchoredUnixPath("").ToSystemPath(), packageMode: Multi, }, { name: "turbo.json at parent dir, has package.json, has workspace and packages key", fs: []file{ {path: turbopath.AnchoredUnixPath("execution/path/subdir/.file").ToSystemPath()}, {path: turbopath.AnchoredUnixPath("turbo.json").ToSystemPath()}, { path: turbopath.AnchoredUnixPath("package.json").ToSystemPath(), content: []byte("{ \"workspaces\": [ \"clobbered\" ], \"packages\": [ \"exists\" ] }"), }, }, executionDirectory: turbopath.AnchoredUnixPath("execution/path/subdir").ToSystemPath(), rootPath: turbopath.AnchoredUnixPath("").ToSystemPath(), packageMode: Multi, }, // Scenario 1B { name: "turbo.json at current dir, has package.json, no workspaces", fs: []file{ {path: turbopath.AnchoredUnixPath("turbo.json").ToSystemPath()}, { path: turbopath.AnchoredUnixPath("package.json").ToSystemPath(), content: []byte("{}"), }, }, executionDirectory: turbopath.AnchoredUnixPath("").ToSystemPath(), rootPath: turbopath.AnchoredUnixPath("").ToSystemPath(), packageMode: Single, }, { name: "turbo.json at parent dir, has package.json, no workspaces", fs: []file{ {path: turbopath.AnchoredUnixPath("execution/path/subdir/.file").ToSystemPath()}, {path: turbopath.AnchoredUnixPath("turbo.json").ToSystemPath()}, { path: turbopath.AnchoredUnixPath("package.json").ToSystemPath(), content: []byte("{}"), }, }, executionDirectory: turbopath.AnchoredUnixPath("execution/path/subdir").ToSystemPath(), rootPath: turbopath.AnchoredUnixPath("").ToSystemPath(), packageMode: Single, }, { name: "turbo.json at parent dir, has package.json, no workspaces, includes pnpm", fs: []file{ {path: turbopath.AnchoredUnixPath("execution/path/subdir/.file").ToSystemPath()}, {path: turbopath.AnchoredUnixPath("turbo.json").ToSystemPath()}, { path: turbopath.AnchoredUnixPath("package.json").ToSystemPath(), content: []byte("{}"), }, { path: turbopath.AnchoredUnixPath("pnpm-workspace.yaml").ToSystemPath(), content: []byte(""), }, }, executionDirectory: turbopath.AnchoredUnixPath("execution/path/subdir").ToSystemPath(), rootPath: turbopath.AnchoredUnixPath("").ToSystemPath(), packageMode: Single, }, // Scenario 2A { name: "no turbo.json, no package.json at current", fs: []file{}, executionDirectory: turbopath.AnchoredUnixPath("").ToSystemPath(), rootPath: turbopath.AnchoredUnixPath("").ToSystemPath(), packageMode: Multi, }, { name: "no turbo.json, no package.json at parent", fs: []file{ {path: turbopath.AnchoredUnixPath("execution/path/subdir/.file").ToSystemPath()}, }, executionDirectory: turbopath.AnchoredUnixPath("execution/path/subdir").ToSystemPath(), rootPath: turbopath.AnchoredUnixPath("execution/path/subdir").ToSystemPath(), packageMode: Multi, }, // Scenario 2B { name: "no turbo.json, has package.json with workspaces at current", fs: []file{ { path: turbopath.AnchoredUnixPath("package.json").ToSystemPath(), content: []byte("{ \"workspaces\": [ \"exists\" ] }"), }, }, executionDirectory: turbopath.AnchoredUnixPath("").ToSystemPath(), rootPath: turbopath.AnchoredUnixPath("").ToSystemPath(), packageMode: Multi, }, { name: "no turbo.json, has package.json with workspaces at parent", fs: []file{ {path: turbopath.AnchoredUnixPath("execution/path/subdir/.file").ToSystemPath()}, { path: turbopath.AnchoredUnixPath("package.json").ToSystemPath(), content: []byte("{ \"workspaces\": [ \"exists\" ] }"), }, }, executionDirectory: turbopath.AnchoredUnixPath("execution/path/subdir").ToSystemPath(), rootPath: turbopath.AnchoredUnixPath("execution/path/subdir").ToSystemPath(), packageMode: Multi, }, { name: "no turbo.json, has package.json with pnpm workspaces at parent", fs: []file{ {path: turbopath.AnchoredUnixPath("execution/path/subdir/.file").ToSystemPath()}, { path: turbopath.AnchoredUnixPath("package.json").ToSystemPath(), content: []byte("{ \"workspaces\": [ \"exists\" ] }"), }, { path: turbopath.AnchoredUnixPath("pnpm-workspace.yaml").ToSystemPath(), content: []byte("packages:\n - docs"), }, }, executionDirectory: turbopath.AnchoredUnixPath("execution/path/subdir").ToSystemPath(), rootPath: turbopath.AnchoredUnixPath("execution/path/subdir").ToSystemPath(), packageMode: Multi, }, // Scenario 3A { name: "no turbo.json, lots of package.json files but no workspaces", fs: []file{ { path: turbopath.AnchoredUnixPath("package.json").ToSystemPath(), content: []byte("{}"), }, { path: turbopath.AnchoredUnixPath("one/package.json").ToSystemPath(), content: []byte("{}"), }, { path: turbopath.AnchoredUnixPath("one/two/package.json").ToSystemPath(), content: []byte("{}"), }, { path: turbopath.AnchoredUnixPath("one/two/three/package.json").ToSystemPath(), content: []byte("{}"), }, }, executionDirectory: turbopath.AnchoredUnixPath("one/two/three").ToSystemPath(), rootPath: turbopath.AnchoredUnixPath("one/two/three").ToSystemPath(), packageMode: Single, }, // Scenario 3BI { name: "no turbo.json, lots of package.json files, and a workspace at the root that matches execution directory", fs: []file{ { path: turbopath.AnchoredUnixPath("package.json").ToSystemPath(), content: []byte("{ \"workspaces\": [ \"one/two/three\" ] }"), }, { path: turbopath.AnchoredUnixPath("one/package.json").ToSystemPath(), content: []byte("{}"), }, { path: turbopath.AnchoredUnixPath("one/two/package.json").ToSystemPath(), content: []byte("{}"), }, { path: turbopath.AnchoredUnixPath("one/two/three/package.json").ToSystemPath(), content: []byte("{}"), }, }, executionDirectory: turbopath.AnchoredUnixPath("one/two/three").ToSystemPath(), rootPath: turbopath.AnchoredUnixPath("one/two/three").ToSystemPath(), packageMode: Multi, }, // Scenario 3BII { name: "no turbo.json, lots of package.json files, and a workspace at the root that matches execution directory", fs: []file{ { path: turbopath.AnchoredUnixPath("package.json").ToSystemPath(), content: []byte("{ \"workspaces\": [ \"does-not-exist\" ] }"), }, { path: turbopath.AnchoredUnixPath("one/package.json").ToSystemPath(), content: []byte("{}"), }, { path: turbopath.AnchoredUnixPath("one/two/package.json").ToSystemPath(), content: []byte("{}"), }, { path: turbopath.AnchoredUnixPath("one/two/three/package.json").ToSystemPath(), content: []byte("{}"), }, }, executionDirectory: turbopath.AnchoredUnixPath("one/two/three").ToSystemPath(), rootPath: turbopath.AnchoredUnixPath("one/two/three").ToSystemPath(), packageMode: Single, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { fsRoot := turbopath.AbsoluteSystemPath(t.TempDir()) for _, file := range tt.fs { path := file.path.RestoreAnchor(fsRoot) assert.NilError(t, path.Dir().MkdirAll(0777)) assert.NilError(t, path.WriteFile(file.content, 0777)) } turboRoot, packageMode := InferRoot(tt.executionDirectory.RestoreAnchor(fsRoot)) if !reflect.DeepEqual(turboRoot, tt.rootPath.RestoreAnchor(fsRoot)) { t.Errorf("InferRoot() turboRoot = %v, want %v", turboRoot, tt.rootPath.RestoreAnchor(fsRoot)) } if packageMode != tt.packageMode { t.Errorf("InferRoot() packageMode = %v, want %v", packageMode, tt.packageMode) } }) } }