package dashboard import ( "context" "encoding/json" "net/http" "net/http/httptest" "testing" "time" "hyapp-admin-server/internal/config" ) func TestAslanHTTPDashboardSourceStatisticsOverview(t *testing.T) { requestedDates := map[string]bool{} server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("Authorization") != "" { t.Fatalf("aslan dashboard request should not send authorization header") } if r.URL.Path != "/console/datav/aslan/region-country/statistics" { t.Fatalf("unexpected path: %s", r.URL.Path) } statDay := r.URL.Query().Get("date") requestedDates[statDay] = true w.Header().Set("Content-Type", "application/json") switch statDay { case "2026-06-30": _ = json.NewEncoder(w).Encode([]map[string]any{{ "date": "2026-06-30", "regionId": "legacy-middle-east", "regionCode": "MIDDLE_EAST", "regionName": "中东区", "dailyRecharge": "111.25", "dailyActive": 14, "dailyRegister": 4, "goldBalance": 1300, "hostSalaryBalance": "190.50", "countries": []map[string]any{ {"countryCode": "SA", "countryName": "Saudi Arabia", "dailyRecharge": "100.25", "dailyActive": 12, "dailyRegister": 3, "goldBalance": 1000, "hostSalaryBalance": "150.50"}, {"countryCode": "AE", "countryName": "United Arab Emirates", "dailyRecharge": "11.00", "dailyActive": 2, "dailyRegister": 1, "goldBalance": 300, "hostSalaryBalance": "40.00"}, }, }}) case "2026-06-29": _ = json.NewEncoder(w).Encode(map[string]any{"data": []map[string]any{{ "date": "2026-06-29", "regionId": "legacy-middle-east", "regionCode": "MIDDLE_EAST", "regionName": "中东区", "dailyRecharge": "50.00", "dailyActive": 10, "dailyRegister": 2, "goldBalance": 800, "hostSalaryBalance": "100.00", "countries": []map[string]any{ {"countryCode": "SA", "countryName": "Saudi Arabia", "dailyRecharge": "50.00", "dailyActive": 10, "dailyRegister": 2, "goldBalance": 800, "hostSalaryBalance": "100.00"}, }, }}}) default: t.Fatalf("unexpected date: %s", statDay) } })) defer server.Close() source, err := NewAslanHTTPDashboardSource(config.DashboardExternalSourceConfig{ Enabled: true, SourceType: "aslan_http", AppCode: "aslan", AppName: "Aslan", BaseURL: server.URL, OverviewPath: "/console/datav/aslan/region-country/statistics", StatTimezone: "Asia/Shanghai", RequestTimeout: time.Second, }, staticDashboardRegionResolver{appCode: "aslan", regionID: 1001, countryCodes: []string{"SA"}}) if err != nil { t.Fatalf("build source: %v", err) } shanghai, err := time.LoadLocation("Asia/Shanghai") if err != nil { t.Fatalf("load location: %v", err) } start := time.Date(2026, 6, 30, 0, 0, 0, 0, shanghai) overview, err := source.StatisticsOverview(context.Background(), StatisticsQuery{ AppCode: "aslan", StatTZ: "Asia/Shanghai", StartMS: start.UnixMilli(), EndMS: start.AddDate(0, 0, 1).UnixMilli(), RegionID: 1001, }) if err != nil { t.Fatalf("StatisticsOverview: %v", err) } if !requestedDates["2026-06-30"] || !requestedDates["2026-06-29"] { t.Fatalf("date requests mismatch: %#v", requestedDates) } assertEqual(t, overview["app_code"], "aslan") assertEqual(t, overview["stat_tz"], "Asia/Shanghai") assertInt64(t, overview["recharge_usd_minor"], 10_025) assertInt64(t, overview["active_users"], 12) assertInt64(t, overview["new_users"], 3) assertInt64(t, overview["registered_users"], 3) assertInt64(t, overview["coin_total"], 1_000) assertInt64(t, overview["salary_usd_minor"], 15_050) assertInt64(t, overview["arpu_usd_minor"], 835) if overview["paid_users"] != nil || overview["recharge_users"] != nil || overview["arppu_usd_minor"] != nil { t.Fatalf("aslan payer fields should be nil when the remote API does not return payer counts: %#v", overview) } countries, ok := overview["country_breakdown"].([]map[string]any) if !ok || len(countries) != 1 { t.Fatalf("country_breakdown mismatch: %#v", overview["country_breakdown"]) } assertEqual(t, countries[0]["country_code"], "SA") assertEqual(t, countries[0]["region_id"], int64(1001)) assertInt64(t, countries[0]["recharge_usd_minor"], 10_025) sources, ok := overview["report_metric_sources"].([]map[string]any) if !ok { t.Fatalf("report_metric_sources mismatch: %#v", overview["report_metric_sources"]) } assertMetricSourceUnavailable(t, sources, "arppu_usd_minor") assertMetricSourceUnavailable(t, sources, "paid_users") assertMetricSourceUnavailable(t, sources, "recharge_users") assertMetricSourceUnavailable(t, sources, "game_turnover") assertMetricSourceUnavailable(t, sources, "retention.day1_rate") }