Files
2026-11-07 10:47:32 +08:00

151 lines
3.8 KiB
Go

package datacenter
import (
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"sync"
"testing"
"time"
)
func sample() Record {
return Record{
VideoID: "12345",
CompanyPlatform: "黄果短剧",
PublishedData: "01/09/2026",
ScriptName: "剧本A",
DramaName: "短剧A",
DramaNo: "第1集",
VideoURL: "https://cdn.example.com/a.mp4",
PlatformURL: "https://example.com/a",
Views: 10000,
Clicks: 8200,
Likes: 300,
Comments: Int(0),
}
}
func TestFillDefaults(t *testing.T) {
r := sample()
r.fillDefaults()
if r.Company != DefaultCompany {
t.Fatalf("company = %q", r.Company)
}
if r.DataDate != time.Now().UTC().Format(DateLayout) {
t.Fatalf("data_date = %q", r.DataDate)
}
if r.RoleID == nil {
t.Fatal("role_id 应为空数组而不是 null")
}
// 显式传的日期不能被覆盖
r2 := sample()
r2.DataDate = "21/09/2026"
r2.fillDefaults()
if r2.DataDate != "21/09/2026" {
t.Fatalf("data_date 被覆盖为 %q", r2.DataDate)
}
}
func TestValidate(t *testing.T) {
s := sample()
if err := s.validate(); err != nil && !strings.Contains(err.Error(), "data_date") {
t.Fatalf("sample 不应报错: %v", err)
}
bad := sample()
bad.ScriptName = ""
bad.VideoURL = ""
if err := bad.validate(); err == nil ||
!strings.Contains(err.Error(), "script_name") ||
!strings.Contains(err.Error(), "video_url") {
t.Fatalf("应报缺 script_name/video_url: %v", err)
}
wrongDate := sample()
wrongDate.DataDate = "2026-09-21"
if err := wrongDate.validate(); err == nil || !strings.Contains(err.Error(), "DD/MM/YYYY") {
t.Fatalf("应报日期格式错误: %v", err)
}
}
func TestJSONShape(t *testing.T) {
b, err := JSON(sample())
if err != nil {
t.Fatal(err)
}
var got struct {
Records []map[string]any `json:"records"`
}
if err := json.Unmarshal(b, &got); err != nil {
t.Fatal(err)
}
row := got.Records[0]
if row["data_date"] != time.Now().UTC().Format(DateLayout) {
t.Fatalf("data_date = %v", row["data_date"])
}
if row["company"] != DefaultCompany {
t.Fatalf("company = %v", row["company"])
}
if comments, ok := row["comments"].(float64); !ok || comments != 0 {
t.Fatalf("comments 应该发出 0,实际 %v", row["comments"])
}
if shares, ok := row["shares"]; ok {
t.Fatalf("shares 没填就不应出现,实际 %v", shares)
}
}
// TestSendAsync 验证异步上报不阻塞、按顺序发完,且 Close 会等队列清空。
func TestSendAsync(t *testing.T) {
var mu sync.Mutex
var got []string // 收到的 video_id,按到达顺序
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var req struct {
Records []struct {
VideoID string `json:"video_id"`
} `json:"records"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
t.Errorf("解码请求失败: %v", err)
}
mu.Lock()
for _, rec := range req.Records {
got = append(got, rec.VideoID)
}
mu.Unlock()
w.WriteHeader(http.StatusOK)
}))
defer srv.Close()
c := New("ld_test")
c.Endpoint = srv.URL
c.OnError = func(err error) { t.Errorf("异步上报不应报错: %v", err) }
for _, id := range []string{"a", "b", "c"} {
r := sample()
r.VideoID = id
c.SendAsync(r)
}
c.Close() // 等发完
mu.Lock()
defer mu.Unlock()
if strings.Join(got, ",") != "a,b,c" {
t.Fatalf("发送顺序不对: %v", got)
}
}
// TestSendAsyncAfterClose 验证 Close 之后再上报不会 panic,而是走 OnError。
func TestSendAsyncAfterClose(t *testing.T) {
c := New("ld_test")
var reported []error
c.OnError = func(err error) { reported = append(reported, err) }
c.Close() // 没调过 SendAsync,应该是空操作
c.SendAsync(sample())
c.Close() // 重复调也安全
if len(reported) != 1 || !strings.Contains(reported[0].Error(), "已 Close") {
t.Fatalf("应报一次「已 Close」: %v", reported)
}
}