151 lines
3.8 KiB
Go
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)
|
|
}
|
|
}
|