package main import ( "bytes" "context" "encoding/json" "fmt" "os" "strings" "testing" "github.com/novox/mesh-controller/internal/link" ) // `delivery check` with one head answers the id of the check it asked, as data (novox/hq issue 1311): the prose // the check says goes to standard error, so standard output is the document alone and answerOf reads it. func TestDeliveryCheckOfOneHeadAnswersTheRequestID(t *testing.T) { argv := []string{"delivery", "check", "--members", "[]"} doc, err := json.Marshal(recheckAnswer("check-42", "novox/x@abc")) if err != nil { t.Fatal(err) } // Seen failing first: the prose the command used to print on standard output before the document. polluted := append([]byte("novox/x#7 (abc): asked the build seat as check-42\n"), doc...) if got := answerOf(argv, polluted, "", true); got.Answer != nil { t.Fatalf("prose before the document should leave no answer; got %v", got.Answer) } got := answerOf(argv, doc, "novox/x#7 (abc): asked the build seat as check-42\n", true) m, ok := got.Answer.(map[string]any) if !ok || m["asked"] != "check-42" || m["rechecked"] != "novox/x@abc" { t.Fatalf("the answer is %v; want asked check-42 and rechecked novox/x@abc", got.Answer) } if !strings.Contains(got.Output, "asked the build seat") { t.Fatalf("the prose is still said in the text: %q", got.Output) } } // The text of the other callers does not change: sayChecked writes the same line to the writer it is given, and // the controller's own path still gives PullUpdated standard output. func TestSayCheckedWritesTheSameLineToItsWriter(t *testing.T) { var w bytes.Buffer sayChecked(context.Background(), &w, link.Checked{ID: "c1", Owner: "novox", Repo: "x", Number: 7, Commit: "0123456789abcdef", Verdict: "pass", Summary: "fine"}) want := "c1: novox/x#7 at 01234567 checked on a machine that did not say which: gate PASS — fine; repository none\n" if w.String() != want { t.Fatalf("sayChecked said %q; want %q", w.String(), want) } } // Whatever the check prints to standard output while it runs (checks.go namedForTheCheck, the inventory's pointer // repair) must not reach the answer's standard output: it is exactly one JSON document (novox/hq issue 1311). func TestOneHeadRecheckLeavesStandardOutputTheAnswerAlone(t *testing.T) { r, w, err := os.Pipe() if err != nil { t.Fatal(err) } e, ew, err := os.Pipe() if err != nil { t.Fatal(err) } realOut, realErr := os.Stdout, os.Stderr os.Stdout, os.Stderr = w, ew runErr := recheckOneHead("novox/x@abc", func(v any) error { return json.NewEncoder(os.Stdout).Encode(v) }, func() (string, error) { fmt.Println(" the earlier checks' builds could not be read, so the check builds") fmt.Fprintln(os.Stdout, "repaired a pointer") return "check-42", nil }) os.Stdout, os.Stderr = realOut, realErr w.Close() ew.Close() if runErr != nil { t.Fatal(runErr) } var out, errOut bytes.Buffer _, _ = out.ReadFrom(r) _, _ = errOut.ReadFrom(e) var doc map[string]any if err := json.Unmarshal(out.Bytes(), &doc); err != nil || doc["asked"] != "check-42" || doc["rechecked"] != "novox/x@abc" { t.Fatalf("standard output %q is not the answer alone (%v)", out.String(), err) } if !strings.Contains(errOut.String(), "repaired a pointer") { t.Fatalf("the prose should be on standard error: %q", errOut.String()) } }