// Copyright 2016-2022, Pulumi Corporation. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package workspace import ( "os" "path/filepath" "testing" "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // In the tests below we use temporary directories and then expect DetectProjectAndPath to return a path to // that directory. However DetectProjectAndPath will do symlink resolution, while t.TempDir() normally does // not. This can lead to asserts especially on macos where TmpDir will have returned /var/folders/XX, but // after sym link resolution that is /private/var/folders/XX. func mkTempDir(t *testing.T) string { tmpDir := t.TempDir() result, err := filepath.EvalSymlinks(tmpDir) assert.NoError(t, err) return result } //nolint:paralleltest // Theses test use and change the current working directory func TestDetectProjectAndPath(t *testing.T) { tmpDir := mkTempDir(t) cwd, err := os.Getwd() assert.NoError(t, err) defer func() { err := os.Chdir(cwd); assert.NoError(t, err) }() err = os.Chdir(tmpDir) assert.NoError(t, err) yamlPath := filepath.Join(tmpDir, "Pulumi.yaml") yamlContents := "name: some_project\ndescription: Some project\nruntime: nodejs\n" err = os.WriteFile(yamlPath, []byte(yamlContents), 0o600) assert.NoError(t, err) project, path, err := DetectProjectAndPath() assert.NoError(t, err) assert.Equal(t, yamlPath, path) assert.Equal(t, tokens.PackageName("some_project"), project.Name) assert.Equal(t, "Some project", *project.Description) assert.Equal(t, "nodejs", project.Runtime.name) } //nolint:paralleltest // Theses test use and change the current working directory func TestProjectStackPath(t *testing.T) { expectedPath := func(expectedPath string) func(t *testing.T, projectDir, path string, err error) { return func(t *testing.T, projectDir, path string, err error) { assert.NoError(t, err) assert.Equal(t, filepath.Join(projectDir, expectedPath), path) } } tests := []struct { name string yamlContents string validate func(t *testing.T, projectDir, path string, err error) }{{ "WithoutStackConfigDir", "name: some_project\ndescription: Some project\nruntime: nodejs\n", expectedPath("Pulumi.my_stack.yaml"), }, { "WithStackConfigDir", "name: some_project\ndescription: Some project\nruntime: nodejs\nstackConfigDir: stacks\n", expectedPath(filepath.Join("stacks", "Pulumi.my_stack.yaml")), }, { "WithConfig", "name: some_project\ndescription: Some project\nruntime: nodejs\nconfig: stacks\n", expectedPath(filepath.Join("stacks", "Pulumi.my_stack.yaml")), }, { "WithBoth", "name: some_project\ndescription: Some project\nruntime: nodejs\nconfig: stacksA\nstackConfigDir: stacksB\n", func(t *testing.T, projectDir, path string, err error) { errorMsg := "Should not use both config and stackConfigDir to define the stack directory. " + "Use only stackConfigDir instead." assert.EqualError(t, err, errorMsg) }, }} for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { tmpDir := mkTempDir(t) cwd, err := os.Getwd() assert.NoError(t, err) defer func() { err := os.Chdir(cwd); assert.NoError(t, err) }() err = os.Chdir(tmpDir) assert.NoError(t, err) err = os.WriteFile( filepath.Join(tmpDir, "Pulumi.yaml"), []byte(tt.yamlContents), 0o600) assert.NoError(t, err) _, path, err := DetectProjectStackPath("my_stack") tt.validate(t, tmpDir, path, err) }) } } //nolint:paralleltest // Theses test use and change the current working directory func TestDetectProjectUnreadableParent(t *testing.T) { // Regression test for https://github.com/pulumi/pulumi/issues/12481 tmpDir := mkTempDir(t) cwd, err := os.Getwd() require.NoError(t, err) defer func() { err := os.Chdir(cwd); assert.NoError(t, err) }() // unreadable parent directory parentDir := filepath.Join(tmpDir, "root") err = os.Mkdir(parentDir, 0o300) require.NoError(t, err) // Make it readable so we can clean it up later defer func() { err := os.Chmod(parentDir, 0o700); assert.NoError(t, err) }() // readable current directory currentDir := filepath.Join(parentDir, "current") err = os.Mkdir(currentDir, 0o700) require.NoError(t, err) err = os.Chdir(currentDir) require.NoError(t, err) _, _, err = DetectProjectAndPath() assert.ErrorIs(t, err, ErrProjectNotFound) }