81 lines
3.3 KiB
Go
81 lines
3.3 KiB
Go
package command
|
||
|
||
import (
|
||
"bytes"
|
||
"testing"
|
||
)
|
||
|
||
func TestSendGiftIdempotencyIgnoresGatewayResolvedSnapshots(t *testing.T) {
|
||
base := SendGift{
|
||
Base: Base{CommandID: "combo-1_b1", Room: "room-1", ActorID: 42, DeviceID: "device-first"},
|
||
TargetType: "user", TargetUserIDs: []int64{43}, TargetUserID: 43,
|
||
GiftID: "rose", GiftCount: 9, ComboSessionID: "combo-1", BatchSeq: 1,
|
||
SenderRegionID: 1001, SenderCountryID: 840,
|
||
TargetHostScopes: []GiftTargetHostScope{{TargetUserID: 43, TargetIsHost: true, TargetHostRegionID: 1001}},
|
||
SenderDisplayProfile: GiftDisplayProfile{UserID: 42, Username: "before"},
|
||
}
|
||
retry := base
|
||
retry.SenderRegionID = 2002
|
||
retry.SenderCountryID = 356
|
||
retry.TargetHostScopes = []GiftTargetHostScope{{TargetUserID: 43, TargetIsHost: false}}
|
||
retry.SenderDisplayProfile = GiftDisplayProfile{UserID: 42, Username: "after"}
|
||
retry.DeviceID = "device-after-token-rotation"
|
||
|
||
left, err := IdempotencyPayloadForCommand(base)
|
||
if err != nil {
|
||
t.Fatalf("base payload: %v", err)
|
||
}
|
||
right, err := IdempotencyPayloadForCommand(retry)
|
||
if err != nil {
|
||
t.Fatalf("retry payload: %v", err)
|
||
}
|
||
if !bytes.Equal(left, right) {
|
||
t.Fatalf("gateway snapshot drift changed client idempotency semantics:\nleft=%s\nright=%s", left, right)
|
||
}
|
||
}
|
||
|
||
func TestSendGiftCommandLogPreservesTrustedDeviceIDAndReadsLegacyPayload(t *testing.T) {
|
||
original := SendGift{
|
||
Base: Base{
|
||
AppCode: "lalu", CommandID: "cmd-device-recovery", Room: "room-1", ActorID: 42,
|
||
SessionID: "sess-not-a-device", DeviceID: "device-auth-session-1", SentAtMS: 1_000,
|
||
},
|
||
TargetType: "user", TargetUserIDs: []int64{43}, TargetUserID: 43,
|
||
GiftID: "rose", GiftCount: 1,
|
||
}
|
||
payload, err := Serialize(original)
|
||
if err != nil {
|
||
t.Fatalf("serialize send gift device snapshot: %v", err)
|
||
}
|
||
recovered, err := Deserialize(original.Type(), payload)
|
||
if err != nil {
|
||
t.Fatalf("deserialize send gift device snapshot: %v", err)
|
||
}
|
||
got, ok := recovered.(*SendGift)
|
||
if !ok || got.DeviceID != "device-auth-session-1" || got.SessionID != "sess-not-a-device" {
|
||
t.Fatalf("recovered trusted device snapshot mismatch: %#v", recovered)
|
||
}
|
||
|
||
// 滚动升级前的 command log 不包含 device_id;反序列化必须保持可恢复,
|
||
// 并留下空值让 fixed_v2 兼容、dynamic_v3 在 owner 处明确拒绝。
|
||
legacy, err := Deserialize(original.Type(), []byte(`{"app_code":"lalu","command_id":"cmd-legacy","actor_user_id":42,"room_id":"room-1","session_id":"sess-legacy","target_type":"user","target_user_id":43,"gift_id":"rose","gift_count":1}`))
|
||
if err != nil {
|
||
t.Fatalf("deserialize legacy send gift: %v", err)
|
||
}
|
||
legacyGift := legacy.(*SendGift)
|
||
if legacyGift.DeviceID != "" || legacyGift.SessionID != "sess-legacy" {
|
||
t.Fatalf("legacy command must not derive device from session: %+v", legacyGift.Base)
|
||
}
|
||
}
|
||
|
||
func TestSendGiftIdempotencyKeepsComboBatchIdentity(t *testing.T) {
|
||
base := SendGift{Base: Base{CommandID: "combo-1_b1", Room: "room-1", ActorID: 42}, TargetType: "user", TargetUserIDs: []int64{43}, TargetUserID: 43, GiftID: "rose", GiftCount: 1, ComboSessionID: "combo-1", BatchSeq: 1}
|
||
reused := base
|
||
reused.BatchSeq = 2
|
||
left, _ := IdempotencyPayloadForCommand(base)
|
||
right, _ := IdempotencyPayloadForCommand(reused)
|
||
if bytes.Equal(left, right) {
|
||
t.Fatal("same command_id reused for another combo batch was not detected")
|
||
}
|
||
}
|