package mysql import ( "context" "runtime" "testing" "time" "hyapp/internal/testutil/mysqlschema" ) func TestQueryOverviewUsesActivityLuckyGiftDayStats(t *testing.T) { ctx := context.Background() _, file, _, _ := runtime.Caller(0) statsSchema := mysqlschema.New(t, mysqlschema.Config{ InitDBPath: mysqlschema.InitDBPath(t, file, "..", "..", "..", "deploy", "mysql", "initdb", "001_statistics_service.sql"), DatabasePrefix: "hy_stats_query_test", }) activitySchema := mysqlschema.New(t, mysqlschema.Config{ EnvVar: "ACTIVITY_SERVICE_MYSQL_TEST_DSN", InitDBPath: mysqlschema.InitDBPath(t, file, "..", "..", "..", "..", "activity-service", "deploy", "mysql", "initdb", "001_activity_service.sql"), DatabasePrefix: "hy_activity_query_test", }) repository, err := Open(ctx, statsSchema.DSN, activitySchema.DSN) if err != nil { t.Fatalf("open repository: %v", err) } t.Cleanup(func() { _ = repository.Close() }) if _, err := repository.db.ExecContext(ctx, ` INSERT INTO stat_app_day_country ( app_code, stat_day, country_id, lucky_gift_turnover, lucky_gift_payout, updated_at_ms ) VALUES ('lalu', '2026-06-06', 210, 100, 900, 1)`); err != nil { t.Fatalf("seed stale statistics overview: %v", err) } if _, err := repository.db.ExecContext(ctx, ` INSERT INTO stat_lucky_gift_pool_day_country ( app_code, stat_day, country_id, pool_id, turnover_coin, payout_coin, updated_at_ms ) VALUES ('lalu', '2026-06-06', 210, 'lucky', 100, 900, 1)`); err != nil { t.Fatalf("seed stale statistics pool: %v", err) } if _, err := repository.activityDB.ExecContext(ctx, ` INSERT INTO lucky_draw_pool_day_stats ( app_code, stat_day, visible_region_id, pool_id, gift_id, draw_count, turnover_coin, payout_coin, base_reward_coin, room_atmosphere_reward_coin, activity_subsidy_coin, created_at_ms, updated_at_ms ) VALUES ('lalu', '2026-06-06', 210, 'super_lucky', '', 10, 2744832, 2401885, 2401885, 0, 0, 1, 1), ('lalu', '2026-06-06', 210, 'lucky', '', 5, 148322, 126415, 126415, 0, 0, 1, 1)`); err != nil { t.Fatalf("seed activity databi stats: %v", err) } start := time.Date(2026, 6, 6, 0, 0, 0, 0, time.UTC).UnixMilli() overview, err := repository.QueryOverview(ctx, OverviewQuery{AppCode: "lalu", StartMS: start, EndMS: start + int64(24*time.Hour/time.Millisecond), CountryID: 210}) if err != nil { t.Fatalf("query overview: %v", err) } if overview.LuckyGiftTurnover != 2_893_154 || overview.LuckyGiftPayout != 2_528_300 || overview.LuckyGiftProfit != 364_854 { t.Fatalf("activity lucky gift overview not applied: %+v", overview) } if len(overview.LuckyGiftPools) != 2 { t.Fatalf("activity lucky gift pools not applied: %+v", overview.LuckyGiftPools) } if overview.LuckyGiftPools[0].PoolID != "super_lucky" || overview.LuckyGiftPools[0].TurnoverCoin != 2_744_832 || overview.LuckyGiftPools[0].PayoutCoin != 2_401_885 { t.Fatalf("super_lucky pool mismatch: %+v", overview.LuckyGiftPools[0]) } } func TestQueryOverviewReturnsPreviousPeriodDeltaRates(t *testing.T) { ctx := context.Background() _, file, _, _ := runtime.Caller(0) statsSchema := mysqlschema.New(t, mysqlschema.Config{ InitDBPath: mysqlschema.InitDBPath(t, file, "..", "..", "..", "deploy", "mysql", "initdb", "001_statistics_service.sql"), DatabasePrefix: "hy_stats_query_delta_test", }) repository, err := Open(ctx, statsSchema.DSN) if err != nil { t.Fatalf("open repository: %v", err) } t.Cleanup(func() { _ = repository.Close() }) if _, err := repository.db.ExecContext(ctx, ` INSERT INTO stat_app_day_country ( app_code, stat_day, country_id, active_users, paid_users, recharge_usd_minor, new_user_recharge_usd_minor, mifapay_recharge_usd_minor, gift_coin_spent, coin_seller_transfer_coin, lucky_gift_turnover, lucky_gift_payout, game_turnover, game_payout, game_refund, updated_at_ms ) VALUES ('lalu', '2026-06-05', 210, 10, 2, 100, 50, 100, 300, 80, 100, 50, 500, 100, 50, 1), ('lalu', '2026-06-06', 210, 20, 5, 200, 100, 200, 600, 200, 400, 100, 1000, 400, 100, 1)`); err != nil { t.Fatalf("seed delta statistics: %v", err) } start := time.Date(2026, 6, 6, 0, 0, 0, 0, time.UTC).UnixMilli() overview, err := repository.QueryOverview(ctx, OverviewQuery{AppCode: "lalu", StartMS: start, EndMS: start + int64(24*time.Hour/time.Millisecond), CountryID: 210}) if err != nil { t.Fatalf("query overview: %v", err) } if overview.RechargeDeltaRate != 1 || overview.NewUserRechargeDeltaRate != 1 || overview.ActiveUsersDeltaRate != 1 { t.Fatalf("top delta rates mismatch: %+v", overview) } if overview.PaidUsersDeltaRate != 1.5 || overview.ARPUDeltaRate != 0 || overview.ARPPUDeltaRate != -0.2 { t.Fatalf("user delta rates mismatch: %+v", overview) } if overview.GiftCoinSpentDeltaRate != 1 || overview.CoinSellerTransferDeltaRate != 1.5 || overview.LuckyGiftTurnoverDeltaRate != 3 || overview.LuckyGiftPayoutDeltaRate != 1 || overview.LuckyGiftProfitDeltaRate != 5 { t.Fatalf("coin delta rates mismatch: %+v", overview) } if overview.GameTurnoverDeltaRate != 1 || overview.GameProfitDeltaRate < 0.428 || overview.GameProfitDeltaRate > 0.429 { t.Fatalf("game delta rates mismatch: %+v", overview) } } func TestQueryOverviewReturnsCountryBreakdownAndIncludesCoinSellerRecharge(t *testing.T) { ctx := context.Background() _, file, _, _ := runtime.Caller(0) statsSchema := mysqlschema.New(t, mysqlschema.Config{ InitDBPath: mysqlschema.InitDBPath(t, file, "..", "..", "..", "deploy", "mysql", "initdb", "001_statistics_service.sql"), DatabasePrefix: "hy_stats_query_country_test", }) repository, err := Open(ctx, statsSchema.DSN) if err != nil { t.Fatalf("open repository: %v", err) } t.Cleanup(func() { _ = repository.Close() }) if _, err := repository.db.ExecContext(ctx, ` INSERT INTO stat_app_day_country ( app_code, stat_day, country_id, region_id, active_users, paid_users, recharge_usd_minor, coin_seller_recharge_usd_minor, google_recharge_usd_minor, updated_at_ms ) VALUES ('lalu', '2026-06-06', 86, 210, 10, 2, 150, 400, 150, 1), ('lalu', '2026-06-06', 66, 210, 5, 1, 100, 0, 100, 1), ('lalu', '2026-06-06', 99, 220, 3, 1, 900, 0, 900, 1)`); err != nil { t.Fatalf("seed country statistics: %v", err) } start := time.Date(2026, 6, 6, 0, 0, 0, 0, time.UTC).UnixMilli() overview, err := repository.QueryOverview(ctx, OverviewQuery{AppCode: "lalu", StartMS: start, EndMS: start + int64(24*time.Hour/time.Millisecond), RegionID: 210}) if err != nil { t.Fatalf("query overview: %v", err) } if overview.RechargeUSDMinor != 650 || overview.CoinSellerRechargeUSDMinor != 400 { t.Fatalf("overview recharge mismatch: %+v", overview) } if len(overview.CountryBreakdown) != 2 { t.Fatalf("country breakdown should come from aggregate query: %+v", overview.CountryBreakdown) } if overview.CountryBreakdown[0].CountryID != 86 || overview.CountryBreakdown[0].RechargeUSDMinor != 550 || overview.CountryBreakdown[0].ARPPUUSDMinor != 275 { t.Fatalf("first country breakdown mismatch: %+v", overview.CountryBreakdown[0]) } }