Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Code

392 commit(s) on main

Merge change request 01M70WES5ZZDJ6RP226WB11BBZ: Let people rename and remove their own passkeys keenan · 1h ago 4b8c9e1d
history blame raw 3.0 KiB
1package landing
2
3// The gate reads one job's newest run for a commit and turns it into a gap or
4// nothing. Before #246 every non-success was a refusal, so a runner that could not
5// start the job red-flagged a commit whose tests passed. These cases pin which
6// statuses are the author's problem and which are the machine's.
7
8import (
9 "strings"
10 "testing"
11
12 "rodent.software/vole/ci"
13 "rodent.software/vole/ci/runs"
14)
15
16func TestCheckGapReadsEveryRunStatus(t *testing.T) {
17 t.Parallel()
18 wf := &ci.Workflow{Path: ".vole/workflows/verify.yaml", Branches: []string{"*"}}
19 job := ci.Job{ID: "verify"}
20 const sha = "0123456789abcdef0123456789abcdef01234567"
21
22 cases := []struct {
23 name string
24 run *runs.Run
25 gap bool
26 wait bool
27 wantHas []string
28 }{
29 {
30 name: "no run is a wait", gap: true, wait: true,
31 wantHas: []string{"no run", "push the branch"},
32 },
33 {
34 name: "a failed job is a refusal", wait: false, gap: true,
35 run: &runs.Run{Status: runs.StatusFailed, ExitCode: 1},
36 wantHas: []string{"failed", "exit 1"},
37 },
38 {
39 name: "a canceled job is a refusal", wait: false, gap: true,
40 run: &runs.Run{Status: runs.StatusCanceled},
41 wantHas: []string{"canceled"},
42 },
43 {
44 // The case this rule exists for: the runner could not build the image, so
45 // nothing ran and the commit is not what stopped it.
46 name: "a job that never started is a wait", wait: true, gap: true,
47 run: &runs.Run{
48 ID: "01M6YY97B5V966TVRED4QA2643", Status: runs.StatusError,
49 Failure: runs.FailureSetup,
50 FailureMessage: `building image-file "verify" exited 125`,
51 },
52 wantHas: []string{
53 "could not run",
54 `building image-file "verify" exited 125`,
55 "vole run rerun 01M6YY97B5V966TVRED4QA2643",
56 },
57 },
58 {
59 name: "a queued job is a wait", gap: true, wait: true,
60 run: &runs.Run{Status: runs.StatusQueued},
61 wantHas: []string{"queued", "wait"},
62 },
63 {
64 name: "a passing job is no gap",
65 run: &runs.Run{Status: runs.StatusSuccess},
66 },
67 }
68 for _, c := range cases {
69 t.Run(c.name, func(t *testing.T) {
70 t.Parallel()
71 problem, wait, gap := checkGap(wf, job, sha, c.run)
72 if gap != c.gap || wait != c.wait {
73 t.Fatalf("checkGap = gap %v wait %v, want gap %v wait %v (%q)",
74 gap, wait, c.gap, c.wait, problem)
75 }
76 for _, want := range c.wantHas {
77 if !strings.Contains(problem, want) {
78 t.Fatalf("the gap does not say %q: %q", want, problem)
79 }
80 }
81 })
82 }
83}
84
85// A run with no recorded reason still says something a reader can act on, rather than
86// trailing off after the status.
87func TestCheckGapNamesAMissingReason(t *testing.T) {
88 t.Parallel()
89 wf := &ci.Workflow{Path: "verify.yaml"}
90 run := &runs.Run{ID: "run1", Status: runs.StatusError, Failure: runs.FailureSetup}
91 problem, wait, gap := checkGap(wf, ci.Job{ID: "verify"}, "0123456", run)
92 if !gap || !wait {
93 t.Fatalf("checkGap = gap %v wait %v", gap, wait)
94 }
95 if !strings.Contains(problem, "the runner recorded no reason") {
96 t.Fatalf("the gap does not account for a missing reason: %q", problem)
97 }
98}
99