cmd/gitbay-runner/report_test.go
63 lines · 1804 bytes
1package main
2
3import (
4 "errors"
5 "os/exec"
6 "testing"
7 "time"
8)
9
10// exitErr fabricates the error ssh returns for a given exit status.
11func exitErr(t *testing.T, code int) error {
12 t.Helper()
13 err := exec.Command("sh", "-c", "exit "+itoa(code)).Run()
14 var ee *exec.ExitError
15 if !errors.As(err, &ee) || ee.ExitCode() != code {
16 t.Fatalf("could not fabricate exit %d: %v", code, err)
17 }
18 return err
19}
20
21func itoa(n int) string {
22 return string(rune('0'+n/100)) + string(rune('0'+n/10%10)) + string(rune('0'+n%10))
23}
24
25// A connection failure (ssh exit 255) is retried until it succeeds; the
26// result is not lost to a server that was restarting (#179).
27func TestReportRetriesConnectionFailure(t *testing.T) {
28 calls := 0
29 err := reportWithRetry(func() (string, error) {
30 calls++
31 if calls < 3 {
32 return "ssh: connect to host 127.0.0.1 port 22: Connection refused", exitErr(t, 255)
33 }
34 return "", nil
35 }, 7, []time.Duration{0, 0, 0, 0})
36 if err != nil || calls != 3 {
37 t.Fatalf("err=%v calls=%d, want success on the third attempt", err, calls)
38 }
39}
40
41// The server's own refusal is an answer, not a transient: no retry.
42func TestReportDoesNotRetryServerAnswer(t *testing.T) {
43 calls := 0
44 err := reportWithRetry(func() (string, error) {
45 calls++
46 return `{"error":"build 7 is not running"}`, exitErr(t, 3)
47 }, 7, []time.Duration{0, 0})
48 if err == nil || calls != 1 {
49 t.Fatalf("err=%v calls=%d, want one attempt and an error", err, calls)
50 }
51}
52
53// Retries are bounded: after the delays are spent the error surfaces.
54func TestReportGivesUp(t *testing.T) {
55 calls := 0
56 err := reportWithRetry(func() (string, error) {
57 calls++
58 return "", exitErr(t, 255)
59 }, 7, []time.Duration{0, 0})
60 if err == nil || calls != 3 {
61 t.Fatalf("err=%v calls=%d, want three attempts then an error", err, calls)
62 }
63}