rehearse refused only a verb's process. Since mesh-cli (ADR 0272 §4) the serving controller runs an ordinary mesh-cli line without a verb, naming its caller: such a line, from an agent's account, read as the terminal and could start a question the operator did not ask. rehearse now asks startedAtTheTerminal. And main's mesh-cli test helper asked is cliAsked, beside the asker's asked.
306 lines
14 KiB
Go
306 lines
14 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/nats-io/nats.go"
|
|
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
"github.com/novox/mesh-controller/internal/link"
|
|
"github.com/novox/mesh-controller/internal/testbus"
|
|
)
|
|
|
|
// echoEnvironment makes the test binary, run as a command line, say the verb and caller it was given (TestMain).
|
|
const echoEnvironment = "MESH_TEST_ECHO_ENVIRONMENT"
|
|
|
|
var cliNodes = []inventory.Node{
|
|
{Name: "control", Account: "operator"},
|
|
{Name: "laptop", Account: "operator"},
|
|
{Name: "unnamed"},
|
|
}
|
|
|
|
func cliAsked(account string, uid uint32, line ...string) link.CLIAsked {
|
|
return link.CLIAsked{Line: line, Account: account, UID: uid, Session: "session-1.scope"}
|
|
}
|
|
|
|
// Who is the controller's terminal, and who is an ordinary call or refused (novox/hq ADR 0272 §4).
|
|
func TestMeshCLIIsTheTerminalOnlyForTheControlNodesOperator(t *testing.T) {
|
|
control := []string{"control"}
|
|
cases := []struct {
|
|
name, node string
|
|
asked link.CLIAsked
|
|
control []string
|
|
terminal bool
|
|
refused string
|
|
why string
|
|
}{
|
|
{"the control-node's operator", "control", cliAsked("operator", 1000, "status"), control, true, "", "the controller's terminal"},
|
|
{"another node's operator", "laptop", cliAsked("operator", 1000, "status"), control, false, "", "agents on laptop may run as operator"},
|
|
{"another account", "control", cliAsked("agent", 1001, "status"), control, false, "operator account (operator) only", ""},
|
|
{"root", "control", cliAsked("root", 0, "status"), control, false, "never root", ""},
|
|
{"a node with no operator account", "unnamed", cliAsked("operator", 1000, "status"), control, false, "does not know unnamed's operator account", ""},
|
|
{"a node the mesh does not know", "elsewhere", cliAsked("operator", 1000, "status"), control, false, "not a node this mesh knows", ""},
|
|
{"two control-nodes", "control", cliAsked("operator", 1000, "status"), []string{"control", "laptop"}, false, "", "2 control-nodes"},
|
|
}
|
|
for _, c := range cases {
|
|
v := judgeCLI(c.node, c.asked, cliNodes, c.control)
|
|
if v.terminal != c.terminal {
|
|
t.Errorf("%s: terminal %v, want %v (%+v)", c.name, v.terminal, c.terminal, v)
|
|
}
|
|
if c.refused == "" && v.refused != "" || c.refused != "" && !strings.Contains(v.refused, c.refused) {
|
|
t.Errorf("%s: refused %q, want %q", c.name, v.refused, c.refused)
|
|
}
|
|
if c.why != "" && !strings.Contains(v.why, c.why) {
|
|
t.Errorf("%s: why %q, want %q", c.name, v.why, c.why)
|
|
}
|
|
}
|
|
}
|
|
|
|
// The terminal runs its line without MESH_VERB, even where this process carries one; an ordinary call names
|
|
// mesh-cli; both record who asked through mesh-cli.
|
|
func TestTheTerminalRunsWithoutAVerbAndAnOrdinaryCallNamesMeshCLI(t *testing.T) {
|
|
t.Setenv(echoEnvironment, "1")
|
|
t.Setenv("MESH_VERB", "leaked-from-the-serving-process")
|
|
// The serving controller marks itself (ADR 0266); its terminal line for mesh-cli is still the terminal's.
|
|
t.Setenv(servedVar, "1")
|
|
ctx := context.Background()
|
|
|
|
a := runForMeshCLI(ctx, "control", cliAsked("operator", 1000, "status"), cliVerdict{terminal: true, why: "the terminal"})
|
|
if a.Exit != 0 || a.Refused != "" || !a.Terminal {
|
|
t.Fatalf("the terminal's line did not run: %+v", a)
|
|
}
|
|
if got := string(a.Stdout); !strings.Contains(got, `verb=""`) || !strings.Contains(got, "operator through mesh-cli on control") ||
|
|
!strings.Contains(got, "terminal=true") {
|
|
t.Fatalf("the terminal's line ran with %s", got)
|
|
}
|
|
|
|
a = runForMeshCLI(ctx, "laptop", cliAsked("operator", 1000, "status"), cliVerdict{why: "not the terminal"})
|
|
if a.Exit != 0 || a.Terminal || a.Why != "not the terminal" {
|
|
t.Fatalf("an ordinary line did not run as one: %+v", a)
|
|
}
|
|
if got := string(a.Stdout); !strings.Contains(got, `verb="mesh-cli"`) || !strings.Contains(got, "terminal=false") {
|
|
t.Fatalf("an ordinary line ran with %s", got)
|
|
}
|
|
}
|
|
|
|
// An ordinary call meets every refusal of the generic command verb, and nothing runs; a server is never run.
|
|
func TestAnOrdinaryCallMeetsTheCommandVerbsRefusals(t *testing.T) {
|
|
t.Setenv(echoEnvironment, "1")
|
|
ctx := context.Background()
|
|
ordinary := cliVerdict{why: "not the terminal"}
|
|
a := runForMeshCLI(ctx, "laptop", cliAsked("operator", 1000, "cleanup", "delete", "x"), ordinary)
|
|
if a.Refused == "" || len(a.Stdout) != 0 || a.Exit != 1 || a.Why != "not the terminal" {
|
|
t.Fatalf("a repair without --why ran as an ordinary call: %+v", a)
|
|
}
|
|
a = runForMeshCLI(ctx, "laptop", cliAsked("operator", 1000, "settings", "set", "claude-code", "{}"), ordinary)
|
|
if a.Refused != "" || !strings.Contains(string(a.Stdout), `verb="mesh-cli"`) {
|
|
t.Fatalf("an ordinary settings set did not run through the settings verb's path with MESH_VERB set: %+v", a)
|
|
}
|
|
for _, server := range []string{"serve", "api", "board"} {
|
|
a := runForMeshCLI(ctx, "control", cliAsked("operator", 1000, server), cliVerdict{terminal: true})
|
|
if a.Refused == "" || len(a.Stdout) != 0 {
|
|
t.Fatalf("%s was run for mesh-cli: %+v", server, a)
|
|
}
|
|
}
|
|
a = runForMeshCLI(ctx, "control", cliAsked("agent", 1001, "status"), cliVerdict{refused: "agent is not answered"})
|
|
if a.Refused != "agent is not answered" || len(a.Stdout) != 0 {
|
|
t.Fatalf("a refused line ran: %+v", a)
|
|
}
|
|
}
|
|
|
|
// **Which node is the terminal does not follow a verb** (review of ADR 0272): the controller's module is assigned
|
|
// and unassigned at the terminal alone, so no caller of `assign` can move the terminal to a node of its choosing.
|
|
// Asked through the acts themselves, as the verbs ask them; refused before any store is touched (none is given).
|
|
func TestTheControllersModuleIsMovedAtTheTerminalAlone(t *testing.T) {
|
|
t.Setenv("MESH_VERB", "assign")
|
|
ctx := context.Background()
|
|
if _, err := assignWith(ctx, nil, "laptop", assignOptions{}, "zsh", "mesh-controller"); err == nil ||
|
|
!strings.Contains(err.Error(), "terminal") {
|
|
t.Fatalf("assigning the controller through a verb was not refused: %v", err)
|
|
}
|
|
if _, err := assign(ctx, nil, "laptop", "mesh-controller"); err == nil || !strings.Contains(err.Error(), "terminal") {
|
|
t.Fatalf("assigning the controller through a verb was not refused: %v", err)
|
|
}
|
|
t.Setenv("MESH_VERB", "unassign")
|
|
if _, err := unassign(ctx, nil, "control", "mesh-controller"); err == nil || !strings.Contains(err.Error(), "terminal") {
|
|
t.Fatalf("unassigning the controller through a verb was not refused: %v", err)
|
|
}
|
|
// Another module through a verb, and the controller's at the terminal, are not refused for it.
|
|
if err := refusedMovingTheController([]string{"zsh"}); err != nil {
|
|
t.Fatalf("another module was refused: %v", err)
|
|
}
|
|
t.Setenv("MESH_VERB", "")
|
|
if err := refusedMovingTheController([]string{"mesh-controller"}); err != nil {
|
|
t.Fatalf("the terminal was refused: %v", err)
|
|
}
|
|
}
|
|
|
|
// An ordinary `settings set|clear` goes down the settings verb's own path: composed as that verb composes it, and
|
|
// run with MESH_VERB set, so its refusals — the terminal-only keys among them — are the settings command's own,
|
|
// not a blanket refusal of the generic command (review of ADR 0272).
|
|
func TestAnOrdinarySettingsLineTakesTheSettingsVerbsPath(t *testing.T) {
|
|
cases := []struct {
|
|
line []string
|
|
want string
|
|
err string
|
|
}{
|
|
{[]string{"settings", "set", "zsh", `{"execute":"withhold"}`, "--node", "laptop"}, `settings set zsh {"execute":"withhold"} --node laptop`, ""},
|
|
{[]string{"settings", "set", "zsh", "{}", "--replace"}, "settings set zsh {} --replace", ""},
|
|
{[]string{"settings", "clear", "zsh", "--node", "laptop"}, "settings clear zsh --node laptop", ""},
|
|
{[]string{"settings", "show", "zsh", "--history"}, "settings show zsh --history", ""},
|
|
{[]string{"settings", "preferences"}, "settings preferences", ""},
|
|
{[]string{"settings", "set", "zsh"}, "", "settings"},
|
|
{[]string{"settings", "set", "zsh", "{}", "--sideways"}, "", "--sideways"},
|
|
}
|
|
for _, c := range cases {
|
|
argv, err := ordinaryLine(c.line)
|
|
if c.err != "" {
|
|
if err == nil || !strings.Contains(err.Error(), c.err) {
|
|
t.Errorf("%q: refused with %v, want %q", c.line, err, c.err)
|
|
}
|
|
continue
|
|
}
|
|
if err != nil || strings.Join(argv, " ") != c.want {
|
|
t.Errorf("%q: composed %q (%v), want %q", c.line, argv, err, c.want)
|
|
}
|
|
}
|
|
// Anything else still meets the generic command verb's refusals.
|
|
if _, err := ordinaryLine([]string{"retire", "delete", "x"}); err == nil {
|
|
t.Error("a repair composed as an ordinary line")
|
|
}
|
|
}
|
|
|
|
// Every line is said in the controller's journal, with its call, who asked where and how it ran — its command word
|
|
// only, never the rest of the line (review of ADR 0272).
|
|
func TestEveryMeshCLILineIsSaidInTheJournal(t *testing.T) {
|
|
t.Setenv(echoEnvironment, "1")
|
|
var said []string
|
|
was := cliJournal
|
|
cliJournal = func(line string) { said = append(said, line) }
|
|
t.Cleanup(func() { cliJournal = was })
|
|
ctx := link.WithCallID(context.Background(), "call-1")
|
|
runForMeshCLI(ctx, "control", cliAsked("operator", 1000, "settings", "set", "x", `{"password":"s3cret"}`),
|
|
cliVerdict{terminal: true, why: "the terminal"})
|
|
runForMeshCLI(ctx, "control", cliAsked("agent", 1001, "status"), cliVerdict{refused: "agent is not answered"})
|
|
all := strings.Join(said, "\n")
|
|
if len(said) != 2 || !strings.Contains(all, "call-1") || !strings.Contains(all, "operator on control") ||
|
|
!strings.Contains(all, "as the controller's terminal") || !strings.Contains(all, "refused") {
|
|
t.Fatalf("the journal said %q", said)
|
|
}
|
|
if strings.Contains(all, "s3cret") {
|
|
t.Fatalf("the journal carries the line's values: %q", said)
|
|
}
|
|
}
|
|
|
|
// **An ordinary line runs only what the generic command verb would** (review of ADR 0272, ADR 0266): its reading
|
|
// forms, and never a command that is the terminal's alone. Each of these, from another node's operator, is refused
|
|
// and runs nothing.
|
|
func TestAnOrdinaryLineRunsNothingTheCommandVerbWouldRefuse(t *testing.T) {
|
|
t.Setenv(echoEnvironment, "1")
|
|
for _, line := range [][]string{
|
|
{"node", "account", "control", "x"},
|
|
{"node", "agent-account", "control", "x", "--clear"},
|
|
{"token", "issue", "laptop"},
|
|
{"secret", "export", "x"},
|
|
{"operator", "key", "set", "x"},
|
|
{"assign", "laptop", "zsh"},
|
|
} {
|
|
if _, err := ordinaryLine(line); err == nil {
|
|
t.Errorf("%q composed as an ordinary line", line)
|
|
}
|
|
a := runForMeshCLI(context.Background(), "laptop", cliAsked("operator", 1000, line...), cliVerdict{why: "not the terminal"})
|
|
if a.Refused == "" || len(a.Stdout) != 0 {
|
|
t.Errorf("%q ran as an ordinary line: %+v", line, a)
|
|
}
|
|
}
|
|
if argv, err := ordinaryLine([]string{"status"}); err != nil || argv[0] != "status" {
|
|
t.Fatalf("a read was refused: %v", err)
|
|
}
|
|
}
|
|
|
|
// **`calls` never shows a mesh-cli line's answer** (review of ADR 0272): the verb's own answer is read for a line
|
|
// served over a bus, and neither the answer's text nor the base64 JSON writes its bytes in is there.
|
|
func TestTheCallsVerbNeverShowsAMeshCLILinesAnswer(t *testing.T) {
|
|
conn, err := nats.Connect(testbus.URL(t))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer conn.Close()
|
|
stop, err := link.OverNATS{Conn: conn}.ServeCLI(func(context.Context, string, link.CLIAsked) link.CLIAnswer {
|
|
return link.CLIAnswer{Stdout: []byte("s3cret-join")}
|
|
}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer stop()
|
|
body, _ := json.Marshal(link.CLIAsked{Line: []string{"token", "issue", "x"}, Account: "operator"})
|
|
msg, err := conn.Request(link.CLISubject("control"), body, 5*time.Second)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(string(msg.Data), base64.StdEncoding.EncodeToString([]byte("s3cret-join"))) {
|
|
t.Fatalf("the asker was not given its answer: %s", msg.Data)
|
|
}
|
|
recent, _ := link.Calls.Recent()
|
|
var id string
|
|
for _, c := range recent {
|
|
if c.Seat == link.CLISeat {
|
|
id = c.ID
|
|
break
|
|
}
|
|
}
|
|
shown, err := callsAnswer(link.Calls, id)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
said, _ := json.Marshal(shown)
|
|
if strings.Contains(string(said), "s3cret-join") ||
|
|
strings.Contains(string(said), base64.StdEncoding.EncodeToString([]byte("s3cret-join"))) {
|
|
t.Fatalf("calls showed a mesh-cli line's answer: %s", said)
|
|
}
|
|
}
|
|
|
|
// **Only the terminal's line carries the terminal's mark** (review of ADR 0272): a mark the serving controller's
|
|
// environment holds — leaked, or set by anything — is stripped from every other command line it runs, a verb's and
|
|
// an ordinary mesh-cli line alike, so neither reads as the terminal.
|
|
func TestTheTerminalsMarkIsStrippedFromEveryOtherLine(t *testing.T) {
|
|
t.Setenv(echoEnvironment, "1")
|
|
t.Setenv(cliTerminalVar, "1")
|
|
t.Setenv(servedVar, "1")
|
|
for _, env := range [][]string{commandEnvironment("someone", "status", false)} {
|
|
for _, kv := range env {
|
|
if strings.HasPrefix(kv, cliTerminalVar+"=") {
|
|
t.Fatalf("a verb's line carries the terminal's mark: %s", kv)
|
|
}
|
|
}
|
|
}
|
|
a := runForMeshCLI(context.Background(), "laptop", cliAsked("operator", 1000, "status"), cliVerdict{why: "not the terminal"})
|
|
if got := string(a.Stdout); !strings.Contains(got, "terminal=false") || !strings.Contains(got, `verb="mesh-cli"`) {
|
|
t.Fatalf("an ordinary line with the mark in the serving environment ran as %s", got)
|
|
}
|
|
a = runForMeshCLI(context.Background(), "control", cliAsked("operator", 1000, "status"), cliVerdict{terminal: true})
|
|
if got := string(a.Stdout); !strings.Contains(got, "terminal=true") {
|
|
t.Fatalf("the terminal's line ran as %s", got)
|
|
}
|
|
}
|
|
|
|
// **Only a login session is the terminal** (review of ADR 0272): the operator's account on the control-node, asking
|
|
// from a service — the tool runner, a user unit — and not a login session, is an ordinary call.
|
|
func TestOnlyALoginSessionIsTheTerminal(t *testing.T) {
|
|
control := []string{"control"}
|
|
v := judgeCLI("control", link.CLIAsked{Line: []string{"status"}, Account: "operator", UID: 1000}, cliNodes, control)
|
|
if v.terminal || v.refused != "" || !strings.Contains(v.why, "login session") {
|
|
t.Fatalf("a line from no login session reads %+v", v)
|
|
}
|
|
v = judgeCLI("control", link.CLIAsked{Line: []string{"status"}, Account: "operator", UID: 1000, Session: "session-3.scope"},
|
|
cliNodes, control)
|
|
if !v.terminal {
|
|
t.Fatalf("a line from a login session on the control-node reads %+v", v)
|
|
}
|
|
}
|