Files
ai-customer-service/internal/http/handlers/platform_webhook_handler_test.go
2026-05-06 10:45:51 +08:00

113 lines
4.3 KiB
Go

package handlers
import (
"context"
"net/http"
"net/http/httptest"
"strings"
"testing"
intentdomain "github.com/bridge/ai-customer-service/internal/domain/intent"
"github.com/bridge/ai-customer-service/internal/domain/message"
"github.com/bridge/ai-customer-service/internal/domain/platformevent"
"github.com/bridge/ai-customer-service/internal/platformadapter"
"github.com/bridge/ai-customer-service/internal/service/dialog"
"github.com/bridge/ai-customer-service/internal/service/handoff"
)
type stubPlatformDialogProcessor struct {
result *dialog.Result
err error
msg *message.UnifiedMessage
}
func (s *stubPlatformDialogProcessor) Process(_ context.Context, msg *message.UnifiedMessage) (*dialog.Result, error) {
s.msg = msg
if s.err != nil {
return nil, s.err
}
return s.result, nil
}
type stubPlatformEventWriter struct {
events []platformevent.Event
err error
}
func (s *stubPlatformEventWriter) InsertPendingBatch(_ context.Context, events []platformevent.Event) error {
s.events = append(s.events, events...)
return s.err
}
func TestPlatformWebhookHandler_ShouldEnqueueMessageReceivedAndReplyGenerated(t *testing.T) {
registry := platformadapter.NewRegistry(platformadapter.NewSub2APIAdapter())
processor := &stubPlatformDialogProcessor{result: &dialog.Result{SessionID: "sess-1", Reply: "好的", Intent: &intentdomain.Result{Intent: intentdomain.IntentRefund, Confidence: 0.9}}}
writer := &stubPlatformEventWriter{}
handler := NewPlatformWebhookHandler(processor, registry, writer)
body := `{"message_id":"m1","channel":"sub2api","open_id":"u1","content":"我要退款"}`
req := httptest.NewRequest(http.MethodPost, "/api/v1/customer-service/platforms/sub2api/webhook", strings.NewReader(body))
rr := httptest.NewRecorder()
handler.Handle(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status = %d, want 200; body=%s", rr.Code, rr.Body.String())
}
if processor.msg == nil || processor.msg.OpenID != "u1" {
t.Fatalf("processor msg = %+v, want mapped message", processor.msg)
}
if !strings.Contains(rr.Body.String(), `"accepted":true`) {
t.Fatalf("response body = %s, want accepted=true", rr.Body.String())
}
if len(writer.events) != 4 {
t.Fatalf("events len = %d, want 4", len(writer.events))
}
if writer.events[0].EventType != platformevent.TypeMessageReceived {
t.Fatalf("first event type = %s", writer.events[0].EventType)
}
if writer.events[len(writer.events)-1].EventType != platformevent.TypeReplyGenerated {
t.Fatalf("last event type = %s", writer.events[len(writer.events)-1].EventType)
}
}
func TestPlatformWebhookHandler_ShouldEnqueueHandoffAndTicketCreatedWhenNeeded(t *testing.T) {
registry := platformadapter.NewRegistry(platformadapter.NewSub2APIAdapter())
processor := &stubPlatformDialogProcessor{result: &dialog.Result{
SessionID: "sess-1",
Reply: "已转人工",
Intent: &intentdomain.Result{Intent: intentdomain.IntentHandoff, Confidence: 0.88},
Handoff: &handoff.Decision{ShouldHandoff: true, Priority: "P1", Reason: "complaint"},
TicketID: "ticket-1",
}}
writer := &stubPlatformEventWriter{}
handler := NewPlatformWebhookHandler(processor, registry, writer)
body := `{"message_id":"m1","channel":"sub2api","open_id":"u1","content":"我要投诉"}`
req := httptest.NewRequest(http.MethodPost, "/api/v1/customer-service/platforms/sub2api/webhook", strings.NewReader(body))
rr := httptest.NewRecorder()
handler.Handle(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status = %d, want 200; body=%s", rr.Code, rr.Body.String())
}
if len(writer.events) != 6 {
t.Fatalf("events len = %d, want 6", len(writer.events))
}
if writer.events[3].EventType != platformevent.TypeHandoffTriggered {
t.Fatalf("handoff event type = %s", writer.events[3].EventType)
}
if writer.events[4].EventType != platformevent.TypeTicketCreated {
t.Fatalf("ticket event type = %s", writer.events[4].EventType)
}
}
func TestPlatformWebhookHandler_ShouldRejectUnknownPlatform(t *testing.T) {
handler := NewPlatformWebhookHandler(&stubPlatformDialogProcessor{}, platformadapter.NewRegistry(platformadapter.NewSub2APIAdapter()), &stubPlatformEventWriter{})
req := httptest.NewRequest(http.MethodPost, "/api/v1/customer-service/platforms/unknown/webhook", strings.NewReader(`{}`))
rr := httptest.NewRecorder()
handler.Handle(rr, req)
if rr.Code != http.StatusNotFound {
t.Fatalf("status = %d, want 404", rr.Code)
}
}