github.com/ablease/cli@v6.37.1-0.20180613014814-3adbb7d7fb19+incompatible/command/v2/app_command_test.go (about) 1 package v2_test 2 3 import ( 4 "errors" 5 "time" 6 7 "code.cloudfoundry.org/bytefmt" 8 "code.cloudfoundry.org/cli/actor/actionerror" 9 "code.cloudfoundry.org/cli/actor/v2action" 10 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv2/constant" 11 "code.cloudfoundry.org/cli/command/commandfakes" 12 . "code.cloudfoundry.org/cli/command/v2" 13 "code.cloudfoundry.org/cli/command/v2/v2fakes" 14 "code.cloudfoundry.org/cli/types" 15 "code.cloudfoundry.org/cli/util/configv3" 16 "code.cloudfoundry.org/cli/util/ui" 17 . "github.com/onsi/ginkgo" 18 . "github.com/onsi/gomega" 19 . "github.com/onsi/gomega/gbytes" 20 ) 21 22 var _ = Describe("App Command", func() { 23 var ( 24 cmd AppCommand 25 testUI *ui.UI 26 fakeConfig *commandfakes.FakeConfig 27 fakeSharedActor *commandfakes.FakeSharedActor 28 fakeActor *v2fakes.FakeAppActor 29 binaryName string 30 executeErr error 31 ) 32 33 BeforeEach(func() { 34 testUI = ui.NewTestUI(nil, NewBuffer(), NewBuffer()) 35 fakeConfig = new(commandfakes.FakeConfig) 36 fakeSharedActor = new(commandfakes.FakeSharedActor) 37 fakeActor = new(v2fakes.FakeAppActor) 38 39 cmd = AppCommand{ 40 UI: testUI, 41 Config: fakeConfig, 42 SharedActor: fakeSharedActor, 43 Actor: fakeActor, 44 } 45 46 cmd.RequiredArgs.AppName = "some-app" 47 48 binaryName = "faceman" 49 fakeConfig.BinaryNameReturns(binaryName) 50 }) 51 52 JustBeforeEach(func() { 53 executeErr = cmd.Execute(nil) 54 }) 55 56 Context("when checking target fails", func() { 57 BeforeEach(func() { 58 fakeSharedActor.CheckTargetReturns(actionerror.NotLoggedInError{BinaryName: binaryName}) 59 }) 60 61 It("returns an error if the check fails", func() { 62 Expect(executeErr).To(MatchError(actionerror.NotLoggedInError{BinaryName: "faceman"})) 63 64 Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1)) 65 checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0) 66 Expect(checkTargetedOrg).To(BeTrue()) 67 Expect(checkTargetedSpace).To(BeTrue()) 68 }) 69 }) 70 71 Context("when the user is logged in, and org and space are targeted", func() { 72 BeforeEach(func() { 73 fakeConfig.HasTargetedOrganizationReturns(true) 74 fakeConfig.TargetedOrganizationReturns(configv3.Organization{Name: "some-org"}) 75 fakeConfig.HasTargetedSpaceReturns(true) 76 fakeConfig.TargetedSpaceReturns(configv3.Space{ 77 GUID: "some-space-guid", 78 Name: "some-space"}) 79 fakeConfig.CurrentUserReturns( 80 configv3.User{Name: "some-user"}, 81 nil) 82 }) 83 84 Context("when getting the current user returns an error", func() { 85 var expectedErr error 86 87 BeforeEach(func() { 88 expectedErr = errors.New("getting current user error") 89 fakeConfig.CurrentUserReturns( 90 configv3.User{}, 91 expectedErr) 92 }) 93 94 It("returns the error", func() { 95 Expect(executeErr).To(MatchError(expectedErr)) 96 }) 97 }) 98 99 It("displays flavor text", func() { 100 Expect(testUI.Out).To(Say("Showing health and status for app some-app in org some-org / space some-space as some-user...")) 101 }) 102 103 Context("when the --guid flag is provided", func() { 104 BeforeEach(func() { 105 cmd.GUID = true 106 }) 107 108 Context("when no errors occur", func() { 109 BeforeEach(func() { 110 fakeActor.GetApplicationByNameAndSpaceReturns( 111 v2action.Application{GUID: "some-guid"}, 112 v2action.Warnings{"warning-1", "warning-2"}, 113 nil) 114 }) 115 116 It("displays the application guid and all warnings", func() { 117 Expect(executeErr).ToNot(HaveOccurred()) 118 119 Expect(testUI.Out).To(Say("some-guid")) 120 Expect(testUI.Err).To(Say("warning-1")) 121 Expect(testUI.Err).To(Say("warning-2")) 122 }) 123 }) 124 125 Context("when an error is encountered getting the app", func() { 126 Context("when the error is translatable", func() { 127 BeforeEach(func() { 128 fakeActor.GetApplicationByNameAndSpaceReturns( 129 v2action.Application{}, 130 v2action.Warnings{"warning-1", "warning-2"}, 131 actionerror.ApplicationNotFoundError{Name: "some-app"}) 132 }) 133 134 It("returns a translatable error and all warnings", func() { 135 Expect(executeErr).To(MatchError(actionerror.ApplicationNotFoundError{Name: "some-app"})) 136 137 Expect(testUI.Err).To(Say("warning-1")) 138 Expect(testUI.Err).To(Say("warning-2")) 139 }) 140 }) 141 142 Context("when the error is not translatable", func() { 143 var expectedErr error 144 145 BeforeEach(func() { 146 expectedErr = errors.New("get app summary error") 147 fakeActor.GetApplicationByNameAndSpaceReturns( 148 v2action.Application{}, 149 v2action.Warnings{"warning-1", "warning-2"}, 150 expectedErr) 151 }) 152 153 It("returns the error and all warnings", func() { 154 Expect(executeErr).To(MatchError(expectedErr)) 155 156 Expect(testUI.Err).To(Say("warning-1")) 157 Expect(testUI.Err).To(Say("warning-2")) 158 }) 159 }) 160 }) 161 }) 162 163 Context("when the --guid flag is not provided", func() { 164 Context("when the app is a buildpack app", func() { 165 Context("when no errors occur", func() { 166 var ( 167 applicationSummary v2action.ApplicationSummary 168 warnings []string 169 ) 170 171 BeforeEach(func() { 172 applicationSummary = v2action.ApplicationSummary{ 173 Application: v2action.Application{ 174 Name: "some-app", 175 GUID: "some-app-guid", 176 Instances: types.NullInt{Value: 3, IsSet: true}, 177 Memory: types.NullByteSizeInMb{IsSet: true, Value: 128}, 178 PackageUpdatedAt: time.Unix(0, 0), 179 DetectedBuildpack: types.FilteredString{IsSet: true, Value: "some-buildpack"}, 180 State: "STARTED", 181 }, 182 IsolationSegment: "some-isolation-segment", 183 Stack: v2action.Stack{ 184 Name: "potatos", 185 }, 186 Routes: []v2action.Route{ 187 { 188 Host: "banana", 189 Domain: v2action.Domain{ 190 Name: "fruit.com", 191 }, 192 Path: "/hi", 193 }, 194 { 195 Domain: v2action.Domain{ 196 Name: "foobar.com", 197 }, 198 Port: types.NullInt{IsSet: true, Value: 13}, 199 }, 200 }, 201 } 202 warnings = []string{"app-summary-warning"} 203 }) 204 205 Context("when the app does not have running instances", func() { 206 BeforeEach(func() { 207 applicationSummary.RunningInstances = []v2action.ApplicationInstanceWithStats{} 208 fakeActor.GetApplicationSummaryByNameAndSpaceReturns(applicationSummary, warnings, nil) 209 }) 210 211 It("displays the app summary, 'no running instances' message, and all warnings", func() { 212 Expect(testUI.Out).To(Say("Showing health and status for app some-app in org some-org / space some-space as some-user...")) 213 Expect(testUI.Out).To(Say("")) 214 215 Expect(testUI.Out).To(Say("name:\\s+some-app")) 216 Expect(testUI.Out).To(Say("requested state:\\s+started")) 217 Expect(testUI.Out).To(Say("instances:\\s+0\\/3")) 218 // Note: in real life, iso segs are tied to *running* instances, so this field 219 // would be blank 220 Expect(testUI.Out).To(Say("isolation segment:\\s+some-isolation-segment")) 221 Expect(testUI.Out).To(Say("usage:\\s+128M x 3 instances")) 222 Expect(testUI.Out).To(Say("routes:\\s+banana.fruit.com/hi, foobar.com:13")) 223 Expect(testUI.Out).To(Say("last uploaded:\\s+\\w{3} [0-3]\\d \\w{3} [0-2]\\d:[0-5]\\d:[0-5]\\d \\w+ \\d{4}")) 224 Expect(testUI.Out).To(Say("stack:\\s+potatos")) 225 Expect(testUI.Out).To(Say("buildpack:\\s+some-buildpack")) 226 Expect(testUI.Out).To(Say("")) 227 Expect(testUI.Out).To(Say("There are no running instances of this app")) 228 229 Expect(testUI.Err).To(Say("app-summary-warning")) 230 }) 231 232 It("should not display the instance table", func() { 233 Expect(testUI.Out).NotTo(Say("state\\s+since\\s+cpu\\s+memory\\s+disk")) 234 }) 235 }) 236 237 Context("when the app has running instances", func() { 238 BeforeEach(func() { 239 applicationSummary.RunningInstances = []v2action.ApplicationInstanceWithStats{ 240 { 241 ID: 0, 242 State: v2action.ApplicationInstanceState(constant.ApplicationInstanceRunning), 243 Since: 1403140717.984577, 244 CPU: 0.73, 245 Disk: 50 * bytefmt.MEGABYTE, 246 DiskQuota: 2048 * bytefmt.MEGABYTE, 247 Memory: 100 * bytefmt.MEGABYTE, 248 MemoryQuota: 128 * bytefmt.MEGABYTE, 249 Details: "info from the backend", 250 }, 251 { 252 ID: 1, 253 State: v2action.ApplicationInstanceState(constant.ApplicationInstanceCrashed), 254 Since: 1403100000.900000, 255 CPU: 0.37, 256 Disk: 50 * bytefmt.MEGABYTE, 257 DiskQuota: 2048 * bytefmt.MEGABYTE, 258 Memory: 100 * bytefmt.MEGABYTE, 259 MemoryQuota: 128 * bytefmt.MEGABYTE, 260 Details: "potato", 261 }, 262 } 263 }) 264 265 Context("when the isolation segment is not empty", func() { 266 BeforeEach(func() { 267 fakeActor.GetApplicationSummaryByNameAndSpaceReturns(applicationSummary, warnings, nil) 268 }) 269 270 It("displays app summary, instance table, and all warnings", func() { 271 Expect(testUI.Out).To(Say("Showing health and status for app some-app in org some-org / space some-space as some-user...")) 272 Expect(testUI.Out).To(Say("")) 273 Expect(testUI.Out).To(Say("name:\\s+some-app")) 274 Expect(testUI.Out).To(Say("requested state:\\s+started")) 275 Expect(testUI.Out).To(Say("instances:\\s+1\\/3")) 276 Expect(testUI.Out).To(Say("isolation segment:\\s+some-isolation-segment")) 277 Expect(testUI.Out).To(Say("usage:\\s+128M x 3 instances")) 278 Expect(testUI.Out).To(Say("routes:\\s+banana.fruit.com/hi, foobar.com:13")) 279 Expect(testUI.Out).To(Say("last uploaded:\\s+\\w{3} [0-3]\\d \\w{3} [0-2]\\d:[0-5]\\d:[0-5]\\d \\w+ \\d{4}")) 280 Expect(testUI.Out).To(Say("stack:\\s+potatos")) 281 Expect(testUI.Out).To(Say("buildpack:\\s+some-buildpack")) 282 Expect(testUI.Out).To(Say("")) 283 Expect(testUI.Out).To(Say("state\\s+since\\s+cpu\\s+memory\\s+disk\\s+details")) 284 Expect(testUI.Out).To(Say(`#0\s+running\s+2014-06-19T01:18:37Z\s+73.0%\s+100M of 128M\s+50M of 2G\s+info from the backend`)) 285 Expect(testUI.Out).To(Say(`#1\s+crashed\s+2014-06-18T14:00:00Z\s+37.0%\s+100M of 128M\s+50M of 2G\s+potato`)) 286 287 Expect(testUI.Err).To(Say("app-summary-warning")) 288 289 Expect(fakeActor.GetApplicationSummaryByNameAndSpaceCallCount()).To(Equal(1)) 290 appName, spaceGUID := fakeActor.GetApplicationSummaryByNameAndSpaceArgsForCall(0) 291 Expect(appName).To(Equal("some-app")) 292 Expect(spaceGUID).To(Equal("some-space-guid")) 293 }) 294 }) 295 296 Context("when the isolation segment is empty", func() { 297 BeforeEach(func() { 298 applicationSummary.IsolationSegment = "" 299 fakeActor.GetApplicationSummaryByNameAndSpaceReturns(applicationSummary, warnings, nil) 300 }) 301 302 It("displays app summary, instance table, and all warnings", func() { 303 Expect(testUI.Out).To(Say("Showing health and status for app some-app in org some-org / space some-space as some-user...")) 304 Expect(testUI.Out).To(Say("")) 305 Expect(testUI.Out).To(Say("name:\\s+some-app")) 306 Expect(testUI.Out).To(Say("requested state:\\s+started")) 307 Expect(testUI.Out).To(Say("instances:\\s+1\\/3")) 308 Expect(testUI.Out).ToNot(Say("isolation segment:\\s+")) 309 Expect(testUI.Out).To(Say("usage:\\s+128M x 3 instances")) 310 Expect(testUI.Out).To(Say("routes:\\s+banana.fruit.com/hi, foobar.com:13")) 311 Expect(testUI.Out).To(Say("last uploaded:\\s+\\w{3} [0-3]\\d \\w{3} [0-2]\\d:[0-5]\\d:[0-5]\\d \\w+ \\d{4}")) 312 Expect(testUI.Out).To(Say("stack:\\s+potatos")) 313 Expect(testUI.Out).To(Say("buildpack:\\s+some-buildpack")) 314 Expect(testUI.Out).To(Say("")) 315 Expect(testUI.Out).To(Say("state\\s+since\\s+cpu\\s+memory\\s+disk\\s+details")) 316 Expect(testUI.Out).To(Say(`#0\s+running\s+2014-06-19T01:18:37Z\s+73.0%\s+100M of 128M\s+50M of 2G\s+info from the backend`)) 317 Expect(testUI.Out).To(Say(`#1\s+crashed\s+2014-06-18T14:00:00Z\s+37.0%\s+100M of 128M\s+50M of 2G\s+potato`)) 318 319 Expect(testUI.Err).To(Say("app-summary-warning")) 320 321 Expect(fakeActor.GetApplicationSummaryByNameAndSpaceCallCount()).To(Equal(1)) 322 appName, spaceGUID := fakeActor.GetApplicationSummaryByNameAndSpaceArgsForCall(0) 323 Expect(appName).To(Equal("some-app")) 324 Expect(spaceGUID).To(Equal("some-space-guid")) 325 }) 326 }) 327 }) 328 }) 329 330 Context("when an error is encountered getting app summary", func() { 331 Context("when the error is not translatable", func() { 332 var expectedErr error 333 334 BeforeEach(func() { 335 expectedErr = errors.New("get app summary error") 336 fakeActor.GetApplicationSummaryByNameAndSpaceReturns( 337 v2action.ApplicationSummary{}, 338 nil, 339 expectedErr) 340 }) 341 342 It("returns the error", func() { 343 Expect(executeErr).To(MatchError(expectedErr)) 344 }) 345 }) 346 347 Context("when the error is translatable", func() { 348 BeforeEach(func() { 349 fakeActor.GetApplicationSummaryByNameAndSpaceReturns( 350 v2action.ApplicationSummary{}, 351 nil, 352 actionerror.ApplicationNotFoundError{Name: "some-app"}) 353 }) 354 355 It("returns a translatable error", func() { 356 Expect(executeErr).To(MatchError(actionerror.ApplicationNotFoundError{Name: "some-app"})) 357 }) 358 }) 359 }) 360 }) 361 362 Context("when the app is a Docker app", func() { 363 var applicationSummary v2action.ApplicationSummary 364 365 BeforeEach(func() { 366 applicationSummary = v2action.ApplicationSummary{ 367 Application: v2action.Application{ 368 Name: "some-app", 369 GUID: "some-app-guid", 370 Instances: types.NullInt{Value: 3, IsSet: true}, 371 Memory: types.NullByteSizeInMb{IsSet: true, Value: 128}, 372 PackageUpdatedAt: time.Unix(0, 0), 373 State: "STARTED", 374 DockerImage: "some-docker-image", 375 }, 376 Stack: v2action.Stack{ 377 Name: "potatos", 378 }, 379 Routes: []v2action.Route{ 380 { 381 Host: "banana", 382 Domain: v2action.Domain{ 383 Name: "fruit.com", 384 }, 385 Path: "/hi", 386 }, 387 { 388 Domain: v2action.Domain{ 389 Name: "foobar.com", 390 }, 391 Port: types.NullInt{IsSet: true, Value: 13}, 392 }, 393 }, 394 } 395 fakeActor.GetApplicationSummaryByNameAndSpaceReturns(applicationSummary, nil, nil) 396 }) 397 398 It("displays the Docker image and does not display buildpack", func() { 399 Expect(testUI.Out).To(Say("name:\\s+some-app")) 400 Expect(testUI.Out).To(Say("docker image:\\s+some-docker-image")) 401 402 b, ok := testUI.Out.(*Buffer) 403 Expect(ok).To(BeTrue()) 404 Expect(string(b.Contents())).ToNot(MatchRegexp("buildpack:")) 405 }) 406 }) 407 }) 408 }) 409 })