internal/control/webhook_test.go

main
gitbay/internal/control/webhook_test.go history · blame · raw

84 lines · 3569 bytes

2 symbols in this file
 1package control
 2
 3import (
 4	"bytes"
 5	"strings"
 6	"testing"
 7
 8	"gitbay.org/gitbay/internal/protocol"
 9	"gitbay.org/gitbay/internal/store"
10)
11
12// TestWebhookAddRefusedURLIsAFailure: a URL the server refuses after
13// parsing it — here one that resolves to a loopback address — is the
14// caller's value being rejected, not a malformed command line. Exit 1
15// carries the sentence verbatim to every client; exit 2 reads as an
16// app bug and is shown as a generic error (#187).
17func TestWebhookAddRefusedURLIsAFailure(t *testing.T) {
18	st, repo, uid := newQueueTestRepo(t)
19	var out, errOut bytes.Buffer
20	c := &Ctx{
21		User:   store.User{ID: uid, Username: "alice"},
22		Store:  st,
23		Scope:  "full",
24		Stdin:  strings.NewReader(""),
25		Stdout: &out,
26		Stderr: &errOut,
27	}
28	code := Dispatch(c, []string{"webhook", "add", repo.Path(), "http://127.0.0.1/hook"})
29	if code != protocol.ExitFailure {
30		t.Fatalf("exit %d, want %d (%s)", code, protocol.ExitFailure, strings.TrimSpace(errOut.String()))
31	}
32	if !strings.Contains(errOut.String(), "private or local address") {
33		t.Errorf("message %q lacks the server's reason", strings.TrimSpace(errOut.String()))
34	}
35	// The shape of the command line is still a usage error.
36	out.Reset()
37	errOut.Reset()
38	if code := Dispatch(c, []string{"webhook", "add", repo.Path()}); code != protocol.ExitUsage {
39		t.Errorf("missing url: exit %d, want %d", code, protocol.ExitUsage)
40	}
41}
42
43// The signing secret arrives on stdin with --secret -, like a build
44// secret: a value on the command line is refused before anything is
45// stored, since argv shows in /proc and in shell history (#284).
46func TestWebhookAddSecretFromStdin(t *testing.T) {
47	st, repo, uid := newQueueTestRepo(t)
48	run := func(stdin string, argv ...string) (string, int) {
49		c, errOut := pruneCtx(st, t.TempDir(), store.User{ID: uid, Username: "alice"})
50		c.Cfg.Limits.WriteRate = -1
51		c.Cfg.Webhooks.AllowLocal = true
52		c.Stdin = strings.NewReader(stdin)
53		code := Dispatch(c, argv)
54		return errOut.String(), code
55	}
56	msg, code := run("", "webhook", "add", repo.Path(), "http://127.0.0.1/hook", "--secret", "s3cret")
57	if code != protocol.ExitUsage || !strings.Contains(msg, "--secret -") {
58		t.Fatalf("literal secret: exit %d, %q", code, msg)
59	}
60	if msg, code := run("", "webhook", "add", repo.Path(), "http://127.0.0.1/hook", "--secret", "-"); code != protocol.ExitUsage || !strings.Contains(msg, "no secret on stdin") {
61		t.Fatalf("empty stdin: exit %d, %q", code, msg)
62	}
63	if hooks, err := st.ListWebhooks(repo.ID); err != nil || len(hooks) != 0 {
64		t.Fatalf("a refused add stored %+v (%v)", hooks, err)
65	}
66	if msg, code := run("s3cret\n", "webhook", "add", repo.Path(), "http://127.0.0.1/hook", "--secret", "-"); code != protocol.ExitOK {
67		t.Fatalf("piped secret: exit %d, %q", code, msg)
68	}
69	// Without --secret nothing reads stdin and the hook is unsigned.
70	if msg, code := run("not a secret\n", "webhook", "add", repo.Path(), "http://127.0.0.1/other"); code != protocol.ExitOK {
71		t.Fatalf("no secret: exit %d, %q", code, msg)
72	}
73	// A secret from a file with CRLF line endings loses the \r too.
74	if msg, code := run("crlf\r\n", "webhook", "add", repo.Path(), "http://127.0.0.1/crlf", "--secret", "-"); code != protocol.ExitOK {
75		t.Fatalf("CRLF secret: exit %d, %q", code, msg)
76	}
77	hooks, err := st.ListWebhooks(repo.ID)
78	if err != nil || len(hooks) != 3 {
79		t.Fatalf("hooks: %+v %v", hooks, err)
80	}
81	if hooks[0].Secret != "s3cret" || hooks[1].Secret != "" || hooks[2].Secret != "crlf" {
82		t.Fatalf("secrets: %q, %q, %q", hooks[0].Secret, hooks[1].Secret, hooks[2].Secret)
83	}
84}