fix(appearance): sync close Android theme wallpaper fallback
Public projection of telesrv 2c8b7cb4c20692d00017bd84f68dd11d7915ec33 with neutral appearance seed naming. Covers gramsrv#29.
This commit is contained in:
parent
af616e8c12
commit
94f3843d23
5 changed files with 170 additions and 13 deletions
|
|
@ -50,9 +50,56 @@ func LookupWallPaper(input tg.InputWallPaperClass) (tg.WallPaperClass, bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// DrKLO normally installs a default theme with the nested wallpaper slug
|
||||||
|
// stored on ThemeAccent. During accent restoration it can instead fall back
|
||||||
|
// to ThemeInfo.slug, which is the slug of the exact Theme advertised by
|
||||||
|
// account.getThemes. Accept that server-issued alias only when every setting
|
||||||
|
// of the matched theme points at one unambiguous file wallpaper.
|
||||||
|
if in, ok := input.(*tg.InputWallPaperSlug); ok {
|
||||||
|
if wallpaper, ok := lookupChatThemeWallpaperAlias(catalog.ChatThemes, in.Slug); ok {
|
||||||
|
return DefaultWallPaper(wallpaper), true
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func lookupChatThemeWallpaperAlias(themes []appearance.ChatTheme, slug string) (appearance.Wallpaper, bool) {
|
||||||
|
if slug == "" {
|
||||||
|
return appearance.Wallpaper{}, false
|
||||||
|
}
|
||||||
|
var resolved appearance.Wallpaper
|
||||||
|
found := false
|
||||||
|
for _, theme := range themes {
|
||||||
|
if theme.Slug != slug || len(theme.Settings) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var themeWallpaper appearance.Wallpaper
|
||||||
|
for i, settings := range theme.Settings {
|
||||||
|
wallpaper := settings.Wallpaper
|
||||||
|
if wallpaper.Slug == "" || wallpaper.ID == 0 {
|
||||||
|
return appearance.Wallpaper{}, false
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
themeWallpaper = wallpaper
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !sameWallpaperIdentity(themeWallpaper, wallpaper) {
|
||||||
|
return appearance.Wallpaper{}, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found && !sameWallpaperIdentity(resolved, themeWallpaper) {
|
||||||
|
return appearance.Wallpaper{}, false
|
||||||
|
}
|
||||||
|
resolved = themeWallpaper
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
return resolved, found
|
||||||
|
}
|
||||||
|
|
||||||
|
func sameWallpaperIdentity(a, b appearance.Wallpaper) bool {
|
||||||
|
return a.ID == b.ID && a.AccessHash == b.AccessHash && a.Slug == b.Slug
|
||||||
|
}
|
||||||
|
|
||||||
// LookupWallPapers resolves multiple wallpapers from the Default seed catalog.
|
// LookupWallPapers resolves multiple wallpapers from the Default seed catalog.
|
||||||
func LookupWallPapers(inputs []tg.InputWallPaperClass) ([]tg.WallPaperClass, bool) {
|
func LookupWallPapers(inputs []tg.InputWallPaperClass) ([]tg.WallPaperClass, bool) {
|
||||||
out := make([]tg.WallPaperClass, 0, len(inputs))
|
out := make([]tg.WallPaperClass, 0, len(inputs))
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ func GlobalPrivacySettings() *tg.GlobalPrivacySettings {
|
||||||
return &tg.GlobalPrivacySettings{}
|
return &tg.GlobalPrivacySettings{}
|
||||||
}
|
}
|
||||||
|
|
||||||
const chatThemesHash int64 = 2026062501
|
const chatThemesHash int64 = 2026080101
|
||||||
const uniqueGiftChatThemesHash int64 = 2026061201
|
const uniqueGiftChatThemesHash int64 = 2026061201
|
||||||
const wallPapersHash int64 = 2026062502
|
const wallPapersHash int64 = 2026062502
|
||||||
const peerColorsHash = 2026061202
|
const peerColorsHash = 2026061202
|
||||||
|
|
@ -233,9 +233,14 @@ func WallPapers(hash int64) tg.AccountWallPapersClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
// chatThemeBaseThemes enumerates the base themes every emoji chat theme ships
|
// chatThemeBaseThemes enumerates the base themes every emoji chat theme ships
|
||||||
// settings for. The client matches a chat theme's settings to whichever base
|
// settings for. The order is part of the DrKLO client contract: the basic Chat
|
||||||
// theme the user currently runs, and DrKLO's theme picker only surfaces a chat
|
// Settings picker selects index 0 for day and index 2 for night, matching its
|
||||||
// theme whose settings cover at least four base themes
|
// built-in Blue, Day, Night, Dark Blue ordering. Arctic is an additional base
|
||||||
|
// and therefore follows those four stable slots.
|
||||||
|
//
|
||||||
|
// The client matches a chat theme's settings to whichever base theme the user
|
||||||
|
// currently runs, and DrKLO's theme picker only surfaces a chat theme whose
|
||||||
|
// settings cover at least four base themes
|
||||||
// (MediaDataController.generateEmojiPreviewThemes drops anything whose
|
// (MediaDataController.generateEmojiPreviewThemes drops anything whose
|
||||||
// items.size() < 4, one item per ThemeSettings). dark selects the bubble/accent
|
// items.size() < 4, one item per ThemeSettings). dark selects the bubble/accent
|
||||||
// palette used for that base theme.
|
// palette used for that base theme.
|
||||||
|
|
@ -245,9 +250,9 @@ var chatThemeBaseThemes = []struct {
|
||||||
}{
|
}{
|
||||||
{func() tg.BaseThemeClass { return &tg.BaseThemeClassic{} }, false},
|
{func() tg.BaseThemeClass { return &tg.BaseThemeClassic{} }, false},
|
||||||
{func() tg.BaseThemeClass { return &tg.BaseThemeDay{} }, false},
|
{func() tg.BaseThemeClass { return &tg.BaseThemeDay{} }, false},
|
||||||
{func() tg.BaseThemeClass { return &tg.BaseThemeArctic{} }, false},
|
|
||||||
{func() tg.BaseThemeClass { return &tg.BaseThemeTinted{} }, true},
|
|
||||||
{func() tg.BaseThemeClass { return &tg.BaseThemeNight{} }, true},
|
{func() tg.BaseThemeClass { return &tg.BaseThemeNight{} }, true},
|
||||||
|
{func() tg.BaseThemeClass { return &tg.BaseThemeTinted{} }, true},
|
||||||
|
{func() tg.BaseThemeClass { return &tg.BaseThemeArctic{} }, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
func AutoDownloadSettings() *tg.AccountAutoDownloadSettings {
|
func AutoDownloadSettings() *tg.AccountAutoDownloadSettings {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/iamxvbaba/td/tg"
|
"github.com/iamxvbaba/td/tg"
|
||||||
|
|
||||||
|
"telesrv/internal/seed/appearance"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNotifySettingsDefaultIsAudible(t *testing.T) {
|
func TestNotifySettingsDefaultIsAudible(t *testing.T) {
|
||||||
|
|
@ -216,6 +218,39 @@ func TestDefaultThemesAreDefaultFlaggedForPicker(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDefaultThemeSettingsFollowDrKLOPickerIndices(t *testing.T) {
|
||||||
|
for _, theme := range DefaultThemeList() {
|
||||||
|
settings, ok := theme.GetSettings()
|
||||||
|
if !ok || len(settings) < 5 {
|
||||||
|
t.Fatalf("theme %d settings len=%d ok=%v, want five stable base slots", theme.ID, len(settings), ok)
|
||||||
|
}
|
||||||
|
if _, ok := settings[0].BaseTheme.(*tg.BaseThemeClassic); !ok {
|
||||||
|
t.Fatalf("theme %d settings[0] base = %T, want classic", theme.ID, settings[0].BaseTheme)
|
||||||
|
}
|
||||||
|
if _, ok := settings[1].BaseTheme.(*tg.BaseThemeDay); !ok {
|
||||||
|
t.Fatalf("theme %d settings[1] base = %T, want day", theme.ID, settings[1].BaseTheme)
|
||||||
|
}
|
||||||
|
if _, ok := settings[2].BaseTheme.(*tg.BaseThemeNight); !ok {
|
||||||
|
t.Fatalf("theme %d settings[2] base = %T, want night", theme.ID, settings[2].BaseTheme)
|
||||||
|
}
|
||||||
|
if _, ok := settings[3].BaseTheme.(*tg.BaseThemeTinted); !ok {
|
||||||
|
t.Fatalf("theme %d settings[3] base = %T, want tinted", theme.ID, settings[3].BaseTheme)
|
||||||
|
}
|
||||||
|
if _, ok := settings[4].BaseTheme.(*tg.BaseThemeArctic); !ok {
|
||||||
|
t.Fatalf("theme %d settings[4] base = %T, want arctic", theme.ID, settings[4].BaseTheme)
|
||||||
|
}
|
||||||
|
wallpaperClass, ok := settings[2].GetWallpaper()
|
||||||
|
wallpaper, fileWallpaper := wallpaperClass.(*tg.WallPaper)
|
||||||
|
if !ok || !fileWallpaper || !wallpaper.GetDark() {
|
||||||
|
t.Fatalf("theme %d night slot wallpaper = %#v ok=%v, want dark file wallpaper", theme.ID, wallpaperClass, ok)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := ChatThemes(2026062501).(*tg.AccountThemesNotModified); ok {
|
||||||
|
t.Fatal("pre-fix chat theme hash was not invalidated after settings reorder")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestLookupDefaultThemeValidatesIdentity(t *testing.T) {
|
func TestLookupDefaultThemeValidatesIdentity(t *testing.T) {
|
||||||
themes := DefaultThemeList()
|
themes := DefaultThemeList()
|
||||||
if len(themes) == 0 {
|
if len(themes) == 0 {
|
||||||
|
|
@ -366,12 +401,38 @@ func TestDefaultThemeWallpaperReferencesResolve(t *testing.T) {
|
||||||
t.Fatalf("theme %d settings[%d] wallpaper slug lookup = %#v", theme.ID, i, bySlug)
|
t.Fatalf("theme %d settings[%d] wallpaper slug lookup = %#v", theme.ID, i, bySlug)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
byThemeSlug, ok := LookupWallPaper(&tg.InputWallPaperSlug{Slug: theme.Slug})
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("theme %d fallback wallpaper alias %q was not resolvable", theme.ID, theme.Slug)
|
||||||
|
}
|
||||||
|
aliasWallpaper, ok := byThemeSlug.(*tg.WallPaper)
|
||||||
|
firstWallpaperClass, firstOK := settings[0].GetWallpaper()
|
||||||
|
firstWallpaper, firstFile := firstWallpaperClass.(*tg.WallPaper)
|
||||||
|
if !ok || !firstOK || !firstFile || aliasWallpaper.ID != firstWallpaper.ID || aliasWallpaper.Slug != firstWallpaper.Slug {
|
||||||
|
t.Fatalf("theme %d fallback alias = %#v, want settings[0] wallpaper %#v", theme.ID, byThemeSlug, firstWallpaperClass)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if checked == 0 {
|
if checked == 0 {
|
||||||
t.Fatal("default themes exposed no file wallpapers")
|
t.Fatal("default themes exposed no file wallpapers")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestChatThemeWallpaperAliasRejectsAmbiguousSettings(t *testing.T) {
|
||||||
|
themes := []appearance.ChatTheme{{
|
||||||
|
Slug: "theme-alias",
|
||||||
|
Settings: []appearance.ThemeSettings{
|
||||||
|
{Wallpaper: appearance.Wallpaper{ID: 1, AccessHash: 11, Slug: "wallpaper-one"}},
|
||||||
|
{Wallpaper: appearance.Wallpaper{ID: 2, AccessHash: 22, Slug: "wallpaper-two"}},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
if _, ok := lookupChatThemeWallpaperAlias(themes, "theme-alias"); ok {
|
||||||
|
t.Fatal("ambiguous theme wallpaper alias was accepted")
|
||||||
|
}
|
||||||
|
if _, ok := lookupChatThemeWallpaperAlias(themes, "unknown"); ok {
|
||||||
|
t.Fatal("unknown theme wallpaper alias was accepted")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestStarsRevenueStatsIsZeroBalanceCompatStub(t *testing.T) {
|
func TestStarsRevenueStatsIsZeroBalanceCompatStub(t *testing.T) {
|
||||||
got := StarsRevenueStats(false)
|
got := StarsRevenueStats(false)
|
||||||
if got == nil {
|
if got == nil {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/iamxvbaba/td/tg"
|
"github.com/iamxvbaba/td/tg"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"github.com/iamxvbaba/td/tlprofile"
|
"github.com/iamxvbaba/td/tlprofile"
|
||||||
"telesrv/internal/branding"
|
"telesrv/internal/branding"
|
||||||
|
|
@ -289,6 +290,7 @@ func (r *Router) registerAccount(d *tlprofile.Dispatcher) {
|
||||||
return false, tgerr400("WALLPAPER_INVALID")
|
return false, tgerr400("WALLPAPER_INVALID")
|
||||||
}
|
}
|
||||||
if _, ok := tdesktop.LookupWallPaper(req.Wallpaper); !ok {
|
if _, ok := tdesktop.LookupWallPaper(req.Wallpaper); !ok {
|
||||||
|
r.log.Info("wallpaper reference rejected", wallpaperReferenceLogFields("account.installWallPaper", req.Wallpaper)...)
|
||||||
return false, tgerr400("WALLPAPER_INVALID")
|
return false, tgerr400("WALLPAPER_INVALID")
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
|
|
@ -463,6 +465,20 @@ func (r *Router) registerAccount(d *tlprofile.Dispatcher) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func wallpaperReferenceLogFields(method string, input tg.InputWallPaperClass) []zap.Field {
|
||||||
|
fields := []zap.Field{zap.String("method", method)}
|
||||||
|
switch wallpaper := input.(type) {
|
||||||
|
case *tg.InputWallPaperSlug:
|
||||||
|
return append(fields, zap.String("input_type", "inputWallPaperSlug"), zap.String("slug", wallpaper.Slug))
|
||||||
|
case *tg.InputWallPaper:
|
||||||
|
return append(fields, zap.String("input_type", "inputWallPaper"), zap.Int64("wallpaper_id", wallpaper.ID))
|
||||||
|
case *tg.InputWallPaperNoFile:
|
||||||
|
return append(fields, zap.String("input_type", "inputWallPaperNoFile"), zap.Int64("wallpaper_id", wallpaper.ID))
|
||||||
|
default:
|
||||||
|
return append(fields, zap.String("input_type", "unknown"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Router) onAccountGetPassword(ctx context.Context) (*tg.AccountPassword, error) {
|
func (r *Router) onAccountGetPassword(ctx context.Context) (*tg.AccountPassword, error) {
|
||||||
if r.deps.Account == nil {
|
if r.deps.Account == nil {
|
||||||
return tgPassword(domain.PasswordSettings{SecureRandom: []byte("telesrv-tdesktop-dev-secure-rand")}), nil
|
return tgPassword(domain.PasswordSettings{SecureRandom: []byte("telesrv-tdesktop-dev-secure-rand")}), nil
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ import (
|
||||||
"github.com/iamxvbaba/td/clock"
|
"github.com/iamxvbaba/td/clock"
|
||||||
"github.com/iamxvbaba/td/tg"
|
"github.com/iamxvbaba/td/tg"
|
||||||
"go.uber.org/zap/zaptest"
|
"go.uber.org/zap/zaptest"
|
||||||
|
|
||||||
|
"telesrv/internal/compat/tdesktop"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccountGetChatThemesReturnsStaticThemes(t *testing.T) {
|
func TestAccountGetChatThemesReturnsStaticThemes(t *testing.T) {
|
||||||
|
|
@ -106,6 +108,21 @@ func TestAccountWallpaperSeedLookupAndAckRPCs(t *testing.T) {
|
||||||
t.Fatalf("getWallPaper id = %d, want %d", oneWallpaper.ID, first.ID)
|
t.Fatalf("getWallPaper id = %d, want %d", oneWallpaper.ID, first.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defaultTheme := tdesktop.DefaultThemeList()[0]
|
||||||
|
themeAliasInput := &tg.InputWallPaperSlug{Slug: defaultTheme.Slug}
|
||||||
|
var aliasReq bin.Buffer
|
||||||
|
if err := (&tg.AccountGetWallPaperRequest{Wallpaper: themeAliasInput}).Encode(&aliasReq); err != nil {
|
||||||
|
t.Fatalf("encode getWallPaper theme alias request: %v", err)
|
||||||
|
}
|
||||||
|
aliasGot, err := r.Dispatch(ctx, [8]byte{}, 0, &aliasReq)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("dispatch getWallPaper theme alias: %v", err)
|
||||||
|
}
|
||||||
|
aliasWallpaper, ok := aliasGot.(*tg.WallPaper)
|
||||||
|
if !ok || aliasWallpaper.Slug == defaultTheme.Slug {
|
||||||
|
t.Fatalf("getWallPaper theme alias = %#v, want resolved wallpaper identity", aliasGot)
|
||||||
|
}
|
||||||
|
|
||||||
nofileInput := &tg.InputWallPaperNoFile{ID: 930000000000000001}
|
nofileInput := &tg.InputWallPaperNoFile{ID: 930000000000000001}
|
||||||
var nofileReq bin.Buffer
|
var nofileReq bin.Buffer
|
||||||
if err := (&tg.AccountGetWallPaperRequest{Wallpaper: nofileInput}).Encode(&nofileReq); err != nil {
|
if err := (&tg.AccountGetWallPaperRequest{Wallpaper: nofileInput}).Encode(&nofileReq); err != nil {
|
||||||
|
|
@ -127,6 +144,7 @@ func TestAccountWallpaperSeedLookupAndAckRPCs(t *testing.T) {
|
||||||
if err := (&tg.AccountGetMultiWallPapersRequest{Wallpapers: []tg.InputWallPaperClass{
|
if err := (&tg.AccountGetMultiWallPapersRequest{Wallpapers: []tg.InputWallPaperClass{
|
||||||
input,
|
input,
|
||||||
&tg.InputWallPaperSlug{Slug: first.Slug},
|
&tg.InputWallPaperSlug{Slug: first.Slug},
|
||||||
|
themeAliasInput,
|
||||||
nofileInput,
|
nofileInput,
|
||||||
}}).Encode(&multiReq); err != nil {
|
}}).Encode(&multiReq); err != nil {
|
||||||
t.Fatalf("encode getMultiWallPapers request: %v", err)
|
t.Fatalf("encode getMultiWallPapers request: %v", err)
|
||||||
|
|
@ -135,16 +153,18 @@ func TestAccountWallpaperSeedLookupAndAckRPCs(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("dispatch getMultiWallPapers: %v", err)
|
t.Fatalf("dispatch getMultiWallPapers: %v", err)
|
||||||
}
|
}
|
||||||
if vector, ok := dispatchCanonicalValue(multiGot).([]tg.WallPaperClass); !ok || len(vector) != 3 {
|
if vector, ok := dispatchCanonicalValue(multiGot).([]tg.WallPaperClass); !ok || len(vector) != 4 {
|
||||||
t.Fatalf("getMultiWallPapers = %T %#v, want 3 wallpapers", multiGot, multiGot)
|
t.Fatalf("getMultiWallPapers = %T %#v, want 4 wallpapers", multiGot, multiGot)
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, request := range map[string]bin.Encoder{
|
for name, request := range map[string]bin.Encoder{
|
||||||
"save": &tg.AccountSaveWallPaperRequest{Wallpaper: input},
|
"save": &tg.AccountSaveWallPaperRequest{Wallpaper: input},
|
||||||
"install": &tg.AccountInstallWallPaperRequest{Wallpaper: input},
|
"install": &tg.AccountInstallWallPaperRequest{Wallpaper: input},
|
||||||
"save_nofile": &tg.AccountSaveWallPaperRequest{Wallpaper: nofileInput},
|
"save_theme_alias": &tg.AccountSaveWallPaperRequest{Wallpaper: themeAliasInput},
|
||||||
"install_nofile": &tg.AccountInstallWallPaperRequest{Wallpaper: nofileInput},
|
"install_theme_alias": &tg.AccountInstallWallPaperRequest{Wallpaper: themeAliasInput},
|
||||||
"reset": &tg.AccountResetWallPapersRequest{},
|
"save_nofile": &tg.AccountSaveWallPaperRequest{Wallpaper: nofileInput},
|
||||||
|
"install_nofile": &tg.AccountInstallWallPaperRequest{Wallpaper: nofileInput},
|
||||||
|
"reset": &tg.AccountResetWallPapersRequest{},
|
||||||
} {
|
} {
|
||||||
var encoded bin.Buffer
|
var encoded bin.Buffer
|
||||||
if err := request.Encode(&encoded); err != nil {
|
if err := request.Encode(&encoded); err != nil {
|
||||||
|
|
@ -166,6 +186,14 @@ func TestAccountWallpaperSeedLookupAndAckRPCs(t *testing.T) {
|
||||||
if _, err := r.Dispatch(ctx, [8]byte{}, 0, &badReq); err == nil || !strings.Contains(err.Error(), "WALLPAPER_INVALID") {
|
if _, err := r.Dispatch(ctx, [8]byte{}, 0, &badReq); err == nil || !strings.Contains(err.Error(), "WALLPAPER_INVALID") {
|
||||||
t.Fatalf("bad getWallPaper err = %v, want WALLPAPER_INVALID", err)
|
t.Fatalf("bad getWallPaper err = %v, want WALLPAPER_INVALID", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var unknownAliasReq bin.Buffer
|
||||||
|
if err := (&tg.AccountInstallWallPaperRequest{Wallpaper: &tg.InputWallPaperSlug{Slug: "unknown-theme-alias"}}).Encode(&unknownAliasReq); err != nil {
|
||||||
|
t.Fatalf("encode unknown theme alias install request: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := r.Dispatch(ctx, [8]byte{}, 0, &unknownAliasReq); err == nil || !strings.Contains(err.Error(), "WALLPAPER_INVALID") {
|
||||||
|
t.Fatalf("unknown theme alias install err = %v, want WALLPAPER_INVALID", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPaymentsGetStarGiftCollectionsNoServiceFallbackAndValidatesPeer(t *testing.T) {
|
func TestPaymentsGetStarGiftCollectionsNoServiceFallbackAndValidatesPeer(t *testing.T) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue