256 lines
10 KiB
Go
256 lines
10 KiB
Go
package check
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
// fakeForge answers the forge's two reads from files and commits it is given.
|
|
type fakeForge struct {
|
|
goMods map[string]string // `<repository> <path> <ref>` -> text
|
|
onTrunk map[string]bool // by commit prefix
|
|
err error
|
|
errAt map[string]error // by ref: a read at that ref fails with it
|
|
}
|
|
|
|
func (f *fakeForge) File(_ context.Context, repository, path, ref string) (string, error) {
|
|
if f.err != nil {
|
|
return "", f.err
|
|
}
|
|
if e := f.errAt[ref]; e != nil {
|
|
return "", e
|
|
}
|
|
if s, ok := f.goMods[repository+" "+path+" "+ref]; ok {
|
|
return s, nil
|
|
}
|
|
return "", fmt.Errorf("no file %s of %s at %s: %w", path, repository, ref, ErrNotFound)
|
|
}
|
|
|
|
func (f *fakeForge) FullSHA(_ context.Context, _ string, commit string) (string, error) {
|
|
if f.err != nil {
|
|
return "", f.err
|
|
}
|
|
return commit + strings.Repeat("0", 40-len(commit)), nil
|
|
}
|
|
|
|
func (f *fakeForge) OnTrunk(_ context.Context, _ string, commit string) (bool, error) {
|
|
if f.err != nil {
|
|
return false, f.err
|
|
}
|
|
return f.onTrunk[commit[:12]], nil
|
|
}
|
|
|
|
// capturedGoMod is the go.mod the forge answered (testdata/captured/forge-get-file-go-mod.json), pinned to the commit given.
|
|
func capturedGoMod(t *testing.T, pin string) string {
|
|
t.Helper()
|
|
raw, err := os.ReadFile(filepath.Join("testdata", "captured", "forge-get-file-go-mod.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var a struct{ File struct{ Content string } }
|
|
if err := json.Unmarshal(raw, &a); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return strings.Replace(a.File.Content, "cb54756c3865", pin, 1)
|
|
}
|
|
|
|
func TestTheSDKPinIsTheCommitOfAPseudoVersion(t *testing.T) { // 1624
|
|
if c, ok := SDKPin(capturedGoMod(t, "cb54756c3865")); !ok || c != "cb54756c3865" {
|
|
t.Errorf("pin = %q, %v", c, ok)
|
|
}
|
|
if _, ok := SDKPin("module x\n\nrequire git.novox.be/novox/mesh-sdk/go v0.2.0\n"); ok {
|
|
t.Error("a tagged version is no commit")
|
|
}
|
|
if c, ok := SDKPin("module x\n\nrequire git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261021100209-cb54756c3865\n"); !ok || c != "cb54756c3865" {
|
|
t.Errorf("a one-line require: %q, %v", c, ok)
|
|
}
|
|
if _, ok := SDKPin("module x\n\ngo 1.26\n"); ok {
|
|
t.Error("a go.mod that does not require mesh-sdk has a pin")
|
|
}
|
|
}
|
|
|
|
// groupBench is a group of mesh-delivery#7 and the SDK's pull request 12, mesh-delivery's go.mod at its head naming pin.
|
|
func groupBench(t *testing.T, pin string, trunk ...string) *bench {
|
|
b := newBench(t)
|
|
b.fetch.Modules = []string{"modules/m"}
|
|
forge := &fakeForge{goMods: map[string]string{repoA + " modules/m/go.mod " + head1: capturedGoMod(t, pin)}, onTrunk: map[string]bool{}}
|
|
for _, c := range trunk {
|
|
forge.onTrunk[c] = true
|
|
}
|
|
b.ex.Forge = forge
|
|
return b
|
|
}
|
|
|
|
func TestAMemberPinnedToTheSDKMembersHeadPassesAndIsBuiltAgainstIt(t *testing.T) { // 1624/AC2, AC4
|
|
b := groupBench(t, head2[:12])
|
|
v := b.judge(t, caseOf(t, pullOf(repoA, 7, head1), pullOf(repoSDK, 12, head2))).Verdict
|
|
if v.State != delivery.VerdictPass {
|
|
t.Fatalf("verdict = %+v", v)
|
|
}
|
|
var besideSDK bool
|
|
for _, f := range b.steps.tool("fetch") {
|
|
if f.args["repository"] == repoA {
|
|
refs, _ := f.args["beside"].([]BesideRef)
|
|
for _, r := range refs {
|
|
besideSDK = besideSDK || (r.Repository == repoSDK && r.Commit == head2)
|
|
}
|
|
}
|
|
}
|
|
if !besideSDK {
|
|
t.Error("mesh-delivery's tests were not given the SDK member at its head")
|
|
}
|
|
}
|
|
|
|
func TestAMemberPinnedToAnOlderHeadOfTheSDKMemberFailsNamingTheMemberAndTheCommit(t *testing.T) { // 1624/AC2, AC4
|
|
b := groupBench(t, "9aaa5c80ac5c") // a commit of the SDK branch, not its head, not on the trunk
|
|
v := b.judge(t, caseOf(t, pullOf(repoA, 7, head1), pullOf(repoSDK, 12, head2))).Verdict
|
|
f := v.Failed
|
|
if v.State != delivery.VerdictFail || f.Machine != "shanks" || !strings.Contains(f.Words, repoA+"#7") || !strings.Contains(f.Words, "9aaa5c80ac5c") ||
|
|
!strings.Contains(f.Words, "neither on mesh-sdk's trunk nor the group's head") || !strings.Contains(f.Words, short12(head2)) {
|
|
t.Fatalf("verdict = %+v", v)
|
|
}
|
|
if n := len(b.steps.tool("test")); n != 0 {
|
|
t.Errorf("%d test steps ran after a stale pin", n)
|
|
}
|
|
}
|
|
|
|
func TestAMemberPinnedToACommitOnTheTrunkPasses(t *testing.T) { // 1624/AC2, AC4
|
|
b := groupBench(t, "cb54756c3865", "cb54756c3865")
|
|
if v := b.judge(t, caseOf(t, pullOf(repoA, 7, head1), pullOf(repoSDK, 12, head2))).Verdict; v.State != delivery.VerdictPass {
|
|
t.Fatalf("verdict = %+v", v)
|
|
}
|
|
// A lone pull request of a Go member is held to the trunk alone.
|
|
b = groupBench(t, "9aaa5c80ac5c")
|
|
v := b.judge(t, caseOf(t, pullOf(repoA, 7, head1))).Verdict
|
|
if v.State != delivery.VerdictFail || !strings.Contains(v.Failed.Words, "the group has no mesh-sdk pull request") {
|
|
t.Errorf("a foreign pin with no SDK member: %+v", v)
|
|
}
|
|
}
|
|
|
|
func TestAPinOnlyTheSDKHeadCarriesThatDownloadCannotResolveFailsNamingIssue1148(t *testing.T) { // 1624/AC5, ADR 0453 clause 12
|
|
b := groupBench(t, head2[:12])
|
|
b.steps.handlers["build_go_fetch"] = func(c stepCall) (any, error) {
|
|
if c.args["repository"] == repoA {
|
|
return nil, fmt.Errorf("go mod download: git.novox.be/novox/mesh-sdk/go@v0.1.14-0.20261021100209-%s: invalid version: unknown revision", head2[:12])
|
|
}
|
|
return b.fetch, nil
|
|
}
|
|
v := b.judge(t, caseOf(t, pullOf(repoA, 7, head1), pullOf(repoSDK, 12, head2))).Verdict
|
|
if v.State != delivery.VerdictFail || !strings.Contains(v.Failed.Words, "issue 1148") || !strings.Contains(v.Failed.Words, head2[:12]) {
|
|
t.Errorf("verdict = %+v, want a failure naming issue 1148 and the commit", v)
|
|
}
|
|
}
|
|
|
|
func TestAPinThatCannotBeCheckedFailsAndNeverPasses(t *testing.T) { // ADR 0453 clause 12
|
|
b := groupBench(t, "cb54756c3865")
|
|
b.ex.Forge.(*fakeForge).err = fmt.Errorf("the forge is down")
|
|
v := b.judge(t, caseOf(t, pullOf(repoA, 7, head1), pullOf(repoSDK, 12, head2))).Verdict
|
|
if v.State != delivery.VerdictFail || !strings.Contains(v.Failed.Words, "the forge is down") {
|
|
t.Errorf("verdict = %+v", v)
|
|
}
|
|
}
|
|
|
|
// trunkOnlyBench is a group of mesh-delivery#7 whose fetch holds two Go modules: modules/m, which the head holds, and
|
|
// modules/new, which only the trunk holds (added on the trunk after the branch was cut). The SDK pin of each go.mod is given.
|
|
func trunkOnlyBench(t *testing.T, headPin, trunkPin string) *bench {
|
|
b := newBench(t)
|
|
b.fetch.Modules = []string{"modules/m", "modules/new"}
|
|
forge := &fakeForge{goMods: map[string]string{
|
|
repoA + " modules/m/go.mod " + head1: capturedGoMod(t, headPin),
|
|
repoA + " modules/m/go.mod " + trunkA: capturedGoMod(t, headPin),
|
|
repoA + " modules/new/go.mod " + trunkA: capturedGoMod(t, trunkPin),
|
|
}, onTrunk: map[string]bool{"cb54756c3865": true}}
|
|
b.ex.Forge = forge
|
|
return b
|
|
}
|
|
|
|
func TestAModuleOnlyTheTrunkHoldsIsReadAtTheTrunkAndPasses(t *testing.T) { // 1845/AC1
|
|
b := trunkOnlyBench(t, "cb54756c3865", "cb54756c3865")
|
|
if v := b.judge(t, caseOf(t, pullOf(repoA, 7, head1))).Verdict; v.State != delivery.VerdictPass {
|
|
t.Fatalf("verdict = %+v", v)
|
|
}
|
|
}
|
|
|
|
func TestAStalePinOfAModuleOnlyTheTrunkHoldsStillFailsNamingTheFileAndTheCommit(t *testing.T) { // 1845/AC2
|
|
b := trunkOnlyBench(t, "cb54756c3865", "9aaa5c80ac5c")
|
|
v := b.judge(t, caseOf(t, pullOf(repoA, 7, head1))).Verdict
|
|
if v.State != delivery.VerdictFail || !strings.Contains(v.Failed.Words, repoA+"#7") || !strings.Contains(v.Failed.Words, "modules/new/go.mod") || !strings.Contains(v.Failed.Words, "9aaa5c80ac5c") {
|
|
t.Fatalf("verdict = %+v", v)
|
|
}
|
|
}
|
|
|
|
func TestAModuleTheHeadHoldsIsJudgedByTheHeadsPin(t *testing.T) { // 1845/AC3
|
|
b := trunkOnlyBench(t, "9aaa5c80ac5c", "cb54756c3865") // the head's pin is stale, the trunk's go.mod of the same module is fine
|
|
b.ex.Forge.(*fakeForge).goMods[repoA+" modules/m/go.mod "+trunkA] = capturedGoMod(t, "cb54756c3865")
|
|
v := b.judge(t, caseOf(t, pullOf(repoA, 7, head1))).Verdict
|
|
if v.State != delivery.VerdictFail || !strings.Contains(v.Failed.Words, "modules/m/go.mod") || !strings.Contains(v.Failed.Words, "9aaa5c80ac5c") {
|
|
t.Fatalf("verdict = %+v", v)
|
|
}
|
|
}
|
|
|
|
func TestAReadThatFailsForAnotherReasonStillFailsAndIsNotTriedAtTheTrunk(t *testing.T) { // 1845/AC4
|
|
b := trunkOnlyBench(t, "cb54756c3865", "cb54756c3865")
|
|
b.ex.Forge.(*fakeForge).errAt = map[string]error{head1: fmt.Errorf("the forge answered 500")}
|
|
v := b.judge(t, caseOf(t, pullOf(repoA, 7, head1))).Verdict
|
|
if v.State != delivery.VerdictFail || !strings.Contains(v.Failed.Words, "modules/m/go.mod") || !strings.Contains(v.Failed.Words, "the forge answered 500") {
|
|
t.Fatalf("verdict = %+v", v)
|
|
}
|
|
}
|
|
|
|
type runsAt map[string]string
|
|
|
|
func (r runsAt) CommitOf(module string) string { return r[module] }
|
|
|
|
func besideOf(t *testing.T, b *bench, repository string) map[string]BesideRef {
|
|
t.Helper()
|
|
out := map[string]BesideRef{}
|
|
for _, f := range b.steps.tool("fetch") {
|
|
if f.args["repository"] == repository {
|
|
refs, _ := f.args["beside"].([]BesideRef)
|
|
for _, r := range refs {
|
|
out[r.Repository] = r
|
|
}
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func TestTheSiblingsAreWhereTheTestsStateThem(t *testing.T) { // review D2, ADR 0484 clause 4
|
|
const controller, catalogue, host = "novox/mesh-controller", "novox/mesh-catalog", "novox/mesh-host"
|
|
hostRuns := strings.Repeat("7", 40)
|
|
b := newBench(t)
|
|
b.ex.Trunks = trunksOf(t, map[string]string{controller: trunkA, catalogue: trunkS, host: trunkA2, SDKRepository: "9999999999999999999999999999999999999999"})
|
|
b.ex.Runs = runsAt{"mesh-host": hostRuns}
|
|
forge := &fakeForge{goMods: map[string]string{controller + " go.mod " + head1: capturedGoMod(t, "cb54756c3865")}, onTrunk: map[string]bool{"cb54756c3865": true}}
|
|
b.ex.Forge = forge
|
|
// A group of the controller and the catalogue: the controller's tests read the catalogue's trunk, not the group's head.
|
|
b.judge(t, caseOf(t, pullOf(controller, 7, head1), pullOf(catalogue, 12, head2)))
|
|
got := besideOf(t, b, controller)
|
|
if c := got[catalogue]; c.Commit != trunkS || c.Ref != "" {
|
|
t.Errorf("the catalogue beside the controller = %+v, want its trunk %s", c, trunkS)
|
|
}
|
|
if h := got[host]; h.Commit != hostRuns {
|
|
t.Errorf("the node-engine beside the controller = %+v, want the commit the mesh runs %s", h, hostRuns)
|
|
}
|
|
if s := got[SDKRepository]; s.Commit != "cb54756c3865"+strings.Repeat("0", 28) {
|
|
t.Errorf("mesh-sdk beside the controller = %+v, want the commit its go.mod pins, whole", s)
|
|
}
|
|
}
|
|
|
|
func TestThePinOfTheControllerThatCannotBeReadFailsTheCheck(t *testing.T) { // review D2
|
|
b := newBench(t)
|
|
b.ex.Trunks = trunksOf(t, map[string]string{"novox/mesh-controller": trunkA})
|
|
b.ex.Forge = &fakeForge{goMods: map[string]string{}, onTrunk: map[string]bool{}}
|
|
v := b.judge(t, caseOf(t, pullOf("novox/mesh-controller", 7, head1))).Verdict
|
|
if v.State != delivery.VerdictFail || !strings.Contains(v.Failed.Words, "go.mod") {
|
|
t.Errorf("verdict = %+v", v)
|
|
}
|
|
}
|