Files
2026-09-16 06:26:16 +02:00

119 lines
4.4 KiB
Go

package outbound
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/example/notify-gateway/internal/config"
)
func TestWebhookDeliveryAndDryRun(t *testing.T) {
calls := 0
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
calls++
if r.Method != "POST" || r.Header.Get("Authorization") != "Bearer private-token" || r.Header.Get("Content-Type") != "application/json" {
t.Error("incorrect outbound request")
}
var payload map[string]any
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil || payload["message"] != "Grüße" {
t.Error("incorrect payload")
}
w.WriteHeader(http.StatusNoContent)
}))
defer srv.Close()
o := config.OutboundConfig{ID: "test", Provider: "webhook", URL: srv.URL, BearerToken: "private-token"}
r, err := Send(context.Background(), o, map[string]any{"message": "Grüße"})
if err != nil || calls != 0 || !strings.Contains(r.Body, `"dry_run":true`) || strings.Contains(r.Body, "private-token") {
t.Fatalf("dry-run: %+v %v calls=%d", r, err, calls)
}
o.Live = true
r, err = Send(context.Background(), o, map[string]any{"message": "Grüße"})
if err != nil || r.StatusCode != 204 || calls != 1 {
t.Fatalf("live: %+v %v calls=%d", r, err, calls)
}
}
func TestHTTPFailuresAndRedirectsDoNotLeakSecrets(t *testing.T) {
for _, status := range []int{302, 400, 429, 500} {
t.Run(fmt.Sprint(status), func(t *testing.T) {
calls := 0
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
calls++
w.Header().Set("Location", "/secret")
w.WriteHeader(status)
fmt.Fprint(w, "private-token")
}))
defer srv.Close()
r, err := Send(context.Background(), config.OutboundConfig{ID: "test", Provider: "webhook", URL: srv.URL + "/private-token", Live: true}, map[string]any{})
if err == nil || r.StatusCode != status || calls != 1 || r.Body != "" || strings.Contains(err.Error(), "private-token") {
t.Fatalf("result=%+v err=%v calls=%d", r, err, calls)
}
})
}
}
type transportFunc func(*http.Request) (*http.Response, error)
func (f transportFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) }
func TestDiscordWaitAndTransportErrorRedaction(t *testing.T) {
previous := http.DefaultTransport
t.Cleanup(func() { http.DefaultTransport = previous })
http.DefaultTransport = transportFunc(func(r *http.Request) (*http.Response, error) {
if r.URL.Query().Get("wait") != "true" || r.URL.Query().Get("thread_id") != "123" {
t.Error("Discord query parameters missing")
}
return nil, fmt.Errorf("failure at %s", r.URL)
})
_, err := Send(context.Background(), config.OutboundConfig{ID: "discord", Provider: "discord", URL: "https://discord.com/api/webhooks/123/private-token?thread_id=123&wait=false", Live: true}, map[string]any{"content": "hello"})
if err == nil || strings.Contains(err.Error(), "private-token") {
t.Fatalf("unexpected error: %v", err)
}
}
func TestCanceledContext(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel()
_, err := Send(ctx, config.OutboundConfig{ID: "test", Provider: "webhook", URL: "http://127.0.0.1:1/private-token", Live: true}, map[string]any{})
if err != context.Canceled {
t.Fatalf("error=%v", err)
}
}
func TestNtfyAndGotifyProtocols(t *testing.T) {
for _, provider := range []string{"ntfy", "gotify"} {
t.Run(provider, func(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var payload map[string]any
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
t.Fatal(err)
}
if payload["title"] != "Test" || payload["message"] != "Hello" {
t.Error(payload)
}
if provider == "ntfy" {
if payload["topic"] != "ops" || payload["priority"] != float64(5) || r.Header.Get("Authorization") != "Bearer secret" {
t.Error("ntfy protocol", payload)
}
} else {
if r.Header.Get("X-Gotify-Key") != "secret" || r.Header.Get("Authorization") != "" {
t.Error("gotify auth")
}
}
w.WriteHeader(200)
}))
defer srv.Close()
t.Setenv("TEST_PROVIDER_TOKEN", "secret")
_, err := Send(context.Background(), config.OutboundConfig{ID: provider, Provider: provider, URL: srv.URL, Topic: "ops", BearerToken: "env:TEST_PROVIDER_TOKEN", Live: true}, map[string]any{"title": "Test", "message": "Hello", "priority": 9})
if err != nil {
t.Fatal(err)
}
})
}
}