topfans/backend/pkg/push/uni_push_client_test.go

148 lines
4.5 KiB
Go

package push
import (
"context"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"strings"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// fakeServer 接收推送请求并保存;返回 status 由调用方控制。
type fakeServer struct {
mu sync.Mutex
requests []Payload
status int
}
func (f *fakeServer) handler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
_ = r.Body.Close()
var p Payload
_ = json.Unmarshal(body, &p)
f.mu.Lock()
f.requests = append(f.requests, p)
status := f.status
f.mu.Unlock()
w.WriteHeader(status)
_, _ = w.Write([]byte(`{"errcode":0}`))
}
}
func (f *fakeServer) last() Payload {
f.mu.Lock()
defer f.mu.Unlock()
if len(f.requests) == 0 {
return Payload{}
}
return f.requests[len(f.requests)-1]
}
func (f *fakeServer) count() int {
f.mu.Lock()
defer f.mu.Unlock()
return len(f.requests)
}
// TestUniPushClient_SendHappy 验证:成功路径下请求体含正确字段。
func TestUniPushClient_SendHappy(t *testing.T) {
srv := &fakeServer{status: http.StatusOK}
ts := httptest.NewServer(srv.handler())
defer ts.Close()
c := NewUniPushClient(ts.URL, 2*time.Second, nil)
err := c.Send(context.Background(), Payload{
CIDs: []string{"cid-a", "cid-b"},
Title: "你好",
Content: "有新消息",
Data: map[string]interface{}{"notification_id": int64(42), "type": "like"},
})
require.NoError(t, err)
assert.Equal(t, 1, srv.count())
got := srv.last()
assert.ElementsMatch(t, []string{"cid-a", "cid-b"}, got.CIDs)
assert.Equal(t, "你好", got.Title)
assert.Equal(t, "有新消息", got.Content)
assert.NotEmpty(t, got.RequestID, "request_id 应自动生成")
assert.EqualValues(t, 42, got.Data["notification_id"])
assert.Equal(t, "like", got.Data["type"])
}
// TestUniPushClient_SendNonSuccess 验证:非 2xx 返回 error。
func TestUniPushClient_SendNonSuccess(t *testing.T) {
srv := &fakeServer{status: http.StatusBadRequest}
ts := httptest.NewServer(srv.handler())
defer ts.Close()
c := NewUniPushClient(ts.URL, 2*time.Second, nil)
err := c.Send(context.Background(), Payload{
CIDs: []string{"cid-a"}, Title: "x", Content: "y",
})
require.Error(t, err)
assert.Contains(t, err.Error(), "status=400")
}
// TestUniPushClient_EmptyCIDs 验证:cids 为空直接跳过(不发送 HTTP 请求)。
func TestUniPushClient_EmptyCIDs(t *testing.T) {
srv := &fakeServer{status: http.StatusOK}
ts := httptest.NewServer(srv.handler())
defer ts.Close()
c := NewUniPushClient(ts.URL, 2*time.Second, nil)
err := c.Send(context.Background(), Payload{Title: "x", Content: "y"})
require.NoError(t, err)
assert.Equal(t, 0, srv.count(), "cids 为空时不应发请求")
}
// TestUniPushClient_EmptyURL 验证:URL 为空时返回 error。
func TestUniPushClient_EmptyURL(t *testing.T) {
c := NewUniPushClient("", 2*time.Second, nil)
err := c.Send(context.Background(), Payload{CIDs: []string{"cid-a"}, Title: "x", Content: "y"})
require.Error(t, err)
assert.Contains(t, err.Error(), "not initialized")
}
// TestUniPushClient_Timeout 验证:超时场景下返回 error。
func TestUniPushClient_Timeout(t *testing.T) {
slow := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(300 * time.Millisecond)
w.WriteHeader(http.StatusOK)
}))
defer slow.Close()
c := NewUniPushClient(slow.URL, 50*time.Millisecond, nil)
err := c.Send(context.Background(), Payload{CIDs: []string{"cid-a"}, Title: "x", Content: "y"})
require.Error(t, err)
assert.True(t, strings.Contains(err.Error(), "timeout") ||
strings.Contains(err.Error(), "context deadline") ||
strings.Contains(err.Error(), "Client.Timeout"),
"expected timeout error, got: %v", err)
}
// TestUniPushClient_RequestIDGenerated 验证:未传 request_id 时自动生成。
func TestUniPushClient_RequestIDGenerated(t *testing.T) {
srv := &fakeServer{status: http.StatusOK}
ts := httptest.NewServer(srv.handler())
defer ts.Close()
c := NewUniPushClient(ts.URL, 2*time.Second, nil)
_ = c.Send(context.Background(), Payload{CIDs: []string{"x"}, Title: "t", Content: "c"})
got := srv.last()
assert.True(t, strings.HasPrefix(got.RequestID, ""), "request_id 应存在")
assert.Greater(t, len(got.RequestID), 5)
}
// TestNoopPusher 验证:NoopPusher.Send 不报错也不发请求。
func TestNoopPusher(t *testing.T) {
var p Pusher = NoopPusher{}
assert.NoError(t, p.Send(context.Background(), Payload{CIDs: []string{"x"}}))
}