249 lines
9.4 KiB
Go

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)
}
}