208 lines
8.0 KiB
Go
208 lines
8.0 KiB
Go
package check
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
// The judged_by answer of mesh-build-go's digest and test steps (ADR 0535 §5; the milestone file's interface for issue
|
|
// 1898): "installed" for a step its installed program ran, "candidate <merge commit>" for one the candidate's program ran
|
|
// under runner workspace; an installed module older than the runner names nothing (it ignores the unknown argument: the
|
|
// SDK's tool server checks no argument against the input schema).
|
|
|
|
const buildGoFile = "modules/mesh-build-go/cmd/mesh-build-go/digest.go"
|
|
|
|
// candidateBench is a bench whose fetch changes the Go build module's directory, and whose build module runs the candidate
|
|
// when asked to (a module with the runner) or ignores the runner (one without).
|
|
func candidateBench(t *testing.T, takesRunner bool) *bench {
|
|
b := newBench(t)
|
|
b.fetch.Changed = []string{"README.md", buildGoFile}
|
|
judged := func(c stepCall) string {
|
|
if !takesRunner {
|
|
return ""
|
|
}
|
|
if c.args["runner"] == RunnerWorkspace {
|
|
return "candidate " + b.fetch.Merge
|
|
}
|
|
return "installed"
|
|
}
|
|
b.steps.handlers["build_go_digest"] = func(c stepCall) (any, error) {
|
|
d := echoBeside(b, b.digest[c.args["level"].(string)])
|
|
d.JudgedBy = judged(c)
|
|
return d, nil
|
|
}
|
|
b.test = func(c stepCall) (any, error) {
|
|
r := captured[Tested](t, "go-test-slow")
|
|
r.Level, _ = c.args["level"].(string)
|
|
r.JudgedBy = judged(c)
|
|
return r, nil
|
|
}
|
|
return b
|
|
}
|
|
|
|
func TestAMergeChangingTheGoBuildModuleIsAskedWithTheCandidateRunner(t *testing.T) { // 1898/AC1
|
|
b := candidateBench(t, true)
|
|
b.judge(t, caseOf(t, pullOf(catalogueRepository, 9, head1)))
|
|
asked := append(b.steps.tool("digest"), b.steps.tool("test")...)
|
|
if len(asked) < 3 {
|
|
t.Fatalf("asked %+v, want the digest and both levels", asked)
|
|
}
|
|
for _, c := range asked {
|
|
if c.args["runner"] != RunnerWorkspace {
|
|
t.Errorf("%s %v was asked with runner %v, want %s", c.tool, c.args["level"], c.args["runner"], RunnerWorkspace)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAMergeNotChangingTheGoBuildModuleIsAskedWithoutARunner(t *testing.T) { // 1898/AC1
|
|
for _, tc := range []struct {
|
|
name, repo, path string
|
|
}{
|
|
{"another module of the catalogue", catalogueRepository, "modules/mesh-build-node/cmd/mesh-build-node/main.go"},
|
|
{"a look-alike path in another repository", repoA, buildGoFile},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
b := candidateBench(t, true)
|
|
b.fetch.Changed = []string{tc.path}
|
|
out := b.judge(t, caseOf(t, pullOf(tc.repo, 9, head1)))
|
|
for _, c := range append(b.steps.tool("digest"), b.steps.tool("test")...) {
|
|
if _, ok := c.args["runner"]; ok {
|
|
t.Errorf("%s was asked with runner %v", c.tool, c.args["runner"])
|
|
}
|
|
}
|
|
if len(out.Judged) != 0 {
|
|
t.Errorf("judged = %v, want nothing said of the program", out.Judged)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAnInstalledModuleWithoutTheRunnerJudgesAsTodayAndTheLineSaysSo(t *testing.T) { // 1898/AC2
|
|
b := candidateBench(t, false)
|
|
cs := caseOf(t, pullOf(catalogueRepository, 9, head1))
|
|
out := b.judge(t, cs)
|
|
if out.Verdict.State != delivery.VerdictPass {
|
|
t.Fatalf("verdict = %+v", out.Verdict)
|
|
}
|
|
// As today: the digests key kept verdicts, and the levels are asked without the runner the module ignored.
|
|
for _, c := range b.steps.tool("test") {
|
|
if _, ok := c.args["runner"]; ok {
|
|
t.Errorf("the %v level was asked again with the runner the module does not take", c.args["level"])
|
|
}
|
|
}
|
|
if keys, _ := b.kept.Keys(context.Background()); len(keys) != 2 {
|
|
t.Errorf("kept %v, want the passing package at both levels, as today", keys)
|
|
}
|
|
p := cs.Pulls[0]
|
|
want := "judged by the installed mesh-build-go: it does not run a candidate yet"
|
|
if got := out.Judged[pullKey(p)]; got != want {
|
|
t.Fatalf("judged = %q, want %q", got, want)
|
|
}
|
|
if l := Line(out.Verdict, cs, p, out.Builds, out.Slowest, out.Judged); !strings.Contains(l, want) {
|
|
t.Errorf("line %q does not say %q", l, want)
|
|
}
|
|
}
|
|
|
|
func TestACandidateJudgedCheckTakesNoKeptVerdictAndWritesNone(t *testing.T) { // 1898/AC3
|
|
b := candidateBench(t, true)
|
|
if _, err := (Kept{Store: b.kept}).Keep(context.Background(), "unit", digA, keptValue("fixture/slow", 2)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
out := b.judge(t, caseOf(t, pullOf(catalogueRepository, 9, head1)))
|
|
if out.Verdict.State != delivery.VerdictPass {
|
|
t.Fatalf("verdict = %+v", out.Verdict)
|
|
}
|
|
tests := b.steps.tool("test")
|
|
if len(tests) != 2 {
|
|
t.Fatalf("test calls = %+v, want both levels", tests)
|
|
}
|
|
for _, c := range tests {
|
|
if _, ok := c.args["packages"]; ok {
|
|
t.Errorf("the %v level was asked for %v, want it whole", c.args["level"], c.args["packages"])
|
|
}
|
|
}
|
|
if u := out.Verdict.Levels[delivery.LevelUnit]; u.Kept != 0 {
|
|
t.Errorf("unit = %+v, want nothing taken", u)
|
|
}
|
|
if keys, _ := b.kept.Keys(context.Background()); len(keys) != 1 || keys[0] != "unit."+digA {
|
|
t.Errorf("kept keys = %v, want only the one kept before", keys)
|
|
}
|
|
}
|
|
|
|
func TestTheLineNamesTheCandidateThatJudged(t *testing.T) { // 1898/AC6
|
|
b := candidateBench(t, true)
|
|
cs := caseOf(t, pullOf(catalogueRepository, 9, head1))
|
|
out := b.judge(t, cs)
|
|
p := cs.Pulls[0]
|
|
want := "judged by the candidate's mesh-build-go at " + b.fetch.Merge[:12]
|
|
for name, l := range map[string]string{
|
|
"line": Line(out.Verdict, cs, p, out.Builds, out.Slowest, out.Judged),
|
|
"report": Report(out.Verdict, cs, p, out.Builds, out.Slowest, out.Judged),
|
|
} {
|
|
if !strings.Contains(l, want) {
|
|
t.Errorf("the %s %q does not say %q", name, l, want)
|
|
}
|
|
}
|
|
// A long failing line keeps the program it was judged by; only the lesser parts go.
|
|
v := out.Verdict
|
|
v.State = delivery.VerdictFail
|
|
v.Failed = delivery.FailedFirst{Test: "unit: git.novox.be/novox/mesh-catalog/modules/mesh-build-go/cmd/mesh-build-go.TestADigestFoldsTheSiblings", Words: strings.Repeat("x", 60)}
|
|
if l := Line(v, cs, p, out.Builds, out.Slowest, out.Judged); !strings.Contains(l, "judged by the candidate's") {
|
|
t.Errorf("the long line %q dropped the program", l)
|
|
}
|
|
}
|
|
|
|
func TestInAGroupOnlyTheBuildModulesPullRequestNamesItsProgram(t *testing.T) { // 1898/AC6
|
|
b := candidateBench(t, true)
|
|
cs := caseOf(t, pullOf(catalogueRepository, 9, head1), pullOf(repoA, 7, head1))
|
|
out := b.judge(t, cs)
|
|
for _, p := range cs.Pulls {
|
|
l := Report(out.Verdict, cs, p, out.Builds, out.Slowest, out.Judged)
|
|
says := strings.Contains(l, "judged by")
|
|
if says != (p.Repository == catalogueRepository) {
|
|
t.Errorf("%s#%d: %q", p.Repository, p.Number, l)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestACandidateThatDoesNotCompileFailsTheCheckWithTheCompilersWords(t *testing.T) { // ADR 0535 §3, checker side
|
|
b := candidateBench(t, true)
|
|
refusal := "the candidate's mesh-build-go does not compile: modules/mesh-build-go/cmd/mesh-build-go/digest.go:12:2: undefined: x"
|
|
b.steps.handlers["build_go_digest"] = func(stepCall) (any, error) { return nil, errorString(refusal) }
|
|
b.test = func(stepCall) (any, error) { return nil, errorString(refusal) }
|
|
out := b.judge(t, caseOf(t, pullOf(catalogueRepository, 9, head1)))
|
|
if out.Verdict.State != delivery.VerdictFail || !strings.Contains(out.Verdict.Failed.Words, "undefined: x") {
|
|
t.Fatalf("verdict = %+v, want a failure naming the compile error", out.Verdict)
|
|
}
|
|
if n := len(b.steps.tool("test")); n != 1 {
|
|
t.Errorf("%d test steps, want the first one's failure to end the check", n)
|
|
}
|
|
}
|
|
|
|
type errorString string
|
|
|
|
func (e errorString) Error() string { return string(e) }
|
|
|
|
func TestTheStatusTheForgeShowsNamesTheProgramThatJudged(t *testing.T) { // 1898/AC6, the forge's line
|
|
ctx := context.Background()
|
|
words := judgedByCandidate + "fedcba987654"
|
|
j := &judgeFunc{f: func(context.Context, *Case, int) (Outcome, error) {
|
|
return Outcome{Verdict: passing(), Judged: map[string]string{repoA + "#7": words}}, nil
|
|
}}
|
|
r := newRig(t, j)
|
|
r.ch.Candidate(ctx, "feat/x", candidateOf(t, "feat/x", pullOf(repoA, 7, head1)))
|
|
r.ch.Wait()
|
|
key, _ := delivery.CheckerOwedKey(repoA, 7, head1)
|
|
raw, _, _, _ := r.owed.Get(ctx, key)
|
|
o, err := delivery.DecodeCheckerOwed(raw)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if o.State != "success" || !strings.Contains(o.Text, words) {
|
|
t.Errorf("status %+v, want success saying %q", o, words)
|
|
}
|
|
}
|