feat(01-03): add deterministic rich CLI output
- Inject bonfire.Output from stdin/stdout/stderr with stdlib widgets and x/term - Degrade spinner, progress, table and prompts without a TTY or color - Reject plugin command names that are not namespace:verb
This commit is contained in:
@@ -18,17 +18,13 @@ import (
|
||||
func TestGreeterHelloPrintsLayeredConfig(t *testing.T) {
|
||||
got := runHello(t, nil)
|
||||
want := "name=hello-app posts_per_page=10 debug=false extra=hello-from-optional events=ok collected=greeter handled=true"
|
||||
if got != want {
|
||||
t.Fatalf("output = %q, want %q", got, want)
|
||||
}
|
||||
assertGreeting(t, got, want)
|
||||
}
|
||||
|
||||
func TestGreeterHelloSeesDevelopmentOverlay(t *testing.T) {
|
||||
got := runHello(t, map[string]string{"SUMMER_ENV": "development"})
|
||||
want := "name=hello-dev posts_per_page=10 debug=true extra=hello-from-optional events=ok collected=greeter handled=true"
|
||||
if got != want {
|
||||
t.Fatalf("output = %q, want %q", got, want)
|
||||
}
|
||||
assertGreeting(t, got, want)
|
||||
}
|
||||
|
||||
func TestGreeterHelloSeesEnvVarOverrideOfPluginDefault(t *testing.T) {
|
||||
@@ -36,9 +32,7 @@ func TestGreeterHelloSeesEnvVarOverrideOfPluginDefault(t *testing.T) {
|
||||
"SUMMER_GOLEM15__HELLO__POSTS_PER_PAGE": "25",
|
||||
})
|
||||
want := "name=hello-app posts_per_page=25 debug=false extra=hello-from-optional events=ok collected=greeter handled=true"
|
||||
if got != want {
|
||||
t.Fatalf("output = %q, want %q", got, want)
|
||||
}
|
||||
assertGreeting(t, got, want)
|
||||
}
|
||||
|
||||
func runHello(t *testing.T, env map[string]string) string {
|
||||
@@ -56,9 +50,7 @@ func runHello(t *testing.T, env map[string]string) string {
|
||||
func TestGreeterRunsWithoutOptionalPlugin(t *testing.T) {
|
||||
got := runHelloIDs(t, []string{"golem15.hello", "golem15.greeter"}, nil)
|
||||
want := "name=hello-app posts_per_page=10 debug=false extra= events=ok collected=greeter handled=true"
|
||||
if got != want {
|
||||
t.Fatalf("output = %q, want %q", got, want)
|
||||
}
|
||||
assertGreeting(t, got, want)
|
||||
}
|
||||
|
||||
func runHelloIDs(t *testing.T, ids []string, env map[string]string) string {
|
||||
@@ -82,7 +74,10 @@ func runHelloIDs(t *testing.T, ids []string, env map[string]string) string {
|
||||
}
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
root := bonfire.NewRoot("hello", commands, &buf)
|
||||
root, err := bonfire.NewRoot("hello", commands, &buf)
|
||||
if err != nil {
|
||||
t.Fatalf("NewRoot: %v", err)
|
||||
}
|
||||
root.SetArgs([]string{"greeter:hello"})
|
||||
if err := root.Execute(); err != nil {
|
||||
t.Fatalf("execute: %v", err)
|
||||
@@ -104,9 +99,27 @@ func TestBuiltBinaryGreeterHello(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("bin/hello greeter:hello: %v\n%s", err, out)
|
||||
}
|
||||
if got := strings.TrimSpace(string(out)); got != "name=hello-app posts_per_page=10 debug=false extra=hello-from-optional events=ok collected=greeter handled=true" {
|
||||
t.Fatalf("output = %q, want layered production config with optional extra and events", got)
|
||||
got := strings.TrimSpace(string(out))
|
||||
want := "name=hello-app posts_per_page=10 debug=false extra=hello-from-optional events=ok collected=greeter handled=true"
|
||||
assertGreeting(t, got, want)
|
||||
}
|
||||
|
||||
func assertGreeting(t *testing.T, got, want string) {
|
||||
t.Helper()
|
||||
if !strings.Contains(got, "plugin\tstatus") || !strings.Contains(got, "golem15.greeter\tactive") {
|
||||
t.Fatalf("missing non-tty table:\n%s", got)
|
||||
}
|
||||
if lastLine(got) != want {
|
||||
t.Fatalf("greeting = %q, want %q\nfull:\n%s", lastLine(got), want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func lastLine(s string) string {
|
||||
s = strings.TrimSpace(s)
|
||||
if i := strings.LastIndex(s, "\n"); i >= 0 {
|
||||
return s[i+1:]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func runSummerBuild(t *testing.T) string {
|
||||
|
||||
@@ -37,7 +37,10 @@ func run(args []string, out io.Writer) error {
|
||||
commands = append(commands, hasCommands.Commands()...)
|
||||
}
|
||||
}
|
||||
root := bonfire.NewRoot("hello", commands, out)
|
||||
root, err := bonfire.NewRoot("hello", commands, out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
root.SetArgs(args)
|
||||
return root.Execute()
|
||||
}
|
||||
|
||||
@@ -82,6 +82,10 @@ func (p *Plugin) Commands() []bonfire.Command {
|
||||
}
|
||||
handled = ok
|
||||
}
|
||||
out.Table([]string{"plugin", "status"}, [][]string{{"golem15.greeter", "active"}})
|
||||
if _, err := out.Confirm("show greeting?", true); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Printf("name=%s posts_per_page=%d debug=%t extra=%s events=%s collected=%s handled=%t\n", name, posts, debug, p.extra, events, collected, handled)
|
||||
return nil
|
||||
},
|
||||
|
||||
@@ -3,8 +3,6 @@ package greeter
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -15,17 +13,6 @@ import (
|
||||
"git.golem15.com/golem15/summercms/pact"
|
||||
)
|
||||
|
||||
type testOut struct{ bytes.Buffer }
|
||||
|
||||
func (o *testOut) Write(p []byte) (int, error) { return o.Buffer.Write(p) }
|
||||
func (o *testOut) Println(a ...any) { fmt.Fprintln(&o.Buffer, a...) }
|
||||
func (o *testOut) Printf(format string, a ...any) {
|
||||
fmt.Fprintf(&o.Buffer, format, a...)
|
||||
}
|
||||
|
||||
var _ bonfire.Output = (*testOut)(nil)
|
||||
var _ io.Writer = (*testOut)(nil)
|
||||
|
||||
type extraMsg struct{ msg string }
|
||||
|
||||
func (e extraMsg) Message() string { return e.msg }
|
||||
@@ -78,9 +65,10 @@ func runHelloCmd(t *testing.T, p *Plugin) string {
|
||||
if len(cmds) == 0 {
|
||||
t.Fatal("no commands")
|
||||
}
|
||||
var out testOut
|
||||
if err := cmds[0].Run(context.Background(), nil, &out); err != nil {
|
||||
var buf bytes.Buffer
|
||||
out := bonfire.NewOutput(strings.NewReader(""), &buf, &buf)
|
||||
if err := cmds[0].Run(context.Background(), nil, out); err != nil {
|
||||
t.Fatalf("Run: %v", err)
|
||||
}
|
||||
return strings.TrimSpace(out.String())
|
||||
return strings.TrimSpace(buf.String())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user