package forgejo import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "net/http/httptest" "strings" "testing" ) func signBody(body, secret string) string { mac := hmac.New(sha256.New, []byte(secret)) _, _ = mac.Write([]byte(body)) return hex.EncodeToString(mac.Sum(nil)) } func TestVerifyAndParseWithForgejoHeaders(t *testing.T) { secret := "supersecret" body := `{"action":"closed","issue":{"number":7,"title":"[Cairn] crash"}}` req := httptest.NewRequest("POST", "/webhooks/forgejo", strings.NewReader(body)) req.Header.Set("X-Forgejo-Event", "issues") req.Header.Set("X-Forgejo-Signature", signBody(body, secret)) event, eventType, err := VerifyAndParse(req, secret) if err != nil { t.Fatalf("VerifyAndParse returned error: %v", err) } if eventType != "issues" { t.Fatalf("expected event type issues, got %q", eventType) } if event == nil || event.Issue == nil || event.Issue.Number != 7 { t.Fatalf("unexpected parsed event: %#v", event) } } func TestVerifyAndParseWithGiteaFallbackHeaders(t *testing.T) { secret := "fallbacksecret" body := `{"action":"reopened","issue":{"number":42,"title":"[Cairn] crash"}}` req := httptest.NewRequest("POST", "/webhooks/forgejo", strings.NewReader(body)) req.Header.Set("X-Gitea-Event", "issues") req.Header.Set("X-Gitea-Signature", signBody(body, secret)) event, eventType, err := VerifyAndParse(req, secret) if err != nil { t.Fatalf("VerifyAndParse returned error: %v", err) } if eventType != "issues" { t.Fatalf("expected event type issues, got %q", eventType) } if event == nil || event.Issue == nil || event.Issue.Number != 42 { t.Fatalf("unexpected parsed event: %#v", event) } } func TestVerifyAndParseRejectsBadSignature(t *testing.T) { secret := "supersecret" body := `{"action":"closed"}` req := httptest.NewRequest("POST", "/webhooks/forgejo", strings.NewReader(body)) req.Header.Set("X-Forgejo-Event", "issues") req.Header.Set("X-Forgejo-Signature", "bad-signature") _, _, err := VerifyAndParse(req, secret) if err == nil { t.Fatal("expected error for bad signature, got nil") } } func TestVerifyAndParseWithoutSecretSkipsHMAC(t *testing.T) { body := `{"action":"closed"}` req := httptest.NewRequest("POST", "/webhooks/forgejo", strings.NewReader(body)) req.Header.Set("X-Forgejo-Event", "issues") event, eventType, err := VerifyAndParse(req, "") if err != nil { t.Fatalf("VerifyAndParse returned error: %v", err) } if eventType != "issues" { t.Fatalf("expected event type issues, got %q", eventType) } if event == nil || event.Action != "closed" { t.Fatalf("unexpected parsed event: %#v", event) } } func TestVerifyHMAC(t *testing.T) { body := []byte("payload") secret := "abc123" sig := signBody(string(body), secret) if !verifyHMAC(body, sig, secret) { t.Fatal("expected verifyHMAC to accept valid signature") } if verifyHMAC(body, "invalid", secret) { t.Fatal("expected verifyHMAC to reject invalid signature") } }