github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/resource/deploy/source_query_test.go (about)

     1  // Copyright 2016-2018, Pulumi Corporation.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package deploy
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  
    21  	pbempty "github.com/golang/protobuf/ptypes/empty"
    22  	"github.com/pulumi/pulumi/sdk/v3/go/common/util/result"
    23  	pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go"
    24  	"github.com/stretchr/testify/assert"
    25  )
    26  
    27  func TestQuerySource_Trivial_Wait(t *testing.T) {
    28  	t.Parallel()
    29  
    30  	// Trivial querySource returns immediately with `Wait()`, even with multiple invocations.
    31  
    32  	// Success case.
    33  	resmon1 := mockQueryResmon{}
    34  	qs1, _ := newTestQuerySource(&resmon1, func(*querySource) result.Result {
    35  		return nil
    36  	})
    37  
    38  	qs1.forkRun()
    39  
    40  	res := qs1.Wait()
    41  	assert.Nil(t, res)
    42  	assert.False(t, resmon1.cancelled)
    43  
    44  	res = qs1.Wait()
    45  	assert.Nil(t, res)
    46  	assert.False(t, resmon1.cancelled)
    47  
    48  	// Failure case.
    49  	resmon2 := mockQueryResmon{}
    50  	qs2, _ := newTestQuerySource(&resmon2, func(*querySource) result.Result {
    51  		return result.Error("failed")
    52  	})
    53  
    54  	qs2.forkRun()
    55  
    56  	res = qs2.Wait()
    57  	assert.False(t, res.IsBail())
    58  	assert.NotNil(t, res.Error())
    59  	assert.False(t, resmon2.cancelled)
    60  
    61  	res = qs2.Wait()
    62  	assert.False(t, res.IsBail())
    63  	assert.NotNil(t, res.Error())
    64  	assert.False(t, resmon2.cancelled)
    65  }
    66  
    67  func TestQuerySource_Async_Wait(t *testing.T) {
    68  	t.Parallel()
    69  
    70  	// `Wait()` executes asynchronously.
    71  
    72  	// Success case.
    73  	//
    74  	//    test blocks until querySource signals execution has started
    75  	// -> querySource blocks until test acknowledges querySource's signal
    76  	// -> test blocks on `Wait()` until querySource completes.
    77  	qs1Start, qs1StartAck := make(chan interface{}), make(chan interface{})
    78  	resmon1 := mockQueryResmon{}
    79  	qs1, _ := newTestQuerySource(&resmon1, func(*querySource) result.Result {
    80  		qs1Start <- struct{}{}
    81  		<-qs1StartAck
    82  		return nil
    83  	})
    84  
    85  	qs1.forkRun()
    86  
    87  	// Wait until querySource starts, then acknowledge starting.
    88  	<-qs1Start
    89  	go func() {
    90  		qs1StartAck <- struct{}{}
    91  	}()
    92  
    93  	// Wait for querySource to complete.
    94  	res := qs1.Wait()
    95  	assert.Nil(t, res)
    96  	assert.False(t, resmon1.cancelled)
    97  
    98  	res = qs1.Wait()
    99  	assert.Nil(t, res)
   100  	assert.False(t, resmon1.cancelled)
   101  
   102  	// Cancellation case.
   103  	//
   104  	//    test blocks until querySource signals execution has started
   105  	// -> querySource blocks until test acknowledges querySource's signal
   106  	// -> test blocks on `Wait()` until querySource completes.
   107  	qs2Start, qs2StartAck := make(chan interface{}), make(chan interface{})
   108  	resmon2 := mockQueryResmon{}
   109  	qs2, cancelQs2 := newTestQuerySource(&resmon2, func(*querySource) result.Result {
   110  		qs2Start <- struct{}{}
   111  		// Block forever.
   112  		<-qs2StartAck
   113  		return nil
   114  	})
   115  
   116  	qs2.forkRun()
   117  
   118  	// Wait until querySource starts, then cancel.
   119  	<-qs2Start
   120  	go func() {
   121  		cancelQs2()
   122  	}()
   123  
   124  	// Wait for querySource to complete.
   125  	res = qs2.Wait()
   126  	assert.Nil(t, res)
   127  	assert.True(t, resmon2.cancelled)
   128  
   129  	res = qs2.Wait()
   130  	assert.Nil(t, res)
   131  	assert.True(t, resmon2.cancelled)
   132  }
   133  
   134  func TestQueryResourceMonitor_UnsupportedOperations(t *testing.T) {
   135  	t.Parallel()
   136  
   137  	rm := &queryResmon{}
   138  
   139  	_, err := rm.ReadResource(context.TODO(), nil)
   140  	assert.Error(t, err)
   141  	assert.Equal(t, "Query mode does not support reading resources", err.Error())
   142  
   143  	_, err = rm.RegisterResource(context.TODO(), nil)
   144  	assert.Error(t, err)
   145  	assert.Equal(t, "Query mode does not support creating, updating, or deleting resources", err.Error())
   146  
   147  	_, err = rm.RegisterResourceOutputs(context.TODO(), nil)
   148  	assert.Error(t, err)
   149  	assert.Equal(t, "Query mode does not support registering resource operations", err.Error())
   150  }
   151  
   152  //
   153  // Test querySoruce constructor.
   154  //
   155  
   156  func newTestQuerySource(mon SourceResourceMonitor,
   157  	runLangPlugin func(*querySource) result.Result) (*querySource, context.CancelFunc) {
   158  
   159  	cancel, cancelFunc := context.WithCancel(context.Background())
   160  
   161  	return &querySource{
   162  		mon:               mon,
   163  		runLangPlugin:     runLangPlugin,
   164  		langPluginFinChan: make(chan result.Result),
   165  		cancel:            cancel,
   166  	}, cancelFunc
   167  }
   168  
   169  //
   170  // Mock resource monitor.
   171  //
   172  
   173  type mockQueryResmon struct {
   174  	cancelled bool
   175  }
   176  
   177  var _ SourceResourceMonitor = (*mockQueryResmon)(nil)
   178  
   179  func (rm *mockQueryResmon) Address() string {
   180  	panic("not implemented")
   181  }
   182  func (rm *mockQueryResmon) Cancel() error {
   183  	rm.cancelled = true
   184  	return nil
   185  }
   186  func (rm *mockQueryResmon) Invoke(ctx context.Context,
   187  	req *pulumirpc.ResourceInvokeRequest) (*pulumirpc.InvokeResponse, error) {
   188  
   189  	panic("not implemented")
   190  }
   191  func (rm *mockQueryResmon) Call(ctx context.Context,
   192  	req *pulumirpc.CallRequest) (*pulumirpc.CallResponse, error) {
   193  
   194  	panic("not implemented")
   195  }
   196  func (rm *mockQueryResmon) ReadResource(ctx context.Context,
   197  	req *pulumirpc.ReadResourceRequest) (*pulumirpc.ReadResourceResponse, error) {
   198  
   199  	panic("not implemented")
   200  }
   201  func (rm *mockQueryResmon) RegisterResource(ctx context.Context,
   202  	req *pulumirpc.RegisterResourceRequest) (*pulumirpc.RegisterResourceResponse, error) {
   203  
   204  	panic("not implemented")
   205  }
   206  func (rm *mockQueryResmon) RegisterResourceOutputs(ctx context.Context,
   207  	req *pulumirpc.RegisterResourceOutputsRequest) (*pbempty.Empty, error) {
   208  
   209  	panic("not implemented")
   210  }