Files
creator-hub/internal/controlplane/api/creator_events_test.go
T

191 lines
8.1 KiB
Go

package api
import (
"context"
"testing"
"time"
"git.ipao.vip/rogee/creator-hub/internal/creator"
hub "git.ipao.vip/rogee/creator-hub/internal/environment"
)
func TestCreatorListenerPrimitives(t *testing.T) {
env := hub.EnvironmentContext{Env: hub.Env{Alias: "browser/a"}, BindingVersion: 3, RuntimeID: "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", RuntimeNetworkID: "native-dddddddddddddddddddddddddddddddd", Exit: hub.NetworkExit{ID: "exit"}}
generation := creatorListenerGeneration(env)
if generation != "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd:native-dddddddddddddddddddddddddddddddd:3" {
t.Fatalf("unexpected generation: %q", generation)
}
tokenA, tokenB := creatorListenerSessionToken(env), creatorListenerSessionToken(env)
if tokenA == tokenB {
t.Fatal("listener session tokens must be unique")
}
if listenerBoundaryPointer(time.Time{}) != nil {
t.Fatal("zero boundary must remain nil")
}
boundary := listenerBoundaryPointer(time.Date(2024, 1, 1, 1, 0, 0, 0, time.FixedZone("test", 3600)))
if boundary == nil || !boundary.Equal(time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)) {
t.Fatalf("boundary was not normalized: %v", boundary)
}
binding := creatorEventBinding{accountID: "account", uid: "uid", env: env, gateway: hub.Gateway{Name: "gateway", Endpoint: "http://gateway", Token: "token"}, sessionToken: "session"}
if binding.key() == "" || pathForCreatorEvent(env) != "/v1/browsers/browser%2Fa/douyin/events" {
t.Fatalf("unexpected listener identity: key=%q path=%q", binding.key(), pathForCreatorEvent(env))
}
}
func TestCreatorEventListenerShutdownAndEventKinds(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel()
if waitCreatorEventBackoff(ctx, time.Hour) {
t.Fatal("cancelled listener backoff must stop")
}
manager := &creatorEventListenerManager{items: map[string]creatorEventListenerHandle{
"account": {cancel: func() {}, done: func() chan struct{} { ch := make(chan struct{}); close(ch); return ch }()},
}}
manager.close()
if len(manager.items) != 0 {
t.Fatal("listener manager did not clear handles")
}
for _, kind := range []string{"error", "reconnected", "open", "baseline", "close", "unknown"} {
handleCreatorGatewayEvent(context.Background(), nil, creatorEventBinding{}, creatorGatewayEvent{Kind: kind}, nil, nil)
}
if err := manager.reconcile(context.Background(), nil, nil, nil, nil, nil); err != creator.ErrUnavailable {
t.Fatalf("nil listener dependencies: %v", err)
}
}
func TestCreatorUpdateHubPublishesAndUnsubscribes(t *testing.T) {
hub := &creatorUpdateHub{subscribers: make(map[chan struct{}]struct{})}
updates, unsubscribe := hub.subscribe()
hub.publish()
select {
case <-updates:
default:
t.Fatal("publish did not notify subscriber")
}
unsubscribe()
hub.publish()
select {
case _, ok := <-updates:
if ok {
t.Fatal("unsubscribed channel received an update")
}
default:
}
}
func TestGatewayGenerationPayloadIncludesCurrentProxyExit(t *testing.T) {
payload := gatewayGenerationPayload(hub.EnvironmentContext{
BindingVersion: 3,
RuntimeID: "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
RuntimeNetworkID: "native-dddddddddddddddddddddddddddddddd",
Exit: hub.NetworkExit{ID: "exit-current"},
})
if payload["binding_version"] != int64(3) || payload["runtime_id"] != "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" || payload["network_id"] != "native-dddddddddddddddddddddddddddddddd" || payload["network_exit_id"] != "exit-current" {
t.Fatalf("unexpected generation payload: %#v", payload)
}
}
func TestCreatorGatewayEventNeedsBaseline(t *testing.T) {
if ok, reason := creatorGatewayEventNeedsBaseline(creatorGatewayEvent{Kind: "notice"}, true); !ok || reason != "缺少平台事件时间" {
t.Fatalf("missing platform time must be held at baseline: %v %q", ok, reason)
}
if ok, reason := creatorGatewayEventNeedsBaseline(creatorGatewayEvent{Kind: "notice", Notice: &creatorGatewayEventNotice{PlatformEventAt: "2024-01-01T00:00:00Z"}}, false); !ok || reason != "监听边界未确认" {
t.Fatalf("unconfirmed listener boundary must be held: %v %q", ok, reason)
}
if ok, reason := creatorGatewayEventNeedsBaseline(creatorGatewayEvent{Kind: "notice", Notice: &creatorGatewayEventNotice{PlatformEventAt: "2024-01-01T00:00:00Z"}}, true); ok || reason != "" {
t.Fatalf("confirmed event boundary should be actionable: %v %q", ok, reason)
}
}
func TestCreatorGatewayBoundary(t *testing.T) {
boundary, ready, reason := creatorGatewayBoundary(creatorGatewayEvent{
BoundaryAt: "2024-01-01T00:00:00Z",
BoundarySource: "douyin_identity_extra_now",
})
if !ready || !boundary.Equal(time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)) || reason != "" {
t.Fatalf("unexpected valid boundary: %v %v %q", boundary, ready, reason)
}
if _, ready, reason := creatorGatewayBoundary(creatorGatewayEvent{BoundaryAt: "2024-01-01T00:00:00Z"}); ready || reason != "平台边界来源未验证" {
t.Fatalf("unverified boundary must stay blocked: %v %q", ready, reason)
}
}
func TestCreatorEventBeforeBoundary(t *testing.T) {
boundary := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
before := creatorGatewayEvent{Notice: &creatorGatewayEventNotice{PlatformEventAt: "2023-12-31T23:59:59Z"}}
after := creatorGatewayEvent{Notice: &creatorGatewayEventNotice{PlatformEventAt: "2024-01-01T00:00:01Z"}}
invalid := creatorGatewayEvent{Notice: &creatorGatewayEventNotice{PlatformEventAt: "not-a-time"}}
if !creatorEventBeforeBoundary(before, boundary) || !creatorEventBeforeBoundary(invalid, boundary) || creatorEventBeforeBoundary(after, boundary) {
t.Fatalf("unexpected boundary classification")
}
}
func TestCreatorEventFromGatewayNotice(t *testing.T) {
event, err := creatorEventFromGatewayNotice("account-1", creatorGatewayEventNotice{
EventKey: "9007199254740993",
EventType: "comment",
InteractorUID: "7654321",
CommentID: "987654",
WorkID: "123456",
PlatformEventAt: "2023-11-14T22:13:20+00:00",
GatewayReceivedAt: "2023-11-14T22:13:21+00:00",
})
if err != nil {
t.Fatal(err)
}
if event.Platform != "douyin" || event.ReceivingAccountID != "account-1" || event.EventKey != "9007199254740993" || event.InteractorUID != "7654321" || event.CommentID != "987654" || event.WorkID != "123456" {
t.Fatalf("unexpected event: %+v", event)
}
if event.PlatformEventAt == nil || event.PlatformEventAt.UTC().Format("2006-01-02T15:04:05Z07:00") != "2023-11-14T22:13:20Z" {
t.Fatalf("unexpected event time: %+v", event.PlatformEventAt)
}
if event.GatewayReceivedAt == nil || event.GatewayReceivedAt.UTC().Format("2006-01-02T15:04:05Z07:00") != "2023-11-14T22:13:21Z" {
t.Fatalf("unexpected gateway receipt time: %v", event.GatewayReceivedAt)
}
}
func TestCreatorEventFromGatewayNoticeRejectsInvalidIdentity(t *testing.T) {
_, err := creatorEventFromGatewayNotice("account-1", creatorGatewayEventNotice{
EventKey: "1",
EventType: "follow",
InteractorUID: "not-a-uid",
})
if err == nil {
t.Fatal("expected invalid interactor UID")
}
}
func TestCreatorEventFromGatewayNoticeRejectsInvalidGatewayReceiptTime(t *testing.T) {
_, err := creatorEventFromGatewayNotice("account-1", creatorGatewayEventNotice{
EventKey: "1",
EventType: "like",
GatewayReceivedAt: "not-a-time",
})
if err == nil {
t.Fatal("expected invalid gateway receipt time")
}
}
func TestCreatorEventFromGatewayNoticePreservesMessageType(t *testing.T) {
result, err := creatorEventFromGatewayNotice("account-1", creatorGatewayEventNotice{
EventKey: "123",
EventType: "dm",
InteractorUID: "456",
MessageType: creator.MessageTypeImage,
})
if err != nil || result.MessageType != creator.MessageTypeImage {
t.Fatalf("message type was not preserved: result=%#v err=%v", result, err)
}
}
func TestCreatorEventFromGatewayNoticeRejectsInvalidTime(t *testing.T) {
_, err := creatorEventFromGatewayNotice("account-1", creatorGatewayEventNotice{
EventKey: "1",
EventType: "like",
PlatformEventAt: "not-a-time",
})
if err == nil {
t.Fatal("expected invalid platform event time")
}
}