github.com/replicatedhq/ship@v0.55.0/pkg/lifecycle/render/web/step_test.go (about) 1 package web 2 3 import ( 4 "context" 5 "encoding/base64" 6 "io/ioutil" 7 "net/http" 8 "testing" 9 10 "github.com/jarcoal/httpmock" 11 "github.com/pkg/errors" 12 "github.com/replicatedhq/libyaml" 13 "github.com/replicatedhq/ship/pkg/api" 14 "github.com/replicatedhq/ship/pkg/lifecycle/render/root" 15 "github.com/replicatedhq/ship/pkg/templates" 16 "github.com/replicatedhq/ship/pkg/testing/logger" 17 "github.com/spf13/afero" 18 "github.com/spf13/viper" 19 "github.com/stretchr/testify/require" 20 ) 21 22 type TestWebAsset struct { 23 Name string 24 Asset api.WebAsset 25 ExpectFiles map[string]interface{} 26 ExpectedErr error 27 MockedRespBody string 28 RegisterResponders func() 29 } 30 31 func TestWebStep(t *testing.T) { 32 tests := []TestWebAsset{ 33 { 34 Name: "get", 35 Asset: api.WebAsset{ 36 AssetShared: api.AssetShared{ 37 Dest: "asset.txt", 38 }, 39 URL: "http://foo.bar", 40 }, 41 RegisterResponders: func() { 42 httpmock.RegisterResponder("GET", "http://foo.bar", 43 httpmock.NewStringResponder(200, "hi from foo.bar")) 44 }, 45 ExpectFiles: map[string]interface{}{ 46 "asset.txt": "hi from foo.bar", 47 }, 48 ExpectedErr: nil, 49 }, 50 { 51 Name: "get error", 52 Asset: api.WebAsset{ 53 AssetShared: api.AssetShared{ 54 Dest: "asset.txt", 55 }, 56 URL: "http://foo.bar", 57 }, 58 RegisterResponders: func() { 59 httpmock.RegisterResponder("GET", "http://foo.bar", 60 httpmock.NewStringResponder(500, "NOPE!")) 61 }, 62 ExpectFiles: map[string]interface{}{}, 63 ExpectedErr: errors.New("Get web asset from http://foo.bar: received response with status 500"), 64 }, 65 { 66 Name: "post", 67 Asset: api.WebAsset{ 68 AssetShared: api.AssetShared{ 69 Dest: "asset.txt", 70 }, 71 Body: "stuff to post", 72 Method: "POST", 73 URL: "http://foo.bar", 74 BodyFormat: "text/plain", 75 }, 76 RegisterResponders: func() { 77 httpmock.RegisterResponder("POST", "http://foo.bar", 78 httpmock.NewStringResponder(200, "hi from foo.bar")) 79 }, 80 ExpectFiles: map[string]interface{}{ 81 "asset.txt": "hi from foo.bar", 82 }, 83 ExpectedErr: nil, 84 }, 85 { 86 Name: "post error", 87 Asset: api.WebAsset{ 88 AssetShared: api.AssetShared{ 89 Dest: "asset.txt", 90 }, 91 Body: "stuff to post", 92 Method: "POST", 93 URL: "http://foo.bar", 94 BodyFormat: "text/plain", 95 }, 96 RegisterResponders: func() { 97 httpmock.RegisterResponder("POST", "http://foo.bar", 98 httpmock.NewStringResponder(500, "NOPE!")) 99 }, 100 ExpectFiles: map[string]interface{}{}, 101 ExpectedErr: errors.New("Get web asset from http://foo.bar: received response with status 500"), 102 }, 103 { 104 Name: "headers", 105 Asset: api.WebAsset{ 106 AssetShared: api.AssetShared{ 107 Dest: "asset.txt", 108 }, 109 Body: "some stuff to post", 110 Headers: map[string][]string{ 111 "Authorization": {"my auth"}, 112 }, 113 Method: "POST", 114 URL: "http://foo.bar", 115 BodyFormat: "text/plain", 116 }, 117 RegisterResponders: func() { 118 httpmock.RegisterResponder("POST", "http://foo.bar", 119 func(req *http.Request) (*http.Response, error) { 120 header := req.Header.Get("Authorization") 121 122 if header != "my auth" { 123 return httpmock.NewStringResponse(500, "mock headers != test headers"), nil 124 } 125 126 return httpmock.NewStringResponse(200, "hi from foo.bar"), nil 127 }) 128 }, 129 ExpectFiles: map[string]interface{}{ 130 "asset.txt": "hi from foo.bar", 131 }, 132 ExpectedErr: nil, 133 }, 134 { 135 Name: "headers error", 136 Asset: api.WebAsset{ 137 AssetShared: api.AssetShared{ 138 Dest: "asset.txt", 139 }, 140 Body: "some stuff to post", 141 Headers: map[string][]string{ 142 "Authorization": {"my auth"}, 143 }, 144 Method: "POST", 145 URL: "http://foo.bar", 146 BodyFormat: "text/plain", 147 }, 148 RegisterResponders: func() { 149 httpmock.RegisterResponder("POST", "http://foo.bar", 150 func(req *http.Request) (*http.Response, error) { 151 header := req.Header.Get("Authorization") 152 153 decoded, _ := base64.StdEncoding.DecodeString(header) 154 if string(decoded) != "NOT my auth" { 155 return httpmock.NewStringResponse(500, "mock headers != test headers"), nil 156 } 157 158 return httpmock.NewStringResponse(200, "hi from foo.bar"), nil 159 }) 160 }, 161 ExpectFiles: map[string]interface{}{}, 162 ExpectedErr: errors.New("Get web asset from http://foo.bar: received response with status 500"), 163 }, 164 { 165 Name: "advanced post with headers", 166 Asset: api.WebAsset{ 167 AssetShared: api.AssetShared{ 168 Dest: "asset.txt", 169 }, 170 Body: "some stuff to post", 171 Headers: map[string][]string{ 172 "Authorization": {"my auth"}, 173 }, 174 Method: "POST", 175 URL: "http://foo.bar", 176 BodyFormat: "text/plain", 177 }, 178 RegisterResponders: func() { 179 httpmock.RegisterResponder("POST", "http://foo.bar", 180 func(req *http.Request) (*http.Response, error) { 181 header := req.Header.Get("Authorization") 182 183 decoded, _ := base64.StdEncoding.DecodeString(header) 184 if string(decoded) != "my auth" { 185 return httpmock.NewStringResponse(500, "mock headers != test headers"), nil 186 } 187 188 body, _ := ioutil.ReadAll(req.Body) 189 if string(body) != "some stuff to post" { 190 return httpmock.NewStringResponse(500, "mock body != test body"), nil 191 } 192 193 resp, err := httpmock.NewJsonResponse(200, "some stuff to post") 194 if err != nil { 195 return httpmock.NewStringResponse(500, "NOPE!"), nil 196 } 197 return resp, nil 198 }) 199 }, 200 ExpectFiles: map[string]interface{}{}, 201 ExpectedErr: errors.New("Get web asset from http://foo.bar: received response with status 500"), 202 }, 203 { 204 Name: "illegal dest path", 205 Asset: api.WebAsset{ 206 AssetShared: api.AssetShared{ 207 Dest: "/bin/runc", 208 }, 209 URL: "http://foo.bar", 210 }, 211 RegisterResponders: func() { 212 httpmock.RegisterResponder("GET", "http://foo.bar", 213 httpmock.NewStringResponder(200, "hi from foo.bar")) 214 }, 215 ExpectFiles: map[string]interface{}{}, 216 ExpectedErr: errors.Wrap(errors.New("cannot write to an absolute path: /bin/runc"), "write web asset"), 217 }, 218 { 219 Name: "illegal dest path", 220 Asset: api.WebAsset{ 221 AssetShared: api.AssetShared{ 222 Dest: "../../../bin/runc", 223 }, 224 URL: "http://foo.bar", 225 }, 226 RegisterResponders: func() { 227 httpmock.RegisterResponder("GET", "http://foo.bar", 228 httpmock.NewStringResponder(200, "hi from foo.bar")) 229 }, 230 ExpectFiles: map[string]interface{}{}, 231 ExpectedErr: errors.Wrap(errors.New("cannot write to a path that is a parent of the working dir: ../../../bin/runc"), "write web asset"), 232 }, 233 } 234 235 for _, test := range tests { 236 t.Run(test.Name, func(t *testing.T) { 237 req := require.New(t) 238 239 v := viper.New() 240 testLogger := &logger.TestLogger{T: t} 241 rootFs := root.Fs{ 242 Afero: afero.Afero{Fs: afero.NewMemMapFs()}, 243 RootPath: "", 244 } 245 client := &http.Client{} 246 247 step := &DefaultStep{ 248 Logger: testLogger, 249 Fs: rootFs.Afero, 250 Viper: v, 251 BuilderBuilder: &templates.BuilderBuilder{ 252 Logger: testLogger, 253 Viper: v, 254 }, 255 Client: client, 256 } 257 258 httpmock.Activate() 259 defer httpmock.DeactivateAndReset() 260 test.RegisterResponders() 261 262 err := step.Execute( 263 rootFs, 264 test.Asset, 265 api.ReleaseMetadata{}, 266 map[string]interface{}{}, 267 []libyaml.ConfigGroup{}, 268 )(context.Background()) 269 270 if test.ExpectedErr == nil { 271 req.NoError(err) 272 } else { 273 req.Error(err, "expected error "+test.ExpectedErr.Error()) 274 req.Equal(test.ExpectedErr.Error(), err.Error()) 275 } 276 277 for name, contents := range test.ExpectFiles { 278 actual, err := rootFs.ReadFile(name) 279 req.NoError(err) 280 req.Equal(contents, string(actual)) 281 } 282 283 }) 284 } 285 }