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

103 lines
3.9 KiB
Go

package gateway
import (
"context"
"encoding/json"
"github.com/example/notify-gateway/internal/config"
"github.com/example/notify-gateway/internal/divera"
"github.com/example/notify-gateway/internal/model"
"net/http"
"net/http/httptest"
"path/filepath"
"strings"
"testing"
)
func TestRepeatedRegexMustMatchEveryField(t *testing.T) {
m := config.Mapping{ChannelRegex: "^alarm$", TitleRegex: "^alarm$"}
if matches(m, model.InboundMessage{Channel: "alarm", Title: "other"}) {
t.Fatal("same pattern must be checked for both fields")
}
}
func TestProviderPayloads(t *testing.T) {
m := config.Mapping{Target: "discord", TitleTemplate: "{{.Title}}", TextTemplate: "{{.Message}}"}
p, kind, err := buildPayload(m, model.InboundMessage{Title: "Test", Message: "@everyone Grüße"})
if err != nil || kind != "discord" || p["content"] != "Test\n@everyone Grüße" {
t.Fatalf("%v %s %v", p, kind, err)
}
b, _ := json.Marshal(p)
if !strings.Contains(string(b), `"allowed_mentions":{"parse":[]}`) {
t.Fatalf("mentions not disabled: %s", b)
}
for _, body := range []string{"", strings.Repeat("a", 2001), strings.Repeat("😀", 1001)} {
if _, _, err := buildPayload(m, model.InboundMessage{Message: body}); err == nil {
t.Fatal("expected empty/oversized payload to fail")
}
}
if _, _, err := buildPayload(m, model.InboundMessage{Message: strings.Repeat("a", 2000)}); err != nil {
t.Fatal(err)
}
m.Target = "webhook"
p, _, err = buildPayload(m, model.InboundMessage{Source: "ntfy", Channel: "ops", Title: "hello", Message: "world", Raw: map[string]any{"secret": "private"}})
if err != nil || p["source"] != "ntfy" || p["message"] != "world" {
t.Fatalf("%v %v", p, err)
}
if _, ok := p["raw"]; ok {
t.Fatal("raw input must not be forwarded automatically")
}
}
func TestDispatchContinuesAfterFailedDestination(t *testing.T) {
store, err := config.Open(filepath.Join(t.TempDir(), "config.json"))
if err != nil {
t.Fatal(err)
}
failing := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(429) }))
defer failing.Close()
c := store.Get()
c.Outbounds = []config.OutboundConfig{{ID: "bad", Provider: "webhook", URL: failing.URL, Live: true}, {ID: "dry", Provider: "discord", URL: "https://discord.com/api/webhooks/123/secret"}}
c.Mappings = []config.Mapping{
{ID: "first", Enabled: true, Target: "webhook", OutboundID: "bad", TextTemplate: "{{.Message}}"},
{ID: "second", Enabled: true, Target: "discord", OutboundID: "dry", TextTemplate: "{{.Message}}"},
{ID: "legacy", Enabled: true, Target: "alarm", TextTemplate: "{{.Message}}"},
}
if err := store.Replace(c); err != nil {
t.Fatal(err)
}
d := New(store, divera.New(func() config.DiveraConfig { return store.Get().Divera }))
results, err := d.Dispatch(context.Background(), model.InboundMessage{Message: "test"})
if err == nil || len(results) != 3 {
t.Fatalf("results=%+v error=%v", results, err)
}
if results[0].StatusCode != 429 || results[0].Error == "" || results[1].Error != "" || results[1].StatusCode != 200 || results[2].StatusCode != 200 {
t.Fatalf("results=%+v", results)
}
}
func TestMatches(t *testing.T) {
m := config.Mapping{Enabled: true, Source: "ntfy", ChannelRegex: "^alarm-.*$", MessageRegex: "Feuer", MinPriority: 3}
msg := model.InboundMessage{Source: "ntfy", Channel: "alarm-1", Message: "Feuer im Gebäude", Priority: 4}
if !matches(m, msg) {
t.Fatal("expected mapping to match")
}
msg.Priority = 2
if matches(m, msg) {
t.Fatal("priority should not match")
}
}
func TestBuildPayload(t *testing.T) {
m := config.Mapping{Target: "alarm", TitleTemplate: "{{.Title}}", TextTemplate: "{{.Message}}", NotificationType: 3, Groups: []int64{12}, SendPush: true}
p, kind, err := buildPayload(m, model.InboundMessage{Title: "B3", Message: "Rauch"})
if err != nil {
t.Fatal(err)
}
if kind != "alarms" {
t.Fatalf("kind=%s", kind)
}
a := p["Alarm"].(map[string]any)
if a["title"] != "B3" {
t.Fatalf("title=%v", a["title"])
}
}