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