Some checks failed
CI / test (push) Failing after 4s
Watches issue/PR comments and pushes for flagged phrases; warns commenters via reply and opens a redacted correction PR for flagged file content, never rewriting history or deleting content directly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
114 lines
2.9 KiB
Go
114 lines
2.9 KiB
Go
package webhook
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.folja.dev/awayatan/yaju-is-always-watching-you/internal/forgejo"
|
|
)
|
|
|
|
type fakeDispatcher struct {
|
|
comments int
|
|
pushes int
|
|
}
|
|
|
|
func (f *fakeDispatcher) HandleIssueComment(forgejo.IssueCommentPayload) error {
|
|
f.comments++
|
|
return nil
|
|
}
|
|
|
|
func (f *fakeDispatcher) HandlePush(forgejo.PushPayload) error {
|
|
f.pushes++
|
|
return nil
|
|
}
|
|
|
|
func sign(secret, body string) string {
|
|
mac := hmac.New(sha256.New, []byte(secret))
|
|
mac.Write([]byte(body))
|
|
return hex.EncodeToString(mac.Sum(nil))
|
|
}
|
|
|
|
func TestServeHTTP_RejectsBadSignature(t *testing.T) {
|
|
fd := &fakeDispatcher{}
|
|
h := NewHandler("secret", fd)
|
|
|
|
body := `{}`
|
|
req := httptest.NewRequest(http.MethodPost, "/webhook", strings.NewReader(body))
|
|
req.Header.Set("X-Forgejo-Event", "push")
|
|
req.Header.Set("X-Forgejo-Signature", "deadbeef")
|
|
rec := httptest.NewRecorder()
|
|
|
|
h.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusUnauthorized {
|
|
t.Fatalf("status = %d, want %d", rec.Code, http.StatusUnauthorized)
|
|
}
|
|
if fd.pushes != 0 {
|
|
t.Fatalf("dispatcher should not run on bad signature, pushes = %d", fd.pushes)
|
|
}
|
|
}
|
|
|
|
func TestServeHTTP_DispatchesPush(t *testing.T) {
|
|
fd := &fakeDispatcher{}
|
|
h := NewHandler("secret", fd)
|
|
|
|
body := `{"ref":"refs/heads/main"}`
|
|
req := httptest.NewRequest(http.MethodPost, "/webhook", strings.NewReader(body))
|
|
req.Header.Set("X-Forgejo-Event", "push")
|
|
req.Header.Set("X-Forgejo-Signature", sign("secret", body))
|
|
rec := httptest.NewRecorder()
|
|
|
|
h.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusNoContent {
|
|
t.Fatalf("status = %d, want %d, body=%s", rec.Code, http.StatusNoContent, rec.Body.String())
|
|
}
|
|
if fd.pushes != 1 {
|
|
t.Fatalf("pushes = %d, want 1", fd.pushes)
|
|
}
|
|
}
|
|
|
|
func TestServeHTTP_DispatchesIssueComment(t *testing.T) {
|
|
fd := &fakeDispatcher{}
|
|
h := NewHandler("secret", fd)
|
|
|
|
body := `{"action":"created"}`
|
|
req := httptest.NewRequest(http.MethodPost, "/webhook", strings.NewReader(body))
|
|
req.Header.Set("X-Forgejo-Event", "issue_comment")
|
|
req.Header.Set("X-Forgejo-Signature", sign("secret", body))
|
|
rec := httptest.NewRecorder()
|
|
|
|
h.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusNoContent {
|
|
t.Fatalf("status = %d, want %d, body=%s", rec.Code, http.StatusNoContent, rec.Body.String())
|
|
}
|
|
if fd.comments != 1 {
|
|
t.Fatalf("comments = %d, want 1", fd.comments)
|
|
}
|
|
}
|
|
|
|
func TestServeHTTP_IgnoresUnhandledEvent(t *testing.T) {
|
|
fd := &fakeDispatcher{}
|
|
h := NewHandler("secret", fd)
|
|
|
|
body := `{}`
|
|
req := httptest.NewRequest(http.MethodPost, "/webhook", strings.NewReader(body))
|
|
req.Header.Set("X-Forgejo-Event", "fork")
|
|
req.Header.Set("X-Forgejo-Signature", sign("secret", body))
|
|
rec := httptest.NewRecorder()
|
|
|
|
h.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusNoContent {
|
|
t.Fatalf("status = %d, want %d", rec.Code, http.StatusNoContent)
|
|
}
|
|
if fd.comments != 0 || fd.pushes != 0 {
|
|
t.Fatalf("dispatcher should not be called for unhandled events")
|
|
}
|
|
}
|