Add WriterUI (#17051)
This special purpose UI provides commands that can benefit from direct access to the io.Reader and io.Writers of the base cli.Ui. It can traverse a chain of ColoredUis to find the base. Currently, it can retrieve writers from a cli.BasicUi (or cli.MockUi for testing). Renames ui.go and ui_test.go to log_ui.go and log_ui_test.go
This commit is contained in:
parent
e9fec4ebc8
commit
61f997d806
|
@ -0,0 +1,83 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package ui
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/mitchellh/cli"
|
||||
)
|
||||
|
||||
// WriterUI is an implementation of the cli.Ui interface which can be used for
|
||||
// commands that need to have direct access to the underlying UI readers and
|
||||
// writers.
|
||||
type WriterUI struct {
|
||||
// Ui is the wrapped cli.Ui that supplies the functions for the thin shims
|
||||
Ui cli.Ui
|
||||
|
||||
reader io.Reader
|
||||
writer io.Writer
|
||||
errorWriter io.Writer
|
||||
|
||||
// baseUi stores the basic UI that was used to create this WriterUI. It
|
||||
// allows us to call its functions and not implement them again.
|
||||
baseUi cli.Ui
|
||||
}
|
||||
|
||||
// NewWriterUI generates a new cli.Ui that can be used for commands that
|
||||
// need access to the underlying UI's writers for copying large amounts of
|
||||
// data without local buffering. The caller is required to pass a UI
|
||||
// chain ending in a cli.BasicUi (or a cli.MockUi for testing).
|
||||
//
|
||||
// Currently, the UIs in the chain need to be pointers to a cli.ColoredUi,
|
||||
// cli.BasicUi, or cli.MockUi to work correctly.
|
||||
func NewWriterUI(ui cli.Ui) (*WriterUI, error) {
|
||||
var done bool
|
||||
wUI := WriterUI{Ui: ui}
|
||||
|
||||
for !done {
|
||||
if ui == nil {
|
||||
break
|
||||
}
|
||||
|
||||
switch u := ui.(type) {
|
||||
case *cli.MockUi:
|
||||
wUI.reader = u.InputReader
|
||||
wUI.writer = u.OutputWriter
|
||||
wUI.errorWriter = u.ErrorWriter
|
||||
wUI.baseUi = u
|
||||
done = true
|
||||
case *cli.BasicUi:
|
||||
wUI.reader = u.Reader
|
||||
wUI.writer = u.Writer
|
||||
wUI.errorWriter = u.ErrorWriter
|
||||
wUI.baseUi = u
|
||||
done = true
|
||||
case *cli.ColoredUi:
|
||||
ui = u.Ui
|
||||
default:
|
||||
return nil, fmt.Errorf("writer ui: unsupported Ui type: %T", ui)
|
||||
}
|
||||
}
|
||||
|
||||
if !done {
|
||||
return nil, errors.New("failed to generate command UI")
|
||||
}
|
||||
|
||||
return &wUI, nil
|
||||
}
|
||||
|
||||
func (w *WriterUI) InputReader() io.Reader { return w.reader }
|
||||
func (w *WriterUI) OutputWriter() io.Writer { return w.writer }
|
||||
func (w *WriterUI) ErrorWriter() io.Writer { return w.errorWriter }
|
||||
|
||||
func (w *WriterUI) Output(message string) { w.Ui.Output(message) }
|
||||
func (w *WriterUI) Info(message string) { w.Ui.Info(message) }
|
||||
func (w *WriterUI) Warn(message string) { w.Ui.Warn(message) }
|
||||
func (w *WriterUI) Error(message string) { w.Ui.Error(message) }
|
||||
|
||||
func (w *WriterUI) Ask(query string) (string, error) { return w.Ui.Ask(query) }
|
||||
func (w *WriterUI) AskSecret(query string) (string, error) { return w.Ui.AskSecret(query) }
|
|
@ -0,0 +1,513 @@
|
|||
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")
|
||||
}
|
Loading…
Reference in New Issue