119 lines
4.4 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|
|
}
|