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