340 lines
11 KiB
Go
340 lines
11 KiB
Go
package appconfig
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"hyapp-admin-server/internal/model"
|
|
)
|
|
|
|
func TestH5LinkModelFromPayloadValidatesDynamicConfig(t *testing.T) {
|
|
item, err := h5LinkModelFromPayload(h5LinkPayload{
|
|
Key: "host-center.v2",
|
|
Label: "Host Center",
|
|
URL: "https://h5.example.com/host",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("h5 config should be valid: %v", err)
|
|
}
|
|
if item.Group != h5LinkGroup || item.Key != "host-center.v2" || item.Description != "Host Center" || item.Value != "https://h5.example.com/host" {
|
|
t.Fatalf("h5 config model mismatch: %+v", item)
|
|
}
|
|
}
|
|
|
|
func TestH5LinkModelFromPayloadRejectsInvalidKey(t *testing.T) {
|
|
_, err := h5LinkModelFromPayload(h5LinkPayload{
|
|
Key: "host center",
|
|
Label: "Host Center",
|
|
URL: "https://h5.example.com/host",
|
|
})
|
|
if err == nil {
|
|
t.Fatal("expected invalid key to fail")
|
|
}
|
|
}
|
|
|
|
func TestBannerModelFromRequestRequiresRoomSmallImageForRoomScope(t *testing.T) {
|
|
_, err := bannerModelFromRequest("lalu", bannerRequest{
|
|
CoverURL: "https://cdn.example.com/banner.png",
|
|
BannerType: "h5",
|
|
DisplayScope: bannerDisplayScopeRoom,
|
|
Param: "https://h5.example.com/activity",
|
|
Status: bannerStatusActive,
|
|
Platform: "android",
|
|
EndsAtMs: time.Now().UTC().Add(time.Hour).UnixMilli(),
|
|
})
|
|
if err == nil {
|
|
t.Fatal("expected room scope without room small image to fail")
|
|
}
|
|
}
|
|
|
|
func TestBannerModelFromRequestKeepsRoomScopeScheduleFields(t *testing.T) {
|
|
startsAtMs := time.Now().UTC().Add(time.Hour).UnixMilli()
|
|
endsAtMs := startsAtMs + int64(time.Hour/time.Millisecond)
|
|
item, err := bannerModelFromRequest("lalu", bannerRequest{
|
|
CoverURL: "https://cdn.example.com/banner.png",
|
|
RoomSmallImageURL: "https://cdn.example.com/banner-small.png",
|
|
BannerType: "h5",
|
|
DisplayScopes: []string{bannerDisplayScopeHome, bannerDisplayScopeRoom},
|
|
Param: "https://h5.example.com/activity",
|
|
Status: bannerStatusActive,
|
|
Platform: "android",
|
|
StartsAtMs: startsAtMs,
|
|
EndsAtMs: endsAtMs,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("banner model should be valid: %v", err)
|
|
}
|
|
if item.DisplayScope != "home,room" || item.RoomSmallImageURL == "" {
|
|
t.Fatalf("room display fields mismatch: %+v", item)
|
|
}
|
|
if item.StartsAtMS != startsAtMs || item.EndsAtMS != endsAtMs || item.Status != bannerStatusActive {
|
|
t.Fatalf("schedule fields mismatch: %+v", item)
|
|
}
|
|
}
|
|
|
|
func TestBannerModelFromRequestExpiresEndedActiveBanner(t *testing.T) {
|
|
item, err := bannerModelFromRequest("lalu", bannerRequest{
|
|
CoverURL: "https://cdn.example.com/banner.png",
|
|
RoomSmallImageURL: "https://cdn.example.com/ignored.png",
|
|
BannerType: "app",
|
|
DisplayScope: bannerDisplayScopeHome,
|
|
Status: bannerStatusActive,
|
|
Platform: "ios",
|
|
EndsAtMs: 1,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("banner model should accept ended active banner: %v", err)
|
|
}
|
|
if item.Status != bannerStatusExpired {
|
|
t.Fatalf("ended active banner must become expired: %+v", item)
|
|
}
|
|
if item.RoomSmallImageURL != "" {
|
|
t.Fatalf("non-room banner must not retain room small image: %+v", item)
|
|
}
|
|
}
|
|
|
|
func TestAppBannerFromModelReturnsDisplayScopes(t *testing.T) {
|
|
item := appBannerFromModel(bannerModel("home,room,recharge"))
|
|
|
|
if item.DisplayScope != "home,room,recharge" {
|
|
t.Fatalf("display scope should keep stored value: %+v", item)
|
|
}
|
|
if len(item.DisplayScopes) != 3 || item.DisplayScopes[0] != bannerDisplayScopeHome || item.DisplayScopes[1] != bannerDisplayScopeRoom || item.DisplayScopes[2] != bannerDisplayScopeRecharge {
|
|
t.Fatalf("display scopes mismatch: %+v", item.DisplayScopes)
|
|
}
|
|
}
|
|
|
|
func TestSplashScreenModelFromRequestValidatesH5Config(t *testing.T) {
|
|
startsAtMs := time.Now().UTC().Add(time.Hour).UnixMilli()
|
|
endsAtMs := startsAtMs + int64(time.Hour/time.Millisecond)
|
|
item, err := splashScreenModelFromRequest("lalu", splashScreenRequest{
|
|
CoverURL: "https://cdn.example.com/splash.png",
|
|
SplashType: "h5",
|
|
Param: "https://h5.example.com/splash",
|
|
Status: bannerStatusActive,
|
|
Platform: "android",
|
|
SortOrder: 7,
|
|
RegionID: 1001,
|
|
CountryCode: "us",
|
|
Description: "launch campaign",
|
|
DisplayDurationMs: 4200,
|
|
StartsAtMs: startsAtMs,
|
|
EndsAtMs: endsAtMs,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("splash screen model should be valid: %v", err)
|
|
}
|
|
if item.AppCode != "lalu" || item.SplashType != "h5" || item.CountryCode != "US" || item.DisplayDurationMS != 4200 || item.StartsAtMS != startsAtMs || item.EndsAtMS != endsAtMs {
|
|
t.Fatalf("splash screen model mismatch: %+v", item)
|
|
}
|
|
}
|
|
|
|
func TestSplashScreenModelFromRequestDefaultsDisplayDuration(t *testing.T) {
|
|
item, err := splashScreenModelFromRequest("lalu", splashScreenRequest{
|
|
CoverURL: "https://cdn.example.com/splash.png",
|
|
SplashType: "app",
|
|
Status: bannerStatusActive,
|
|
Platform: "android",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("splash screen model should accept missing display duration: %v", err)
|
|
}
|
|
if item.DisplayDurationMS != splashDefaultDisplayDurationMS {
|
|
t.Fatalf("display duration should default to %d, got %+v", splashDefaultDisplayDurationMS, item)
|
|
}
|
|
}
|
|
|
|
func TestSplashScreenModelFromRequestRejectsNegativeDisplayDuration(t *testing.T) {
|
|
_, err := splashScreenModelFromRequest("lalu", splashScreenRequest{
|
|
CoverURL: "https://cdn.example.com/splash.png",
|
|
SplashType: "app",
|
|
Status: bannerStatusActive,
|
|
Platform: "android",
|
|
DisplayDurationMs: -1,
|
|
})
|
|
if err == nil {
|
|
t.Fatal("expected negative display duration to fail")
|
|
}
|
|
}
|
|
|
|
func TestSplashScreenModelFromRequestRequiresH5Link(t *testing.T) {
|
|
_, err := splashScreenModelFromRequest("lalu", splashScreenRequest{
|
|
CoverURL: "https://cdn.example.com/splash.png",
|
|
SplashType: "h5",
|
|
Status: bannerStatusActive,
|
|
Platform: "android",
|
|
})
|
|
if err == nil {
|
|
t.Fatal("expected h5 splash screen without link to fail")
|
|
}
|
|
}
|
|
|
|
func TestSplashScreenModelFromRequestExpiresEndedActiveConfig(t *testing.T) {
|
|
item, err := splashScreenModelFromRequest("lalu", splashScreenRequest{
|
|
CoverURL: "https://cdn.example.com/splash.png",
|
|
SplashType: "app",
|
|
Status: bannerStatusActive,
|
|
Platform: "ios",
|
|
EndsAtMs: 1,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("ended app splash screen should be accepted: %v", err)
|
|
}
|
|
if item.Status != bannerStatusExpired {
|
|
t.Fatalf("ended active splash screen must become expired: %+v", item)
|
|
}
|
|
}
|
|
|
|
func TestAppSplashScreenFromModelMapsDTOFields(t *testing.T) {
|
|
item := appSplashScreenFromModel(model.AppSplashScreen{
|
|
ID: 9,
|
|
AppCode: "lalu",
|
|
CoverURL: "https://cdn.example.com/splash.png",
|
|
SplashType: "app",
|
|
Param: "profile",
|
|
Status: bannerStatusDisabled,
|
|
Platform: "ios",
|
|
SortOrder: 3,
|
|
RegionID: 2002,
|
|
CountryCode: "BR",
|
|
Description: "disabled campaign",
|
|
DisplayDurationMS: 5000,
|
|
StartsAtMS: 1700000000000,
|
|
EndsAtMS: 1800000000000,
|
|
CreatedAtMS: 1690000000000,
|
|
UpdatedAtMS: 1700000002000,
|
|
})
|
|
|
|
if item.ID != 9 || item.SplashType != "app" || item.Param != "profile" || item.RegionID != 2002 || item.DisplayDurationMs != 5000 || item.UpdatedAtMs != 1700000002000 {
|
|
t.Fatalf("splash screen dto mismatch: %+v", item)
|
|
}
|
|
}
|
|
|
|
func TestPopupModelFromRequestValidatesOptionalImageAndPeriod(t *testing.T) {
|
|
startsAtMs := time.Now().UTC().Add(time.Hour).UnixMilli()
|
|
endsAtMs := startsAtMs + int64(time.Hour/time.Millisecond)
|
|
item, err := popupModelFromRequest("lalu", popupRequest{
|
|
Code: "new-user-popup",
|
|
Name: "新人活动",
|
|
ImageURL: "",
|
|
JumpType: "h5",
|
|
JumpURL: "https://h5.example.com/new-user",
|
|
DisplayPeriodDays: 2,
|
|
Status: bannerStatusActive,
|
|
SortOrder: 4,
|
|
StartsAtMs: startsAtMs,
|
|
EndsAtMs: endsAtMs,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("popup model should be valid: %v", err)
|
|
}
|
|
if item.AppCode != "lalu" || item.Code != "new-user-popup" || item.Name != "新人活动" || item.ImageURL != "" || item.JumpType != "h5" || item.DisplayPeriodDays != 2 || item.StartsAtMS != startsAtMs || item.EndsAtMS != endsAtMs {
|
|
t.Fatalf("popup model mismatch: %+v", item)
|
|
}
|
|
}
|
|
|
|
func TestPopupModelFromRequestRejectsInvalidJumpURL(t *testing.T) {
|
|
_, err := popupModelFromRequest("lalu", popupRequest{
|
|
Code: "activity-popup",
|
|
Name: "活动弹窗",
|
|
JumpType: "h5",
|
|
JumpURL: "https://h5.example.com/new user",
|
|
Status: bannerStatusActive,
|
|
})
|
|
if err == nil {
|
|
t.Fatal("expected popup h5 jump url with whitespace to fail")
|
|
}
|
|
}
|
|
|
|
func TestPopupModelFromRequestRejectsInvalidCode(t *testing.T) {
|
|
_, err := popupModelFromRequest("lalu", popupRequest{
|
|
Code: "activity popup",
|
|
Name: "活动弹窗",
|
|
JumpType: "app",
|
|
JumpURL: "activity/detail",
|
|
Status: bannerStatusActive,
|
|
})
|
|
if err == nil {
|
|
t.Fatal("expected popup code with whitespace to fail")
|
|
}
|
|
}
|
|
|
|
func TestPopupModelFromRequestExpiresEndedActivePopup(t *testing.T) {
|
|
item, err := popupModelFromRequest("lalu", popupRequest{
|
|
Code: "history-popup",
|
|
Name: "历史活动",
|
|
JumpType: "app",
|
|
JumpURL: "activity/detail",
|
|
Status: bannerStatusActive,
|
|
EndsAtMs: 1,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("ended popup should be accepted: %v", err)
|
|
}
|
|
if item.Status != bannerStatusExpired {
|
|
t.Fatalf("ended active popup must become expired: %+v", item)
|
|
}
|
|
}
|
|
|
|
func TestAppPopupFromModelMapsDTOFields(t *testing.T) {
|
|
item := appPopupFromModel(model.AppPopup{
|
|
ID: 10,
|
|
AppCode: "lalu",
|
|
Code: "recharge-popup",
|
|
Name: "充值活动",
|
|
ImageURL: "https://cdn.example.com/popup.png",
|
|
JumpType: "app",
|
|
JumpURL: "recharge",
|
|
DisplayPeriodDays: 0,
|
|
Status: bannerStatusDisabled,
|
|
SortOrder: 8,
|
|
StartsAtMS: 1700000000000,
|
|
EndsAtMS: 1800000000000,
|
|
CreatedAtMS: 1690000000000,
|
|
UpdatedAtMS: 1700000002000,
|
|
})
|
|
|
|
if item.ID != 10 || item.Code != "recharge-popup" || item.Name != "充值活动" || item.JumpType != "app" || item.JumpURL != "recharge" || item.DisplayPeriodDays != 0 || item.UpdatedAtMs != 1700000002000 {
|
|
t.Fatalf("popup dto mismatch: %+v", item)
|
|
}
|
|
}
|
|
|
|
func bannerModel(displayScope string) model.AppBanner {
|
|
return model.AppBanner{
|
|
ID: 1,
|
|
AppCode: "lalu",
|
|
CoverURL: "https://cdn.example.com/banner.png",
|
|
BannerType: "h5",
|
|
DisplayScope: displayScope,
|
|
Param: "https://h5.example.com/activity",
|
|
Status: bannerStatusActive,
|
|
Platform: "android",
|
|
}
|
|
}
|
|
|
|
func TestExploreTabModelFromRequestValidatesRequiredFields(t *testing.T) {
|
|
item, err := exploreTabModelFromRequest("lalu", exploreTabRequest{
|
|
Enabled: true,
|
|
H5URL: "https://h5.example.com/explore/live",
|
|
SortOrder: 3,
|
|
Tab: "Live",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("explore tab should be valid: %v", err)
|
|
}
|
|
if item.AppCode != "lalu" || item.Tab != "Live" || item.H5URL != "https://h5.example.com/explore/live" || !item.Enabled || item.SortOrder != 3 {
|
|
t.Fatalf("explore tab model mismatch: %+v", item)
|
|
}
|
|
}
|
|
|
|
func TestExploreTabModelFromRequestRejectsWhitespaceURL(t *testing.T) {
|
|
_, err := exploreTabModelFromRequest("lalu", exploreTabRequest{
|
|
Enabled: true,
|
|
H5URL: "https://h5.example.com/explore live",
|
|
Tab: "Live",
|
|
})
|
|
if err == nil {
|
|
t.Fatal("expected whitespace h5 url to fail")
|
|
}
|
|
}
|