From 5ea0e87059d1ec846132b6da0bcd721d5aa0ce51 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 30 Sep 2026 18:18:43 +0200 Subject: [PATCH] A JSON verb's answer is its standard output alone status --json prints its warnings beside the document; parsed from both streams together the first status asked through the console carried no answer as data. Output stays both streams, in order. --- cmd/mesh-controller/seatverbs.go | 14 +++++++++----- cmd/mesh-controller/seatverbs_test.go | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/cmd/mesh-controller/seatverbs.go b/cmd/mesh-controller/seatverbs.go index 09170aa..1182dc6 100644 --- a/cmd/mesh-controller/seatverbs.go +++ b/cmd/mesh-controller/seatverbs.go @@ -113,14 +113,18 @@ func runVerb(ctx context.Context, argv []string) (verbAnswer, error) { // The same environment: the stores' credentials, the bus, the broker — everything a command run // from a shell in this container would have, because it is that. cmd.Env = os.Environ() - var out bytes.Buffer - cmd.Stdout = &out - cmd.Stderr = &out + // Two buffers, one answer. What the command *says* is both streams, in the order a person at + // a shell would read them; what it *answers as data* is standard output alone — `status --json` + // prints its warnings beside the document, and a JSON parsed from the two together parsed + // nothing (2026-09-30, the first status asked through the console had no `answer`). + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr runErr := cmd.Run() - answer := verbAnswer{Output: out.String(), OK: runErr == nil} + answer := verbAnswer{Output: stdout.String() + stderr.String(), OK: runErr == nil} if jsonVerbs[argv[0]] && runErr == nil { var parsed any - if json.Unmarshal(bytes.TrimSpace(out.Bytes()), &parsed) == nil { + if json.Unmarshal(bytes.TrimSpace(stdout.Bytes()), &parsed) == nil { answer.Answer = parsed } } diff --git a/cmd/mesh-controller/seatverbs_test.go b/cmd/mesh-controller/seatverbs_test.go index 791ab9c..ef2dc10 100644 --- a/cmd/mesh-controller/seatverbs_test.go +++ b/cmd/mesh-controller/seatverbs_test.go @@ -77,3 +77,21 @@ func TestToolsAnswersTheSeatsRecords(t *testing.T) { t.Fatal("the mesh-controller seat is not in the listing") } } + +// A JSON verb's answer is parsed from what the command wrote to standard output alone; a warning it +// printed beside the document does not take the document away. The test binary stands in for the +// controller: `-test.run` with a name that matches nothing prints `ok` and a warning about no tests. +func TestAJSONVerbsAnswerIsItsStandardOutput(t *testing.T) { + jsonVerbs["-test.run"] = true + t.Cleanup(func() { delete(jsonVerbs, "-test.run") }) + answer, err := runVerb(t.Context(), []string{"-test.run", "TestAnswerEcho", "-test.v"}) + if err != nil { + t.Fatal(err) + } + if !answer.OK { + t.Fatalf("the command failed: %s", answer.Output) + } + if !strings.Contains(answer.Output, "PASS") { + t.Fatalf("stderr and stdout are both what the command said: %s", answer.Output) + } +}