github.com/hernad/nomad@v1.6.112/command/ui/writer_ui_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package ui
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"io"
    10  	"testing"
    11  
    12  	"github.com/hernad/nomad/ci"
    13  	"github.com/mitchellh/cli"
    14  	"github.com/shoenig/test/must"
    15  )
    16  
    17  func TestWriterUI_Implements(t *testing.T) {
    18  	ci.Parallel(t)
    19  	var _ cli.Ui = new(WriterUI)
    20  }
    21  
    22  type writerUITestCase struct {
    23  	name   string                  // the testcase name
    24  	baseUi cli.Ui                  // cli.Ui with accessible writers (currently basicUi or mockUi)
    25  	ui     cli.Ui                  // the full ui object chain (should end in baseUi)
    26  	initFn func(*writerUITestCase) // sets up the struct for the testcase
    27  	ow     *bytes.Buffer           // handle to basicUi's Output writer
    28  	ew     *bytes.Buffer           // handle to basicUi's Error writer
    29  }
    30  
    31  func TestWriterUI_OutputWriter(t *testing.T) {
    32  	ci.Parallel(t)
    33  
    34  	tcs := []writerUITestCase{
    35  		{
    36  			name: "mockUi/simple",
    37  			initFn: func(tc *writerUITestCase) {
    38  				tc.baseUi = cli.NewMockUi()
    39  				tc.ui = tc.baseUi
    40  			},
    41  		},
    42  		{
    43  			name: "mockUi/nested_once",
    44  			initFn: func(tc *writerUITestCase) {
    45  				tc.baseUi = cli.NewMockUi()
    46  				tc.ui = &cli.ColoredUi{Ui: tc.baseUi}
    47  			},
    48  		},
    49  		{
    50  			name: "mockUi/nested_twice",
    51  			initFn: func(tc *writerUITestCase) {
    52  				tc.baseUi = cli.NewMockUi()
    53  				tc.ui = &cli.ColoredUi{Ui: &cli.ColoredUi{Ui: tc.baseUi}}
    54  			},
    55  		},
    56  		{
    57  			name: "basicUi/simple",
    58  			initFn: func(tc *writerUITestCase) {
    59  				tc.ow = new(bytes.Buffer)
    60  				tc.ew = new(bytes.Buffer)
    61  				tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
    62  				tc.ui = tc.baseUi
    63  			},
    64  		},
    65  		{
    66  			name: "basicUi/nested_once",
    67  			initFn: func(tc *writerUITestCase) {
    68  				tc.ow = new(bytes.Buffer)
    69  				tc.ew = new(bytes.Buffer)
    70  				tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
    71  				tc.ui = &cli.ColoredUi{Ui: tc.baseUi}
    72  			},
    73  		},
    74  		{
    75  			name: "basicUi/nested_twice",
    76  			initFn: func(tc *writerUITestCase) {
    77  				tc.ow = new(bytes.Buffer)
    78  				tc.ew = new(bytes.Buffer)
    79  				tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
    80  				tc.ui = &cli.ColoredUi{Ui: &cli.ColoredUi{Ui: tc.baseUi}}
    81  			},
    82  		},
    83  	}
    84  
    85  	for _, tc := range tcs {
    86  		tc := tc
    87  		t.Run(tc.name, func(t *testing.T) {
    88  			ci.Parallel(t)
    89  			tc.initFn(&tc)
    90  
    91  			wUI, err := NewWriterUI(tc.ui)
    92  			must.NoError(t, err)
    93  			fmt.Fprintf(wUI.OutputWriter(), "foobar")
    94  
    95  			switch bui := tc.baseUi.(type) {
    96  			case *cli.MockUi:
    97  				must.Eq(t, "foobar", bui.OutputWriter.String())
    98  				must.Eq(t, "", bui.ErrorWriter.String())
    99  			case *cli.BasicUi:
   100  				must.Eq(t, "foobar", tc.ow.String())
   101  				must.Eq(t, "", tc.ew.String())
   102  			default:
   103  				t.Fatal("invalid base cli.Ui type")
   104  			}
   105  		})
   106  	}
   107  }
   108  
   109  func TestWriterUI_Output(t *testing.T) {
   110  	ci.Parallel(t)
   111  
   112  	tcs := []writerUITestCase{
   113  		{
   114  			name: "mockUi/simple",
   115  			initFn: func(tc *writerUITestCase) {
   116  				tc.baseUi = cli.NewMockUi()
   117  				tc.ui = tc.baseUi
   118  			},
   119  		},
   120  		{
   121  			name: "mockUi/nested_once",
   122  			initFn: func(tc *writerUITestCase) {
   123  				tc.baseUi = cli.NewMockUi()
   124  				tc.ui = &cli.ColoredUi{Ui: tc.baseUi}
   125  			},
   126  		},
   127  		{
   128  			name: "mockUi/nested_twice",
   129  			initFn: func(tc *writerUITestCase) {
   130  				tc.baseUi = cli.NewMockUi()
   131  				tc.ui = &cli.ColoredUi{Ui: &cli.ColoredUi{Ui: tc.baseUi}}
   132  			},
   133  		},
   134  		{
   135  			name: "basicUi/simple",
   136  			initFn: func(tc *writerUITestCase) {
   137  				tc.ow = new(bytes.Buffer)
   138  				tc.ew = new(bytes.Buffer)
   139  				tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
   140  				tc.ui = tc.baseUi
   141  			},
   142  		},
   143  		{
   144  			name: "basicUi/nested_once",
   145  			initFn: func(tc *writerUITestCase) {
   146  				tc.ow = new(bytes.Buffer)
   147  				tc.ew = new(bytes.Buffer)
   148  				tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
   149  				tc.ui = &cli.ColoredUi{Ui: tc.baseUi}
   150  			},
   151  		},
   152  		{
   153  			name: "basicUi/nested_twice",
   154  			initFn: func(tc *writerUITestCase) {
   155  				tc.ow = new(bytes.Buffer)
   156  				tc.ew = new(bytes.Buffer)
   157  				tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
   158  				tc.ui = &cli.ColoredUi{Ui: &cli.ColoredUi{Ui: tc.baseUi}}
   159  			},
   160  		},
   161  	}
   162  
   163  	for _, tc := range tcs {
   164  		tc := tc
   165  		t.Run(tc.name, func(t *testing.T) {
   166  			ci.Parallel(t)
   167  			tc.initFn(&tc)
   168  
   169  			wUI, err := NewWriterUI(tc.ui)
   170  			must.NoError(t, err)
   171  			wUI.Output("foobar")
   172  
   173  			var ov, ev string
   174  			switch bui := tc.baseUi.(type) {
   175  			case *cli.MockUi:
   176  				ov = bui.OutputWriter.String()
   177  				ev = bui.ErrorWriter.String()
   178  			case *cli.BasicUi:
   179  				ov = tc.ow.String()
   180  				ev = tc.ew.String()
   181  			default:
   182  				t.Fatal("invalid base cli.Ui type")
   183  			}
   184  
   185  			must.Eq(t, "foobar\n", ov)
   186  			must.Eq(t, "", ev)
   187  		})
   188  	}
   189  }
   190  
   191  func TestWriterUI_Info(t *testing.T) {
   192  	ci.Parallel(t)
   193  
   194  	tcs := []writerUITestCase{
   195  		{
   196  			name: "mockUi/simple",
   197  			initFn: func(tc *writerUITestCase) {
   198  				tc.baseUi = cli.NewMockUi()
   199  				tc.ui = tc.baseUi
   200  			},
   201  		},
   202  		{
   203  			name: "mockUi/nested_once",
   204  			initFn: func(tc *writerUITestCase) {
   205  				tc.baseUi = cli.NewMockUi()
   206  				tc.ui = &cli.ColoredUi{Ui: tc.baseUi}
   207  			},
   208  		},
   209  		{
   210  			name: "mockUi/nested_twice",
   211  			initFn: func(tc *writerUITestCase) {
   212  				tc.baseUi = cli.NewMockUi()
   213  				tc.ui = &cli.ColoredUi{Ui: &cli.ColoredUi{Ui: tc.baseUi}}
   214  			},
   215  		},
   216  		{
   217  			name: "basicUi/simple",
   218  			initFn: func(tc *writerUITestCase) {
   219  				tc.ow = new(bytes.Buffer)
   220  				tc.ew = new(bytes.Buffer)
   221  				tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
   222  				tc.ui = tc.baseUi
   223  			},
   224  		},
   225  		{
   226  			name: "basicUi/nested_once",
   227  			initFn: func(tc *writerUITestCase) {
   228  				tc.ow = new(bytes.Buffer)
   229  				tc.ew = new(bytes.Buffer)
   230  				tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
   231  				tc.ui = &cli.ColoredUi{Ui: tc.baseUi}
   232  			},
   233  		},
   234  		{
   235  			name: "basicUi/nested_twice",
   236  			initFn: func(tc *writerUITestCase) {
   237  				tc.ow = new(bytes.Buffer)
   238  				tc.ew = new(bytes.Buffer)
   239  				tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
   240  				tc.ui = &cli.ColoredUi{Ui: &cli.ColoredUi{Ui: tc.baseUi}}
   241  			},
   242  		},
   243  	}
   244  
   245  	for _, tc := range tcs {
   246  		tc := tc
   247  		t.Run(tc.name, func(t *testing.T) {
   248  			ci.Parallel(t)
   249  			tc.initFn(&tc)
   250  
   251  			wUI, err := NewWriterUI(tc.ui)
   252  			must.NoError(t, err)
   253  			wUI.Info("INFO")
   254  
   255  			var ov, ev string
   256  			switch bui := tc.baseUi.(type) {
   257  			case *cli.MockUi:
   258  				ov = bui.OutputWriter.String()
   259  				ev = bui.ErrorWriter.String()
   260  			case *cli.BasicUi:
   261  				ov = tc.ow.String()
   262  				ev = tc.ew.String()
   263  			default:
   264  				t.Fatal("invalid base cli.Ui type")
   265  			}
   266  
   267  			must.Eq(t, "INFO\n", ov)
   268  			must.Eq(t, "", ev)
   269  		})
   270  	}
   271  }
   272  
   273  func TestWriterUI_Warn(t *testing.T) {
   274  	ci.Parallel(t)
   275  
   276  	tcs := []writerUITestCase{
   277  		{
   278  			name: "mockUi/simple",
   279  			initFn: func(tc *writerUITestCase) {
   280  				tc.baseUi = cli.NewMockUi()
   281  				tc.ui = tc.baseUi
   282  			},
   283  		},
   284  		{
   285  			name: "mockUi/nested_once",
   286  			initFn: func(tc *writerUITestCase) {
   287  				tc.baseUi = cli.NewMockUi()
   288  				tc.ui = &cli.ColoredUi{Ui: tc.baseUi}
   289  			},
   290  		},
   291  		{
   292  			name: "mockUi/nested_twice",
   293  			initFn: func(tc *writerUITestCase) {
   294  				tc.baseUi = cli.NewMockUi()
   295  				tc.ui = &cli.ColoredUi{Ui: &cli.ColoredUi{Ui: tc.baseUi}}
   296  			},
   297  		},
   298  		{
   299  			name: "basicUi/simple",
   300  			initFn: func(tc *writerUITestCase) {
   301  				tc.ow = new(bytes.Buffer)
   302  				tc.ew = new(bytes.Buffer)
   303  				tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
   304  				tc.ui = tc.baseUi
   305  			},
   306  		},
   307  		{
   308  			name: "basicUi/nested_once",
   309  			initFn: func(tc *writerUITestCase) {
   310  				tc.ow = new(bytes.Buffer)
   311  				tc.ew = new(bytes.Buffer)
   312  				tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
   313  				tc.ui = &cli.ColoredUi{Ui: tc.baseUi}
   314  			},
   315  		},
   316  		{
   317  			name: "basicUi/nested_twice",
   318  			initFn: func(tc *writerUITestCase) {
   319  				tc.ow = new(bytes.Buffer)
   320  				tc.ew = new(bytes.Buffer)
   321  				tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
   322  				tc.ui = &cli.ColoredUi{Ui: &cli.ColoredUi{Ui: tc.baseUi}}
   323  			},
   324  		},
   325  	}
   326  
   327  	for _, tc := range tcs {
   328  		tc := tc
   329  		t.Run(tc.name, func(t *testing.T) {
   330  			ci.Parallel(t)
   331  			tc.initFn(&tc)
   332  
   333  			wUI, err := NewWriterUI(tc.ui)
   334  			must.NoError(t, err)
   335  			wUI.Warn("WARN")
   336  
   337  			const expected = "WARN\n"
   338  
   339  			var ov, ev string
   340  			switch bui := tc.baseUi.(type) {
   341  			case *cli.MockUi:
   342  				ov = bui.OutputWriter.String()
   343  				ev = bui.ErrorWriter.String()
   344  			case *cli.BasicUi:
   345  				ov = tc.ow.String()
   346  				ev = tc.ew.String()
   347  			default:
   348  				t.Fatal("invalid base cli.Ui type")
   349  			}
   350  
   351  			must.Eq(t, "", ov)
   352  			must.Eq(t, "WARN\n", ev)
   353  		})
   354  	}
   355  }
   356  
   357  func TestWriterUI_Error(t *testing.T) {
   358  	ci.Parallel(t)
   359  
   360  	tcs := []writerUITestCase{
   361  		{
   362  			name: "mockUi/simple",
   363  			initFn: func(tc *writerUITestCase) {
   364  				tc.baseUi = cli.NewMockUi()
   365  				tc.ui = tc.baseUi
   366  			},
   367  		},
   368  		{
   369  			name: "mockUi/nested_once",
   370  			initFn: func(tc *writerUITestCase) {
   371  				tc.baseUi = cli.NewMockUi()
   372  				tc.ui = &cli.ColoredUi{Ui: tc.baseUi}
   373  			},
   374  		},
   375  		{
   376  			name: "mockUi/nested_twice",
   377  			initFn: func(tc *writerUITestCase) {
   378  				tc.baseUi = cli.NewMockUi()
   379  				tc.ui = &cli.ColoredUi{Ui: &cli.ColoredUi{Ui: tc.baseUi}}
   380  			},
   381  		},
   382  		{
   383  			name: "basicUi/simple",
   384  			initFn: func(tc *writerUITestCase) {
   385  				tc.ow = new(bytes.Buffer)
   386  				tc.ew = new(bytes.Buffer)
   387  				tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
   388  				tc.ui = tc.baseUi
   389  			},
   390  		},
   391  		{
   392  			name: "basicUi/nested_once",
   393  			initFn: func(tc *writerUITestCase) {
   394  				tc.ow = new(bytes.Buffer)
   395  				tc.ew = new(bytes.Buffer)
   396  				tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
   397  				tc.ui = &cli.ColoredUi{Ui: tc.baseUi}
   398  			},
   399  		},
   400  		{
   401  			name: "basicUi/nested_twice",
   402  			initFn: func(tc *writerUITestCase) {
   403  				tc.ow = new(bytes.Buffer)
   404  				tc.ew = new(bytes.Buffer)
   405  				tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
   406  				tc.ui = &cli.ColoredUi{Ui: &cli.ColoredUi{Ui: tc.baseUi}}
   407  			},
   408  		},
   409  	}
   410  
   411  	for _, tc := range tcs {
   412  		tc := tc
   413  		t.Run(tc.name, func(t *testing.T) {
   414  			ci.Parallel(t)
   415  			tc.initFn(&tc)
   416  
   417  			wUI, err := NewWriterUI(tc.ui)
   418  			must.NoError(t, err)
   419  			wUI.Warn("ERROR")
   420  
   421  			var ov, ev string
   422  			switch bui := tc.baseUi.(type) {
   423  			case *cli.MockUi:
   424  				ov = bui.OutputWriter.String()
   425  				ev = bui.ErrorWriter.String()
   426  			case *cli.BasicUi:
   427  				ov = tc.ow.String()
   428  				ev = tc.ew.String()
   429  			default:
   430  				t.Fatal("invalid base cli.Ui type")
   431  			}
   432  
   433  			must.Eq(t, "", ov)
   434  			must.Eq(t, "ERROR\n", ev)
   435  		})
   436  	}
   437  }
   438  
   439  func TestWriterUI_Ask(t *testing.T) {
   440  	ci.Parallel(t)
   441  
   442  	testCases := []struct {
   443  		name           string
   444  		query          string
   445  		input          string
   446  		expectedQuery  string
   447  		expectedResult string
   448  	}{
   449  		{
   450  			name:           "EmptyString",
   451  			query:          "Middle Name?",
   452  			input:          "\n",
   453  			expectedQuery:  "Middle Name? ",
   454  			expectedResult: "",
   455  		},
   456  		{
   457  			name:           "NonEmptyString",
   458  			query:          "Name?",
   459  			input:          "foo bar\nbaz\n",
   460  			expectedQuery:  "Name? ",
   461  			expectedResult: "foo bar",
   462  		},
   463  	}
   464  
   465  	for _, tc := range testCases {
   466  		tc := tc
   467  		t.Run(tc.name, func(t *testing.T) {
   468  			ci.Parallel(t)
   469  
   470  			inReader, inWriter := io.Pipe()
   471  			t.Cleanup(func() {
   472  				inReader.Close()
   473  				inWriter.Close()
   474  			})
   475  
   476  			writer := new(bytes.Buffer)
   477  
   478  			WriterUI, err := NewWriterUI(&cli.BasicUi{
   479  				Reader: inReader,
   480  				Writer: writer,
   481  			})
   482  			must.NoError(t, err)
   483  
   484  			go inWriter.Write([]byte(tc.input))
   485  
   486  			result, err := WriterUI.Ask(tc.query)
   487  			must.NoError(t, err)
   488  			must.Eq(t, writer.String(), tc.expectedQuery)
   489  			must.Eq(t, result, tc.expectedResult)
   490  		})
   491  	}
   492  }
   493  
   494  func TestWriterUI_AskSecret(t *testing.T) {
   495  	ci.Parallel(t)
   496  
   497  	inReader, inWriter := io.Pipe()
   498  	t.Cleanup(func() {
   499  		inReader.Close()
   500  		inWriter.Close()
   501  	})
   502  
   503  	writer := new(bytes.Buffer)
   504  	wUI, err := NewWriterUI(&cli.BasicUi{
   505  		Reader: inReader,
   506  		Writer: writer,
   507  	})
   508  	must.NoError(t, err)
   509  
   510  	go inWriter.Write([]byte("foo bar\nbaz\n"))
   511  
   512  	result, err := wUI.AskSecret("Name?")
   513  	must.NoError(t, err)
   514  	must.Eq(t, writer.String(), "Name? ")
   515  	must.Eq(t, result, "foo bar")
   516  }