cmd/gitbay-runner/main_test.go

00033f022fed6fe0b36e1d7a1e2b3f1df6139ccd
gitbay/cmd/gitbay-runner/main_test.go history · blame · raw

74 lines · 2102 bytes

 1package main
 2
 3import (
 4	"errors"
 5	"os/exec"
 6	"strings"
 7	"testing"
 8)
 9
10type failingWriter struct{ n int }
11
12func (f *failingWriter) Write(p []byte) (int, error) {
13	f.n++
14	return 0, errors.New("broken pipe")
15}
16
17// A build's log stream is a recording device. When it breaks, os/exec
18// surfaces the write error through cmd.Wait(), which used to mark a step
19// that had exited 0 as failed — a green suite reported red, with the line
20// explaining it written to the same dead pipe.
21func TestBrokenLogSinkDoesNotFailTheStep(t *testing.T) {
22	sink := &logSink{w: &failingWriter{}}
23
24	cmd := exec.Command("sh", "-c", "echo out; echo err >&2; exit 0")
25	cmd.Stdout, cmd.Stderr = sink, sink
26	if err := cmd.Run(); err != nil {
27		t.Fatalf("step reported failed because its log sink broke: %v", err)
28	}
29	if !sink.broken() {
30		t.Error("sink did not record that the stream was lost")
31	}
32}
33
34// The converse: a step that genuinely fails still fails.
35func TestGenuineStepFailureStillFails(t *testing.T) {
36	sink := &logSink{w: &failingWriter{}}
37	cmd := exec.Command("sh", "-c", "exit 3")
38	cmd.Stdout, cmd.Stderr = sink, sink
39	if err := cmd.Run(); err == nil {
40		t.Fatal("a step exiting 3 was reported as success")
41	}
42}
43
44// Once the stream is gone the sink stops touching it, rather than calling a
45// broken pipe once per write for the rest of a long build.
46func TestLogSinkStopsWritingAfterFailure(t *testing.T) {
47	w := &failingWriter{}
48	sink := &logSink{w: w}
49	for i := 0; i < 5; i++ {
50		if _, err := sink.Write([]byte("x")); err != nil {
51			t.Fatalf("sink returned an error: %v", err)
52		}
53	}
54	if w.n != 1 {
55		t.Errorf("underlying writer called %d times, want 1", w.n)
56	}
57}
58
59// A healthy sink still forwards everything.
60func TestHealthyLogSinkForwards(t *testing.T) {
61	var b strings.Builder
62	sink := &logSink{w: &b}
63	cmd := exec.Command("sh", "-c", "echo hello")
64	cmd.Stdout, cmd.Stderr = sink, sink
65	if err := cmd.Run(); err != nil {
66		t.Fatal(err)
67	}
68	if got := b.String(); !strings.Contains(got, "hello") {
69		t.Errorf("sink dropped output: %q", got)
70	}
71	if sink.broken() {
72		t.Error("healthy sink reported broken")
73	}
74}