package httpd import ( "testing" "github.com/barerepo/server/internal/repocfg" "github.com/barerepo/server/internal/store" ) // A hook gets the events it named and no others, because chapter 23.2 makes the list the filter. func TestAHookOnlyGetsTheEventsItNamed(t *testing.T) { cfg := repocfg.Config{Webhook: []repocfg.Webhook{ {URL: "https://deploy.example/hook", Events: []string{"push"}}, {URL: "https://chat.example/hook", Events: []string{"proposal.opened", "thread.opened"}}, {URL: "https://nothing.example/hook"}, }} cases := []struct { kind string want []string }{ {store.Pushed, []string{"https://deploy.example/hook"}}, {store.ProposalOpened, []string{"https://chat.example/hook"}}, {store.ThreadOpened, []string{"https://chat.example/hook"}}, {store.RunFailed, nil}, } for _, c := range cases { var got []string for _, h := range hooksFor(cfg, c.kind) { got = append(got, h.URL) } if len(got) != len(c.want) { t.Errorf("%s went to %v, wanted %v", c.kind, got, c.want) continue } for i := range got { if got[i] != c.want[i] { t.Errorf("%s went to %v, wanted %v", c.kind, got, c.want) break } } } } // A url with no scheme cannot be delivered to, and a hook with no url is a line somebody started. func TestAHookWithNoURLIsNotDelivered(t *testing.T) { cfg := repocfg.Config{Webhook: []repocfg.Webhook{{Events: []string{"push"}}}} if got := hooksFor(cfg, store.Pushed); len(got) != 0 { t.Errorf("a hook with no url was selected: %v", got) } } // The file names a secret and the server holds the value, so a missing value must say so. 23.2. func TestAMissingSecretIsAnErrorAndNotAnUnsignedDelivery(t *testing.T) { _, err := hookSecret(repocfg.Webhook{URL: "https://x.example", SecretEnv: "BAREREPO_TEST_ABSENT"}) if err == nil { t.Fatal("a named secret that is not set delivered anyway, unsigned") } t.Setenv("BAREREPO_TEST_PRESENT", "swordfish") got, err := hookSecret(repocfg.Webhook{URL: "https://x.example", SecretEnv: "BAREREPO_TEST_PRESENT"}) if err != nil { t.Fatal(err) } if got != "swordfish" { t.Errorf("the secret read as %q", got) } // No secret named is a choice, not a mistake, so it delivers unsigned. got, err = hookSecret(repocfg.Webhook{URL: "https://x.example"}) if err != nil || got != "" { t.Errorf("an unnamed secret gave %q, %v", got, err) } } func TestSplitRepo(t *testing.T) { cases := []struct { in string owner, name string ok bool }{ {"john/johnbot", "john", "johnbot", true}, {"john", "", "", false}, {"/johnbot", "", "", false}, {"john/", "", "", false}, } for _, c := range cases { owner, name, ok := splitRepo(c.in) if ok != c.ok || (ok && (owner != c.owner || name != c.name)) { t.Errorf("%q gave %q %q %v", c.in, owner, name, ok) } } } // Chapter 23.1 calls a webhook the escape hatch, and a file that accepts a name nothing sends is not one. func TestAHookNamingAnEventBarerepoNeverSendsSaysSo(t *testing.T) { got := unknownEvents([]string{"push", "proposal.opened"}) if len(got) != 0 { t.Errorf("real event names were reported as unknown: %v", got) } // The near misses are the ones that matter, because a reader's eye passes over them. got = unknownEvents([]string{"push", "proposal.open", "run.succeeded", "thread.reply"}) want := []string{"proposal.open", "run.succeeded", "thread.reply"} if len(got) != len(want) { t.Fatalf("got %v, wanted %v", got, want) } for i := range want { if got[i] != want[i] { t.Fatalf("got %v, wanted %v", got, want) } } // run.succeeded is the sharpest of them: chapter 19.1 names it only to say it is not an event. if store.KnownKind("run.succeeded") { t.Error("run.succeeded is an event kind, and chapter 19.1 says a green build is not news") } for _, k := range store.Kinds { if !store.KnownKind(k) { t.Errorf("%s is in the list and not known by the check that reads it", k) } } }