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  }