Files
mesh-sdk/go/cli/cli_test.go
T
jochen a5a040c904
mesh/merge-gate pass: builds mesh-sdk, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges; it…
mesh/delivery delivered
go/cli: an outcome not known is not a refusal; the Go binding is checked in the Go toolchain
Review of hq ADR 0272: a line whose answer did not come in time may have
run, and saying "nothing ran" there is a lie. Answer.Unknown and NotKnown
say it may have run and name the call to look at.

The SDK's repo-check ran in the TypeScript toolchain only and said
"NOT TESTED HERE: go/". go/merge-check.sh now runs the Go binding in the Go
toolchain, declared by the root check (mesh-check-also), as the lab does.

Released as go/v0.1.9 (go/v0.1.8 is mesh-sdk #12's).
2026-10-09 12:37:48 +02:00

106 lines
3.3 KiB
Go

package cli
import (
"bytes"
"context"
"errors"
"net"
"path/filepath"
"strings"
"testing"
)
// A request and an answer survive a round trip, bytes and all.
func TestARequestAndAnAnswerSurviveTheirRoundTrip(t *testing.T) {
var b bytes.Buffer
line := []string{"settings", "set", "zsh", `{"execute":"withhold"}`, "--node", "a"}
if err := WriteRequest(&b, Request{Line: line}); err != nil {
t.Fatal(err)
}
got, err := ReadRequest(&b)
if err != nil {
t.Fatal(err)
}
if strings.Join(got.Line, "\x00") != strings.Join(line, "\x00") {
t.Fatalf("the line came back as %q", got.Line)
}
b.Reset()
sent := Answer{Stdout: []byte("ok\n\xff"), Stderr: []byte("said"), Exit: 3, Terminal: true, Why: "the terminal", Cut: true,
Unknown: "no answer; the line may have run — see the call call-1"}
if err := WriteAnswer(&b, sent); err != nil {
t.Fatal(err)
}
a, err := ReadAnswer(&b)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(a.Stdout, sent.Stdout) || string(a.Stderr) != "said" || a.Exit != 3 || !a.Terminal || a.Why != sent.Why || !a.Cut ||
a.Unknown != sent.Unknown {
t.Fatalf("the answer came back as %+v", a)
}
}
// A request over the bound is refused, not read whole; one naming no command is refused.
func TestARequestOverTheBoundOrNamingNothingIsRefused(t *testing.T) {
long := Request{Line: []string{strings.Repeat("x", MaxRequest)}}
var b bytes.Buffer
if err := WriteRequest(&b, long); err != nil {
t.Fatal(err)
}
if _, err := ReadRequest(&b); !errors.Is(err, ErrTooLong) {
t.Fatalf("a request over the bound was read: %v", err)
}
if _, err := ReadRequest(strings.NewReader(`{"line":[]}` + "\n")); err == nil {
t.Fatal("a request naming no command was taken")
}
if _, err := ReadRequest(strings.NewReader("not json\n")); err == nil {
t.Fatal("a request that is not one was taken")
}
}
// Ask speaks the protocol over a socket, as mesh-cli does to the node-engine.
func TestAskSpeaksTheProtocolOverASocket(t *testing.T) {
socket := filepath.Join(t.TempDir(), "cli.sock")
l, err := net.Listen("unix", socket)
if err != nil {
t.Skipf("no unix socket here: %v", err)
}
defer l.Close()
go func() {
conn, err := l.Accept()
if err != nil {
return
}
defer conn.Close()
req, err := ReadRequest(conn)
if err != nil {
_ = WriteAnswer(conn, Refusal(err.Error()))
return
}
_ = WriteAnswer(conn, Answer{Stdout: []byte(strings.Join(req.Line, " "))})
}()
a, err := Ask(context.Background(), socket, []string{"status"})
if err != nil {
t.Fatal(err)
}
if string(a.Stdout) != "status" || a.Exit != 0 || a.Refused != "" {
t.Fatalf("asked status and heard %+v", a)
}
if _, err := Ask(context.Background(), filepath.Join(t.TempDir(), "none.sock"), []string{"status"}); err == nil ||
!strings.Contains(err.Error(), "does not answer") {
t.Fatalf("a missing socket was not said: %v", err)
}
}
// An outcome not known is never a refusal: it says the line may have run, and exits non-zero.
func TestAnUnknownOutcomeIsNotARefusal(t *testing.T) {
a := NotKnown("call-7", "no answer within 6m0s")
if a.Refused != "" || a.Exit == 0 || !strings.Contains(a.Unknown, "may have run") || !strings.Contains(a.Unknown, "call-7") {
t.Fatalf("an unknown outcome reads as %+v", a)
}
if b := NotKnown("", "no answer"); strings.Contains(b.Unknown, "call ") {
t.Fatalf("an unknown outcome with no call names one: %+v", b)
}
}