github.com/swisscom/cloudfoundry-cli@v7.1.0+incompatible/api/cloudcontroller/ccv3/feature_flag_test.go (about) 1 package ccv3_test 2 3 import ( 4 "fmt" 5 "net/http" 6 7 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 8 . "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" 9 . "github.com/onsi/ginkgo" 10 . "github.com/onsi/gomega" 11 . "github.com/onsi/gomega/ghttp" 12 ) 13 14 var _ = Describe("Feature Flags", func() { 15 var client *Client 16 var executeErr error 17 var warnings Warnings 18 var featureFlags []FeatureFlag 19 20 BeforeEach(func() { 21 client, _ = NewTestClient() 22 }) 23 Describe("GetFeatureFlags", func() { 24 25 JustBeforeEach(func() { 26 featureFlags, warnings, executeErr = client.GetFeatureFlags() 27 }) 28 29 When("the cloud controller returns without errors", func() { 30 BeforeEach(func() { 31 response1 := fmt.Sprintf(`{ 32 "pagination": { 33 "next": { 34 "href": "%s/v3/feature_flags?page=2&per_page=2" 35 } 36 }, 37 "resources": [ 38 { 39 "name": "flag1", 40 "enabled": true, 41 "updated_at": "2016-10-17T20:00:42Z", 42 "custom_error_message": null, 43 "links": { 44 "self": { 45 "href": "https://api.example.org/v3/feature_flags/flag1" 46 } 47 } 48 }, 49 { 50 "name": "flag2", 51 "enabled": false, 52 "updated_at": "2016-10-17T20:00:42Z", 53 "custom_error_message": null, 54 "links": { 55 "self": { 56 "href": "https://api.example.org/v3/feature_flags/flag2" 57 } 58 } 59 } 60 ] 61 }`, server.URL()) 62 response2 := `{ 63 "pagination": { 64 "next": null 65 }, 66 "resources": [ 67 { 68 "name": "flag3", 69 "enabled": true, 70 "updated_at": "2016-10-17T20:00:42Z", 71 "custom_error_message": "error message the user sees", 72 "links": { 73 "self": { 74 "href": "https://api.example.org/v3/feature_flags/flag3" 75 } 76 } 77 } 78 ] 79 }` 80 server.AppendHandlers( 81 CombineHandlers( 82 VerifyRequest(http.MethodGet, "/v3/feature_flags"), 83 RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"page1 warning"}}), 84 ), 85 ) 86 server.AppendHandlers( 87 CombineHandlers( 88 VerifyRequest(http.MethodGet, "/v3/feature_flags", "page=2&per_page=2"), 89 RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"page2 warning"}}), 90 ), 91 ) 92 }) 93 94 It("returns feature flags and warnings", func() { 95 Expect(executeErr).NotTo(HaveOccurred()) 96 Expect(warnings).To(ConsistOf("page1 warning", "page2 warning")) 97 Expect(featureFlags).To(ConsistOf( 98 FeatureFlag{ 99 Name: "flag1", 100 Enabled: true, 101 }, 102 FeatureFlag{ 103 Name: "flag2", 104 Enabled: false, 105 }, 106 FeatureFlag{ 107 Name: "flag3", 108 Enabled: true, 109 }, 110 )) 111 }) 112 }) 113 114 When("the cloud controller returns errors and warnings", func() { 115 BeforeEach(func() { 116 errResponse := `{ 117 "errors": [ 118 { 119 "detail": "Feature flag not found", 120 "title": "CF-ResourceNotFound", 121 "code": 10010 122 } 123 ] 124 }` 125 server.AppendHandlers( 126 CombineHandlers( 127 VerifyRequest(http.MethodGet, "/v3/feature_flags"), 128 RespondWith(http.StatusNotFound, errResponse, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 129 ), 130 ) 131 }) 132 133 It("returns a flag not found error", func() { 134 Expect(executeErr).To(MatchError(ccerror.FeatureFlagNotFoundError{})) 135 Expect(warnings).To(Equal(Warnings{"this is a warning"})) 136 }) 137 138 }) 139 140 }) 141 142 Describe("GetFeatureFlag", func() { 143 var ( 144 flagName string 145 flag FeatureFlag 146 warnings Warnings 147 executeError error 148 ) 149 150 JustBeforeEach(func() { 151 flag, warnings, executeError = client.GetFeatureFlag(flagName) 152 }) 153 154 When("The flag exists", func() { 155 BeforeEach(func() { 156 flagName = "flag1" 157 response := `{ 158 "updated_at": "2016-06-08T16:41:39Z", 159 "name": "flag1", 160 "enabled": true, 161 "custom_error_message": "This is wonky", 162 "links": { 163 "self": { "href": "https://example.com/v3/config/feature_flags/flag1" } 164 } 165 }` 166 server.AppendHandlers( 167 CombineHandlers( 168 VerifyRequest(http.MethodGet, fmt.Sprintf("/v3/feature_flags/%s", flagName)), 169 RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 170 ), 171 ) 172 }) 173 174 It("returns the requested flag", func() { 175 Expect(executeError).ToNot(HaveOccurred()) 176 Expect(warnings).To(Equal(Warnings{"this is a warning"})) 177 Expect(flag.Name).To(Equal(flagName)) 178 Expect(flag.Enabled).To(Equal(true)) 179 }) 180 }) 181 When("The flag does not exist", func() { 182 BeforeEach(func() { 183 flagName = "flag1" 184 response := `{ 185 "errors": [ 186 { 187 "detail": "Feature flag not found", 188 "title": "CF-ResourceNotFound", 189 "code": 10010 190 } 191 ] 192 }` 193 194 server.AppendHandlers( 195 CombineHandlers( 196 VerifyRequest(http.MethodGet, fmt.Sprintf("/v3/feature_flags/%s", flagName)), 197 RespondWith(http.StatusNotFound, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 198 ), 199 ) 200 }) 201 202 It("returns a flag not found error", func() { 203 Expect(executeError).To(MatchError(ccerror.FeatureFlagNotFoundError{})) 204 Expect(warnings).To(Equal(Warnings{"this is a warning"})) 205 }) 206 }) 207 When("some other error occurs", func() { 208 BeforeEach(func() { 209 flagName = "flag1" 210 response := `{ 211 "errors": [ 212 { 213 "code": 10008, 214 "detail": "The request is semantically invalid: command presence", 215 "title": "CF-UnprocessableEntity" 216 } 217 ] 218 }` 219 220 server.AppendHandlers( 221 CombineHandlers( 222 VerifyRequest(http.MethodGet, fmt.Sprintf("/v3/feature_flags/%s", flagName)), 223 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 224 ), 225 ) 226 }) 227 228 It("returns the error", func() { 229 Expect(executeError).To(MatchError(ccerror.V3UnexpectedResponseError{ 230 V3ErrorResponse: ccerror.V3ErrorResponse{ 231 Errors: []ccerror.V3Error{ 232 { 233 Code: 10008, 234 Detail: "The request is semantically invalid: command presence", 235 Title: "CF-UnprocessableEntity", 236 }, 237 }, 238 }, 239 ResponseCode: http.StatusTeapot, 240 }, 241 )) 242 Expect(warnings).To(Equal(Warnings{"this is a warning"})) 243 }) 244 }) 245 }) 246 247 Describe("UpdateFeatureFlag", func() { 248 var ( 249 argFlag FeatureFlag 250 expectedBody string 251 ccFlag FeatureFlag 252 warnings Warnings 253 executeError error 254 ) 255 256 BeforeEach(func() { 257 expectedBody = `{"enabled":false}` 258 argFlag = FeatureFlag{ 259 Name: "flag1", 260 Enabled: false, 261 } 262 }) 263 264 JustBeforeEach(func() { 265 ccFlag, warnings, executeError = client.UpdateFeatureFlag(argFlag) 266 }) 267 268 When("The flag exists", func() { 269 BeforeEach(func() { 270 response := `{ 271 "updated_at": "2016-06-08T16:41:39Z", 272 "name": "flag1", 273 "enabled": false, 274 "custom_error_message": "This is wonky", 275 "links": { 276 "self": { "href": "https://example.com/v3/config/feature_flags/flag1" } 277 } 278 }` 279 server.AppendHandlers( 280 CombineHandlers( 281 VerifyRequest(http.MethodPatch, fmt.Sprintf("/v3/feature_flags/%s", argFlag.Name)), 282 VerifyJSON(expectedBody), 283 RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 284 ), 285 ) 286 }) 287 288 It("Successfully updates the flag", func() { 289 Expect(executeError).ToNot(HaveOccurred()) 290 Expect(warnings).To(Equal(Warnings{"this is a warning"})) 291 Expect(ccFlag).To(Equal(argFlag)) 292 }) 293 }) 294 295 When("The flag does not exist", func() { 296 BeforeEach(func() { 297 response := `{ 298 "errors": [ 299 { 300 "detail": "Feature flag not found", 301 "title": "CF-ResourceNotFound", 302 "code": 10010 303 } 304 ] 305 }` 306 server.AppendHandlers( 307 CombineHandlers( 308 VerifyRequest(http.MethodPatch, fmt.Sprintf("/v3/feature_flags/%s", argFlag.Name)), 309 VerifyJSON(expectedBody), 310 RespondWith(http.StatusNotFound, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 311 ), 312 ) 313 }) 314 315 It("returns a flag not found error", func() { 316 Expect(executeError).To(MatchError(ccerror.FeatureFlagNotFoundError{})) 317 Expect(warnings).To(Equal(Warnings{"this is a warning"})) 318 }) 319 }) 320 When("some other error occurs", func() { 321 BeforeEach(func() { 322 response := `{ 323 "errors": [ 324 { 325 "code": 10008, 326 "detail": "The request is semantically invalid: command presence", 327 "title": "CF-UnprocessableEntity" 328 } 329 ] 330 }` 331 332 server.AppendHandlers( 333 CombineHandlers( 334 VerifyRequest(http.MethodPatch, fmt.Sprintf("/v3/feature_flags/%s", argFlag.Name)), 335 VerifyJSON(expectedBody), 336 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 337 ), 338 ) 339 }) 340 341 It("returns the error", func() { 342 Expect(executeError).To(MatchError(ccerror.V3UnexpectedResponseError{ 343 V3ErrorResponse: ccerror.V3ErrorResponse{ 344 Errors: []ccerror.V3Error{ 345 { 346 Code: 10008, 347 Detail: "The request is semantically invalid: command presence", 348 Title: "CF-UnprocessableEntity", 349 }, 350 }, 351 }, 352 ResponseCode: http.StatusTeapot, 353 }, 354 )) 355 Expect(warnings).To(Equal(Warnings{"this is a warning"})) 356 }) 357 }) 358 }) 359 })