fix(summer): send a delegate's stderr to the root's error writer through bonfire.ErrWriter (IN-13, quick-261028-rqd)
This commit is contained in:
@@ -150,13 +150,14 @@ func runAppBinary(ctx context.Context, argv []string, out bonfire.Output) error
|
||||
// streams, so it sees the terminal: it asks the production question, prints
|
||||
// styled summaries and animates its widgets. Otherwise its stdout goes
|
||||
// through out (tests and bonfire.Call roots capture it) and its stderr to
|
||||
// summer's stderr, so log lines and error blocks never mix into data such
|
||||
// as --json output.
|
||||
// the root's error stream (bonfire.ErrWriter), which is os.Stderr for the
|
||||
// real summer, so log lines and error blocks never mix into data such as
|
||||
// --json output, and a root built on buffers captures them.
|
||||
func appStreams(out bonfire.Output, terminal bool) (stdout, stderr io.Writer) {
|
||||
if terminal {
|
||||
return os.Stdout, os.Stderr
|
||||
}
|
||||
return out, os.Stderr
|
||||
return out, bonfire.ErrWriter(out)
|
||||
}
|
||||
|
||||
// runAppCmd runs cmd, the app binary started with argv, forwarding SIGINT
|
||||
|
||||
@@ -346,6 +346,11 @@ func TestDelegateExitCodePassthrough(t *testing.T) {
|
||||
if code != 2 {
|
||||
t.Errorf("exit %d, want the binary's 2", code)
|
||||
}
|
||||
// IN-13: the binary's own error block reaches the root's error
|
||||
// writer.
|
||||
if n := strings.Count(errOut, "ERROR the app binary failed"); n != 1 {
|
||||
t.Errorf("the binary's error block reached the root's error writer %d times, want 1:\nstdout %q\nstderr %q", n, out, errOut)
|
||||
}
|
||||
for _, s := range []string{out, errOut} {
|
||||
if strings.Contains(s, " ERROR ") || strings.Contains(s, "summer: migrate") {
|
||||
t.Errorf("summer printed an error block of its own:\nstdout %q\nstderr %q", out, errOut)
|
||||
@@ -419,9 +424,9 @@ func TestRunAppCmdMasksSecrets(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// TestDelegateStreams (CR-03): off a terminal the app binary's stdout goes
|
||||
// through summer's Output and its stderr to summer's stderr, never into
|
||||
// summer's stdout.
|
||||
// TestDelegateStreams (CR-03, IN-13): off a terminal the app binary's
|
||||
// stdout goes through summer's Output and its stderr to the root's error
|
||||
// writer (bonfire.ErrWriter), never into summer's stdout.
|
||||
func TestDelegateStreams(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("the fake app binary is a shell script")
|
||||
@@ -438,23 +443,42 @@ func TestDelegateStreams(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Chdir(app)
|
||||
out, err := runSummer(t, "route:list", "--json")
|
||||
var stdout, stderr syncBuffer
|
||||
root, err := bonfire.NewRootIO("summer", toolCommands(), strings.NewReader(""), &stdout, &stderr)
|
||||
if err != nil {
|
||||
t.Fatalf("route:list: %v\n%s", err, out)
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(out, "data-line") {
|
||||
t.Errorf("the binary's stdout did not reach summer's Output: %q", out)
|
||||
if code := bonfire.Execute(context.Background(), root, []string{"route:list", "--json"}); code != bonfire.ExitOK {
|
||||
t.Fatalf("route:list: exit %d\nstdout %q\nstderr %q", code, stdout.String(), stderr.String())
|
||||
}
|
||||
if strings.Contains(out, "log-line") {
|
||||
t.Errorf("the binary's stderr landed in summer's stdout: %q", out)
|
||||
if !strings.Contains(stdout.String(), "data-line") {
|
||||
t.Errorf("the binary's stdout did not reach summer's Output: %q", stdout.String())
|
||||
}
|
||||
if strings.Contains(stdout.String(), "log-line") {
|
||||
t.Errorf("the binary's stderr landed in summer's stdout: %q", stdout.String())
|
||||
}
|
||||
if !strings.Contains(stderr.String(), "log-line") {
|
||||
t.Errorf("the binary's stderr did not reach the root's error writer: %q", stderr.String())
|
||||
}
|
||||
}
|
||||
|
||||
// TestAppStreams (CR-03): on a terminal the app binary gets summer's own
|
||||
// stdout and stderr; off a terminal its stdout goes through the Output and
|
||||
// its stderr to summer's stderr.
|
||||
// TestAppStreams (CR-03, IN-13): on a terminal the app binary gets
|
||||
// summer's own stdout and stderr; off a terminal its stdout goes through the
|
||||
// Output and its stderr to the Output's error writer (bonfire.ErrWriter),
|
||||
// which is os.Stderr for the real summer.
|
||||
func TestAppStreams(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
var buf, errBuf bytes.Buffer
|
||||
sep := bonfire.NewOutput(strings.NewReader(""), &buf, &errBuf)
|
||||
if _, stderr := appStreams(sep, false); stderr != io.Writer(&errBuf) {
|
||||
t.Errorf("not a terminal: stderr %T, want the Output's error writer", stderr)
|
||||
}
|
||||
real, err := bonfire.NewRoot("summer", nil, os.Stdout)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if real.ErrOrStderr() != os.Stderr {
|
||||
t.Errorf("the real summer root's error stream is %v, want os.Stderr", real.ErrOrStderr())
|
||||
}
|
||||
out := bonfire.NewOutput(strings.NewReader(""), &buf, &buf)
|
||||
if bonfire.IsTerminal(out) {
|
||||
t.Fatal("a buffer console reports a terminal")
|
||||
@@ -464,7 +488,7 @@ func TestAppStreams(t *testing.T) {
|
||||
t.Errorf("terminal: streams %v, %v; want os.Stdout, os.Stderr", stdout, stderr)
|
||||
}
|
||||
stdout, stderr = appStreams(out, false)
|
||||
if stdout != io.Writer(out) || stderr != os.Stderr {
|
||||
t.Errorf("not a terminal: streams %v, %v; want the Output, os.Stderr", stdout, stderr)
|
||||
if stdout != io.Writer(out) || stderr != io.Writer(&buf) {
|
||||
t.Errorf("not a terminal: streams %T, %T; want the Output and its error writer", stdout, stderr)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user