126 lines
5.0 KiB
Go
126 lines
5.0 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
"github.com/novox/mesh-controller/internal/link"
|
|
)
|
|
|
|
// A branch is tried before its pull request (novox/hq issue 686): the repository's own check asked for a pushed
|
|
// branch or commit, with no pull request.
|
|
|
|
// A try's outcome is heard and left there: no `checked` is said, so the forge's holder sets no status, and
|
|
// nothing is recorded or registered (the handler holds no store here, and would fail reaching for one).
|
|
|
|
// saidOnTheBus keeps the seat events said, and nothing else.
|
|
type saidOnTheBus struct {
|
|
link.Bus
|
|
events []string
|
|
bodies [][]byte
|
|
}
|
|
|
|
func (s *saidOnTheBus) PublishSeatEvent(_ context.Context, seat, event string, body []byte) error {
|
|
s.events = append(s.events, seat+" "+event)
|
|
s.bodies = append(s.bodies, body)
|
|
return nil
|
|
}
|
|
|
|
// A ref the forge does not hold is refused before anything is asked, naming it.
|
|
func TestATryOfAnUnknownRefIsRefusedLoudly(t *testing.T) {
|
|
was := forgeCommit
|
|
defer func() { forgeCommit = was }()
|
|
forgeCommit = func(_ context.Context, owner, repo, ref string) (string, string, error) {
|
|
return "", "", errors.New("404 object does not exist [id: " + ref + "]")
|
|
}
|
|
err := tryCommand(context.Background(), []string{"novox/mesh-controller", "fix/no-such-branch"})
|
|
if err == nil {
|
|
t.Fatal("a try of a ref the forge does not hold was asked")
|
|
}
|
|
for _, want := range []string{"fix/no-such-branch", "novox/mesh-controller", "nothing was asked", "404"} {
|
|
if !strings.Contains(err.Error(), want) {
|
|
t.Errorf("the refusal does not say %q: %v", want, err)
|
|
}
|
|
}
|
|
for _, args := range [][]string{{"mesh-controller", "main"}, {"novox/mesh-controller"}, {"novox/a", "--upload-pack=x"},
|
|
{"novox/a", "main", "--tests", "x; rm -rf /"}, {"novox/a", "main", "--id", "build-1"}} {
|
|
if err := tryCommand(context.Background(), args); err == nil {
|
|
t.Errorf("try %v was taken", args)
|
|
}
|
|
}
|
|
}
|
|
|
|
// The answer of a try is the checker's word, in today's shape (ADR 0501 §5; issue 1851): its verdict, the failing
|
|
// line, how long it took and where; queued is waiting; one that could not run is an error, never a pass.
|
|
func TestATrysAnswerIsTheCheckersWord(t *testing.T) {
|
|
var asked []string
|
|
was := askChecker
|
|
askChecker = func(_ context.Context, tool string, args map[string]any, into any) error {
|
|
asked = append(asked, tool)
|
|
raw, _ := json.Marshal(checkerTryRead{ID: "try-1", Repository: "novox/a", Head: "c0ffee", Trunk: "ca3f43c",
|
|
State: "fail", Said: "the unit level failed: TestX", Took: "5m3s",
|
|
Failed: []string{"unit: a/cmd.TestX: got 1, want 2"}, Machines: []string{"ace"}})
|
|
return json.Unmarshal(raw, into)
|
|
}
|
|
t.Cleanup(func() { askChecker = was })
|
|
if err := tryRead(t.Context(), "try-1"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(asked) != 1 || asked[0] != "checker_try_read" {
|
|
t.Fatalf("a try is read from %v, not the checker", asked)
|
|
}
|
|
a := tryOfChecker(checkerTryRead{ID: "try-1", State: "fail", Took: "5m3s", Head: "c0ffee", Trunk: "ca3f43c",
|
|
Failed: []string{"unit: a/cmd.TestX: got 1, want 2"}, Machines: []string{"ace"}})
|
|
if a.State != "fail" || a.Took != "5m3s" || len(a.Failed) != 1 || a.Commit != "c0ffee" || a.Trunk != "ca3f43c" || a.On != "ace" {
|
|
t.Fatalf("the answer lost the verdict, the time, the failing line or where: %+v", a)
|
|
}
|
|
if a := tryOfChecker(checkerTryRead{ID: "try-2", State: "queued"}); a.State != "waiting" || a.Said == "" {
|
|
t.Fatalf("a try the checker has not started: %+v", a)
|
|
}
|
|
if a := tryOfChecker(checkerTryRead{ID: "try-3", State: "error", Said: "no node holds the go build module"}); a.State != "error" {
|
|
t.Fatalf("a try that could not run: %+v", a)
|
|
}
|
|
// An id is checked before it is asked of anybody.
|
|
for _, id := range []string{"x.>", "*", "a b", "build-1"} {
|
|
if err := tryCommand(context.Background(), []string{"--id", id}); err == nil ||
|
|
!strings.Contains(err.Error(), "not a try's id") {
|
|
t.Errorf("try --id %q: %v", id, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// The verb runs its command, and is declared with the shell habit it replaces (novox/hq ADR 0245).
|
|
func TestTheTryVerbRunsItsCommand(t *testing.T) {
|
|
for _, c := range []struct {
|
|
args map[string]any
|
|
want []string
|
|
}{
|
|
{map[string]any{"repository": "novox/a", "ref": "fix/x"}, []string{"try", "novox/a", "fix/x"}},
|
|
{map[string]any{"repository": "novox/a", "ref": "fix/x", "base": "main", "tests": "^TestX$", "store-only": "true"},
|
|
[]string{"try", "novox/a", "fix/x", "--base", "main", "--tests", "^TestX$", "--store-only"}},
|
|
{map[string]any{"id": "build-1"}, []string{"try", "--id", "build-1"}},
|
|
} {
|
|
got, err := argvFor("try", c.args)
|
|
if err != nil || !reflect.DeepEqual(got, c.want) {
|
|
t.Errorf("try %v: %v %v, want %v", c.args, got, err, c.want)
|
|
}
|
|
}
|
|
if _, err := argvFor("try", map[string]any{"repository": "novox/a"}); err == nil {
|
|
t.Error("a try of no ref was taken")
|
|
}
|
|
for _, v := range catalogue.ControllerVerbs {
|
|
if v.Name == "try" {
|
|
if len(v.Replaces) == 0 {
|
|
t.Error("try names no shell habit it replaces")
|
|
}
|
|
return
|
|
}
|
|
}
|
|
t.Error("try is not a verb of the controller seat")
|
|
}
|