Files
Memoh/internal/channel/webhook_handler_test.go
T
BBQ f376a2abe3 fix(channel): add wechatoa webhook delivery and proxy config (#356)
Unify webhook handling across channel adapters and add the WeChat Official Account channel so inbound routing and replies work without platform-specific handlers. Add adapter-scoped proxy support and stable config field ordering so restricted network environments can deliver WeChat and Telegram messages reliably.
2026-04-10 21:26:11 +08:00

156 lines
3.9 KiB
Go

package channel
import (
"context"
"errors"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/labstack/echo/v4"
)
type fakeWebhookAdapter struct {
channelType ChannelType
calls []struct {
cfg ChannelConfig
method string
}
}
func (a *fakeWebhookAdapter) Type() ChannelType { return a.channelType }
func (a *fakeWebhookAdapter) Descriptor() Descriptor {
return Descriptor{
Type: a.channelType,
DisplayName: strings.ToUpper(a.channelType.String()),
}
}
func (a *fakeWebhookAdapter) HandleWebhook(ctx context.Context, cfg ChannelConfig, handler InboundHandler, r *http.Request, w http.ResponseWriter) error {
a.calls = append(a.calls, struct {
cfg ChannelConfig
method string
}{cfg: cfg, method: r.Method})
if handler != nil {
if err := handler(ctx, cfg, InboundMessage{
Channel: cfg.ChannelType,
BotID: cfg.BotID,
Message: Message{Format: MessageFormatPlain, Text: "hello"},
}); err != nil {
return err
}
}
w.WriteHeader(http.StatusNoContent)
return nil
}
type fakeWebhookStore struct {
configs []ChannelConfig
err error
}
func (s *fakeWebhookStore) ListConfigsByType(_ context.Context, channelType ChannelType) ([]ChannelConfig, error) {
if s.err != nil {
return nil, s.err
}
items := make([]ChannelConfig, 0, len(s.configs))
for _, item := range s.configs {
if item.ChannelType == channelType {
items = append(items, item)
}
}
return items, nil
}
type fakeWebhookManager struct {
registry *Registry
calls []struct {
cfg ChannelConfig
msg InboundMessage
}
}
func (m *fakeWebhookManager) HandleInbound(_ context.Context, cfg ChannelConfig, msg InboundMessage) error {
m.calls = append(m.calls, struct {
cfg ChannelConfig
msg InboundMessage
}{cfg: cfg, msg: msg})
return nil
}
func (m *fakeWebhookManager) Registry() *Registry { return m.registry }
func TestGenericWebhookHandlerDispatchesToAdapter(t *testing.T) {
t.Parallel()
registry := NewRegistry()
adapter := &fakeWebhookAdapter{channelType: ChannelType("testhook")}
registry.MustRegister(adapter)
store := &fakeWebhookStore{
configs: []ChannelConfig{{
ID: "cfg-1",
BotID: "bot-1",
ChannelType: adapter.channelType,
}},
}
manager := &fakeWebhookManager{registry: registry}
h := NewWebhookServerHandler(nil, (*Store)(nil), (*Manager)(nil))
h.store = store
h.manager = manager
h.registry = registry
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/channels/testhook/webhook/cfg-1", strings.NewReader(`{}`))
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
c.SetParamNames("platform", "config_id")
c.SetParamValues("testhook", "cfg-1")
if err := h.Handle(c); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if rec.Code != http.StatusNoContent {
t.Fatalf("unexpected status code: %d", rec.Code)
}
if len(adapter.calls) != 1 {
t.Fatalf("expected adapter to be called once, got %d", len(adapter.calls))
}
if len(manager.calls) != 1 {
t.Fatalf("expected inbound manager to be called once, got %d", len(manager.calls))
}
}
func TestGenericWebhookHandlerRejectsUnknownConfig(t *testing.T) {
t.Parallel()
registry := NewRegistry()
registry.MustRegister(&fakeWebhookAdapter{channelType: ChannelType("testhook")})
manager := &fakeWebhookManager{registry: registry}
h := NewWebhookServerHandler(nil, (*Store)(nil), (*Manager)(nil))
h.store = &fakeWebhookStore{}
h.manager = manager
h.registry = registry
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/channels/testhook/webhook/missing", strings.NewReader(`{}`))
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
c.SetParamNames("platform", "config_id")
c.SetParamValues("testhook", "missing")
err := h.Handle(c)
if err == nil {
t.Fatal("expected not found error")
}
var httpErr *echo.HTTPError
if !errors.As(err, &httpErr) {
t.Fatalf("expected HTTPError, got %T", err)
}
if httpErr.Code != http.StatusNotFound {
t.Fatalf("unexpected status code: %d", httpErr.Code)
}
}