Design 26 has the indexer learn out-of-step generated files from the check's verdict event, so it runs no generator; the test step's answer carries them. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
108 lines
5.7 KiB
Go
108 lines
5.7 KiB
Go
package check
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
// failingResult is a level's test result in which the named tests failed, each in its own package.
|
|
func failingResult(t *testing.T, tests int, failed ...Failure) json.RawMessage {
|
|
t.Helper()
|
|
raw, err := json.Marshal(Tested{Level: delivery.LevelIntegration, Tests: tests, Elapsed: 2, Failed: failed, Passed: len(failed) == 0})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return raw
|
|
}
|
|
|
|
func judgeLevel(t *testing.T, tests ...json.RawMessage) (*delivery.Verdict, *state) {
|
|
t.Helper()
|
|
st := &state{j: &Examiner{}, c: &Case{}, levels: map[string]*acc{delivery.LevelUnit: {}, delivery.LevelIntegration: {}}, nodes: map[string]bool{}}
|
|
v, _ := st.level(t.Context(), &target{pull: Pull{Repository: "novox/x"}}, delivery.LevelAnswer{Level: delivery.LevelIntegration, Tests: tests})
|
|
return settled(joinFailures(v)), st
|
|
}
|
|
|
|
// Issue 1921: two failing tests in two packages are two entries of the verdict's Failures and two lines of a try's
|
|
// failed list, each with its own first line (its cause, issue 1901), whether the build module answered them in one test
|
|
// result (one go test run over both packages) or in two (the changed packages first, then the rest); every result is
|
|
// counted, the ones after a failure too; and Failed keeps the first one's words whole.
|
|
func TestTwoFailingTestsInTwoPackagesAreTwoEntries(t *testing.T) {
|
|
a := Failure{Package: "x/a", Test: "TestA", Cause: "a_test.go:9: want 1, got 2", Output: " a_test.go:3: logged\n a_test.go:9: want 1, got 2\n--- FAIL: TestA (0.00s)"}
|
|
b := Failure{Package: "x/b", Test: "TestB", Cause: "b_test.go:4: boom", Output: " b_test.go:4: boom\n--- FAIL: TestB (0.00s)"}
|
|
for name, tests := range map[string][]json.RawMessage{
|
|
"one result": {failingResult(t, 10, a, b)},
|
|
"two results": {failingResult(t, 4, a), failingResult(t, 3), failingResult(t, 3, b)},
|
|
} {
|
|
v, st := judgeLevel(t, tests...)
|
|
if v == nil {
|
|
t.Fatalf("%s: the level passed", name)
|
|
}
|
|
want := []string{"integration: x/a.TestA: a_test.go:9: want 1, got 2", "integration: x/b.TestB: b_test.go:4: boom"}
|
|
if got := failedLines(*v); strings.Join(got, "\n") != strings.Join(want, "\n") {
|
|
t.Errorf("%s: failed lines\n%s\nwant\n%s", name, strings.Join(got, "\n"), strings.Join(want, "\n"))
|
|
}
|
|
if len(v.Failures) != 2 || v.Failed.Test != "integration: x/a.TestA" || !strings.Contains(v.Failed.Words, "a_test.go:3: logged") {
|
|
t.Errorf("%s: verdict %+v", name, v)
|
|
}
|
|
if run := st.levels[delivery.LevelIntegration].run; run != 10 {
|
|
t.Errorf("%s: %d tests counted, want all 10", name, run)
|
|
}
|
|
if _, err := delivery.EncodeVerdict(delivery.Verdict{Group: "g", Heads: "0123456789ab", State: v.State, Failed: v.Failed, Failures: v.Failures}); err != nil {
|
|
t.Errorf("%s: the verdict is not written: %v", name, err)
|
|
}
|
|
}
|
|
// Design 26 *Out of step* (DLV-C2): the out-of-step files the compare step names, in each result, are the outcome's for
|
|
// the pull request, each once and sorted; a level naming none leaves the outcome with none.
|
|
step := func(files ...string) json.RawMessage {
|
|
raw, _ := json.Marshal(Tested{Level: delivery.LevelUnit, Tests: 1, Passed: false, Why: "out of step", OutOfStep: files})
|
|
return raw
|
|
}
|
|
_, st := judgeLevel(t, step("go/seats/b/contract.go", "go/seats/a/contract.go"), step("go/seats/a/contract.go"))
|
|
if got := st.outcome(delivery.Verdict{State: delivery.VerdictFail}).OutOfStep["novox/x#0"]; strings.Join(got, ",") != "go/seats/a/contract.go,go/seats/b/contract.go" {
|
|
t.Errorf("out of step %v", got)
|
|
}
|
|
if _, st := judgeLevel(t, failingResult(t, 2, a)); st.outcome(delivery.Verdict{State: delivery.VerdictFail}).OutOfStep != nil {
|
|
t.Errorf("a level naming no out-of-step file has %v", st.outOfStep)
|
|
}
|
|
}
|
|
|
|
// Issue 1921: past delivery.MaxFailures the list names the first fifty and how many more, the status line says how many
|
|
// failed and names the first, cut so "and N more" stays whole, and each entry's line is cut to MaxFailureLine.
|
|
func TestFailuresAreCappedWithHowManyMore(t *testing.T) {
|
|
var fs []Failure
|
|
for i := range 60 {
|
|
fs = append(fs, Failure{Package: fmt.Sprintf("x/p%d", i), Test: "TestT", Cause: strings.Repeat("y", 500)})
|
|
}
|
|
v, _ := judgeLevel(t, failingResult(t, 60, fs...))
|
|
if len(v.Failures) != delivery.MaxFailures+1 || v.Failures[delivery.MaxFailures].Test != "and 10 more" {
|
|
t.Fatalf("%d entries, last %+v", len(v.Failures), v.Failures[len(v.Failures)-1])
|
|
}
|
|
if len(v.Failures[0].Words) > MaxFailureLine {
|
|
t.Errorf("an entry's line is %d bytes", len(v.Failures[0].Words))
|
|
}
|
|
if n := failedCount(*v); n != 60 {
|
|
t.Errorf("failedCount = %d, want 60", n)
|
|
}
|
|
cs := &Case{Pulls: []Pull{{Repository: "novox/x", Head: "0123456789abcdef", Trunk: "fedcba9876543210"}}}
|
|
if line := Line(*v, cs, cs.Pulls[0], nil, nil, nil); !strings.HasPrefix(line, "failed (60): integration: x/p0.TestT: yyy") ||
|
|
!strings.Contains(line, "…; and 59 more | 0123456789ab on fedcba987654") {
|
|
t.Errorf("line %q", line)
|
|
}
|
|
// A composition failing after them is counted in the last entry (the operator, 2026-11-02).
|
|
withComposition(v, delivery.CompositionVerdict{State: delivery.CompositionFail, Machine: "g14", Words: "route-proxy is defined twice"})
|
|
if n := failedCount(*v); n != 61 || v.Failed.Test != "integration: x/p0.TestT" {
|
|
t.Errorf("after the composition failedCount = %d, failed %+v", n, v.Failed)
|
|
}
|
|
|
|
// A verdict that names no list (a conflict, a refused step, a verdict written before issue 1921) is one failed line, as
|
|
// before.
|
|
one := delivery.Verdict{State: delivery.VerdictFail, Failed: delivery.FailedFirst{Machine: "g14", Words: "files in conflict: go.mod\nmore"}}
|
|
if got := failedLines(one); len(got) != 1 || got[0] != "g14: files in conflict: go.mod" {
|
|
t.Fatalf("%q", got)
|
|
}
|
|
}
|