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 }