package check import ( "context" "strings" "testing" "time" "git.novox.be/novox/mesh-sdk/go/delivery" ) // What the gate reads of the checker (R4e, lead's interface with dev-r4e-gate): the verdict's heads are the gate's hash over // `#=`, a lone pull request is a group of one, and a move of a trunk marks // mesh/check pending on every open pull request into it, every member's for a group, before any check starts. func owedState(t testing.TB, r *rig, repo string, number int, head string) string { t.Helper() key, err := delivery.CheckerOwedKey(repo, number, head) if err != nil { t.Fatal(err) } raw, _, found, _ := r.owed.Get(context.Background(), key) if !found { return "" } o, err := delivery.DecodeCheckerOwed(raw) if err != nil { t.Fatal(err) } return o.State } func TestTheVerdictsHeadsAreTheGatesHashOverLowercaseHeads(t *testing.T) { // gate (b), (c) ctx := context.Background() r := newRig(t, passJudge()) upper := strings.ToUpper(head1[:20]) + head1[20:] r.setTrunks(t, map[string]string{"Novox/Mesh-Delivery": trunkA, repoA: trunkA, repoSDK: trunkS}) cand := candidateOf(t, "feat/x", pullOf("Novox/Mesh-Delivery", 7, upper)) r.ch.Candidate(ctx, "feat%2Fx", cand) r.ch.Wait() want, _ := delivery.HeadsHash([]delivery.Head{{Repository: repoA, Number: 7, Head: head1}}) if v := r.verdict(t, keyOf(t, "feat/x", cand)); v.Heads != want { t.Errorf("heads = %q, want the gate's hash of the lone pull request, a group of one, %q", v.Heads, want) } } func TestAGroupsVerdictHeadsCoverEveryMember(t *testing.T) { // gate (b): hq's member too, as the gate's ask covers it ctx := context.Background() r := newRig(t, passJudge()) cand := candidateOf(t, "feat/x", pullOf(repoA, 7, head1), pullOf(repoSDK, 12, head2), pullOf(hqRepo, 3, head2)) r.ch.Candidate(ctx, "feat%2Fx", cand) r.ch.Wait() want, _ := delivery.HeadsHash([]delivery.Head{{Repository: repoA, Number: 7, Head: head1}, {Repository: repoSDK, Number: 12, Head: head2}, {Repository: hqRepo, Number: 3, Head: head2}}) if v := r.verdict(t, keyOf(t, "feat/x", cand)); v.Heads != want { t.Errorf("heads = %q, want %q", v.Heads, want) } } func TestATrunkMoveMarksEveryOpenPullRequestIntoItPendingAtOnce(t *testing.T) { // gate (a), lead decision L4 (1302's checker part) ctx := context.Background() r := newRig(t, passJudge()) r.ch.Candidate(ctx, "feat%2Fg", candidateOf(t, "feat/g", pullOf(repoA, 7, head1), pullOf(repoSDK, 12, head2))) r.ch.Candidate(ctx, "feat%2Fs", candidateOf(t, "feat/s", pullOf(repoSDK, 13, head1))) r.ch.Wait() for _, p := range []struct { repo string n int head string }{{repoA, 7, head1}, {repoSDK, 12, head2}, {repoSDK, 13, head1}} { if s := owedState(t, r, p.repo, p.n, p.head); s != "success" { t.Fatalf("%s#%d is %q before the move", p.repo, p.n, s) } } // mesh-delivery's trunk moves; the re-judges it asks are held in their judge (in the stream, since R4g) meanwhile. release := make(chan struct{}) r.ch.Judge = &judgeFunc{f: func(context.Context, *Case, int) (Outcome, error) { <-release; return Outcome{Verdict: passing()}, nil }} defer func() { close(release); r.ch.Wait() }() r.setTrunks(t, map[string]string{repoA: trunkA2, repoSDK: trunkS}) r.ch.TrunkMoved(ctx) if s := owedState(t, r, repoA, 7, head1); s != "pending" { t.Errorf("mesh-delivery#7, into the moved trunk, is %q, want pending", s) } if s := owedState(t, r, repoSDK, 12, head2); s != "pending" { t.Errorf("mesh-sdk#12, a member of the same group, is %q, want pending", s) } if s := owedState(t, r, repoSDK, 13, head1); s != "success" { t.Errorf("mesh-sdk#13, alone into a trunk that did not move, is %q, want its success to stand", s) } } func TestACheckThatEndsAfterItsTrunkMovedOwesPendingNotItsVerdict(t *testing.T) { // gate (a): a pass on an old trunk is never shown ctx := context.Background() var r *rig release, judged := make(chan struct{}), make(chan struct{}) j := &judgeFunc{f: func(ctx context.Context, c *Case, call int) (Outcome, error) { if call == 1 { r.setTrunks(t, map[string]string{repoA: trunkA2, repoSDK: trunkS}) // the trunk moves while it runs defer close(judged) } else { <-release // the check asked again for the new trunk waits in the stream meanwhile } return Outcome{Verdict: passing()}, nil }} r = newRig(t, j) r.ch.Candidate(ctx, "feat%2Fx", candidateOf(t, "feat/x", pullOf(repoA, 7, head1))) <-judged for deadline := time.Now().Add(2 * time.Second); owedState(t, r, repoA, 7, head1) == "" && time.Now().Before(deadline); time.Sleep(time.Millisecond) { } time.Sleep(20 * time.Millisecond) if s := owedState(t, r, repoA, 7, head1); s != "pending" { t.Errorf("the status is %q, want pending: the pass was judged on a trunk that moved", s) } close(release) r.ch.Wait() }