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 }