129 lines
6.3 KiB
Go
129 lines
6.3 KiB
Go
package main
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
"github.com/novox/mesh-controller/internal/link"
|
|
)
|
|
|
|
// 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.
|
|
|
|
// An ordinary call meets every refusal of the generic command verb, and nothing runs; a server is never run.
|
|
|
|
// **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).
|
|
|
|
// 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).
|
|
|
|
// **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.
|
|
|
|
// **`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.
|
|
|
|
// **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.
|
|
|
|
// **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)
|
|
}
|
|
}
|