// Copyright (c) The OpenTofu Authors // SPDX-License-Identifier: MPL-2.0 // Copyright (c) 2023 HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package cloudplugin1 import ( "bytes" "context" "errors" "fmt" "io" "testing" "github.com/opentofu/opentofu/internal/cloudplugin/cloudproto1" "github.com/opentofu/opentofu/internal/cloudplugin/mock_cloudproto1" "go.uber.org/mock/gomock" ) var mockError = "this is a mock error" func testGRPCloudClient(t *testing.T, ctrl *gomock.Controller, client *mock_cloudproto1.MockCommandService_ExecuteClient, executeError error) *GRPCCloudClient { t.Helper() if client != nil && executeError != nil { t.Fatal("one of client or executeError must be nil") } result := mock_cloudproto1.NewMockCommandServiceClient(ctrl) result.EXPECT().Execute( gomock.Any(), gomock.Any(), gomock.Any(), ).Return(client, executeError) return &GRPCCloudClient{ client: result, context: context.Background(), } } func Test_GRPCCloudClient_ExecuteError(t *testing.T) { ctrl := gomock.NewController(t) gRPCClient := testGRPCloudClient(t, ctrl, nil, errors.New(mockError)) buffer := bytes.Buffer{} exitCode := gRPCClient.Execute([]string{"example"}, io.Discard, &buffer) if exitCode != 1 { t.Fatalf("expected exit %d, got %d", 1, exitCode) } if buffer.String() != mockError { t.Errorf("expected error %q, got %q", mockError, buffer.String()) } } func Test_GRPCCloudClient_Execute_RecvError(t *testing.T) { ctrl := gomock.NewController(t) executeClient := mock_cloudproto1.NewMockCommandService_ExecuteClient(ctrl) executeClient.EXPECT().Recv().Return(nil, errors.New(mockError)) gRPCClient := testGRPCloudClient(t, ctrl, executeClient, nil) buffer := bytes.Buffer{} exitCode := gRPCClient.Execute([]string{"example"}, io.Discard, &buffer) if exitCode != 1 { t.Fatalf("expected exit %d, got %d", 1, exitCode) } mockRecvError := fmt.Sprintf("Failed to receive command response from cloudplugin: %s", mockError) if buffer.String() != mockRecvError { t.Errorf("expected error %q, got %q", mockRecvError, buffer.String()) } } func Test_GRPCCloudClient_Execute_Invalid_Exit(t *testing.T) { ctrl := gomock.NewController(t) executeClient := mock_cloudproto1.NewMockCommandService_ExecuteClient(ctrl) executeClient.EXPECT().Recv().Return( &cloudproto1.CommandResponse{ Data: &cloudproto1.CommandResponse_ExitCode{ ExitCode: 3_000, }, }, nil, ) gRPCClient := testGRPCloudClient(t, ctrl, executeClient, nil) exitCode := gRPCClient.Execute([]string{"example"}, io.Discard, io.Discard) if exitCode != 255 { t.Fatalf("expected exit %q, got %q", 255, exitCode) } } func Test_GRPCCloudClient_Execute(t *testing.T) { ctrl := gomock.NewController(t) executeClient := mock_cloudproto1.NewMockCommandService_ExecuteClient(ctrl) gomock.InOrder( executeClient.EXPECT().Recv().Return( &cloudproto1.CommandResponse{ Data: &cloudproto1.CommandResponse_Stdout{ Stdout: []byte("firstcall\n"), }, }, nil, ), executeClient.EXPECT().Recv().Return( &cloudproto1.CommandResponse{ Data: &cloudproto1.CommandResponse_Stdout{ Stdout: []byte("secondcall\n"), }, }, nil, ), executeClient.EXPECT().Recv().Return( &cloudproto1.CommandResponse{ Data: &cloudproto1.CommandResponse_ExitCode{ ExitCode: 99, }, }, nil, ), ) gRPCClient := testGRPCloudClient(t, ctrl, executeClient, nil) stdoutBuffer := bytes.Buffer{} exitCode := gRPCClient.Execute([]string{"example"}, &stdoutBuffer, io.Discard) if exitCode != 99 { t.Fatalf("expected exit %q, got %q", 99, exitCode) } if stdoutBuffer.String() != "firstcall\nsecondcall\n" { t.Errorf("expected output %q, got %q", "firstcall\nsecondcall\n", stdoutBuffer.String()) } }