forgejo-tickets/internal/forgejo/webhook_test.go

125 lines
3.5 KiB
Go

package forgejo
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"net/http"
"net/http/httptest"
"testing"
)
func computeHMAC(body []byte, secret string) string {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(body)
return hex.EncodeToString(mac.Sum(nil))
}
func TestVerifyWebhookSignature_Valid(t *testing.T) {
secret := "test-webhook-secret"
payload := []byte(`{"action":"opened","issue":{"number":1,"title":"Test","state":"open"}}`)
signature := computeHMAC(payload, secret)
req := httptest.NewRequest(http.MethodPost, "/webhook", bytes.NewReader(payload))
req.Header.Set("X-Forgejo-Signature", signature)
body, err := VerifyWebhookSignature(req, secret)
if err != nil {
t.Fatalf("expected no error, got: %v", err)
}
if !bytes.Equal(body, payload) {
t.Errorf("expected body %q, got %q", payload, body)
}
}
func TestVerifyWebhookSignature_InvalidSignature(t *testing.T) {
secret := "test-webhook-secret"
payload := []byte(`{"action":"opened"}`)
req := httptest.NewRequest(http.MethodPost, "/webhook", bytes.NewReader(payload))
req.Header.Set("X-Forgejo-Signature", "deadbeef00000000000000000000000000000000000000000000000000000000")
_, err := VerifyWebhookSignature(req, secret)
if err == nil {
t.Fatal("expected error for invalid signature, got nil")
}
}
func TestVerifyWebhookSignature_MissingHeader(t *testing.T) {
payload := []byte(`{"action":"opened"}`)
req := httptest.NewRequest(http.MethodPost, "/webhook", bytes.NewReader(payload))
// No X-Forgejo-Signature header set
_, err := VerifyWebhookSignature(req, "some-secret")
if err == nil {
t.Fatal("expected error for missing signature header, got nil")
}
expected := "missing X-Forgejo-Signature header"
if err.Error() != expected {
t.Errorf("expected error message %q, got %q", expected, err.Error())
}
}
func TestVerifyWebhookSignature_WrongSecret(t *testing.T) {
payload := []byte(`{"action":"opened"}`)
signature := computeHMAC(payload, "correct-secret")
req := httptest.NewRequest(http.MethodPost, "/webhook", bytes.NewReader(payload))
req.Header.Set("X-Forgejo-Signature", signature)
_, err := VerifyWebhookSignature(req, "wrong-secret")
if err == nil {
t.Fatal("expected error for wrong secret, got nil")
}
}
func TestParseWebhookPayload_Valid(t *testing.T) {
data := []byte(`{"action":"opened","issue":{"number":42,"title":"Bug report","state":"open"}}`)
payload, err := ParseWebhookPayload(data)
if err != nil {
t.Fatalf("expected no error, got: %v", err)
}
if payload.Action != "opened" {
t.Errorf("expected action %q, got %q", "opened", payload.Action)
}
if payload.Issue.Number != 42 {
t.Errorf("expected issue number 42, got %d", payload.Issue.Number)
}
if payload.Issue.Title != "Bug report" {
t.Errorf("expected issue title %q, got %q", "Bug report", payload.Issue.Title)
}
if payload.Issue.State != "open" {
t.Errorf("expected issue state %q, got %q", "open", payload.Issue.State)
}
}
func TestParseWebhookPayload_InvalidJSON(t *testing.T) {
data := []byte(`{not valid json}`)
_, err := ParseWebhookPayload(data)
if err == nil {
t.Fatal("expected error for invalid JSON, got nil")
}
}
func TestParseWebhookPayload_EmptyObject(t *testing.T) {
data := []byte(`{}`)
payload, err := ParseWebhookPayload(data)
if err != nil {
t.Fatalf("expected no error for empty object, got: %v", err)
}
if payload.Action != "" {
t.Errorf("expected empty action, got %q", payload.Action)
}
if payload.Issue.Number != 0 {
t.Errorf("expected issue number 0, got %d", payload.Issue.Number)
}
}