open-nomad/command/ui/writer_ui_test.go

517 lines
11 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package ui
import (
"bytes"
"fmt"
"io"
"testing"
"github.com/hashicorp/nomad/ci"
"github.com/mitchellh/cli"
"github.com/shoenig/test/must"
)
func TestWriterUI_Implements(t *testing.T) {
ci.Parallel(t)
var _ cli.Ui = new(WriterUI)
}
type writerUITestCase struct {
name string // the testcase name
baseUi cli.Ui // cli.Ui with accessible writers (currently basicUi or mockUi)
ui cli.Ui // the full ui object chain (should end in baseUi)
initFn func(*writerUITestCase) // sets up the struct for the testcase
ow *bytes.Buffer // handle to basicUi's Output writer
ew *bytes.Buffer // handle to basicUi's Error writer
}
func TestWriterUI_OutputWriter(t *testing.T) {
ci.Parallel(t)
tcs := []writerUITestCase{
{
name: "mockUi/simple",
initFn: func(tc *writerUITestCase) {
tc.baseUi = cli.NewMockUi()
tc.ui = tc.baseUi
},
},
{
name: "mockUi/nested_once",
initFn: func(tc *writerUITestCase) {
tc.baseUi = cli.NewMockUi()
tc.ui = &cli.ColoredUi{Ui: tc.baseUi}
},
},
{
name: "mockUi/nested_twice",
initFn: func(tc *writerUITestCase) {
tc.baseUi = cli.NewMockUi()
tc.ui = &cli.ColoredUi{Ui: &cli.ColoredUi{Ui: tc.baseUi}}
},
},
{
name: "basicUi/simple",
initFn: func(tc *writerUITestCase) {
tc.ow = new(bytes.Buffer)
tc.ew = new(bytes.Buffer)
tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
tc.ui = tc.baseUi
},
},
{
name: "basicUi/nested_once",
initFn: func(tc *writerUITestCase) {
tc.ow = new(bytes.Buffer)
tc.ew = new(bytes.Buffer)
tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
tc.ui = &cli.ColoredUi{Ui: tc.baseUi}
},
},
{
name: "basicUi/nested_twice",
initFn: func(tc *writerUITestCase) {
tc.ow = new(bytes.Buffer)
tc.ew = new(bytes.Buffer)
tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
tc.ui = &cli.ColoredUi{Ui: &cli.ColoredUi{Ui: tc.baseUi}}
},
},
}
for _, tc := range tcs {
tc := tc
t.Run(tc.name, func(t *testing.T) {
ci.Parallel(t)
tc.initFn(&tc)
wUI, err := NewWriterUI(tc.ui)
must.NoError(t, err)
fmt.Fprintf(wUI.OutputWriter(), "foobar")
switch bui := tc.baseUi.(type) {
case *cli.MockUi:
must.Eq(t, "foobar", bui.OutputWriter.String())
must.Eq(t, "", bui.ErrorWriter.String())
case *cli.BasicUi:
must.Eq(t, "foobar", tc.ow.String())
must.Eq(t, "", tc.ew.String())
default:
t.Fatal("invalid base cli.Ui type")
}
})
}
}
func TestWriterUI_Output(t *testing.T) {
ci.Parallel(t)
tcs := []writerUITestCase{
{
name: "mockUi/simple",
initFn: func(tc *writerUITestCase) {
tc.baseUi = cli.NewMockUi()
tc.ui = tc.baseUi
},
},
{
name: "mockUi/nested_once",
initFn: func(tc *writerUITestCase) {
tc.baseUi = cli.NewMockUi()
tc.ui = &cli.ColoredUi{Ui: tc.baseUi}
},
},
{
name: "mockUi/nested_twice",
initFn: func(tc *writerUITestCase) {
tc.baseUi = cli.NewMockUi()
tc.ui = &cli.ColoredUi{Ui: &cli.ColoredUi{Ui: tc.baseUi}}
},
},
{
name: "basicUi/simple",
initFn: func(tc *writerUITestCase) {
tc.ow = new(bytes.Buffer)
tc.ew = new(bytes.Buffer)
tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
tc.ui = tc.baseUi
},
},
{
name: "basicUi/nested_once",
initFn: func(tc *writerUITestCase) {
tc.ow = new(bytes.Buffer)
tc.ew = new(bytes.Buffer)
tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
tc.ui = &cli.ColoredUi{Ui: tc.baseUi}
},
},
{
name: "basicUi/nested_twice",
initFn: func(tc *writerUITestCase) {
tc.ow = new(bytes.Buffer)
tc.ew = new(bytes.Buffer)
tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
tc.ui = &cli.ColoredUi{Ui: &cli.ColoredUi{Ui: tc.baseUi}}
},
},
}
for _, tc := range tcs {
tc := tc
t.Run(tc.name, func(t *testing.T) {
ci.Parallel(t)
tc.initFn(&tc)
wUI, err := NewWriterUI(tc.ui)
must.NoError(t, err)
wUI.Output("foobar")
var ov, ev string
switch bui := tc.baseUi.(type) {
case *cli.MockUi:
ov = bui.OutputWriter.String()
ev = bui.ErrorWriter.String()
case *cli.BasicUi:
ov = tc.ow.String()
ev = tc.ew.String()
default:
t.Fatal("invalid base cli.Ui type")
}
must.Eq(t, "foobar\n", ov)
must.Eq(t, "", ev)
})
}
}
func TestWriterUI_Info(t *testing.T) {
ci.Parallel(t)
tcs := []writerUITestCase{
{
name: "mockUi/simple",
initFn: func(tc *writerUITestCase) {
tc.baseUi = cli.NewMockUi()
tc.ui = tc.baseUi
},
},
{
name: "mockUi/nested_once",
initFn: func(tc *writerUITestCase) {
tc.baseUi = cli.NewMockUi()
tc.ui = &cli.ColoredUi{Ui: tc.baseUi}
},
},
{
name: "mockUi/nested_twice",
initFn: func(tc *writerUITestCase) {
tc.baseUi = cli.NewMockUi()
tc.ui = &cli.ColoredUi{Ui: &cli.ColoredUi{Ui: tc.baseUi}}
},
},
{
name: "basicUi/simple",
initFn: func(tc *writerUITestCase) {
tc.ow = new(bytes.Buffer)
tc.ew = new(bytes.Buffer)
tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
tc.ui = tc.baseUi
},
},
{
name: "basicUi/nested_once",
initFn: func(tc *writerUITestCase) {
tc.ow = new(bytes.Buffer)
tc.ew = new(bytes.Buffer)
tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
tc.ui = &cli.ColoredUi{Ui: tc.baseUi}
},
},
{
name: "basicUi/nested_twice",
initFn: func(tc *writerUITestCase) {
tc.ow = new(bytes.Buffer)
tc.ew = new(bytes.Buffer)
tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
tc.ui = &cli.ColoredUi{Ui: &cli.ColoredUi{Ui: tc.baseUi}}
},
},
}
for _, tc := range tcs {
tc := tc
t.Run(tc.name, func(t *testing.T) {
ci.Parallel(t)
tc.initFn(&tc)
wUI, err := NewWriterUI(tc.ui)
must.NoError(t, err)
wUI.Info("INFO")
var ov, ev string
switch bui := tc.baseUi.(type) {
case *cli.MockUi:
ov = bui.OutputWriter.String()
ev = bui.ErrorWriter.String()
case *cli.BasicUi:
ov = tc.ow.String()
ev = tc.ew.String()
default:
t.Fatal("invalid base cli.Ui type")
}
must.Eq(t, "INFO\n", ov)
must.Eq(t, "", ev)
})
}
}
func TestWriterUI_Warn(t *testing.T) {
ci.Parallel(t)
tcs := []writerUITestCase{
{
name: "mockUi/simple",
initFn: func(tc *writerUITestCase) {
tc.baseUi = cli.NewMockUi()
tc.ui = tc.baseUi
},
},
{
name: "mockUi/nested_once",
initFn: func(tc *writerUITestCase) {
tc.baseUi = cli.NewMockUi()
tc.ui = &cli.ColoredUi{Ui: tc.baseUi}
},
},
{
name: "mockUi/nested_twice",
initFn: func(tc *writerUITestCase) {
tc.baseUi = cli.NewMockUi()
tc.ui = &cli.ColoredUi{Ui: &cli.ColoredUi{Ui: tc.baseUi}}
},
},
{
name: "basicUi/simple",
initFn: func(tc *writerUITestCase) {
tc.ow = new(bytes.Buffer)
tc.ew = new(bytes.Buffer)
tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
tc.ui = tc.baseUi
},
},
{
name: "basicUi/nested_once",
initFn: func(tc *writerUITestCase) {
tc.ow = new(bytes.Buffer)
tc.ew = new(bytes.Buffer)
tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
tc.ui = &cli.ColoredUi{Ui: tc.baseUi}
},
},
{
name: "basicUi/nested_twice",
initFn: func(tc *writerUITestCase) {
tc.ow = new(bytes.Buffer)
tc.ew = new(bytes.Buffer)
tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
tc.ui = &cli.ColoredUi{Ui: &cli.ColoredUi{Ui: tc.baseUi}}
},
},
}
for _, tc := range tcs {
tc := tc
t.Run(tc.name, func(t *testing.T) {
ci.Parallel(t)
tc.initFn(&tc)
wUI, err := NewWriterUI(tc.ui)
must.NoError(t, err)
wUI.Warn("WARN")
const expected = "WARN\n"
var ov, ev string
switch bui := tc.baseUi.(type) {
case *cli.MockUi:
ov = bui.OutputWriter.String()
ev = bui.ErrorWriter.String()
case *cli.BasicUi:
ov = tc.ow.String()
ev = tc.ew.String()
default:
t.Fatal("invalid base cli.Ui type")
}
must.Eq(t, "", ov)
must.Eq(t, "WARN\n", ev)
})
}
}
func TestWriterUI_Error(t *testing.T) {
ci.Parallel(t)
tcs := []writerUITestCase{
{
name: "mockUi/simple",
initFn: func(tc *writerUITestCase) {
tc.baseUi = cli.NewMockUi()
tc.ui = tc.baseUi
},
},
{
name: "mockUi/nested_once",
initFn: func(tc *writerUITestCase) {
tc.baseUi = cli.NewMockUi()
tc.ui = &cli.ColoredUi{Ui: tc.baseUi}
},
},
{
name: "mockUi/nested_twice",
initFn: func(tc *writerUITestCase) {
tc.baseUi = cli.NewMockUi()
tc.ui = &cli.ColoredUi{Ui: &cli.ColoredUi{Ui: tc.baseUi}}
},
},
{
name: "basicUi/simple",
initFn: func(tc *writerUITestCase) {
tc.ow = new(bytes.Buffer)
tc.ew = new(bytes.Buffer)
tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
tc.ui = tc.baseUi
},
},
{
name: "basicUi/nested_once",
initFn: func(tc *writerUITestCase) {
tc.ow = new(bytes.Buffer)
tc.ew = new(bytes.Buffer)
tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
tc.ui = &cli.ColoredUi{Ui: tc.baseUi}
},
},
{
name: "basicUi/nested_twice",
initFn: func(tc *writerUITestCase) {
tc.ow = new(bytes.Buffer)
tc.ew = new(bytes.Buffer)
tc.baseUi = &cli.BasicUi{Writer: tc.ow, ErrorWriter: tc.ew}
tc.ui = &cli.ColoredUi{Ui: &cli.ColoredUi{Ui: tc.baseUi}}
},
},
}
for _, tc := range tcs {
tc := tc
t.Run(tc.name, func(t *testing.T) {
ci.Parallel(t)
tc.initFn(&tc)
wUI, err := NewWriterUI(tc.ui)
must.NoError(t, err)
wUI.Warn("ERROR")
var ov, ev string
switch bui := tc.baseUi.(type) {
case *cli.MockUi:
ov = bui.OutputWriter.String()
ev = bui.ErrorWriter.String()
case *cli.BasicUi:
ov = tc.ow.String()
ev = tc.ew.String()
default:
t.Fatal("invalid base cli.Ui type")
}
must.Eq(t, "", ov)
must.Eq(t, "ERROR\n", ev)
})
}
}
func TestWriterUI_Ask(t *testing.T) {
ci.Parallel(t)
testCases := []struct {
name string
query string
input string
expectedQuery string
expectedResult string
}{
{
name: "EmptyString",
query: "Middle Name?",
input: "\n",
expectedQuery: "Middle Name? ",
expectedResult: "",
},
{
name: "NonEmptyString",
query: "Name?",
input: "foo bar\nbaz\n",
expectedQuery: "Name? ",
expectedResult: "foo bar",
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
ci.Parallel(t)
inReader, inWriter := io.Pipe()
t.Cleanup(func() {
inReader.Close()
inWriter.Close()
})
writer := new(bytes.Buffer)
WriterUI, err := NewWriterUI(&cli.BasicUi{
Reader: inReader,
Writer: writer,
})
must.NoError(t, err)
go inWriter.Write([]byte(tc.input))
result, err := WriterUI.Ask(tc.query)
must.NoError(t, err)
must.Eq(t, writer.String(), tc.expectedQuery)
must.Eq(t, result, tc.expectedResult)
})
}
}
func TestWriterUI_AskSecret(t *testing.T) {
ci.Parallel(t)
inReader, inWriter := io.Pipe()
t.Cleanup(func() {
inReader.Close()
inWriter.Close()
})
writer := new(bytes.Buffer)
wUI, err := NewWriterUI(&cli.BasicUi{
Reader: inReader,
Writer: writer,
})
must.NoError(t, err)
go inWriter.Write([]byte("foo bar\nbaz\n"))
result, err := wUI.AskSecret("Name?")
must.NoError(t, err)
must.Eq(t, writer.String(), "Name? ")
must.Eq(t, result, "foo bar")
}