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.
98 lines
3.2 KiB
Go
98 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-controller/internal/catalogue"
|
|
)
|
|
|
|
// Every verb the mesh-controller seat declares is one this binary can run, with the arguments the
|
|
// schema names and no other (novox/hq ADR 0154, ADR 0035).
|
|
func TestEveryDeclaredVerbHasACommandLine(t *testing.T) {
|
|
for _, v := range catalogue.ControllerVerbs {
|
|
if v.Name == "tools" {
|
|
continue
|
|
}
|
|
args := map[string]any{}
|
|
props, _ := v.Input["properties"].(map[string]any)
|
|
for name := range props {
|
|
args[name] = "x"
|
|
}
|
|
argv, err := argvFor(v.Name, args)
|
|
if err != nil {
|
|
t.Errorf("%s: %v", v.Name, err)
|
|
continue
|
|
}
|
|
if argv[0] == "" {
|
|
t.Errorf("%s: empty command", v.Name)
|
|
}
|
|
}
|
|
}
|
|
|
|
// A required argument missing is refused in the verb's own words, before anything runs.
|
|
func TestAVerbMissingWhatItNeedsIsRefused(t *testing.T) {
|
|
if _, err := argvFor("node", map[string]any{}); err == nil || !strings.Contains(err.Error(), `node needs "node"`) {
|
|
t.Fatalf("node without a machine was accepted: %v", err)
|
|
}
|
|
if _, err := argvFor("upgrade", map[string]any{}); err == nil {
|
|
t.Fatal("a verb the seat does not serve was accepted")
|
|
}
|
|
}
|
|
|
|
// A push and a build are sent, not waited for: the asker reads status for what happened.
|
|
func TestActsDoNotBlockTheCall(t *testing.T) {
|
|
argv, _ := argvFor("push", map[string]any{"node": "one"})
|
|
if strings.Join(argv, " ") != "push one --wait 0" {
|
|
t.Fatalf("push waits: %v", argv)
|
|
}
|
|
argv, _ = argvFor("build", map[string]any{"repository": "novox/x", "path": "modules/x"})
|
|
if strings.Join(argv, " ") != "build novox/x --wait 0 --path modules/x" {
|
|
t.Fatalf("build: %v", argv)
|
|
}
|
|
}
|
|
|
|
// What `tools` answers is the seats' records, with each verb's schema.
|
|
func TestToolsAnswersTheSeatsRecords(t *testing.T) {
|
|
handlers, err := seatToolHandlers()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(handlers) != len(catalogue.ControllerVerbs) {
|
|
t.Fatalf("%d handlers for %d verbs", len(handlers), len(catalogue.ControllerVerbs))
|
|
}
|
|
answer := seatTools()
|
|
seats, _ := answer["seats"].([]map[string]any)
|
|
var found bool
|
|
for _, s := range seats {
|
|
if s["seat"] == catalogue.ControllerSeatName {
|
|
found = true
|
|
tools, _ := s["tools"].([]map[string]any)
|
|
if len(tools) != len(catalogue.ControllerVerbs) || tools[0]["input"] == nil {
|
|
t.Fatalf("the controller seat's tools are not listed in full: %v", tools)
|
|
}
|
|
}
|
|
}
|
|
if !found {
|
|
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)
|
|
}
|
|
}
|