max_webhook_test.go
Rama main · réplica de lectura del repositorio original.
package main
import (
"bytes"
"fmt"
"log/slog"
"net/http"
"strings"
"testing"
"time"
)
func TestMaxWebhookReadsOnlyRoutingFields(t *testing.T) {
for _, tc := range []struct {
name, recipient, body, mid string
}{
{"body ID takes precedence", `"post_id":123`, `"mid":"mid.1","text":"post"`, "mid.1"},
{"numeric fallback keeps precision", `"post_id":9007199254740993`, `"text":"post"`, "9007199254740993"},
{"string fallback", `"post_id":"mid.fallback"`, `"text":"post"`, "mid.fallback"},
{"irrelevant recipient field", `"user_id":"not-an-integer"`, `"mid":"mid.2","text":"post"`, "mid.2"},
} {
t.Run(tc.name, func(t *testing.T) {
db := testStore(t)
src := &maxSource{channel: maxChat{id: -42}}
hook := maxWebhook("secret", db, map[int64]*maxSource{-42: src})
payload := fmt.Sprintf(`{"update_type":"message_created","message":{"recipient":{"chat_id":-42,"chat_type":"channel",%s},"body":{%s}}}`, tc.recipient, tc.body)
for range 2 {
submitHook(t, hook, "X-Max-Bot-Api-Secret", "secret", payload, http.StatusOK)
}
post, err := db.next(t.Context(), channelKey{maxPlatform, -42}, time.Now())
if err != nil || post.key != "message:"+tc.mid || len(post.payloads) != 1 {
t.Fatalf("post not persisted exactly once: key=%q, payloads=%d, err=%v", post.key, len(post.payloads), err)
}
})
}
}
func TestMaxWebhookLogsMalformedEventWithoutPayload(t *testing.T) {
var output bytes.Buffer
previous := slog.Default()
slog.SetDefault(slog.New(slog.NewTextHandler(&output, nil)))
t.Cleanup(func() { slog.SetDefault(previous) })
db := testStore(t)
hook := maxWebhook("secret", db, map[int64]*maxSource{-42: {channel: maxChat{id: -42}}})
for _, tc := range []struct {
name, payload string
}{
{"envelope", `{"update_type":"message_created","timestamp":"invalid","message":{"body":{"text":"private-post-contents"}}}`},
{"routing fields", `{"update_type":"message_created","message":{"recipient":{"chat_id":"invalid","chat_type":"channel"},"body":{"mid":"mid.1","text":"private-post-contents"}}}`},
{"fallback ID", `{"update_type":"message_created","message":{"recipient":{"chat_id":-42,"chat_type":"channel","post_id":true},"body":{"text":"private-post-contents"}}}`},
} {
t.Run(tc.name, func(t *testing.T) {
output.Reset()
submitHook(t, hook, "X-Max-Bot-Api-Secret", "secret", tc.payload, http.StatusBadRequest)
log := output.String()
if !strings.Contains(log, "message_created") || !strings.Contains(log, "cannot unmarshal") {
t.Fatalf("missing event type or decode error: %q", log)
}
if strings.Contains(log, "private-post-contents") || strings.Contains(log, tc.payload) {
t.Fatalf("payload leaked: %q", log)
}
})
}
if post, err := db.next(t.Context(), channelKey{maxPlatform, -42}, time.Now()); err != nil || post.row != 0 {
t.Fatalf("invalid posts must not be queued: row=%d, err=%v", post.row, err)
}
}