package ledger import "testing" func TestVipBenefitPresentationRoundTripPreservesOtherMetadata(t *testing.T) { presentation := &VipBenefitPresentation{ Description: "头像框会自动合成当前用户头像", PreviewItems: []VipBenefitPreviewItem{{ PreviewID: "frame-main", MediaType: "animation", AnimationURL: "https://cdn.example/frame.svga", PreviewURL: "https://cdn.example/frame.png", CompositionType: "user_avatar_center", SortOrder: 10, }}, NumericReward: &VipBenefitNumericReward{Label: "每日返现", Value: 100, Unit: "金币", Period: "daily"}, } metadata, err := MergeVipBenefitPresentation(`{"coin_amount":100}`, presentation) if err != nil { t.Fatalf("merge presentation failed: %v", err) } parsed, err := ParseVipBenefitPresentation(metadata) if err != nil || parsed == nil { t.Fatalf("parse presentation failed: parsed=%+v err=%v", parsed, err) } if parsed.Description != presentation.Description || len(parsed.PreviewItems) != 1 || parsed.PreviewItems[0].CompositionType != "user_avatar_center" || parsed.NumericReward == nil || parsed.NumericReward.Value != 100 { t.Fatalf("presentation round trip mismatch: %+v", parsed) } if metadata == "" || !containsJSONFragment(metadata, `"coin_amount":100`) { t.Fatalf("merge must preserve owner metadata: %s", metadata) } } func TestVipBenefitPresentationRejectsImplicitClientRules(t *testing.T) { invalid := VipBenefitPresentation{PreviewItems: []VipBenefitPreviewItem{{ PreviewID: "wave", MediaType: "animation", CompositionType: "guess_from_benefit_code", }}} if err := ValidateVipBenefitPresentation(invalid); err == nil { t.Fatal("unknown composition type must be rejected") } } func containsJSONFragment(value string, fragment string) bool { for index := 0; index+len(fragment) <= len(value); index++ { if value[index:index+len(fragment)] == fragment { return true } } return false }