github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/backend/httpstate/client/client_test.go (about)

     1  // Copyright 2016-2021, 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  package client
    15  
    16  import (
    17  	"compress/gzip"
    18  	"context"
    19  	"encoding/json"
    20  	"net/http"
    21  	"net/http/httptest"
    22  	"testing"
    23  
    24  	"github.com/pulumi/pulumi/sdk/v3/go/common/apitype"
    25  	"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/stretchr/testify/require"
    28  )
    29  
    30  func newMockServer(statusCode int, message string) *httptest.Server {
    31  	return httptest.NewServer(
    32  		http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
    33  			rw.WriteHeader(statusCode)
    34  			_, err := rw.Write([]byte(message))
    35  			if err != nil {
    36  				return
    37  			}
    38  		}))
    39  }
    40  
    41  func newMockServerRequestProcessor(statusCode int, processor func(req *http.Request) string) *httptest.Server {
    42  	return httptest.NewServer(
    43  		http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
    44  			rw.WriteHeader(statusCode)
    45  			_, err := rw.Write([]byte(processor(req)))
    46  			if err != nil {
    47  				return
    48  			}
    49  		}))
    50  }
    51  
    52  func newMockClient(server *httptest.Server) *Client {
    53  	return &Client{
    54  		apiURL:   server.URL,
    55  		apiToken: "",
    56  		apiUser:  "",
    57  		diag:     nil,
    58  		client: &defaultRESTClient{
    59  			client: &defaultHTTPClient{
    60  				client: http.DefaultClient,
    61  			},
    62  		},
    63  	}
    64  }
    65  
    66  func TestAPIErrorResponses(t *testing.T) {
    67  	t.Parallel()
    68  
    69  	t.Run("TestAuthError", func(t *testing.T) {
    70  		t.Parallel()
    71  
    72  		// check 401 error is handled
    73  		unauthorizedServer := newMockServer(401, "401: Unauthorized")
    74  		defer unauthorizedServer.Close()
    75  
    76  		unauthorizedClient := newMockClient(unauthorizedServer)
    77  		_, _, unauthorizedErr := unauthorizedClient.GetCLIVersionInfo(context.Background())
    78  
    79  		assert.Error(t, unauthorizedErr)
    80  		assert.Equal(t, unauthorizedErr.Error(), "this command requires logging in; try running `pulumi login` first")
    81  	})
    82  	t.Run("TestRateLimitError", func(t *testing.T) {
    83  		t.Parallel()
    84  
    85  		// test handling 429: Too Many Requests/rate-limit response
    86  		rateLimitedServer := newMockServer(429, "rate-limit error")
    87  		defer rateLimitedServer.Close()
    88  
    89  		rateLimitedClient := newMockClient(rateLimitedServer)
    90  		_, _, rateLimitErr := rateLimitedClient.GetCLIVersionInfo(context.Background())
    91  
    92  		assert.Error(t, rateLimitErr)
    93  		assert.Equal(t, rateLimitErr.Error(), "pulumi service: request rate-limit exceeded")
    94  	})
    95  	t.Run("TestDefaultError", func(t *testing.T) {
    96  		t.Parallel()
    97  
    98  		// test handling non-standard error message
    99  		defaultErrorServer := newMockServer(418, "I'm a teapot")
   100  		defer defaultErrorServer.Close()
   101  
   102  		defaultErrorClient := newMockClient(defaultErrorServer)
   103  		_, _, defaultErrorErr := defaultErrorClient.GetCLIVersionInfo(context.Background())
   104  
   105  		assert.Error(t, defaultErrorErr)
   106  	})
   107  }
   108  
   109  func TestGzip(t *testing.T) {
   110  	t.Parallel()
   111  
   112  	// test handling non-standard error message
   113  	gzipCheckServer := newMockServerRequestProcessor(200, func(req *http.Request) string {
   114  		assert.Equal(t, req.Header.Get("Content-Encoding"), "gzip")
   115  		return "{}"
   116  	})
   117  	defer gzipCheckServer.Close()
   118  	client := newMockClient(gzipCheckServer)
   119  
   120  	// POST /import
   121  	_, err := client.ImportStackDeployment(context.Background(), StackIdentifier{}, nil)
   122  	assert.NoError(t, err)
   123  
   124  	// PATCH /checkpoint
   125  	err = client.PatchUpdateCheckpoint(context.Background(), UpdateIdentifier{}, nil, "")
   126  	assert.NoError(t, err)
   127  
   128  	// POST /events/batch
   129  	err = client.RecordEngineEvents(context.Background(), UpdateIdentifier{}, apitype.EngineEventBatch{}, "")
   130  	assert.NoError(t, err)
   131  
   132  	// POST /events/batch
   133  	_, err = client.BulkDecryptValue(context.Background(), StackIdentifier{}, nil)
   134  	assert.NoError(t, err)
   135  
   136  }
   137  
   138  func TestPatchUpdateCheckpointVerbatimPreservesIndent(t *testing.T) {
   139  	t.Parallel()
   140  
   141  	deployment := apitype.DeploymentV3{
   142  		Resources: []apitype.ResourceV3{{URN: resource.URN("urn1")}},
   143  	}
   144  
   145  	var indented json.RawMessage
   146  	{
   147  		indented1, err := json.MarshalIndent(deployment, "", "")
   148  		require.NoError(t, err)
   149  		untyped := apitype.UntypedDeployment{
   150  			Version:    3,
   151  			Deployment: indented1,
   152  		}
   153  		indented2, err := json.MarshalIndent(untyped, "", "")
   154  		require.NoError(t, err)
   155  		indented = indented2
   156  	}
   157  
   158  	var request apitype.PatchUpdateVerbatimCheckpointRequest
   159  
   160  	server := newMockServerRequestProcessor(200, func(req *http.Request) string {
   161  
   162  		reader, err := gzip.NewReader(req.Body)
   163  		assert.NoError(t, err)
   164  		defer reader.Close()
   165  
   166  		err = json.NewDecoder(reader).Decode(&request)
   167  		assert.NoError(t, err)
   168  
   169  		return "{}"
   170  	})
   171  
   172  	client := newMockClient(server)
   173  
   174  	sequenceNumber := 1
   175  
   176  	err := client.PatchUpdateCheckpointVerbatim(context.Background(),
   177  		UpdateIdentifier{}, sequenceNumber, indented, "token")
   178  	assert.NoError(t, err)
   179  
   180  	assert.Equal(t, string(indented), string(request.UntypedDeployment))
   181  }