// Copyright 2016-2021, 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 auto import ( "context" "os" "path/filepath" "testing" "time" "github.com/pulumi/pulumi/sdk/v3/go/auto/events" "github.com/pulumi/pulumi/sdk/v3/go/auto/optdestroy" "github.com/pulumi/pulumi/sdk/v3/go/auto/optpreview" "github.com/pulumi/pulumi/sdk/v3/go/auto/optrefresh" "github.com/pulumi/pulumi/sdk/v3/go/auto/optup" ptesting "github.com/pulumi/pulumi/sdk/v3/go/common/testing" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) const testPermalink = "Permalink: https://gotest" func TestGetPermalink(t *testing.T) { t.Parallel() tests := map[string]struct { testee string want string err error }{ "successful parsing": {testee: testPermalink + "\n", want: "https://gotest"}, "failed parsing": {testee: testPermalink, err: ErrParsePermalinkFailed}, } //nolint:paralleltest // false positive because range var isn't used directly in t.Run(name) arg for name, test := range tests { name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() got, err := GetPermalink(test.testee) if err != nil { if test.err == nil || test.err != err { t.Errorf("got '%v', want '%v'", err, test.err) } } if got != test.want { t.Errorf("got '%s', want '%s'", got, test.want) } }) } } func TestUpdatePlans(t *testing.T) { t.Parallel() ctx := context.Background() sName := ptesting.RandomStackName() stackName := FullyQualifiedStackName(pulumiOrg, pName, sName) opts := []LocalWorkspaceOption{ SecretsProvider("passphrase"), EnvVars(map[string]string{ "PULUMI_CONFIG_PASSPHRASE": "password", }), } // initialize s, err := NewStackInlineSource(ctx, stackName, pName, func(ctx *pulumi.Context) error { ctx.Export("exp_static", pulumi.String("foo")) return nil }, opts...) require.NoError(t, err, "failed to initialize stack, err: %v", err) defer func() { // -- pulumi stack rm -- err = s.Workspace().RemoveStack(ctx, s.Name()) assert.Nil(t, err, "failed to remove stack. Resources have leaked.") }() // first load settings for created stack stackConfig, err := s.Workspace().StackSettings(ctx, stackName) require.NoError(t, err) stackConfig.SecretsProvider = "passphrase" assert.NoError(t, s.Workspace().SaveStackSettings(ctx, stackName, stackConfig)) // -- pulumi preview -- tempFile, err := os.CreateTemp("", "update_plan.json") defer os.Remove(tempFile.Name()) _, err = s.Preview(ctx, optpreview.Plan(tempFile.Name())) if err != nil { t.Errorf("preview failed, err: %v", err) t.FailNow() } stat, err := tempFile.Stat() if err != nil { t.Errorf("state failed, err: %v", err) t.FailNow() } if stat.Size() == 0 { t.Errorf("expected update plan size to be non-zero") t.FailNow() } // -- pulumi up -- upResult, err := s.Up(ctx, optup.Plan(tempFile.Name())) if err != nil { t.Errorf("up failed, err: %v", err) t.FailNow() } assert.Equal(t, "update", upResult.Summary.Kind) assert.Equal(t, "succeeded", upResult.Summary.Result) // -- pulumi destroy -- dRes, err := s.Destroy(ctx) if err != nil { t.Errorf("destroy failed, err: %v", err) t.FailNow() } assert.Equal(t, "destroy", dRes.Summary.Kind) assert.Equal(t, "succeeded", dRes.Summary.Result) } func TestAlwaysReadsCompleteLine(t *testing.T) { t.Parallel() tmpDir := t.TempDir() tmpFile := tmpDir + "/test.txt" go func() { f, err := os.Create(tmpFile) require.NoError(t, err) defer f.Close() parts := []string{ `{"stdoutEvent": `, ` {"message": "hello", "color": "blue"}}` + "\n", `{"stdoutEvent": {"message":`, ` "world", "color": "red"}}` + "\n", } for _, part := range parts { _, err = f.WriteString(part) require.NoError(t, err) time.Sleep(200 * time.Millisecond) } }() engineEvents := make(chan events.EngineEvent, 20) watcher, err := watchFile(tmpFile, []chan<- events.EngineEvent{engineEvents}) require.NoError(t, err) defer watcher.Close() event1 := <-engineEvents require.NoError(t, event1.Error) assert.Equal(t, "hello", event1.StdoutEvent.Message) assert.Equal(t, "blue", event1.StdoutEvent.Color) event2 := <-engineEvents require.NoError(t, event2.Error) assert.Equal(t, "world", event2.StdoutEvent.Message) assert.Equal(t, "red", event2.StdoutEvent.Color) } func TestDestroyOptsConfigFile(t *testing.T) { t.Parallel() ctx := context.Background() sName := ptesting.RandomStackName() stackName := FullyQualifiedStackName(pulumiOrg, pName, sName) pDir := filepath.Join(".", "test", "testproj") stack, err := NewStackLocalSource(ctx, stackName, pDir) require.NoError(t, err) args := destroyOptsToCmd( &optdestroy.Options{ ConfigFile: filepath.Join(stack.workspace.WorkDir(), "test.yaml"), }, &stack, ) assert.Contains(t, args, "destroy") configFilePath := filepath.Join(stack.workspace.WorkDir(), "test.yaml") assert.Contains(t, args, "--config-file="+configFilePath) } func TestRefreshOptsConfigFile(t *testing.T) { t.Parallel() ctx := context.Background() sName := ptesting.RandomStackName() stackName := FullyQualifiedStackName(pulumiOrg, pName, sName) pDir := filepath.Join(".", "test", "testproj") stack, err := NewStackLocalSource(ctx, stackName, pDir) require.NoError(t, err) args := refreshOptsToCmd( &optrefresh.Options{ ConfigFile: filepath.Join(stack.workspace.WorkDir(), "test.yaml"), }, &stack, true, ) assert.Contains(t, args, "refresh") configFilePath := filepath.Join(stack.workspace.WorkDir(), "test.yaml") assert.Contains(t, args, "--config-file="+configFilePath) }