Files
Charon/backend/integration/notification_http_wrapper_integration_test.go
GitHub Actions bc9f2cf882 chore: enable Gotify and Custom Webhhok notifications and improve payload validation
- Enhanced Notifications component tests to include support for Discord, Gotify, and Webhook provider types.
- Updated test cases to validate the correct handling of provider type options and ensure proper payload structure during creation, preview, and testing.
- Introduced new tests for Gotify token handling and ensured sensitive information is not exposed in the UI.
- Refactored existing tests for clarity and maintainability, including improved assertions and error handling.
- Added comprehensive coverage for payload validation scenarios, including malformed requests and security checks against SSRF and oversized payloads.
2026-02-24 05:34:25 +00:00

125 lines
3.4 KiB
Go

//go:build integration
// +build integration
package integration
import (
"context"
"net/http"
"net/http/httptest"
"strings"
"sync/atomic"
"testing"
"github.com/Wikid82/charon/backend/internal/notifications"
)
func TestNotificationHTTPWrapperIntegration_RetriesOn429AndSucceeds(t *testing.T) {
t.Parallel()
var calls int32
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
current := atomic.AddInt32(&calls, 1)
if current == 1 {
w.WriteHeader(http.StatusTooManyRequests)
return
}
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"ok":true}`))
}))
defer server.Close()
wrapper := notifications.NewNotifyHTTPWrapper()
result, err := wrapper.Send(context.Background(), notifications.HTTPWrapperRequest{
URL: server.URL,
Body: []byte(`{"message":"hello"}`),
})
if err != nil {
t.Fatalf("expected retry success, got error: %v", err)
}
if result.Attempts != 2 {
t.Fatalf("expected 2 attempts, got %d", result.Attempts)
}
}
func TestNotificationHTTPWrapperIntegration_DoesNotRetryOn400(t *testing.T) {
t.Parallel()
var calls int32
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
atomic.AddInt32(&calls, 1)
w.WriteHeader(http.StatusBadRequest)
}))
defer server.Close()
wrapper := notifications.NewNotifyHTTPWrapper()
_, err := wrapper.Send(context.Background(), notifications.HTTPWrapperRequest{
URL: server.URL,
Body: []byte(`{"message":"hello"}`),
})
if err == nil {
t.Fatalf("expected non-retryable 400 error")
}
if atomic.LoadInt32(&calls) != 1 {
t.Fatalf("expected one request attempt, got %d", calls)
}
}
func TestNotificationHTTPWrapperIntegration_RejectsTokenizedQueryWithoutEcho(t *testing.T) {
t.Parallel()
wrapper := notifications.NewNotifyHTTPWrapper()
secret := "pr1-secret-token-value"
_, err := wrapper.Send(context.Background(), notifications.HTTPWrapperRequest{
URL: "http://example.com/hook?token=" + secret,
Body: []byte(`{"message":"hello"}`),
})
if err == nil {
t.Fatalf("expected tokenized query rejection")
}
if !strings.Contains(err.Error(), "query authentication is not allowed") {
t.Fatalf("expected sanitized query-auth rejection, got: %v", err)
}
if strings.Contains(err.Error(), secret) {
t.Fatalf("error must not echo secret token")
}
}
func TestNotificationHTTPWrapperIntegration_HeaderAllowlistSafety(t *testing.T) {
t.Parallel()
var seenAuthHeader string
var seenCookieHeader string
var seenGotifyKey string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
seenAuthHeader = r.Header.Get("Authorization")
seenCookieHeader = r.Header.Get("Cookie")
seenGotifyKey = r.Header.Get("X-Gotify-Key")
w.WriteHeader(http.StatusOK)
}))
defer server.Close()
wrapper := notifications.NewNotifyHTTPWrapper()
_, err := wrapper.Send(context.Background(), notifications.HTTPWrapperRequest{
URL: server.URL,
Headers: map[string]string{
"Authorization": "Bearer should-not-leak",
"Cookie": "session=should-not-leak",
"X-Gotify-Key": "allowed-token",
},
Body: []byte(`{"message":"hello"}`),
})
if err != nil {
t.Fatalf("expected success, got error: %v", err)
}
if seenAuthHeader != "" {
t.Fatalf("authorization header must be stripped")
}
if seenCookieHeader != "" {
t.Fatalf("cookie header must be stripped")
}
if seenGotifyKey != "allowed-token" {
t.Fatalf("expected X-Gotify-Key to pass through")
}
}