Files
aiaa-notification-server/internal/adapter/safew_test.go
T
ryan 0d0cd0c510 feat: 新增 SafeW 出站通知渠道
通过 Bot API sendMessage 投递渲染后的通知,MarkdownV2 自动转义标题与正文。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 00:58:44 +08:00

173 lines
5.0 KiB
Go

package adapter
import (
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"aiaa-notification-service/internal/config"
)
func TestEscapeMarkdownV2(t *testing.T) {
tests := []struct {
name string
in string
want string
}{
{name: "underscore and dot", in: "hello_world.", want: `hello\_world\.`},
{name: "empty", in: "", want: ""},
{name: "no specials", in: "hello", want: "hello"},
{name: "backslash first", in: `a\b`, want: `a\\b`},
{
name: "all specials",
in: "_*[]()~`>#+-=|{}.!\\",
want: "\\_\\*\\[\\]\\(\\)\\~\\`\\>\\#\\+\\-\\=\\|\\{\\}\\.\\!\\\\",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := escapeMarkdownV2(tt.in)
if got != tt.want {
t.Fatalf("escapeMarkdownV2(%q) = %q, want %q", tt.in, got, tt.want)
}
})
}
}
func TestNewSenderSafew(t *testing.T) {
s, err := NewSender("safew", &config.SMTPConfig{}, nil)
if err != nil {
t.Fatalf("NewSender(safew): %v", err)
}
if s.Type() != "safew" {
t.Fatalf("Type() = %q, want safew", s.Type())
}
}
func TestSafeWSenderSendSuccess(t *testing.T) {
var gotPath string
var gotBody map[string]any
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotPath = r.URL.Path
raw, _ := io.ReadAll(r.Body)
if err := json.Unmarshal(raw, &gotBody); err != nil {
t.Errorf("unmarshal request: %v", err)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"ok":true,"result":{}}`))
}))
defer srv.Close()
sender := &SafeWSender{apiBase: srv.URL}
cfg, _ := json.Marshal(map[string]any{"token": "tok-1", "chat_id": "123456789"})
if err := sender.Send("hello_world.", "price=1.5", cfg); err != nil {
t.Fatalf("Send: %v", err)
}
if gotPath != "/bottok-1/sendMessage" {
t.Fatalf("path = %q, want /bottok-1/sendMessage", gotPath)
}
if gotBody["chat_id"] != "123456789" {
t.Fatalf("chat_id = %#v, want \"123456789\"", gotBody["chat_id"])
}
if gotBody["parse_mode"] != "MarkdownV2" {
t.Fatalf("parse_mode = %#v, want MarkdownV2", gotBody["parse_mode"])
}
wantText := "*" + escapeMarkdownV2("hello_world.") + "*\n" + escapeMarkdownV2("price=1.5")
if gotBody["text"] != wantText {
t.Fatalf("text = %#v, want %#v", gotBody["text"], wantText)
}
}
func TestSafeWSenderSendNumericChatID(t *testing.T) {
var gotBody map[string]any
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
raw, _ := io.ReadAll(r.Body)
_ = json.Unmarshal(raw, &gotBody)
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"ok":true}`))
}))
defer srv.Close()
sender := &SafeWSender{apiBase: srv.URL}
cfg := json.RawMessage(`{"token":"tok","chat_id":123456789}`)
if err := sender.Send("t", "c", cfg); err != nil {
t.Fatalf("Send: %v", err)
}
if gotBody["chat_id"] != "123456789" {
t.Fatalf("chat_id = %#v, want string \"123456789\"", gotBody["chat_id"])
}
}
func TestSafeWSenderSendOKFalse(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"ok":false,"description":"chat not found"}`))
}))
defer srv.Close()
sender := &SafeWSender{apiBase: srv.URL}
cfg, _ := json.Marshal(map[string]string{"token": "tok", "chat_id": "1"})
err := sender.Send("t", "c", cfg)
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "chat not found") {
t.Fatalf("error %q should contain description", err)
}
}
func TestSafeWSenderSendHTTPError(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
_, _ = w.Write([]byte(`{"ok":false,"description":"Unauthorized"}`))
}))
defer srv.Close()
sender := &SafeWSender{apiBase: srv.URL}
cfg, _ := json.Marshal(map[string]string{"token": "bad", "chat_id": "1"})
err := sender.Send("t", "c", cfg)
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "Unauthorized") {
t.Fatalf("error %q should contain description", err)
}
}
func TestSafeWSenderSendMissingFields(t *testing.T) {
hits := 0
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
hits++
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"ok":true}`))
}))
defer srv.Close()
sender := &SafeWSender{apiBase: srv.URL}
cases := []json.RawMessage{
json.RawMessage(`{"chat_id":"1"}`),
json.RawMessage(`{"token":"tok"}`),
json.RawMessage(`{"token":" ","chat_id":"1"}`),
json.RawMessage(`{"token":"tok","chat_id":""}`),
json.RawMessage(`{"token":"tok","chat_id":null}`),
}
for _, cfg := range cases {
if err := sender.Send("t", "c", cfg); err == nil {
t.Fatalf("expected error for config %s", cfg)
}
}
if hits != 0 {
t.Fatalf("unexpected HTTP calls: %d", hits)
}
if err := sender.Send("t", "c", json.RawMessage(`{`)); err == nil {
t.Fatal("expected error for invalid json")
}
if hits != 0 {
t.Fatalf("unexpected HTTP calls after invalid json: %d", hits)
}
}