package databi import ( "context" "errors" "reflect" "testing" "time" "hyapp-admin-server/internal/appctx" "hyapp-admin-server/internal/integration/userclient" "hyapp-admin-server/internal/model" "hyapp-admin-server/internal/repository" ) func TestListAppsPreservesLegacyLogoURL(t *testing.T) { service := NewService(nil, nil, nil, nil, WithLegacyApps(LegacyAppDescriptor{ AppCode: "Aslan", AppName: "Aslan", LogoURL: "https://media.example.com/aslan.png", })) apps, err := service.listApps(context.Background()) if err != nil { t.Fatalf("list apps failed: %v", err) } if len(apps) != 1 || apps[0].AppCode != "aslan" || apps[0].LogoURL != "https://media.example.com/aslan.png" { t.Fatalf("legacy app logo mismatch: %+v", apps) } } func testAccess() repository.MoneyAccess { return repository.MoneyAccess{ UserID: 7, Scopes: []model.UserMoneyScope{ {UserID: 7, AppCode: "lalu", RegionID: 0}, {UserID: 7, AppCode: "aslan", RegionID: 11}, {UserID: 7, AppCode: "aslan", RegionID: 12}, }, } } func mustLocation(t *testing.T, name string) *time.Location { t.Helper() location, err := time.LoadLocation(name) if err != nil { t.Fatalf("load location %s: %v", name, err) } return location } func TestRegionDisplay(t *testing.T) { regions := []RegionInfo{{AppCode: "aslan", RegionID: 11, RegionCode: "MENA", RegionName: "中东"}} if _, name := regionDisplay(regions, 0); name != "全部区域" { t.Fatalf("expected 全部区域 for region 0, got %s", name) } if code, name := regionDisplay(regions, 11); code != "MENA" || name != "中东" { t.Fatalf("unexpected display for region 11: %s %s", code, name) } if _, name := regionDisplay(regions, 99); name != "区域 99" { t.Fatalf("expected fallback name for unknown region, got %s", name) } } func TestTopLevelMetricsStripsStructuralKeys(t *testing.T) { day1Rate := 0.5 out := topLevelMetrics(map[string]any{ "recharge_usd_minor": int64(5), "daily_series": []any{"x"}, "country_breakdown": []any{"y"}, "daily_country_breakdown": []any{"z"}, "report_metric_sources": []any{}, "retention": map[string]any{ "day1_base_users": int64(6), "day1_rate": day1Rate, "day1_users": int64(3), }, }) if _, ok := out["daily_series"]; ok { t.Fatalf("expected daily_series stripped") } if out["recharge_usd_minor"] != int64(5) { t.Fatalf("expected metric kept, got %v", out["recharge_usd_minor"]) } if out["d1_retention_rate"] != day1Rate || out["d1_retention_users"] != int64(3) || out["d1_retention_base_users"] != int64(6) { t.Fatalf("expected nested retention flattened, got %#v", out) } } // legacy 雪花区域 ID 超出 JS 安全整数,历史 scope 里存的是 float64 圆整值; // 归一与展示必须能容差还原(线上「区域 20453...600」事故的回归测试)。 func TestRegionDisplayToleratesRoundedIDs(t *testing.T) { exact := int64(2049084769873498113) rounded := int64(float64(exact)) if exact == rounded { t.Fatalf("fixture should lose precision") } regions := []RegionInfo{{AppCode: "aslan", RegionID: exact, RegionCode: "AR", RegionName: "阿语区"}} if _, name := regionDisplay(regions, rounded); name != "阿语区" { t.Fatalf("expected rounded id to resolve region name, got %s", name) } if got := normalizeRegionID(regions, rounded); got != exact { t.Fatalf("expected normalize to exact id, got %d", got) } if !regionInCatalog(regions, rounded) { t.Fatalf("expected rounded id to match catalog") } } func TestMergeAndEnrichCountryDirectory(t *testing.T) { laluDirectory := map[int64]countryDirectoryEntry{} mergeCountryDirectory(laluDirectory, "lalu", []*userclient.Country{ {CountryID: 158, CountryCode: "PK", CountryName: "Pakistan", CountryDisplayName: "巴基斯坦", Flag: "🇵🇰", Enabled: true}, // disabled 国家仍可能出现在历史统计中,必须进入目录。 {CountryID: 157, CountryCode: "BS", CountryName: "Philippines", CountryDisplayName: "菲律宾", Enabled: false}, // country_id=0 是未知维度,不允许目录把它伪装成真实国家。 {CountryID: 0, CountryCode: "UNKNOWN", CountryName: "Unknown", Enabled: true}, }) famiDirectory := map[int64]countryDirectoryEntry{} mergeCountryDirectory(famiDirectory, "fami", []*userclient.Country{ {CountryID: 1163, CountryCode: "BD", CountryName: "Bangladesh", CountryDisplayName: "孟加拉国", Enabled: true}, }) huwaaDirectory := map[int64]countryDirectoryEntry{} mergeCountryDirectory(huwaaDirectory, "huwaa", []*userclient.Country{ {CountryID: 953, CountryCode: "IN", CountryName: "India", CountryDisplayName: "印度", Enabled: true}, }) if len(laluDirectory) != 2 { t.Fatalf("expected two lalu country ids, got %#v", laluDirectory) } if country := laluDirectory[158]; country.AppCode != "lalu" || country.Name != "Pakistan" { t.Fatalf("expected lalu country retained, got %+v", country) } if country := laluDirectory[157]; country.Enabled { t.Fatalf("expected disabled country retained, got %+v", country) } sourceRows := []map[string]any{ {"country_id": int64(158)}, {"country_id": int64(1163)}, {"country_id": int64(953)}, {"country_id": int64(0)}, {"country_id": int64(9999)}, } rows := enrichCountryRows(sourceRows, laluDirectory, false) if got := rowString(rows[0], "country_name"); got != "巴基斯坦" { t.Fatalf("expected display name to win for country 158, got %q", got) } for _, index := range []int{1, 2, 3, 4} { if got := rowString(rows[index], "country_name"); got != unknownCountryName { t.Fatalf("expected row %d to remain unknown in lalu scope, got %q", index, got) } if got := rowString(rows[index], "country_display_name"); got != unknownCountryName { t.Fatalf("expected row %d unknown display name, got %q", index, got) } } if got := rowString(enrichCountryRows([]map[string]any{{"country_id": int64(1163)}}, famiDirectory, false)[0], "country_name"); got != "孟加拉国" { t.Fatalf("expected fami country resolved only in fami scope, got %q", got) } if got := rowString(enrichCountryRows([]map[string]any{{"country_id": int64(953)}}, huwaaDirectory, false)[0], "country_name"); got != "印度" { t.Fatalf("expected huwaa country resolved only in huwaa scope, got %q", got) } if _, mutated := sourceRows[0]["country_name"]; mutated { t.Fatalf("expected enrichment to copy shared statistics rows") } legacyRows := enrichCountryRows([]map[string]any{ { "country_id": int64(158), "country_code": "LEGACY-PK", "country_name": "Legacy Pakistan", }, {"country_id": int64(158), "country": "Legacy country field"}, {"country_id": int64(158), "country_code": "LEGACY-CODE"}, {"country_id": int64(158)}, }, laluDirectory, true) if got := rowString(legacyRows[0], "country_name"); got != "Legacy Pakistan" { t.Fatalf("expected legacy source name preserved, got %q", got) } if got := rowString(legacyRows[0], "country_code"); got != "LEGACY-PK" { t.Fatalf("expected legacy source code preserved, got %q", got) } if got := rowString(legacyRows[1], "country_name"); got != "Legacy country field" { t.Fatalf("expected legacy country field preserved, got %q", got) } if got := rowString(legacyRows[2], "country_name"); got != "LEGACY-CODE" { t.Fatalf("expected legacy code used instead of colliding HyApp id, got %q", got) } if got := rowString(legacyRows[3], "country_name"); got != unknownCountryName { t.Fatalf("legacy numeric id must not borrow HyApp country name, got %q", got) } } type countryDirectoryClientStub struct { userclient.Client countries map[string][]*userclient.Country errors map[string]error calls []string filtered bool } func (c *countryDirectoryClientStub) ListCountries(ctx context.Context, req userclient.ListCountriesRequest) ([]*userclient.Country, error) { appCode := appctx.FromContext(ctx) c.calls = append(c.calls, appCode) if req.Enabled != nil { c.filtered = true } if err := c.errors[appCode]; err != nil { return nil, err } return c.countries[appCode], nil } func TestCountryDirectoriesQueryEachAllowedHyappAndContinueAfterFailure(t *testing.T) { client := &countryDirectoryClientStub{ countries: map[string][]*userclient.Country{ "fami": {{CountryID: 1163, CountryCode: "BD", CountryName: "Bangladesh", CountryDisplayName: "孟加拉国", Enabled: true}}, "lalu": {{CountryID: 158, CountryCode: "PK", CountryName: "Pakistan", CountryDisplayName: "巴基斯坦", Enabled: true}}, }, errors: map[string]error{"huwaa": errors.New("temporary user-service failure")}, } service := NewService(nil, nil, nil, client) directories := service.countryDirectories(context.Background(), []AppInfo{ {AppCode: "lalu", Kind: appKindHyapp}, {AppCode: "aslan", Kind: appKindLegacy}, {AppCode: "huwaa", Kind: appKindHyapp}, {AppCode: "fami", Kind: appKindHyapp}, }) if want := []string{"fami", "huwaa", "lalu"}; !reflect.DeepEqual(client.calls, want) { t.Fatalf("expected each HyApp queried with its own context, got %v want %v", client.calls, want) } if client.filtered { t.Fatalf("expected enabled filter omitted so disabled history can resolve") } if _, ok := directories["fami"][1163]; !ok { t.Fatalf("expected successful app before failure retained") } if _, ok := directories["lalu"][158]; !ok { t.Fatalf("expected apps after failure still queried") } if _, ok := directories["huwaa"]; ok { t.Fatalf("failed app must not receive another app's directory: %#v", directories) } if len(directories) != 2 { t.Fatalf("unexpected directories after one app failure: %#v", directories) } }