142 lines
5.0 KiB
Go
142 lines
5.0 KiB
Go
//go:build integration
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
"github.com/novox/mesh-controller/internal/link"
|
|
)
|
|
|
|
// C1 (ADR 0503 §3, §5; issue 1299, lead decision L11), with the store and a real build queue: a pull request of a
|
|
// repository whose trunk requires mesh/check asks build-agent nothing and says nothing; one whose trunk requires
|
|
// mesh/repo-check alone (hq) has its own merge-check.sh asked, and every verdict said names no gate layer; a protection
|
|
// that cannot be read asks and says nothing.
|
|
func TestThePullRequestChecksAreAskedOnlyForARepositoryTheCheckerDoesNotJudge(t *testing.T) {
|
|
open := aMesh(t)
|
|
ctx := t.Context()
|
|
js, _ := aBuildQueue(t)
|
|
servingBus.Store(js)
|
|
t.Cleanup(func() { servingBus.Store(nil) })
|
|
said := &saidOnTheBus{}
|
|
checkEvents = said
|
|
t.Cleanup(func() { checkEvents = nil })
|
|
if err := open.inventory.RegisterModule(ctx, catalogue.Manifest{Module: "mesh-controller", Version: "1"},
|
|
inventory.Source{Repository: "novox/mesh-controller", Ref: "main", BuiltFrom: "c0", Head: "c0"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
required := map[string][]string{"novox/mesh-controller": {"mesh/check"}, "novox/hq": {"mesh/repo-check"}}
|
|
was := requiredStatusesOf
|
|
requiredStatusesOf = func(_ context.Context, owner, repo, _ string) ([]string, error) {
|
|
if r, ok := required[owner+"/"+repo]; ok {
|
|
return r, nil
|
|
}
|
|
return nil, errors.New("the forge did not answer")
|
|
}
|
|
t.Cleanup(func() { requiredStatusesOf = was })
|
|
queued := func() int {
|
|
q, err := link.ReadQueue(ctx, js, link.TheBuildMachine)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return len(q.Asks)
|
|
}
|
|
pull := func(repo string, number int, mergeCheck bool) link.PullUpdated {
|
|
p := link.PullUpdated{Owner: "novox", Repo: repo, Number: number, Base: "main", Commit: "abcdefabcdefabcdefabcdefabcdefabcdefabcd",
|
|
BaseSHA: "1111111111111111111111111111111111111111", Paths: []string{"main.go"}, ModuleDirsSaid: true}
|
|
p.MergeCheck, p.MergeCheckSaid = mergeCheck, true
|
|
return p
|
|
}
|
|
f := following{open: open, oneShot: true}
|
|
|
|
if err := f.PullUpdated(ctx, pull("mesh-controller", 7, true)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n := queued(); n != 0 || len(said.events) != 0 {
|
|
t.Fatalf("0503 §3: a repository the checker judges asked %d check(s) and said %v", n, said.events)
|
|
}
|
|
if err := f.PullUpdated(ctx, pull("unread", 3, true)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n := queued(); n != 0 || len(said.events) != 0 {
|
|
t.Fatalf("L11: a protection that cannot be read asked %d check(s) and said %v", n, said.events)
|
|
}
|
|
if err := f.PullUpdated(ctx, pull("hq", 9, true)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
q, err := link.ReadQueue(ctx, js, link.TheBuildMachine)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(q.Asks) != 1 || q.Asks[0].Request.Check == nil || len(q.Asks[0].Request.Check.Modules) != 0 ||
|
|
q.Asks[0].Request.Check.Repo != "hq" {
|
|
t.Fatalf("0503 §5: hq's repository check is not the one ask: %+v", q.Asks)
|
|
}
|
|
// A head with no merge-check.sh is said at once, with the repo-check layer alone.
|
|
if err := f.PullUpdated(ctx, pull("hq", 10, false)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(said.events) != 1 {
|
|
t.Fatalf("hq's head with no merge-check.sh was said %d time(s)", len(said.events))
|
|
}
|
|
var c link.Checked
|
|
if err := json.Unmarshal(said.bodies[0], &c); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if c.Gate != nil || c.RepoCheck == nil || c.RepoCheck.Verdict != "warning" {
|
|
t.Fatalf("0503 §5: the verdict said names a gate layer, or no repo-check layer: %+v", c)
|
|
}
|
|
}
|
|
|
|
// C2 (ADR 0503 §3; issue 1299): a delivery group's composed check asks build-agent nothing and answers that the checker
|
|
// judges the group.
|
|
func TestTheGroupCheckAsksNothingAndNamesTheChecker(t *testing.T) {
|
|
aMesh(t)
|
|
ctx := t.Context()
|
|
js, _ := aBuildQueue(t)
|
|
servingBus.Store(js)
|
|
t.Cleanup(func() { servingBus.Store(nil) })
|
|
members := `[{"id":"novox/a@111111111111","repository":"novox/a","base":"main","head":"1111111111111111111111111111111111111111","number":1},` +
|
|
`{"id":"novox/b@222222222222","repository":"novox/b","base":"main","head":"2222222222222222222222222222222222222222","number":2}]`
|
|
out := capturedStdout(t, func() error {
|
|
return deliveryCommand(ctx, []string{"check", "--group", "feat/x", "--members", members})
|
|
})
|
|
if !strings.Contains(out, `"judged-by": "mesh-delivery-checker"`) || !strings.Contains(out, `"asked": ""`) {
|
|
t.Fatalf("0503 §3: the group check answered %s", out)
|
|
}
|
|
q, err := link.ReadQueue(ctx, js, link.TheBuildMachine)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(q.Asks) != 0 {
|
|
t.Fatalf("0503 §3: the group check asked %+v", q.Asks)
|
|
}
|
|
}
|
|
|
|
// capturedStdout is what run writes to standard output.
|
|
func capturedStdout(t *testing.T, run func() error) string {
|
|
t.Helper()
|
|
r, w, err := os.Pipe()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
was := os.Stdout
|
|
os.Stdout = w
|
|
runErr := run()
|
|
os.Stdout = was
|
|
w.Close()
|
|
out, _ := io.ReadAll(r)
|
|
if runErr != nil {
|
|
t.Fatal(runErr)
|
|
}
|
|
return string(out)
|
|
}
|