// Copyright 2016-2023, 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 deploytest import ( "context" "fmt" "testing" "github.com/blang/semver" "github.com/pulumi/pulumi/sdk/v3/go/common/apitype" "github.com/pulumi/pulumi/sdk/v3/go/common/diag" "github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin" "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go" "github.com/stretchr/testify/assert" "google.golang.org/protobuf/types/known/emptypb" ) func TestNewAnalyzerLoaderWithHost(t *testing.T) { t.Parallel() a := NewAnalyzerLoaderWithHost("pkgA", nil) assert.Equal(t, apitype.PluginKind("analyzer"), a.kind) assert.Equal(t, "pkgA", a.name) assert.Equal(t, semver.Version{}, a.version) assert.Equal(t, false, a.useGRPC) } func TestHostEngine(t *testing.T) { t.Parallel() t.Run("unsupported", func(t *testing.T) { t.Parallel() t.Run("GetRootResource", func(t *testing.T) { t.Parallel() engine := &hostEngine{} req := &pulumirpc.GetRootResourceRequest{} _, err := engine.GetRootResource(context.Background(), req) assert.ErrorContains(t, err, "unsupported") }) t.Run("SetRootResource", func(t *testing.T) { t.Parallel() engine := &hostEngine{} req := &pulumirpc.SetRootResourceRequest{} _, err := engine.SetRootResource(context.Background(), req) assert.ErrorContains(t, err, "unsupported") }) }) t.Run("ok", func(t *testing.T) { t.Parallel() t.Run("Log", func(t *testing.T) { t.Parallel() tests := []struct { name string req *pulumirpc.LogRequest expectedError error expectedOutput *emptypb.Empty }{ { name: "DebugSeverity", req: &pulumirpc.LogRequest{Severity: pulumirpc.LogSeverity_DEBUG}, expectedOutput: &emptypb.Empty{}, }, { name: "InfoSeverity", req: &pulumirpc.LogRequest{Severity: pulumirpc.LogSeverity_INFO}, expectedOutput: &emptypb.Empty{}, }, { name: "WarningSeverity", req: &pulumirpc.LogRequest{Severity: pulumirpc.LogSeverity_INFO}, expectedOutput: &emptypb.Empty{}, }, { name: "ErrorSeverity", req: &pulumirpc.LogRequest{Severity: pulumirpc.LogSeverity_INFO}, expectedOutput: &emptypb.Empty{}, }, { name: "InvalidSeverity", req: &pulumirpc.LogRequest{Severity: 99999}, expectedError: fmt.Errorf("Unrecognized logging severity: %v", 99999), }, } hostEngine := &hostEngine{ sink: &NoopSink{}, statusSink: &NoopSink{}, } for _, ephemeral := range []bool{true, false} { for _, tt := range tests { tt := tt tt.req.Ephemeral = ephemeral t.Run(tt.name, func(t *testing.T) { output, err := hostEngine.Log(context.Background(), tt.req) assert.Equal(t, tt.expectedError, err) assert.Equal(t, tt.expectedOutput, output) }) } } }) }) } func TestPluginHostProvider(t *testing.T) { t.Parallel() t.Run("Could not find plugin", func(t *testing.T) { t.Parallel() expectedVersion := semver.MustParse("1.0.0") host := &pluginHost{} _, err := host.Provider(tokens.Package("pkgA"), &expectedVersion) assert.ErrorContains(t, err, "Could not find plugin for (pkgA, 1.0.0)") }) t.Run("error: plugin host is shutting down", func(t *testing.T) { t.Parallel() t.Run("Provider", func(t *testing.T) { t.Parallel() host := &pluginHost{closed: true} _, err := host.Provider(tokens.Package("pkgA"), &semver.Version{}) assert.ErrorIs(t, err, ErrHostIsClosed) }) t.Run("LanguageRuntime", func(t *testing.T) { t.Parallel() host := &pluginHost{closed: true} programInfo := plugin.NewProgramInfo("/", "/", ".", nil) _, err := host.LanguageRuntime("", programInfo) assert.ErrorIs(t, err, ErrHostIsClosed) }) t.Run("SignalCancellation", func(t *testing.T) { t.Parallel() host := &pluginHost{closed: true} err := host.SignalCancellation() assert.ErrorIs(t, err, ErrHostIsClosed) }) t.Run("Analyzer", func(t *testing.T) { t.Parallel() host := &pluginHost{closed: true} _, err := host.Analyzer("") assert.ErrorIs(t, err, ErrHostIsClosed) }) t.Run("CloseProvider", func(t *testing.T) { t.Parallel() host := &pluginHost{closed: true} err := host.CloseProvider(nil) assert.ErrorIs(t, err, ErrHostIsClosed) }) t.Run("EnsurePlugins", func(t *testing.T) { t.Parallel() host := &pluginHost{closed: true} assert.ErrorIs(t, host.EnsurePlugins(nil, 0), ErrHostIsClosed) }) t.Run("PolicyAnalyzer", func(t *testing.T) { t.Parallel() host := &pluginHost{closed: true} _, err := host.PolicyAnalyzer("", "", nil) assert.ErrorIs(t, err, ErrHostIsClosed) }) }) t.Run("GetRequiredPlugins (language runtime is shutting down)", func(t *testing.T) { t.Parallel() host := &pluginHost{ closed: true, languageRuntime: &languageRuntime{ closed: true, }, } _, err := host.GetRequiredPlugins(plugin.ProgramInfo{}, 0) assert.ErrorIs(t, err, ErrLanguageRuntimeIsClosed) }) t.Run("Close", func(t *testing.T) { t.Parallel() host := &pluginHost{closed: true} assert.NoError(t, host.Close()) // Is idempotent. assert.NoError(t, host.Close()) }) t.Run("Log", func(t *testing.T) { t.Parallel() t.Run("closed", func(t *testing.T) { t.Parallel() t.Run("Log", func(t *testing.T) { t.Parallel() var called bool host := &pluginHost{ closed: true, sink: &NoopSink{ LogfF: func(sev diag.Severity, diag *diag.Diag, args ...interface{}) { called = true }, }, } host.Log(diag.Debug, "", "", 0) assert.False(t, called) }) t.Run("LogStatus", func(t *testing.T) { t.Parallel() var called bool host := &pluginHost{ closed: true, statusSink: &NoopSink{ LogfF: func(sev diag.Severity, diag *diag.Diag, args ...interface{}) { called = true }, }, } host.LogStatus(diag.Debug, "", "", 0) assert.False(t, called) }) }) t.Run("ok", func(t *testing.T) { t.Parallel() t.Run("Log", func(t *testing.T) { t.Parallel() var called bool host := &pluginHost{ sink: &NoopSink{ LogfF: func(sev diag.Severity, diag *diag.Diag, args ...interface{}) { called = true }, }, } host.Log(diag.Debug, "", "", 0) assert.True(t, called) }) t.Run("LogStatus", func(t *testing.T) { t.Parallel() var called bool host := &pluginHost{ statusSink: &NoopSink{ LogfF: func(sev diag.Severity, diag *diag.Diag, args ...interface{}) { called = true }, }, } host.LogStatus(diag.Debug, "", "", 0) assert.True(t, called) }) }) }) }