411 lines
18 KiB
Go
411 lines
18 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
|
|
// branches are the branches of a repository with the commit each points at.
|
|
branches map[string]map[string]string
|
|
}
|
|
|
|
func (f *fakeForge) BranchHeads(_ context.Context, repository string) (map[string]string, error) {
|
|
if f.err != nil {
|
|
return nil, f.err
|
|
}
|
|
return f.branches[repository], nil
|
|
}
|
|
|
|
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, _ := refsOf(f.args["beside"]), true
|
|
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)
|
|
}
|
|
// Since R4g the levels run in the member's one check ask before its go.mod pins are judged (ADR 0525 §3); the verdict is
|
|
// still the pin's, whatever the levels said.
|
|
}
|
|
|
|
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, _ := refsOf(f.args["beside"]), true
|
|
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"),
|
|
catalogue + " go.mod " + head2: "module git.novox.be/novox/mesh-catalog\n"}, 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)
|
|
}
|
|
}
|
|
|
|
// 1295/R4g: the node-engine's tests read the controller's manifest, so a mesh-host pull request has mesh-controller beside it:
|
|
// at the group member's head when the group holds one, else at its trunk.
|
|
func hostCase(t *testing.T, pulls ...delivery.CandidatePull) *Case {
|
|
t.Helper()
|
|
c, err := delivery.DecodeCandidate(candidateOf(t, "feat/x", pulls...))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cs := CaseOf(c, map[string]string{"novox/mesh-host": trunkA, controllerRepo: trunkA2})
|
|
if cs == nil {
|
|
t.Fatal("no case")
|
|
}
|
|
return cs
|
|
}
|
|
|
|
func TestTheNodeEnginesTestsHaveTheControllerBesideThem(t *testing.T) {
|
|
const host = "novox/mesh-host"
|
|
b := newBench(t)
|
|
b.ex.Trunks = trunksOf(t, map[string]string{host: trunkA, controllerRepo: trunkA2, catalogueRepository: trunkS, SDKRepository: trunkS})
|
|
b.judge(t, hostCase(t, pullOf(host, 5, head1)))
|
|
if c := besideOf(t, b, host)[controllerRepo]; c.Commit != trunkA2 || c.Ref != "" {
|
|
t.Errorf("the controller beside the node-engine alone = %+v, want its trunk %s", c, trunkA2)
|
|
}
|
|
b = newBench(t)
|
|
b.ex.Trunks = trunksOf(t, map[string]string{host: trunkA, controllerRepo: trunkA2, catalogueRepository: trunkS, SDKRepository: trunkS})
|
|
b.ex.Forge = &fakeForge{goMods: map[string]string{controllerRepo + " go.mod " + head2: capturedGoMod(t, "cb54756c3865")},
|
|
onTrunk: map[string]bool{"cb54756c3865": true}}
|
|
b.judge(t, hostCase(t, pullOf(host, 5, head1), pullOf(controllerRepo, 9, head2)))
|
|
if c := besideOf(t, b, host)[controllerRepo]; c.Commit != head2 || c.Ref != "refs/pull/9/head" {
|
|
t.Errorf("the controller beside the node-engine in a group = %+v, want the member's head %s", c, head2)
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
func TestAForgeThatIsNotHeardFailsAtTheForgeAndIsJudgedAgainWhenTheNodesChange(t *testing.T) { // 1624 C7
|
|
b := newBench(t)
|
|
b.ex.Forge = &fakeForge{err: &Unheard{Err: fmt.Errorf("nats: no responders available for request")}}
|
|
v := b.judge(t, caseOf(t, pullOf(repoA, 7, head1))).Verdict
|
|
if v.State != delivery.VerdictFail || v.Failed.Machine != "forge" {
|
|
t.Fatalf("verdict = %+v, want failed at the forge", v)
|
|
}
|
|
if !IsNodesFailure(v) {
|
|
t.Errorf("a forge that was not heard is not the change's failure: %+v is not judged again when the nodes change", v.Failed)
|
|
}
|
|
}
|
|
|
|
// 1887/A12: the change's own repository is placed beside it at the trunk it is merged onto, for every repository, so a
|
|
// shrink-only list test reads the trunk's copy at $MESH_CHECK_BESIDE/<name>.
|
|
func TestTheChangesOwnRepositoryIsBesideItAtItsTrunk(t *testing.T) {
|
|
b := newBench(t)
|
|
b.judge(t, caseOf(t, pullOf(repoA, 7, head1)))
|
|
got := besideOf(t, b, repoA)
|
|
if own := got[repoA]; own.Commit != trunkA || own.Ref != "" {
|
|
t.Fatalf("the repository beside its own change = %+v, want its trunk %s", own, trunkA)
|
|
}
|
|
}
|
|
|
|
// 1887/A12: a catalogue change's siblings hold the catalogue itself at the trunk it is merged onto, beside mesh-sdk and
|
|
// mesh-controller, so the catalogue's shrink-only list tests read $MESH_CHECK_BESIDE/mesh-catalog.
|
|
func TestACatalogueChangeHasTheCatalogueBesideItAtItsTrunk(t *testing.T) {
|
|
b := newBench(t)
|
|
b.ex.Trunks = trunksOf(t, map[string]string{catalogueRepository: trunkA, SDKRepository: trunkS, controllerRepo: trunkA2})
|
|
b.judge(t, caseOf(t, pullOf(catalogueRepository, 12, head1)))
|
|
got := besideOf(t, b, catalogueRepository)
|
|
if c := got[catalogueRepository]; c.Commit != trunkA || c.Ref != "" {
|
|
t.Fatalf("the catalogue beside its own change = %+v, want its trunk %s", c, trunkA)
|
|
}
|
|
if _, ok := got[SDKRepository]; !ok {
|
|
t.Errorf("mesh-sdk is no longer beside a catalogue change: %v", got)
|
|
}
|
|
}
|
|
|
|
// 1887/A12: a mesh-lab head gets a check: with its trunk known it is judged like any repository, its Go fetch finding
|
|
// the nested modules itself.
|
|
func TestAMeshLabHeadGetsACheck(t *testing.T) {
|
|
const lab = "novox/mesh-lab"
|
|
raw := candidateOf(t, "fix/x", pullOf(lab, 3, head1))
|
|
c, err := delivery.DecodeCandidate(raw)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cs := CaseOf(c, map[string]string{lab: trunkA})
|
|
if cs == nil || len(cs.Pulls) != 1 || cs.Pulls[0].Repository != lab || len(cs.Left) != 0 {
|
|
t.Fatalf("the case = %+v, want mesh-lab#3 judged", cs)
|
|
}
|
|
b := newBench(t)
|
|
if v := b.judge(t, cs).Verdict; v.State != delivery.VerdictPass {
|
|
t.Fatalf("verdict = %+v", v)
|
|
}
|
|
if fetched := b.steps.tool("fetch"); len(fetched) == 0 || fetched[0].args["repository"] != lab {
|
|
t.Fatalf("mesh-lab was not fetched: %+v", fetched)
|
|
}
|
|
}
|
|
|
|
// 1295: the node-engine's step-nine test fails without the controller beside it, so a checker that knows no trunk head of the
|
|
// controller fails the check at the placing, loudly, and does not place none.
|
|
func TestTheNodeEnginesCheckFailsWhereTheControllersTrunkIsNotKnown(t *testing.T) {
|
|
const host = "novox/mesh-host"
|
|
b := newBench(t)
|
|
b.ex.Trunks = trunksOf(t, map[string]string{host: trunkA, catalogueRepository: trunkS, SDKRepository: trunkS})
|
|
v := b.judge(t, hostCase(t, pullOf(host, 5, head1))).Verdict
|
|
if v.State != delivery.VerdictFail || !strings.Contains(v.Failed.Words, "mesh-controller") || !strings.Contains(v.Failed.Words, "cannot be placed") {
|
|
t.Fatalf("verdict = %+v", v)
|
|
}
|
|
}
|
|
|
|
// sameBranchBench is a try of mesh-delivery's feat/r6 at head1 whose go.mod pins the SDK at pin, with the SDK's feat/r6 at head2.
|
|
func sameBranchBench(t *testing.T, pin string) (*bench, *Case) {
|
|
b := groupBench(t, pin)
|
|
b.ex.Forge.(*fakeForge).branches = map[string]map[string]string{
|
|
repoA: {"main": trunkA, "feat/r6": head1},
|
|
repoSDK: {"main": "cb54756c3865" + strings.Repeat("0", 28), "feat/r6": head2},
|
|
}
|
|
c := caseOf(t, pullOf(repoA, 7, head1))
|
|
c.Try = &TryOptions{}
|
|
return b, c
|
|
}
|
|
|
|
func TestATryPinnedToTheHeadOfTheSDKBranchOfTheTriedBranchPasses(t *testing.T) { // 1921/AC1
|
|
b, c := sameBranchBench(t, head2[:12])
|
|
if v := b.judge(t, c).Verdict; v.State != delivery.VerdictPass {
|
|
t.Fatalf("verdict = %+v", v)
|
|
}
|
|
if n := c.Notes(); len(n) != 1 || n[0] != "pin accepted: head of mesh-sdk feat/r6" {
|
|
t.Errorf("notes = %q", n)
|
|
}
|
|
}
|
|
|
|
func TestATryPinnedToAnotherCommitIsStillRefused(t *testing.T) { // 1921/AC2
|
|
b, c := sameBranchBench(t, "9aaa5c80ac5c")
|
|
v := b.judge(t, c).Verdict
|
|
if v.State != delivery.VerdictFail || !strings.Contains(v.Failed.Words, "9aaa5c80ac5c") || !strings.Contains(v.Failed.Words, "neither on mesh-sdk's trunk nor the group's head") {
|
|
t.Fatalf("verdict = %+v", v)
|
|
}
|
|
if len(c.Notes()) != 0 {
|
|
t.Errorf("notes = %q", c.Notes())
|
|
}
|
|
}
|
|
|
|
func TestAMergeCheckWithTheSameBranchPinIsStillRefusedWithoutAGroupPullRequest(t *testing.T) { // 1921/AC3
|
|
b, _ := sameBranchBench(t, head2[:12])
|
|
v := b.judge(t, caseOf(t, pullOf(repoA, 7, head1))).Verdict // no Try: a pull request's check
|
|
if v.State != delivery.VerdictFail || !strings.Contains(v.Failed.Words, "the group has no mesh-sdk pull request") {
|
|
t.Fatalf("verdict = %+v", v)
|
|
}
|
|
}
|