github.com/jenspinney/cli@v6.42.1-0.20190207184520-7450c600020e+incompatible/command/v7/shared/app_summary_displayer_test.go (about) 1 package shared_test 2 3 import ( 4 "time" 5 6 "code.cloudfoundry.org/cli/actor/v2action" 7 "code.cloudfoundry.org/cli/actor/v7action" 8 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" 9 . "code.cloudfoundry.org/cli/command/v7/shared" 10 "code.cloudfoundry.org/cli/integration/helpers" 11 "code.cloudfoundry.org/cli/types" 12 "code.cloudfoundry.org/cli/util/ui" 13 . "github.com/onsi/ginkgo" 14 . "github.com/onsi/gomega" 15 . "github.com/onsi/gomega/gbytes" 16 ) 17 18 var _ = Describe("app summary displayer", func() { 19 var ( 20 appSummaryDisplayer *AppSummaryDisplayer 21 output *Buffer 22 testUI *ui.UI 23 ) 24 25 BeforeEach(func() { 26 output = NewBuffer() 27 testUI = ui.NewTestUI(nil, output, NewBuffer()) 28 29 appSummaryDisplayer = NewAppSummaryDisplayer(testUI) 30 }) 31 32 Describe("AppDisplay", func() { 33 var ( 34 summary v7action.ApplicationSummary 35 displayStartCommand bool 36 ) 37 38 JustBeforeEach(func() { 39 appSummaryDisplayer.AppDisplay(summary, displayStartCommand) 40 }) 41 42 When("the app has instances", func() { 43 When("the process instances are running", func() { 44 var uptime time.Duration 45 46 BeforeEach(func() { 47 uptime = time.Now().Sub(time.Unix(267321600, 0)) 48 summary = v7action.ApplicationSummary{ 49 Application: v7action.Application{ 50 GUID: "some-app-guid", 51 State: constant.ApplicationStarted, 52 }, 53 ProcessSummaries: v7action.ProcessSummaries{ 54 { 55 Process: v7action.Process{ 56 Type: constant.ProcessTypeWeb, 57 MemoryInMB: types.NullUint64{Value: 32, IsSet: true}, 58 DiskInMB: types.NullUint64{Value: 1024, IsSet: true}, 59 }, 60 InstanceDetails: []v7action.ProcessInstance{ 61 v7action.ProcessInstance{ 62 Index: 0, 63 State: constant.ProcessInstanceRunning, 64 MemoryUsage: 1000000, 65 DiskUsage: 1000000, 66 MemoryQuota: 33554432, 67 DiskQuota: 2000000, 68 Uptime: uptime, 69 Details: "Some Details 1", 70 }, 71 v7action.ProcessInstance{ 72 Index: 1, 73 State: constant.ProcessInstanceRunning, 74 MemoryUsage: 2000000, 75 DiskUsage: 2000000, 76 MemoryQuota: 33554432, 77 DiskQuota: 4000000, 78 Uptime: time.Now().Sub(time.Unix(330480000, 0)), 79 Details: "Some Details 2", 80 }, 81 v7action.ProcessInstance{ 82 Index: 2, 83 State: constant.ProcessInstanceRunning, 84 MemoryUsage: 3000000, 85 DiskUsage: 3000000, 86 MemoryQuota: 33554432, 87 DiskQuota: 6000000, 88 Uptime: time.Now().Sub(time.Unix(1277164800, 0)), 89 }, 90 }, 91 }, 92 { 93 Process: v7action.Process{ 94 Type: "console", 95 MemoryInMB: types.NullUint64{Value: 16, IsSet: true}, 96 DiskInMB: types.NullUint64{Value: 512, IsSet: true}, 97 }, 98 InstanceDetails: []v7action.ProcessInstance{ 99 v7action.ProcessInstance{ 100 Index: 0, 101 State: constant.ProcessInstanceRunning, 102 MemoryUsage: 1000000, 103 DiskUsage: 1000000, 104 MemoryQuota: 33554432, 105 DiskQuota: 8000000, 106 Uptime: time.Now().Sub(time.Unix(167572800, 0)), 107 }, 108 }, 109 }, 110 }, 111 } 112 }) 113 114 It("lists information for each of the processes", func() { 115 processTable := helpers.ParseV3AppProcessTable(output.Contents()) 116 Expect(len(processTable.Processes)).To(Equal(2)) 117 118 webProcessSummary := processTable.Processes[0] 119 Expect(webProcessSummary.Type).To(Equal("web")) 120 Expect(webProcessSummary.InstanceCount).To(Equal("3/3")) 121 Expect(webProcessSummary.MemUsage).To(Equal("32M")) 122 123 Expect(webProcessSummary.Instances[0].Memory).To(Equal("976.6K of 32M")) 124 Expect(webProcessSummary.Instances[0].Since).To(MatchRegexp(`\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z`)) 125 Expect(time.Parse(time.RFC3339, webProcessSummary.Instances[0].Since)).To(BeTemporally("~", time.Now().Add(-uptime), 2*time.Second)) 126 Expect(webProcessSummary.Instances[0].Disk).To(Equal("976.6K of 1.9M")) 127 Expect(webProcessSummary.Instances[0].CPU).To(Equal("0.0%")) 128 Expect(webProcessSummary.Instances[0].Details).To(Equal("Some Details 1")) 129 130 Expect(webProcessSummary.Instances[1].Memory).To(Equal("1.9M of 32M")) 131 Expect(webProcessSummary.Instances[1].Disk).To(Equal("1.9M of 3.8M")) 132 Expect(webProcessSummary.Instances[1].CPU).To(Equal("0.0%")) 133 Expect(webProcessSummary.Instances[1].Details).To(Equal("Some Details 2")) 134 135 Expect(webProcessSummary.Instances[2].Memory).To(Equal("2.9M of 32M")) 136 Expect(webProcessSummary.Instances[2].Disk).To(Equal("2.9M of 5.7M")) 137 Expect(webProcessSummary.Instances[2].CPU).To(Equal("0.0%")) 138 139 consoleProcessSummary := processTable.Processes[1] 140 Expect(consoleProcessSummary.Type).To(Equal("console")) 141 Expect(consoleProcessSummary.InstanceCount).To(Equal("1/1")) 142 Expect(consoleProcessSummary.MemUsage).To(Equal("16M")) 143 144 Expect(consoleProcessSummary.Instances[0].Memory).To(Equal("976.6K of 32M")) 145 Expect(consoleProcessSummary.Instances[0].Disk).To(Equal("976.6K of 7.6M")) 146 Expect(consoleProcessSummary.Instances[0].CPU).To(Equal("0.0%")) 147 }) 148 }) 149 150 When("some processes have > 0 instances and others have 0 instances", func() { 151 BeforeEach(func() { 152 summary = v7action.ApplicationSummary{ 153 Application: v7action.Application{ 154 GUID: "some-app-guid", 155 State: constant.ApplicationStarted, 156 }, 157 ProcessSummaries: v7action.ProcessSummaries{ 158 { 159 Process: v7action.Process{ 160 Type: constant.ProcessTypeWeb, 161 MemoryInMB: types.NullUint64{Value: 32, IsSet: true}, 162 DiskInMB: types.NullUint64{Value: 1024, IsSet: true}, 163 }, 164 InstanceDetails: []v7action.ProcessInstance{ 165 v7action.ProcessInstance{ 166 Index: 0, 167 State: constant.ProcessInstanceRunning, 168 MemoryUsage: 1000000, 169 DiskUsage: 1000000, 170 MemoryQuota: 33554432, 171 DiskQuota: 2000000, 172 Uptime: time.Now().Sub(time.Unix(267321600, 0)), 173 }, 174 }, 175 }, 176 { 177 Process: v7action.Process{ 178 Type: "console", 179 MemoryInMB: types.NullUint64{Value: 16, IsSet: true}, 180 DiskInMB: types.NullUint64{Value: 512, IsSet: true}, 181 }, 182 }, 183 }, 184 } 185 }) 186 187 It("lists instance stats for process types that have > 0 instances", func() { 188 Expect(testUI.Out).To(Say(`type:\s+web`)) 189 Expect(testUI.Out).To(Say(`state\s+since\s+cpu\s+memory\s+disk\s+details`)) 190 }) 191 192 It("does not show the instance stats table for process types with 0 instances", func() { 193 Expect(testUI.Out).To(Say(`type:\s+console`)) 194 Expect(testUI.Out).To(Say("There are no running instances of this process.")) 195 }) 196 }) 197 198 When("all the instances for a process are down (but scaled to > 0 instances)", func() { 199 BeforeEach(func() { 200 summary = v7action.ApplicationSummary{ 201 202 ProcessSummaries: []v7action.ProcessSummary{ 203 { 204 Process: v7action.Process{ 205 Type: constant.ProcessTypeWeb, 206 MemoryInMB: types.NullUint64{Value: 32, IsSet: true}, 207 }, 208 InstanceDetails: []v7action.ProcessInstance{{State: constant.ProcessInstanceDown}}, 209 }, 210 }, 211 } 212 }) 213 214 It("displays the instances table", func() { 215 Expect(testUI.Out).To(Say(`type:\s+web`)) 216 Expect(testUI.Out).To(Say(`state\s+since\s+cpu\s+memory\s+disk\s+details`)) 217 }) 218 }) 219 220 Describe("start command", func() { 221 BeforeEach(func() { 222 summary = v7action.ApplicationSummary{ 223 Application: v7action.Application{ 224 GUID: "some-app-guid", 225 State: constant.ApplicationStarted, 226 }, 227 ProcessSummaries: v7action.ProcessSummaries{ 228 { 229 Process: v7action.Process{ 230 Type: constant.ProcessTypeWeb, 231 Command: *types.NewFilteredString("some-command-1"), 232 }, 233 }, 234 { 235 Process: v7action.Process{ 236 Type: "console", 237 Command: *types.NewFilteredString("some-command-2"), 238 }, 239 }, 240 { 241 Process: v7action.Process{ 242 Type: "random", 243 }, 244 }, 245 }, 246 } 247 }) 248 249 When("displayStartCommand is true", func() { 250 BeforeEach(func() { 251 displayStartCommand = true 252 }) 253 254 It("displays the non-empty start command for each process", func() { 255 Expect(testUI.Out).To(Say(`type:\s+web`)) 256 Expect(testUI.Out).To(Say(`start command:\s+some-command-1`)) 257 258 Expect(testUI.Out).To(Say(`type:\s+console`)) 259 Expect(testUI.Out).To(Say(`start command:\s+some-command-2`)) 260 261 Expect(testUI.Out).To(Say(`type:\s+random`)) 262 Expect(testUI.Out).ToNot(Say("start command:")) 263 }) 264 }) 265 266 When("displayStartCommand is false", func() { 267 BeforeEach(func() { 268 displayStartCommand = false 269 }) 270 271 It("hides the start command", func() { 272 Expect(testUI.Out).ToNot(Say("start command:")) 273 }) 274 }) 275 }) 276 }) 277 278 When("the app has no instances", func() { 279 BeforeEach(func() { 280 summary = v7action.ApplicationSummary{ 281 Application: v7action.Application{ 282 GUID: "some-app-guid", 283 State: constant.ApplicationStarted, 284 }, 285 ProcessSummaries: v7action.ProcessSummaries{ 286 { 287 Process: v7action.Process{ 288 Type: constant.ProcessTypeWeb, 289 MemoryInMB: types.NullUint64{Value: 32, IsSet: true}, 290 DiskInMB: types.NullUint64{Value: 1024, IsSet: true}, 291 }, 292 }, 293 { 294 Process: v7action.Process{ 295 Type: "console", 296 MemoryInMB: types.NullUint64{Value: 16, IsSet: true}, 297 DiskInMB: types.NullUint64{Value: 512, IsSet: true}, 298 }, 299 }, 300 }, 301 } 302 }) 303 304 It("lists information for each of the processes", func() { 305 Expect(testUI.Out).To(Say(`type:\s+web`)) 306 Expect(testUI.Out).To(Say(`instances:\s+0/0`)) 307 Expect(testUI.Out).To(Say(`memory usage:\s+32M`)) 308 Expect(testUI.Out).To(Say("There are no running instances of this process.")) 309 310 Expect(testUI.Out).To(Say(`type:\s+console`)) 311 Expect(testUI.Out).To(Say(`instances:\s+0/0`)) 312 Expect(testUI.Out).To(Say(`memory usage:\s+16M`)) 313 Expect(testUI.Out).To(Say("There are no running instances of this process.")) 314 }) 315 316 It("does not display the instance table", func() { 317 Expect(testUI.Out).NotTo(Say(`state\s+since\s+cpu\s+memory\s+disk\s+details`)) 318 }) 319 }) 320 321 When("the app is stopped", func() { 322 BeforeEach(func() { 323 summary = v7action.ApplicationSummary{ 324 Application: v7action.Application{ 325 GUID: "some-app-guid", 326 State: constant.ApplicationStopped, 327 }, 328 ProcessSummaries: v7action.ProcessSummaries{ 329 { 330 Process: v7action.Process{ 331 Type: constant.ProcessTypeWeb, 332 }, 333 }, 334 { 335 Process: v7action.Process{ 336 Type: "console", 337 }, 338 }, 339 }, 340 } 341 }) 342 343 It("lists information for each of the processes", func() { 344 Expect(testUI.Out).To(Say(`type:\s+web`)) 345 Expect(testUI.Out).To(Say("There are no running instances of this process.")) 346 347 Expect(testUI.Out).To(Say(`type:\s+console`)) 348 Expect(testUI.Out).To(Say("There are no running instances of this process.")) 349 }) 350 351 It("does not display the instance table", func() { 352 Expect(testUI.Out).NotTo(Say(`state\s+since\s+cpu\s+memory\s+disk\s+details`)) 353 }) 354 }) 355 356 Describe("isolation segments", func() { 357 When("the isolation segment name is provided", func() { 358 var isolationSegmentName string 359 BeforeEach(func() { 360 isolationSegmentName = "potato beans" 361 summary.ProcessSummaries = v7action.ProcessSummaries{ 362 v7action.ProcessSummary{ 363 InstanceDetails: []v7action.ProcessInstance{ 364 {IsolationSegment: isolationSegmentName}, 365 }, 366 }, 367 } 368 }) 369 370 It("should output the isolation segment name", func() { 371 Expect(testUI.Out).To(Say(`isolation segment:\s+%s`, isolationSegmentName)) 372 }) 373 }) 374 375 When("the application summary has no isolation segment information", func() { 376 BeforeEach(func() { 377 summary = v7action.ApplicationSummary{ 378 Application: v7action.Application{ 379 GUID: "some-app-guid", 380 State: constant.ApplicationStopped, 381 }, 382 } 383 }) 384 385 It("should not output isolation segment header", func() { 386 Expect(testUI.Out).ToNot(Say("isolation segment:")) 387 }) 388 }) 389 }) 390 391 Describe("last upload time", func() { 392 When("the application has a last uploaded time", func() { 393 var createdTime string 394 395 BeforeEach(func() { 396 createdTime = "2006-01-02T15:04:05-07:00" 397 summary.CurrentDroplet.CreatedAt = createdTime 398 }) 399 400 It("displays the uploaded time", func() { 401 t, err := time.Parse(time.RFC3339, createdTime) 402 Expect(err).To(Not(HaveOccurred())) 403 404 time := t.Local().Format("Mon 02 Jan 15:04:05 MST 2006") 405 Expect(testUI.Out).To(Say(`last uploaded:\s+%s`, time)) 406 }) 407 }) 408 409 When("the application does not have a last uploaded time", func() { 410 BeforeEach(func() { 411 summary.CurrentDroplet.CreatedAt = "" 412 }) 413 414 It("leaves last uploaded blank", func() { 415 Expect(testUI.Out).To(Say(`(?m)last uploaded:\s*\n`)) 416 }) 417 }) 418 }) 419 420 When("the application has routes", func() { 421 BeforeEach(func() { 422 summary.Routes = []v2action.Route{ 423 {Host: "route1", Domain: v2action.Domain{Name: "example.com"}}, 424 {Host: "route2", Domain: v2action.Domain{Name: "example.com"}}, 425 } 426 }) 427 428 It("displays routes", func() { 429 Expect(testUI.Out).To(Say(`routes:\s+%s, %s`, "route1.example.com", "route2.example.com")) 430 }) 431 }) 432 433 When("the application has a stack", func() { 434 BeforeEach(func() { 435 summary.CurrentDroplet.Stack = "some-stack" 436 }) 437 438 It("displays stack", func() { 439 Expect(testUI.Out).To(Say(`stack:\s+some-stack`)) 440 }) 441 }) 442 443 When("the application is a docker app", func() { 444 BeforeEach(func() { 445 summary = v7action.ApplicationSummary{ 446 Application: v7action.Application{ 447 GUID: "some-guid", 448 Name: "some-app", 449 State: constant.ApplicationStarted, 450 LifecycleType: constant.AppLifecycleTypeDocker, 451 }, 452 CurrentDroplet: v7action.Droplet{ 453 Image: "docker/some-image", 454 }, 455 } 456 }) 457 458 It("displays the app information", func() { 459 Expect(testUI.Out).To(Say(`name:\s+some-app`)) 460 Expect(testUI.Out).To(Say(`requested state:\s+started`)) 461 Expect(testUI.Out).To(Say(`routes:\s+\n`)) 462 Expect(testUI.Out).To(Say(`stack:\s+\n`)) 463 Expect(testUI.Out).To(Say(`(?m)docker image:\s+docker/some-image$\n`)) 464 }) 465 }) 466 467 When("the application is a buildpack app", func() { 468 BeforeEach(func() { 469 summary = v7action.ApplicationSummary{ 470 CurrentDroplet: v7action.Droplet{ 471 Stack: "cflinuxfs2", 472 Buildpacks: []v7action.DropletBuildpack{ 473 { 474 Name: "ruby_buildpack", 475 DetectOutput: "some-detect-output", 476 }, 477 { 478 Name: "some-buildpack", 479 DetectOutput: "", 480 }, 481 }, 482 }, 483 } 484 }) 485 486 It("displays stack and buildpacks", func() { 487 Expect(testUI.Out).To(Say(`stack:\s+cflinuxfs2`)) 488 Expect(testUI.Out).To(Say(`buildpacks:\s+some-detect-output, some-buildpack`)) 489 }) 490 }) 491 }) 492 })