diff --git a/cmd/mesh-controller/status.go b/cmd/mesh-controller/status.go index 22f3ae3..00d003a 100644 --- a/cmd/mesh-controller/status.go +++ b/cmd/mesh-controller/status.go @@ -46,15 +46,21 @@ func statusCommand(ctx context.Context, args []string) error { return err } defer open.Close() + return statusFor(ctx, open, *asJSON) +} +// statusFor asks and answers, against stores somebody else opened. +// +// Split from the command so what it prints can be read by a test. The sentence it prints when nothing +// is wrong has been acted on and been misleading (novox/hq 04-ISSUES/145, 125), which makes its exact +// words the thing worth holding still. +func statusFor(ctx context.Context, open *stores, asJSON bool) error { asked, err := theThreeQuestions(ctx, open) if err != nil { return err } - wrong, nodes, quiet := asked.wrong, asked.nodes, asked.quiet - behind, sources := asked.behind, asked.sources - if *asJSON { + if asJSON { body, err := statusAsJSON(asked) if err != nil { return err @@ -62,6 +68,18 @@ func statusCommand(ctx context.Context, args []string) error { fmt.Println(string(body)) return nil } + return printStatus(asked) +} + +// printStatus is the words, separated from the questions. +// +// **Its exact sentences have been acted on and been misleading twice** — a held module reading as a +// machine doing what it was told (novox/hq 04-ISSUES/125), and "all doing what they were told" being +// true of a mesh in which no module could reach another (04-ISSUES/145). So they are written where a +// test can read them without a store, a bus or a machine. +func printStatus(asked answers) error { + wrong, nodes, quiet := asked.wrong, asked.nodes, asked.quiet + behind, sources := asked.behind, asked.sources if len(asked.refused) > 0 { // First, above everything else. A machine that cannot be worked out is not running an old @@ -235,6 +253,18 @@ func statusCommand(ctx context.Context, args []string) error { // and getting here means every question was asked and answered. fmt.Printf("%d machine(s), all doing what they were told, all heard from, running what "+ "the mesh would send them, and every module current with its source\n", len(nodes)) + // **And what that sentence does not cover**, because for eleven hours it was true of a mesh + // in which no module could reach another (novox/hq 04-ISSUES/145). Every question above is + // about the relationship between the mesh and a machine — applied what it was sent, matches + // what would be sent, built from what the source has. None of them asks whether a module can + // reach what it requires, and the mesh composes every one of those grants itself. + // + // Said here rather than left to be inferred. A reader who acts on the line above is acting on + // "the machines are as the mesh described them", and the distance between that and "it works" + // is where the eleven hours went. + fmt.Printf("\n That is the mesh and the machines agreeing. Nothing here dials a provision:\n" + + " no grant the mesh composed has been tested, so a module unable to reach what it\n" + + " requires would not appear above (04-ISSUES/145)\n") } return nil } diff --git a/cmd/mesh-controller/well_scope_test.go b/cmd/mesh-controller/well_scope_test.go new file mode 100644 index 0000000..f0baa6b --- /dev/null +++ b/cmd/mesh-controller/well_scope_test.go @@ -0,0 +1,75 @@ +package main + +import ( + "bytes" + "io" + "os" + "strings" + "testing" + + "github.com/novox/mesh-controller/internal/inventory" +) + +// "4 machine(s), all doing what they were told, all heard from, running what the mesh would send +// them, and every module current with its source" was true for eleven hours of a mesh in which no +// module could reach another (novox/hq 04-ISSUES/145). Every question it answers is about the mesh +// and a machine agreeing; none of them dials anything. + +// printed captures what a function writes to stdout. +func printed(t *testing.T, f func() error) string { + t.Helper() + old := os.Stdout + r, w, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + os.Stdout = w + runErr := f() + _ = w.Close() + os.Stdout = old + var buf bytes.Buffer + if _, err := io.Copy(&buf, r); err != nil { + t.Fatal(err) + } + if runErr != nil { + t.Fatal(runErr) + } + return buf.String() +} + +func TestTheAllWellSentenceSaysWhatItDoesNotCover(t *testing.T) { + // A mesh with nothing to say. The sentence below was true of a mesh in which no module could + // reach another, for eleven hours. + got := printed(t, func() error { + return printStatus(answers{nodes: []inventory.Node{{Name: "anchor"}, {Name: "laptop"}}}) + }) + if !strings.Contains(got, "all doing what they were told") { + t.Fatalf("a mesh with nothing to say did not print the all-well sentence:\n%s", got) + } + // And now says what it is not a claim about. + for _, want := range []string{"Nothing here dials a provision", "04-ISSUES/145"} { + if !strings.Contains(got, want) { + t.Fatalf("the all-well sentence does not say %q:\n%s", want, got) + } + } +} + +func TestAMeshWithSomethingToSayDoesNotPrintTheScopeLine(t *testing.T) { + // The scope belongs to the all-well sentence. A mesh with something wrong has specific things to + // read, and appending a caveat to those is noise. + got := printed(t, func() error { + return printStatus(answers{ + nodes: []inventory.Node{{Name: "anchor"}}, + untaken: map[string]map[string]int{"anchor": {"route-proxy": 3}}, + }) + }) + if strings.Contains(got, "Nothing here dials a provision") { + t.Fatalf("a mesh with a held module printed the all-well scope line:\n%s", got) + } + if strings.Contains(got, "all doing what they were told") { + t.Fatalf("a mesh with a held module printed the all-well sentence:\n%s", got) + } + if !strings.Contains(got, "route-proxy") { + t.Fatalf("the held module is not named:\n%s", got) + } +}