setting_service_public_test.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. //go:build unit
  2. package service
  3. import (
  4. "context"
  5. "testing"
  6. "github.com/Wei-Shaw/sub2api/internal/config"
  7. "github.com/stretchr/testify/require"
  8. )
  9. type settingPublicRepoStub struct {
  10. values map[string]string
  11. err error
  12. }
  13. func (s *settingPublicRepoStub) Get(ctx context.Context, key string) (*Setting, error) {
  14. panic("unexpected Get call")
  15. }
  16. func (s *settingPublicRepoStub) GetValue(ctx context.Context, key string) (string, error) {
  17. panic("unexpected GetValue call")
  18. }
  19. func (s *settingPublicRepoStub) Set(ctx context.Context, key, value string) error {
  20. panic("unexpected Set call")
  21. }
  22. func (s *settingPublicRepoStub) GetMultiple(ctx context.Context, keys []string) (map[string]string, error) {
  23. if s.err != nil {
  24. return nil, s.err
  25. }
  26. out := make(map[string]string, len(keys))
  27. for _, key := range keys {
  28. if value, ok := s.values[key]; ok {
  29. out[key] = value
  30. }
  31. }
  32. return out, nil
  33. }
  34. func (s *settingPublicRepoStub) SetMultiple(ctx context.Context, settings map[string]string) error {
  35. panic("unexpected SetMultiple call")
  36. }
  37. func (s *settingPublicRepoStub) GetAll(ctx context.Context) (map[string]string, error) {
  38. panic("unexpected GetAll call")
  39. }
  40. func (s *settingPublicRepoStub) Delete(ctx context.Context, key string) error {
  41. panic("unexpected Delete call")
  42. }
  43. func TestSettingService_GetPublicSettings_ExposesRegistrationEmailSuffixWhitelist(t *testing.T) {
  44. repo := &settingPublicRepoStub{
  45. values: map[string]string{
  46. SettingKeyRegistrationEnabled: "true",
  47. SettingKeyEmailVerifyEnabled: "true",
  48. SettingKeyRegistrationEmailSuffixWhitelist: `["@EXAMPLE.com"," @foo.bar ","*.EDU.CN","@invalid_domain",""]`,
  49. },
  50. }
  51. svc := NewSettingService(repo, &config.Config{})
  52. settings, err := svc.GetPublicSettings(context.Background())
  53. require.NoError(t, err)
  54. require.Equal(t, []string{"@example.com", "@foo.bar", "*.edu.cn"}, settings.RegistrationEmailSuffixWhitelist)
  55. }
  56. func TestSettingService_GetPublicSettings_ExposesTablePreferences(t *testing.T) {
  57. repo := &settingPublicRepoStub{
  58. values: map[string]string{
  59. SettingKeyTableDefaultPageSize: "50",
  60. SettingKeyTablePageSizeOptions: "[20,50,100]",
  61. },
  62. }
  63. svc := NewSettingService(repo, &config.Config{})
  64. settings, err := svc.GetPublicSettings(context.Background())
  65. require.NoError(t, err)
  66. require.Equal(t, 50, settings.TableDefaultPageSize)
  67. require.Equal(t, []int{20, 50, 100}, settings.TablePageSizeOptions)
  68. }
  69. func TestSettingService_GetPublicSettings_ExposesCompactHomeEnabled(t *testing.T) {
  70. repo := &settingPublicRepoStub{
  71. values: map[string]string{
  72. SettingKeyCompactHomeEnabled: "true",
  73. },
  74. }
  75. svc := NewSettingService(repo, &config.Config{})
  76. settings, err := svc.GetPublicSettings(context.Background())
  77. require.NoError(t, err)
  78. require.True(t, settings.CompactHomeEnabled)
  79. missingSettings, err := NewSettingService(&settingPublicRepoStub{values: map[string]string{}}, &config.Config{}).
  80. GetPublicSettings(context.Background())
  81. require.NoError(t, err)
  82. require.False(t, missingSettings.CompactHomeEnabled)
  83. }
  84. func TestSettingService_ChannelMonitorHideThroughputDefaultsToPrivate(t *testing.T) {
  85. missing := NewSettingService(&settingPublicRepoStub{values: map[string]string{}}, &config.Config{}).GetChannelMonitorRuntime(context.Background())
  86. require.True(t, missing.HideThroughput)
  87. public, err := NewSettingService(&settingPublicRepoStub{values: map[string]string{}}, &config.Config{}).GetPublicSettings(context.Background())
  88. require.NoError(t, err)
  89. require.True(t, public.ChannelMonitorHideThroughput)
  90. for _, value := range []string{"false", "0", "off", "disabled"} {
  91. runtime := NewSettingService(&settingPublicRepoStub{values: map[string]string{
  92. SettingKeyChannelMonitorHideThroughput: value,
  93. }}, &config.Config{}).GetChannelMonitorRuntime(context.Background())
  94. require.False(t, runtime.HideThroughput, "value=%q", value)
  95. }
  96. }
  97. func TestSettingService_ChannelMonitorShowQuotaFailsClosed(t *testing.T) {
  98. // 缺省(迁移插入 'false' / 老库无行)一律不展示。
  99. missingRuntime := NewSettingService(&settingPublicRepoStub{values: map[string]string{}}, &config.Config{}).GetChannelMonitorRuntime(context.Background())
  100. require.False(t, missingRuntime.ShowQuota)
  101. missingPublic, err := NewSettingService(&settingPublicRepoStub{values: map[string]string{}}, &config.Config{}).
  102. GetPublicSettings(context.Background())
  103. require.NoError(t, err)
  104. require.False(t, missingPublic.ChannelMonitorShowQuota)
  105. // 仅字面 "true" 视为开启;其余值(含异常值)fail-closed。
  106. runtime := NewSettingService(&settingPublicRepoStub{values: map[string]string{
  107. SettingKeyChannelMonitorShowQuota: "true",
  108. }}, &config.Config{}).GetChannelMonitorRuntime(context.Background())
  109. require.True(t, runtime.ShowQuota)
  110. for _, value := range []string{"false", "TRUE", "1", "yes", "on", "garbage"} {
  111. rt := NewSettingService(&settingPublicRepoStub{values: map[string]string{
  112. SettingKeyChannelMonitorShowQuota: value,
  113. }}, &config.Config{}).GetChannelMonitorRuntime(context.Background())
  114. require.False(t, rt.ShowQuota, "value=%q", value)
  115. }
  116. }
  117. func TestSettingService_ChannelMonitorHideUserRankingDefaultsToVisible(t *testing.T) {
  118. missing := NewSettingService(&settingPublicRepoStub{values: map[string]string{}}, &config.Config{}).GetChannelMonitorRuntime(context.Background())
  119. require.False(t, missing.HideUserRanking)
  120. public, err := NewSettingService(&settingPublicRepoStub{values: map[string]string{}}, &config.Config{}).GetPublicSettings(context.Background())
  121. require.NoError(t, err)
  122. require.False(t, public.ChannelMonitorHideUserRanking)
  123. for _, value := range []string{"true", "1", "on", "enabled"} {
  124. runtime := NewSettingService(&settingPublicRepoStub{values: map[string]string{
  125. SettingKeyChannelMonitorHideUserRanking: value,
  126. }}, &config.Config{}).GetChannelMonitorRuntime(context.Background())
  127. require.True(t, runtime.HideUserRanking, "value=%q", value)
  128. }
  129. }
  130. func TestSettingService_GetPublicSettings_ExposesForceEmailOnThirdPartySignup(t *testing.T) {
  131. repo := &settingPublicRepoStub{
  132. values: map[string]string{
  133. SettingKeyForceEmailOnThirdPartySignup: "true",
  134. },
  135. }
  136. svc := NewSettingService(repo, &config.Config{})
  137. settings, err := svc.GetPublicSettings(context.Background())
  138. require.NoError(t, err)
  139. require.True(t, settings.ForceEmailOnThirdPartySignup)
  140. }
  141. func TestSettingService_GetPublicSettings_ExposesAllowUserViewErrorRequests(t *testing.T) {
  142. repo := &settingPublicRepoStub{
  143. values: map[string]string{
  144. SettingKeyAllowUserViewErrorRequests: "true",
  145. },
  146. }
  147. svc := NewSettingService(repo, &config.Config{})
  148. settings, err := svc.GetPublicSettings(context.Background())
  149. require.NoError(t, err)
  150. require.True(t, settings.AllowUserViewErrorRequests)
  151. }
  152. func TestSettingService_GetPublicSettings_ExposesWeChatOAuthModeCapabilities(t *testing.T) {
  153. svc := NewSettingService(&settingPublicRepoStub{
  154. values: map[string]string{
  155. SettingKeyWeChatConnectEnabled: "true",
  156. SettingKeyWeChatConnectAppID: "wx-mp-app",
  157. SettingKeyWeChatConnectAppSecret: "wx-mp-secret",
  158. SettingKeyWeChatConnectMode: "mp",
  159. SettingKeyWeChatConnectScopes: "snsapi_base",
  160. SettingKeyWeChatConnectOpenEnabled: "true",
  161. SettingKeyWeChatConnectMPEnabled: "true",
  162. SettingKeyWeChatConnectRedirectURL: "https://api.example.com/api/v1/auth/oauth/wechat/callback",
  163. SettingKeyWeChatConnectFrontendRedirectURL: "/auth/wechat/callback",
  164. },
  165. }, &config.Config{})
  166. settings, err := svc.GetPublicSettings(context.Background())
  167. require.NoError(t, err)
  168. require.True(t, settings.WeChatOAuthEnabled)
  169. require.True(t, settings.WeChatOAuthOpenEnabled)
  170. require.True(t, settings.WeChatOAuthMPEnabled)
  171. }
  172. func TestSettingService_GetPublicSettings_DoesNotExposeMobileOnlyWeChatAsWebOAuthAvailable(t *testing.T) {
  173. svc := NewSettingService(&settingPublicRepoStub{
  174. values: map[string]string{
  175. SettingKeyWeChatConnectEnabled: "true",
  176. SettingKeyWeChatConnectMobileEnabled: "true",
  177. SettingKeyWeChatConnectMode: "mobile",
  178. SettingKeyWeChatConnectMobileAppID: "wx-mobile-app",
  179. SettingKeyWeChatConnectMobileAppSecret: "wx-mobile-secret",
  180. SettingKeyWeChatConnectFrontendRedirectURL: "/auth/wechat/callback",
  181. },
  182. }, &config.Config{})
  183. settings, err := svc.GetPublicSettings(context.Background())
  184. require.NoError(t, err)
  185. require.False(t, settings.WeChatOAuthEnabled)
  186. require.False(t, settings.WeChatOAuthOpenEnabled)
  187. require.False(t, settings.WeChatOAuthMPEnabled)
  188. require.True(t, settings.WeChatOAuthMobileEnabled)
  189. }
  190. func TestSettingService_GetPublicSettings_FallsBackToConfigForWeChatOAuthCapabilities(t *testing.T) {
  191. svc := NewSettingService(&settingPublicRepoStub{values: map[string]string{}}, &config.Config{
  192. WeChat: config.WeChatConnectConfig{
  193. Enabled: true,
  194. OpenEnabled: true,
  195. OpenAppID: "wx-open-config",
  196. OpenAppSecret: "wx-open-secret",
  197. FrontendRedirectURL: "/auth/wechat/config-callback",
  198. },
  199. })
  200. settings, err := svc.GetPublicSettings(context.Background())
  201. require.NoError(t, err)
  202. require.True(t, settings.WeChatOAuthEnabled)
  203. require.True(t, settings.WeChatOAuthOpenEnabled)
  204. require.False(t, settings.WeChatOAuthMPEnabled)
  205. require.False(t, settings.WeChatOAuthMobileEnabled)
  206. }