github.com/GoogleCloudPlatform/deploystack@v1.12.8/tui/queue_test.go (about) 1 // Copyright 2023 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package tui 16 17 import ( 18 "path/filepath" 19 "strings" 20 "testing" 21 22 "github.com/GoogleCloudPlatform/deploystack/config" 23 "github.com/charmbracelet/bubbles/spinner" 24 tea "github.com/charmbracelet/bubbletea" 25 "github.com/stretchr/testify/assert" 26 ) 27 28 func getTestQueue(title, subtitle string) Queue { 29 appHeader := newHeader(title, subtitle) 30 stack := config.NewStack() 31 mock := mock{} 32 q := NewQueue(&stack, mock) 33 q.header = appHeader 34 35 return q 36 } 37 38 func TestQueueKeyValue(t *testing.T) { 39 tests := map[string]struct { 40 key string 41 value interface{} 42 }{ 43 "string": { 44 key: "test", 45 value: "alsotest", 46 }, 47 "struct": { 48 key: "test", 49 value: struct{ item string }{item: "test"}, 50 }, 51 } 52 53 for name, tc := range tests { 54 t.Run(name, func(t *testing.T) { 55 q := getTestQueue(appTitle, "test") 56 q.Save(tc.key, tc.value) 57 58 got := q.Get(tc.key) 59 60 if tc.value != got { 61 t.Fatalf("key - want '%s' got '%s'", tc.value, got) 62 } 63 }) 64 } 65 } 66 67 func TestQueueStart(t *testing.T) { 68 firstPage := newPage("firstpage", []component{newTextBlock(explainText)}) 69 secondPage := newPage("secondpage", []component{newTextBlock(explainText)}) 70 71 tests := map[string]struct { 72 models []QueueModel 73 exkey string 74 }{ 75 "single": { 76 models: []QueueModel{&firstPage}, 77 exkey: "firstpage", 78 }, 79 "multiple": { 80 models: []QueueModel{&firstPage, &secondPage}, 81 exkey: "firstpage", 82 }, 83 } 84 85 for name, tc := range tests { 86 t.Run(name, func(t *testing.T) { 87 q := getTestQueue(appTitle, "test") 88 q.add(tc.models...) 89 90 got := q.Start() 91 92 if tc.exkey != got.getKey() { 93 t.Fatalf("key - want '%s' got '%s'", tc.exkey, got.getKey()) 94 } 95 }) 96 } 97 } 98 99 func TestQueueCalculateProgress(t *testing.T) {} 100 101 func TestQueueRemoveModel(t *testing.T) { 102 firstPage := newPage("firstpage", []component{newTextBlock(explainText)}) 103 secondPage := newPage("secondpage", []component{newTextBlock(explainText)}) 104 thirdPage := newPage("thirdpage", []component{newTextBlock(explainText)}) 105 fourthPage := newPage("fourthpage", []component{newTextBlock(explainText)}) 106 107 tests := map[string]struct { 108 models []QueueModel 109 target string 110 want int 111 }{ 112 "one": { 113 models: []QueueModel{&firstPage}, 114 target: "firstpage", 115 want: 0, 116 }, 117 "two": { 118 models: []QueueModel{&firstPage, &secondPage}, 119 target: "firstpage", 120 want: 1, 121 }, 122 "four": { 123 models: []QueueModel{&firstPage, &secondPage, &thirdPage, &fourthPage}, 124 target: "thirdpage", 125 want: 3, 126 }, 127 } 128 129 for name, tc := range tests { 130 t.Run(name, func(t *testing.T) { 131 q := getTestQueue(appTitle, "test") 132 q.add(tc.models...) 133 134 q.removeModel(tc.target) 135 136 got := len(q.models) 137 if tc.want != got { 138 t.Fatalf("want '%d' got '%d'", tc.want, got) 139 } 140 }) 141 } 142 } 143 144 func TestQueueProcess(t *testing.T) { 145 tests := map[string]struct { 146 config string 147 keys []string 148 }{ 149 "basic": { 150 config: "config_basic.yaml", 151 keys: []string{}, 152 }, 153 "complex": { 154 config: "config_complex.yaml", 155 keys: []string{ 156 "project_id", 157 "project_id_2", 158 "project_id" + projNewSuffix, 159 "project_id_2" + projNewSuffix, 160 "project_id" + billNewSuffix, 161 "project_id_2" + billNewSuffix, 162 "billing_account", 163 "gce-use-defaults", 164 "instance-name", 165 "region", 166 "zone", 167 "instance-machine-type-family", 168 "instance-machine-type", 169 "instance-image-project", 170 "instance-image-family", 171 "instance-image", 172 "instance-disksize", 173 "instance-disktype", 174 "instance-webserver", 175 "domain", 176 "domain_email", 177 "domain_phone", 178 "domain_country", 179 "domain_postalcode", 180 "domain_state", 181 "domain_city", 182 "domain_address", 183 "domain_name", 184 "domain_consent", 185 "nodes", 186 }, 187 }, 188 } 189 190 for name, tc := range tests { 191 t.Run(name, func(t *testing.T) { 192 q := getTestQueue(appTitle, "test") 193 testdata := filepath.Join(testFilesDir, "tui/testdata", tc.config) 194 s := readTestFile(testdata) 195 196 config, err := config.NewConfigYAML([]byte(s)) 197 if err != nil { 198 t.Fatalf("could not read in config %s:", err) 199 } 200 q.stack.Config = config 201 202 if err := q.ProcessConfig(); err != nil { 203 t.Fatalf("expected no error, got %s", err) 204 } 205 206 if len(tc.keys) != len(q.models) { 207 t.Logf("Models") 208 for i, v := range q.models { 209 t.Logf("%d:%s", i, v.getKey()) 210 } 211 212 t.Fatalf("count - want '%d' got '%d'", len(tc.keys), len(q.models)) 213 } 214 215 for _, v := range tc.keys { 216 q.removeModel(v) 217 } 218 219 if len(q.models) != 0 { 220 t.Logf("Models remain") 221 for _, v := range q.models { 222 t.Logf("%s", v.getKey()) 223 } 224 225 t.Fatalf("key check - want '%d' got '%d'", 0, len(q.models)) 226 227 } 228 }) 229 } 230 } 231 232 func TestQueueInitialize(t *testing.T) { 233 tests := map[string]struct { 234 keys []string 235 }{ 236 "basic": { 237 keys: []string{ 238 "firstpage", 239 "descpage", 240 "endpage", 241 }, 242 }, 243 } 244 245 for name, tc := range tests { 246 t.Run(name, func(t *testing.T) { 247 q := getTestQueue(appTitle, "test") 248 249 q.InitializeUI() 250 251 if len(tc.keys) != len(q.models) { 252 t.Logf("Models") 253 for i, v := range q.models { 254 t.Logf("%d:%s", i, v.getKey()) 255 } 256 257 t.Fatalf("count - want '%d' got '%d'", len(tc.keys), len(q.models)) 258 } 259 260 for _, v := range tc.keys { 261 q.removeModel(v) 262 } 263 264 if len(q.models) != 0 { 265 t.Logf("Models remain") 266 for _, v := range q.models { 267 t.Logf("%s", v.getKey()) 268 } 269 270 t.Fatalf("key check - want '%d' got '%d'", 0, len(q.models)) 271 272 } 273 }) 274 } 275 } 276 277 func TestQueueCalcPercent(t *testing.T) { 278 279 p1 := newPage("1stpage", []component{newTextBlock(explainText)}) 280 p2 := newPage("2ndpage", []component{newTextBlock(explainText)}) 281 p3 := newPage("3rdpage", []component{newTextBlock(explainText)}) 282 p4 := newPage("4thpage", []component{newTextBlock(explainText)}) 283 tests := map[string]struct { 284 in int 285 want int 286 }{ 287 "50%": { 288 in: 3, 289 want: 50, 290 }, 291 "75%": { 292 in: 4, 293 want: 75, 294 }, 295 } 296 297 for name, tc := range tests { 298 t.Run(name, func(t *testing.T) { 299 q := getTestQueue(appTitle, "test") 300 301 q.InitializeUI() 302 q.insert(&p1, &p2, &p3, &p4) 303 q.current = tc.in 304 305 got := q.calcPercent() 306 307 if tc.want != got { 308 t.Fatalf("want '%d' got '%d'", tc.want, got) 309 } 310 311 }) 312 313 } 314 } 315 316 func TestQueueGoToModel(t *testing.T) { 317 firstPage := newPage("firstpage", []component{newTextBlock("A 1st page")}) 318 secondPage := newPage("secondpage", []component{newTextBlock("A 2nd page")}) 319 thirdPage := newPage("thirdpage", []component{newTextBlock("A 3rd page")}) 320 fourthPage := newPage("fourthpage", []component{newTextBlock("A last page")}) 321 322 tests := map[string]struct { 323 models []QueueModel 324 target string 325 want string 326 wanttype string 327 }{ 328 "one": { 329 models: []QueueModel{&firstPage}, 330 target: "firstpage", 331 want: "A 1st page", 332 wanttype: "nil", 333 }, 334 "two": { 335 models: []QueueModel{&firstPage, &secondPage}, 336 target: "firstpage", 337 want: "A 1st page", 338 wanttype: "nil", 339 }, 340 "four": { 341 models: []QueueModel{&firstPage, &secondPage, &thirdPage, &fourthPage}, 342 target: "thirdpage", 343 want: "A 3rd page", 344 wanttype: "nil", 345 }, 346 347 "quit": { 348 models: []QueueModel{&firstPage, &secondPage, &thirdPage, &fourthPage}, 349 target: "quit", 350 want: "A 3rd page", 351 wanttype: "quitMsg", 352 }, 353 "invalidkey": { 354 models: []QueueModel{&firstPage, &secondPage, &thirdPage, &fourthPage}, 355 target: "aninvalidkey", 356 want: "A 1st page", 357 wanttype: "nil", 358 }, 359 } 360 361 for name, tc := range tests { 362 t.Run(name, func(t *testing.T) { 363 q := getTestQueue(appTitle, "test") 364 q.add(tc.models...) 365 366 got, cmd := q.goToModel(tc.target) 367 368 if tc.wanttype == "nil" && cmd != nil { 369 t.Fatalf("wanted '%s' to be nil got '%+v'", tc.want, cmd) 370 371 if !strings.Contains(got.View(), tc.want) { 372 t.Fatalf("wanted '%s' to be contained in got '%s'", tc.want, got.View()) 373 } 374 } 375 376 if tc.wanttype != "nil" { 377 gotmsg := cmd() 378 wantmsg := tea.Quit() 379 380 if gotmsg != wantmsg { 381 t.Fatalf("wanted '%+v' got '%+v'", wantmsg, gotmsg) 382 } 383 384 } 385 }) 386 } 387 } 388 389 func TestQueueClear(t *testing.T) { 390 firstPage := newPage("firstpage", []component{newTextBlock("A 1st page")}) 391 392 tests := map[string]struct { 393 model page 394 key string 395 value string 396 want string 397 }{ 398 "one": { 399 model: firstPage, 400 key: "firstpage", 401 value: "A value", 402 }, 403 } 404 405 for name, tc := range tests { 406 t.Run(name, func(t *testing.T) { 407 q := getTestQueue(appTitle, "test") 408 q.stack.AddSetting(tc.key, tc.value) 409 tc.model.value = tc.value 410 q.add(&tc.model) 411 412 if q.stack.GetSetting(tc.key) != tc.value { 413 t.Fatalf("stack setting did not happen properly") 414 } 415 416 q.clear(tc.key) 417 418 if q.stack.GetSetting(tc.key) != "" { 419 t.Fatalf("stack clear did not happen properly") 420 } 421 422 if tc.model.value != "" { 423 t.Fatalf("model clear did not happen properly") 424 } 425 }) 426 } 427 } 428 func TestQueueModel(t *testing.T) { 429 tests := map[string]struct { 430 in []interface{} 431 key string 432 want interface{} 433 }{ 434 "basic": { 435 in: []interface{}{ 436 newPage("test", nil), 437 }, 438 key: "test", 439 want: newPage("test", nil), 440 }, 441 "basicwrong": { 442 in: []interface{}{ 443 newPage("test", nil), 444 }, 445 key: "test2", 446 want: nil, 447 }, 448 "multiple": { 449 in: []interface{}{ 450 newPage("test2", nil), 451 newPage("test", nil), 452 newPicker("test", "test", "test3", "", nil), 453 }, 454 key: "test2", 455 want: newPage("test2", nil), 456 }, 457 } 458 459 for name, tc := range tests { 460 t.Run(name, func(t *testing.T) { 461 q := getTestQueue(appTitle, "test") 462 463 for _, v := range tc.in { 464 465 switch qmodel := v.(type) { 466 case page: 467 q.add(&qmodel) 468 469 case picker: 470 qmodel.spinner = spinner.Model{} 471 q.add(&qmodel) 472 } 473 474 } 475 476 switch want := tc.want.(type) { 477 case page: 478 want.queue = &q 479 got := q.Model(tc.key) 480 assert.Equal(t, &want, got) 481 482 case picker: 483 484 want.queue = &q 485 got := q.Model(tc.key).(*picker) 486 want.spinner = spinner.Model{} 487 488 assert.Equal(t, &want, got) 489 case nil: 490 got := q.Model(tc.key) 491 assert.Nil(t, got) 492 } 493 494 }) 495 } 496 } 497 498 func TestQueuePrev(t *testing.T) { 499 tests := map[string]struct { 500 in []interface{} 501 key string 502 want interface{} 503 gotobegin bool 504 }{ 505 "basic": { 506 in: []interface{}{ 507 newPage("test", nil), 508 newPage("test2", nil), 509 newPage("test3", nil), 510 }, 511 want: newPage("test2", nil), 512 }, 513 "basicatzero": { 514 in: []interface{}{ 515 newPage("test", nil), 516 newPage("test2", nil), 517 newPage("test3", nil), 518 }, 519 want: newPage("test", nil), 520 gotobegin: true, 521 }, 522 } 523 524 for name, tc := range tests { 525 t.Run(name, func(t *testing.T) { 526 q := getTestQueue(appTitle, "test") 527 528 for _, v := range tc.in { 529 530 switch qmodel := v.(type) { 531 case page: 532 q.add(&qmodel) 533 case picker: 534 q.add(&qmodel) 535 } 536 537 } 538 539 switch want := tc.want.(type) { 540 case page: 541 want.queue = &q 542 q.Start() 543 q.next() 544 q.next() 545 546 if tc.gotobegin { 547 q.current = 0 548 } 549 550 got, _ := q.prev() 551 552 assert.Equal(t, &want, got) 553 554 case picker: 555 556 case nil: 557 558 } 559 560 }) 561 } 562 }