hyapp-server/pkg/activitymq/messages_test.go
2026-07-15 09:43:33 +08:00

105 lines
4.3 KiB
Go

package activitymq
import (
"strings"
"testing"
"time"
)
func TestActivityTemplateFactRoundTrip(t *testing.T) {
occurredAt := time.Date(2026, time.July, 14, 8, 30, 0, 0, time.UTC).UnixMilli()
want := ActivityTemplateFactMessage{
AppCode: "lalu", EventID: "activity:g1", EventType: EventTypeGiftScored,
TemplateID: "template-1", TemplateCode: "summer_rank", PublishedVersion: 3,
StatDay: "2026-07-14", RegionID: 9, UserID: 1001, GiftID: "rose",
GiftCountDelta: 2, CoinAmountDelta: 200, OccurredAtMS: occurredAt,
}
body, err := EncodeActivityTemplateFactMessage(want)
if err != nil {
t.Fatalf("encode: %v", err)
}
got, err := DecodeActivityTemplateFactMessage(body)
if err != nil {
t.Fatalf("decode: %v", err)
}
if got.MessageType != MessageTypeActivityTemplateFact || got.EventID != want.EventID || got.CoinAmountDelta != 200 {
t.Fatalf("unexpected round trip: %+v", got)
}
}
func TestActivityTemplateFactRejectsMismatchedDayAndIncompleteDimensions(t *testing.T) {
base := ActivityTemplateFactMessage{
AppCode: "lalu", EventID: "activity:t1", EventType: EventTypeTaskProgressed,
TemplateID: "template-1", TemplateCode: "summer_rank", PublishedVersion: 1,
StatDay: "2026-07-13", UserID: 1001, TaskKey: "send_gift", TaskType: "gift",
ProgressDelta: 1, OccurredAtMS: time.Date(2026, time.July, 14, 0, 0, 0, 0, time.UTC).UnixMilli(),
}
if _, err := EncodeActivityTemplateFactMessage(base); err == nil || !strings.Contains(err.Error(), "does not match") {
t.Fatalf("expected stat day mismatch, got %v", err)
}
base.StatDay = "2026-07-14"
base.TaskKey = ""
if _, err := EncodeActivityTemplateFactMessage(base); err == nil {
t.Fatal("expected incomplete task identity rejection")
}
}
func TestTaskClaimedUsesImmutableEarningAttributionDay(t *testing.T) {
earningAt := time.Date(2026, time.July, 14, 23, 50, 0, 0, time.UTC).UnixMilli()
claimedAt := time.Date(2026, time.July, 17, 9, 15, 0, 0, time.UTC).UnixMilli()
want := ActivityTemplateFactMessage{
AppCode: "lalu", EventID: "activity:claim:delayed", EventType: EventTypeTaskClaimed,
TemplateID: "template-1", TemplateCode: "summer_rank", PublishedVersion: 3,
StatDay: "2026-07-14", RegionID: 9, UserID: 1001, TaskKey: "send_gift", TaskType: "gift",
ClaimedDelta: 1, OccurredAtMS: claimedAt, AttributionAtMS: earningAt,
}
body, err := EncodeActivityTemplateFactMessage(want)
if err != nil {
t.Fatalf("encode delayed task claim: %v", err)
}
got, err := DecodeActivityTemplateFactMessage(body)
if err != nil {
t.Fatalf("decode delayed task claim: %v", err)
}
if got.StatDay != "2026-07-14" || got.OccurredAtMS != claimedAt || got.AttributionAtMS != earningAt {
t.Fatalf("task claim attribution changed: %+v", got)
}
// A claim cannot relabel its earning day independently from the persisted
// attribution timestamp, otherwise retry time or a moved user region could
// rewrite already-earned statistics.
want.AttributionAtMS = time.Date(2026, time.July, 15, 0, 0, 0, 0, time.UTC).UnixMilli()
if _, err := EncodeActivityTemplateFactMessage(want); err == nil || !strings.Contains(err.Error(), "attribution") {
t.Fatalf("expected attribution mismatch rejection, got %v", err)
}
}
func TestActivityVisitedRequiresPositiveVisitDelta(t *testing.T) {
visitedAt := time.Date(2026, time.July, 14, 8, 30, 0, 0, time.UTC).UnixMilli()
fact := ActivityTemplateFactMessage{
AppCode: "lalu", EventID: "activity:visit:1", EventType: EventTypeActivityVisited,
TemplateID: "template-1", TemplateCode: "summer_rank", PublishedVersion: 3,
StatDay: "2026-07-14", RegionID: 9, UserID: 1001, OccurredAtMS: visitedAt,
}
if _, err := EncodeActivityTemplateFactMessage(fact); err == nil {
t.Fatal("expected zero visit delta rejection")
}
fact.VisitCountDelta = 1
if _, err := EncodeActivityTemplateFactMessage(fact); err != nil {
t.Fatalf("encode activity visit: %v", err)
}
}
func TestActivityTemplateFactTagExpressionIsSafeAndDeterministic(t *testing.T) {
expression, err := LegacyCompatibleTagExpression(EventTypeTaskClaimed, EventTypeGiftScored, EventTypeTaskClaimed)
if err != nil {
t.Fatalf("expression: %v", err)
}
if expression != "activity_template_fact || GiftScored || TaskClaimed" {
t.Fatalf("unexpected expression: %s", expression)
}
if _, err := EventTypeTag("GiftScored || *"); err == nil {
t.Fatal("expected unsafe tag rejection")
}
}