github.com/opentofu/opentofu@v1.7.1/internal/plugin/grpc_provisioner_test.go (about)

     1  // Copyright (c) The OpenTofu Authors
     2  // SPDX-License-Identifier: MPL-2.0
     3  // Copyright (c) 2023 HashiCorp, Inc.
     4  // SPDX-License-Identifier: MPL-2.0
     5  
     6  package plugin
     7  
     8  import (
     9  	"io"
    10  	"testing"
    11  
    12  	"github.com/golang/mock/gomock"
    13  	"github.com/google/go-cmp/cmp"
    14  	"github.com/google/go-cmp/cmp/cmpopts"
    15  	"github.com/opentofu/opentofu/internal/configs/hcl2shim"
    16  	"github.com/opentofu/opentofu/internal/provisioners"
    17  	proto "github.com/opentofu/opentofu/internal/tfplugin5"
    18  	"github.com/zclconf/go-cty/cty"
    19  
    20  	mockproto "github.com/opentofu/opentofu/internal/plugin/mock_proto"
    21  )
    22  
    23  var _ provisioners.Interface = (*GRPCProvisioner)(nil)
    24  
    25  var (
    26  	equateEmpty   = cmpopts.EquateEmpty()
    27  	typeComparer  = cmp.Comparer(cty.Type.Equals)
    28  	valueComparer = cmp.Comparer(cty.Value.RawEquals)
    29  )
    30  
    31  func mockProvisionerClient(t *testing.T) *mockproto.MockProvisionerClient {
    32  	ctrl := gomock.NewController(t)
    33  	client := mockproto.NewMockProvisionerClient(ctrl)
    34  
    35  	// we always need a GetSchema method
    36  	client.EXPECT().GetSchema(
    37  		gomock.Any(),
    38  		gomock.Any(),
    39  	).Return(provisionerProtoSchema(), nil)
    40  
    41  	return client
    42  }
    43  
    44  func provisionerProtoSchema() *proto.GetProvisionerSchema_Response {
    45  	return &proto.GetProvisionerSchema_Response{
    46  		Provisioner: &proto.Schema{
    47  			Block: &proto.Schema_Block{
    48  				Attributes: []*proto.Schema_Attribute{
    49  					{
    50  						Name:     "attr",
    51  						Type:     []byte(`"string"`),
    52  						Required: true,
    53  					},
    54  				},
    55  			},
    56  		},
    57  	}
    58  }
    59  
    60  func TestGRPCProvisioner_GetSchema(t *testing.T) {
    61  	p := &GRPCProvisioner{
    62  		client: mockProvisionerClient(t),
    63  	}
    64  
    65  	resp := p.GetSchema()
    66  	checkDiags(t, resp.Diagnostics)
    67  }
    68  
    69  func TestGRPCProvisioner_ValidateProvisionerConfig(t *testing.T) {
    70  	client := mockProvisionerClient(t)
    71  	p := &GRPCProvisioner{
    72  		client: client,
    73  	}
    74  
    75  	client.EXPECT().ValidateProvisionerConfig(
    76  		gomock.Any(),
    77  		gomock.Any(),
    78  	).Return(&proto.ValidateProvisionerConfig_Response{}, nil)
    79  
    80  	cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{"attr": "value"})
    81  	resp := p.ValidateProvisionerConfig(provisioners.ValidateProvisionerConfigRequest{Config: cfg})
    82  	checkDiags(t, resp.Diagnostics)
    83  }
    84  
    85  func TestGRPCProvisioner_ProvisionResource(t *testing.T) {
    86  	ctrl := gomock.NewController(t)
    87  	client := mockproto.NewMockProvisionerClient(ctrl)
    88  
    89  	// we always need a GetSchema method
    90  	client.EXPECT().GetSchema(
    91  		gomock.Any(),
    92  		gomock.Any(),
    93  	).Return(provisionerProtoSchema(), nil)
    94  
    95  	stream := mockproto.NewMockProvisioner_ProvisionResourceClient(ctrl)
    96  	stream.EXPECT().Recv().Return(&proto.ProvisionResource_Response{
    97  		Output: "provisioned",
    98  	}, io.EOF)
    99  
   100  	client.EXPECT().ProvisionResource(
   101  		gomock.Any(),
   102  		gomock.Any(),
   103  	).Return(stream, nil)
   104  
   105  	p := &GRPCProvisioner{
   106  		client: client,
   107  	}
   108  
   109  	rec := &provisionRecorder{}
   110  
   111  	resp := p.ProvisionResource(provisioners.ProvisionResourceRequest{
   112  		Config: cty.ObjectVal(map[string]cty.Value{
   113  			"attr": cty.StringVal("value"),
   114  		}),
   115  		Connection: cty.EmptyObjectVal,
   116  		UIOutput:   rec,
   117  	})
   118  
   119  	if resp.Diagnostics.HasErrors() {
   120  		t.Fatal(resp.Diagnostics.Err())
   121  	}
   122  
   123  	if len(rec.output) == 0 || rec.output[0] != "provisioned" {
   124  		t.Fatalf("expected %q, got %q", []string{"provisioned"}, rec.output)
   125  	}
   126  }
   127  
   128  type provisionRecorder struct {
   129  	output []string
   130  }
   131  
   132  func (r *provisionRecorder) Output(s string) {
   133  	r.output = append(r.output, s)
   134  }
   135  
   136  func TestGRPCProvisioner_Stop(t *testing.T) {
   137  	ctrl := gomock.NewController(t)
   138  	client := mockproto.NewMockProvisionerClient(ctrl)
   139  	p := &GRPCProvisioner{
   140  		client: client,
   141  	}
   142  
   143  	client.EXPECT().Stop(
   144  		gomock.Any(),
   145  		gomock.Any(),
   146  	).Return(&proto.Stop_Response{}, nil)
   147  
   148  	err := p.Stop()
   149  	if err != nil {
   150  		t.Fatal(err)
   151  	}
   152  }