setting_handler_auth_source_defaults_test.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. package admin
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/json"
  6. "errors"
  7. "net/http"
  8. "net/http/httptest"
  9. "testing"
  10. "github.com/Wei-Shaw/sub2api/internal/config"
  11. "github.com/Wei-Shaw/sub2api/internal/pkg/response"
  12. "github.com/Wei-Shaw/sub2api/internal/service"
  13. "github.com/gin-gonic/gin"
  14. "github.com/stretchr/testify/require"
  15. )
  16. type settingHandlerRepoStub struct {
  17. values map[string]string
  18. lastUpdates map[string]string
  19. }
  20. func (s *settingHandlerRepoStub) Get(ctx context.Context, key string) (*service.Setting, error) {
  21. panic("unexpected Get call")
  22. }
  23. func (s *settingHandlerRepoStub) GetValue(ctx context.Context, key string) (string, error) {
  24. if s.values != nil {
  25. if value, ok := s.values[key]; ok {
  26. return value, nil
  27. }
  28. }
  29. return "", nil
  30. }
  31. func (s *settingHandlerRepoStub) Set(ctx context.Context, key, value string) error {
  32. panic("unexpected Set call")
  33. }
  34. func (s *settingHandlerRepoStub) GetMultiple(ctx context.Context, keys []string) (map[string]string, error) {
  35. out := make(map[string]string, len(keys))
  36. for _, key := range keys {
  37. if value, ok := s.values[key]; ok {
  38. out[key] = value
  39. }
  40. }
  41. return out, nil
  42. }
  43. func (s *settingHandlerRepoStub) SetMultiple(ctx context.Context, settings map[string]string) error {
  44. s.lastUpdates = make(map[string]string, len(settings))
  45. for key, value := range settings {
  46. s.lastUpdates[key] = value
  47. if s.values == nil {
  48. s.values = map[string]string{}
  49. }
  50. s.values[key] = value
  51. }
  52. return nil
  53. }
  54. func (s *settingHandlerRepoStub) GetAll(ctx context.Context) (map[string]string, error) {
  55. out := make(map[string]string, len(s.values))
  56. for key, value := range s.values {
  57. out[key] = value
  58. }
  59. return out, nil
  60. }
  61. func (s *settingHandlerRepoStub) Delete(ctx context.Context, key string) error {
  62. panic("unexpected Delete call")
  63. }
  64. type failingAuthSourceSettingsRepoStub struct {
  65. values map[string]string
  66. err error
  67. }
  68. func (s *failingAuthSourceSettingsRepoStub) Get(ctx context.Context, key string) (*service.Setting, error) {
  69. panic("unexpected Get call")
  70. }
  71. func (s *failingAuthSourceSettingsRepoStub) GetValue(ctx context.Context, key string) (string, error) {
  72. panic("unexpected GetValue call")
  73. }
  74. func (s *failingAuthSourceSettingsRepoStub) Set(ctx context.Context, key, value string) error {
  75. panic("unexpected Set call")
  76. }
  77. func (s *failingAuthSourceSettingsRepoStub) GetMultiple(ctx context.Context, keys []string) (map[string]string, error) {
  78. out := make(map[string]string, len(keys))
  79. for _, key := range keys {
  80. if value, ok := s.values[key]; ok {
  81. out[key] = value
  82. }
  83. }
  84. return out, nil
  85. }
  86. func (s *failingAuthSourceSettingsRepoStub) SetMultiple(ctx context.Context, settings map[string]string) error {
  87. if _, ok := settings[service.SettingKeyAuthSourceDefaultEmailBalance]; ok {
  88. return s.err
  89. }
  90. for key, value := range settings {
  91. if s.values == nil {
  92. s.values = map[string]string{}
  93. }
  94. s.values[key] = value
  95. }
  96. return nil
  97. }
  98. func (s *failingAuthSourceSettingsRepoStub) GetAll(ctx context.Context) (map[string]string, error) {
  99. out := make(map[string]string, len(s.values))
  100. for key, value := range s.values {
  101. out[key] = value
  102. }
  103. return out, nil
  104. }
  105. func (s *failingAuthSourceSettingsRepoStub) Delete(ctx context.Context, key string) error {
  106. panic("unexpected Delete call")
  107. }
  108. func TestSettingHandler_GetSettings_InjectsAuthSourceDefaults(t *testing.T) {
  109. gin.SetMode(gin.TestMode)
  110. repo := &settingHandlerRepoStub{
  111. values: map[string]string{
  112. service.SettingKeyRegistrationEnabled: "true",
  113. service.SettingKeyPromoCodeEnabled: "true",
  114. service.SettingKeyAuthSourceDefaultEmailBalance: "9.5",
  115. service.SettingKeyAuthSourceDefaultEmailConcurrency: "8",
  116. service.SettingKeyAuthSourceDefaultEmailSubscriptions: `[{"group_id":31,"validity_days":15}]`,
  117. service.SettingKeyForceEmailOnThirdPartySignup: "true",
  118. },
  119. }
  120. svc := service.NewSettingService(repo, &config.Config{Default: config.DefaultConfig{UserConcurrency: 5}})
  121. handler := NewSettingHandler(svc, nil, nil, nil, nil, nil, nil)
  122. rec := httptest.NewRecorder()
  123. c, _ := gin.CreateTestContext(rec)
  124. c.Request = httptest.NewRequest(http.MethodGet, "/api/v1/admin/settings", nil)
  125. handler.GetSettings(c)
  126. require.Equal(t, http.StatusOK, rec.Code)
  127. var resp response.Response
  128. require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
  129. data, ok := resp.Data.(map[string]any)
  130. require.True(t, ok)
  131. require.Equal(t, 9.5, data["auth_source_default_email_balance"])
  132. require.Equal(t, float64(8), data["auth_source_default_email_concurrency"])
  133. require.Equal(t, true, data["force_email_on_third_party_signup"])
  134. subscriptions, ok := data["auth_source_default_email_subscriptions"].([]any)
  135. require.True(t, ok)
  136. require.Len(t, subscriptions, 1)
  137. }
  138. func TestSettingHandler_UpdateSettings_PreservesOmittedAuthSourceDefaults(t *testing.T) {
  139. gin.SetMode(gin.TestMode)
  140. repo := &settingHandlerRepoStub{
  141. values: map[string]string{
  142. service.SettingKeyRegistrationEnabled: "false",
  143. service.SettingKeyPromoCodeEnabled: "true",
  144. service.SettingKeyAuthSourceDefaultEmailBalance: "9.5",
  145. service.SettingKeyAuthSourceDefaultEmailConcurrency: "8",
  146. service.SettingKeyAuthSourceDefaultEmailSubscriptions: `[{"group_id":31,"validity_days":15}]`,
  147. service.SettingKeyAuthSourceDefaultEmailGrantOnSignup: "true",
  148. service.SettingKeyAuthSourceDefaultEmailGrantOnFirstBind: "false",
  149. service.SettingKeyForceEmailOnThirdPartySignup: "true",
  150. },
  151. }
  152. svc := service.NewSettingService(repo, &config.Config{Default: config.DefaultConfig{UserConcurrency: 5}})
  153. handler := NewSettingHandler(svc, nil, nil, nil, nil, nil, nil)
  154. body := map[string]any{
  155. "registration_enabled": true,
  156. "promo_code_enabled": true,
  157. "auth_source_default_email_balance": 12.75,
  158. }
  159. rawBody, err := json.Marshal(body)
  160. require.NoError(t, err)
  161. rec := httptest.NewRecorder()
  162. c, _ := gin.CreateTestContext(rec)
  163. c.Request = httptest.NewRequest(http.MethodPut, "/api/v1/admin/settings", bytes.NewReader(rawBody))
  164. c.Request.Header.Set("Content-Type", "application/json")
  165. handler.UpdateSettings(c)
  166. require.Equal(t, http.StatusOK, rec.Code)
  167. require.Equal(t, "12.75000000", repo.values[service.SettingKeyAuthSourceDefaultEmailBalance])
  168. require.Equal(t, "8", repo.values[service.SettingKeyAuthSourceDefaultEmailConcurrency])
  169. require.Equal(t, `[{"group_id":31,"validity_days":15}]`, repo.values[service.SettingKeyAuthSourceDefaultEmailSubscriptions])
  170. require.Equal(t, "true", repo.values[service.SettingKeyForceEmailOnThirdPartySignup])
  171. var resp response.Response
  172. require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
  173. data, ok := resp.Data.(map[string]any)
  174. require.True(t, ok)
  175. require.Equal(t, 12.75, data["auth_source_default_email_balance"])
  176. require.Equal(t, float64(8), data["auth_source_default_email_concurrency"])
  177. require.Equal(t, true, data["force_email_on_third_party_signup"])
  178. }
  179. func TestSettingHandler_UpdateSettings_PersistsPaymentVisibleMethodsAndAdvancedScheduler(t *testing.T) {
  180. gin.SetMode(gin.TestMode)
  181. repo := &settingHandlerRepoStub{
  182. values: map[string]string{
  183. service.SettingKeyPromoCodeEnabled: "true",
  184. },
  185. }
  186. svc := service.NewSettingService(repo, &config.Config{Default: config.DefaultConfig{UserConcurrency: 5}})
  187. handler := NewSettingHandler(svc, nil, nil, nil, nil, nil, nil)
  188. body := map[string]any{
  189. "promo_code_enabled": true,
  190. "payment_visible_method_alipay_source": "easypay",
  191. "payment_visible_method_wxpay_source": "wxpay",
  192. "payment_visible_method_alipay_enabled": true,
  193. "payment_visible_method_wxpay_enabled": false,
  194. "openai_advanced_scheduler_enabled": true,
  195. "openai_oauth_scheduling_rate_multiplier": 0.05,
  196. "openai_advanced_scheduler_subscription_priority_enabled": true,
  197. }
  198. rawBody, err := json.Marshal(body)
  199. require.NoError(t, err)
  200. rec := httptest.NewRecorder()
  201. c, _ := gin.CreateTestContext(rec)
  202. c.Request = httptest.NewRequest(http.MethodPut, "/api/v1/admin/settings", bytes.NewReader(rawBody))
  203. c.Request.Header.Set("Content-Type", "application/json")
  204. handler.UpdateSettings(c)
  205. require.Equal(t, http.StatusOK, rec.Code)
  206. require.Equal(t, service.VisibleMethodSourceEasyPayAlipay, repo.values[service.SettingPaymentVisibleMethodAlipaySource])
  207. require.Equal(t, service.VisibleMethodSourceOfficialWechat, repo.values[service.SettingPaymentVisibleMethodWxpaySource])
  208. require.Equal(t, "true", repo.values[service.SettingPaymentVisibleMethodAlipayEnabled])
  209. require.Equal(t, "false", repo.values[service.SettingPaymentVisibleMethodWxpayEnabled])
  210. require.Equal(t, "true", repo.values["openai_advanced_scheduler_enabled"])
  211. require.Equal(t, "0.05", repo.values[service.SettingKeyOpenAIOAuthSchedulingRateMultiplier])
  212. require.Equal(t, "true", repo.values[service.SettingKeyOpenAIAdvancedSchedulerSubscriptionPriorityEnabled])
  213. var resp response.Response
  214. require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
  215. data, ok := resp.Data.(map[string]any)
  216. require.True(t, ok)
  217. require.Equal(t, service.VisibleMethodSourceEasyPayAlipay, data["payment_visible_method_alipay_source"])
  218. require.Equal(t, service.VisibleMethodSourceOfficialWechat, data["payment_visible_method_wxpay_source"])
  219. require.Equal(t, true, data["payment_visible_method_alipay_enabled"])
  220. require.Equal(t, false, data["payment_visible_method_wxpay_enabled"])
  221. require.Equal(t, true, data["openai_advanced_scheduler_enabled"])
  222. require.Equal(t, 0.05, data["openai_oauth_scheduling_rate_multiplier"])
  223. require.Equal(t, true, data["openai_advanced_scheduler_subscription_priority_enabled"])
  224. }
  225. func TestSettingHandler_UpdateSettings_PreservesLegacyBlankPaymentVisibleMethodSource(t *testing.T) {
  226. gin.SetMode(gin.TestMode)
  227. repo := &settingHandlerRepoStub{
  228. values: map[string]string{
  229. service.SettingKeyPromoCodeEnabled: "true",
  230. service.SettingPaymentVisibleMethodAlipayEnabled: "true",
  231. service.SettingPaymentVisibleMethodAlipaySource: "",
  232. service.SettingPaymentVisibleMethodWxpayEnabled: "false",
  233. service.SettingPaymentVisibleMethodWxpaySource: "",
  234. },
  235. }
  236. svc := service.NewSettingService(repo, &config.Config{Default: config.DefaultConfig{UserConcurrency: 5}})
  237. handler := NewSettingHandler(svc, nil, nil, nil, nil, nil, nil)
  238. body := map[string]any{
  239. "promo_code_enabled": false,
  240. }
  241. rawBody, err := json.Marshal(body)
  242. require.NoError(t, err)
  243. rec := httptest.NewRecorder()
  244. c, _ := gin.CreateTestContext(rec)
  245. c.Request = httptest.NewRequest(http.MethodPut, "/api/v1/admin/settings", bytes.NewReader(rawBody))
  246. c.Request.Header.Set("Content-Type", "application/json")
  247. handler.UpdateSettings(c)
  248. require.Equal(t, http.StatusOK, rec.Code)
  249. require.Equal(t, "", repo.values[service.SettingPaymentVisibleMethodAlipaySource])
  250. require.Equal(t, "true", repo.values[service.SettingPaymentVisibleMethodAlipayEnabled])
  251. }
  252. func TestSettingHandler_UpdateSettings_PersistsExplicitFalseOIDCCompatibilityFlags(t *testing.T) {
  253. gin.SetMode(gin.TestMode)
  254. repo := &settingHandlerRepoStub{
  255. values: map[string]string{
  256. service.SettingKeyPromoCodeEnabled: "true",
  257. service.SettingKeyOIDCConnectEnabled: "true",
  258. service.SettingKeyOIDCConnectProviderName: "OIDC",
  259. service.SettingKeyOIDCConnectClientID: "oidc-client",
  260. service.SettingKeyOIDCConnectClientSecret: "oidc-secret",
  261. service.SettingKeyOIDCConnectIssuerURL: "https://issuer.example.com",
  262. service.SettingKeyOIDCConnectAuthorizeURL: "https://issuer.example.com/auth",
  263. service.SettingKeyOIDCConnectTokenURL: "https://issuer.example.com/token",
  264. service.SettingKeyOIDCConnectUserInfoURL: "https://issuer.example.com/userinfo",
  265. service.SettingKeyOIDCConnectJWKSURL: "https://issuer.example.com/jwks",
  266. service.SettingKeyOIDCConnectScopes: "openid email profile",
  267. service.SettingKeyOIDCConnectRedirectURL: "https://example.com/api/v1/auth/oauth/oidc/callback",
  268. service.SettingKeyOIDCConnectFrontendRedirectURL: "/auth/oidc/callback",
  269. service.SettingKeyOIDCConnectTokenAuthMethod: "client_secret_post",
  270. service.SettingKeyOIDCConnectUsePKCE: "true",
  271. service.SettingKeyOIDCConnectValidateIDToken: "true",
  272. service.SettingKeyOIDCConnectAllowedSigningAlgs: "RS256",
  273. service.SettingKeyOIDCConnectClockSkewSeconds: "120",
  274. },
  275. }
  276. svc := service.NewSettingService(repo, &config.Config{Default: config.DefaultConfig{UserConcurrency: 5}})
  277. handler := NewSettingHandler(svc, nil, nil, nil, nil, nil, nil)
  278. body := map[string]any{
  279. "promo_code_enabled": true,
  280. "oidc_connect_enabled": true,
  281. "oidc_connect_use_pkce": false,
  282. "oidc_connect_validate_id_token": false,
  283. "oidc_connect_allowed_signing_algs": "",
  284. }
  285. rawBody, err := json.Marshal(body)
  286. require.NoError(t, err)
  287. rec := httptest.NewRecorder()
  288. c, _ := gin.CreateTestContext(rec)
  289. c.Request = httptest.NewRequest(http.MethodPut, "/api/v1/admin/settings", bytes.NewReader(rawBody))
  290. c.Request.Header.Set("Content-Type", "application/json")
  291. handler.UpdateSettings(c)
  292. require.Equal(t, http.StatusOK, rec.Code)
  293. require.Equal(t, "false", repo.values[service.SettingKeyOIDCConnectUsePKCE])
  294. require.Equal(t, "false", repo.values[service.SettingKeyOIDCConnectValidateIDToken])
  295. var resp response.Response
  296. require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
  297. data, ok := resp.Data.(map[string]any)
  298. require.True(t, ok)
  299. require.Equal(t, false, data["oidc_connect_use_pkce"])
  300. require.Equal(t, false, data["oidc_connect_validate_id_token"])
  301. }
  302. func TestSettingHandler_UpdateSettings_DoesNotSolidifyImplicitOIDCSecurityDefaultsOnLegacyUpgrade(t *testing.T) {
  303. gin.SetMode(gin.TestMode)
  304. repo := &settingHandlerRepoStub{
  305. values: map[string]string{
  306. service.SettingKeyPromoCodeEnabled: "true",
  307. service.SettingKeyOIDCConnectEnabled: "true",
  308. service.SettingKeyOIDCConnectProviderName: "OIDC",
  309. service.SettingKeyOIDCConnectClientID: "oidc-client",
  310. service.SettingKeyOIDCConnectClientSecret: "oidc-secret",
  311. service.SettingKeyOIDCConnectIssuerURL: "https://issuer.example.com",
  312. service.SettingKeyOIDCConnectAuthorizeURL: "https://issuer.example.com/auth",
  313. service.SettingKeyOIDCConnectTokenURL: "https://issuer.example.com/token",
  314. service.SettingKeyOIDCConnectUserInfoURL: "https://issuer.example.com/userinfo",
  315. service.SettingKeyOIDCConnectJWKSURL: "https://issuer.example.com/jwks",
  316. service.SettingKeyOIDCConnectScopes: "openid email profile",
  317. service.SettingKeyOIDCConnectRedirectURL: "https://example.com/api/v1/auth/oauth/oidc/callback",
  318. service.SettingKeyOIDCConnectFrontendRedirectURL: "/auth/oidc/callback",
  319. service.SettingKeyOIDCConnectTokenAuthMethod: "client_secret_post",
  320. service.SettingKeyOIDCConnectAllowedSigningAlgs: "RS256",
  321. service.SettingKeyOIDCConnectClockSkewSeconds: "120",
  322. service.SettingKeyOIDCConnectRequireEmailVerified: "false",
  323. service.SettingKeyOIDCConnectUserInfoEmailPath: "",
  324. service.SettingKeyOIDCConnectUserInfoIDPath: "",
  325. service.SettingKeyOIDCConnectUserInfoUsernamePath: "",
  326. },
  327. }
  328. svc := service.NewSettingService(repo, &config.Config{
  329. Default: config.DefaultConfig{UserConcurrency: 5},
  330. OIDC: config.OIDCConnectConfig{
  331. Enabled: true,
  332. ProviderName: "OIDC",
  333. ClientID: "oidc-client",
  334. ClientSecret: "oidc-secret",
  335. IssuerURL: "https://issuer.example.com",
  336. AuthorizeURL: "https://issuer.example.com/auth",
  337. TokenURL: "https://issuer.example.com/token",
  338. UserInfoURL: "https://issuer.example.com/userinfo",
  339. JWKSURL: "https://issuer.example.com/jwks",
  340. Scopes: "openid email profile",
  341. RedirectURL: "https://example.com/api/v1/auth/oauth/oidc/callback",
  342. FrontendRedirectURL: "/auth/oidc/callback",
  343. TokenAuthMethod: "client_secret_post",
  344. UsePKCE: true,
  345. ValidateIDToken: true,
  346. AllowedSigningAlgs: "RS256",
  347. ClockSkewSeconds: 120,
  348. },
  349. })
  350. handler := NewSettingHandler(svc, nil, nil, nil, nil, nil, nil)
  351. body := map[string]any{
  352. "promo_code_enabled": true,
  353. "oidc_connect_enabled": true,
  354. }
  355. rawBody, err := json.Marshal(body)
  356. require.NoError(t, err)
  357. rec := httptest.NewRecorder()
  358. c, _ := gin.CreateTestContext(rec)
  359. c.Request = httptest.NewRequest(http.MethodPut, "/api/v1/admin/settings", bytes.NewReader(rawBody))
  360. c.Request.Header.Set("Content-Type", "application/json")
  361. handler.UpdateSettings(c)
  362. require.Equal(t, http.StatusOK, rec.Code)
  363. require.Equal(t, "false", repo.values[service.SettingKeyOIDCConnectUsePKCE])
  364. require.Equal(t, "false", repo.values[service.SettingKeyOIDCConnectValidateIDToken])
  365. }
  366. func TestSettingHandler_UpdateSettings_RejectsInvalidPaymentVisibleMethodSource(t *testing.T) {
  367. gin.SetMode(gin.TestMode)
  368. repo := &settingHandlerRepoStub{
  369. values: map[string]string{
  370. service.SettingKeyPromoCodeEnabled: "true",
  371. },
  372. }
  373. svc := service.NewSettingService(repo, &config.Config{Default: config.DefaultConfig{UserConcurrency: 5}})
  374. handler := NewSettingHandler(svc, nil, nil, nil, nil, nil, nil)
  375. body := map[string]any{
  376. "promo_code_enabled": true,
  377. "payment_visible_method_alipay_source": "bogus",
  378. }
  379. rawBody, err := json.Marshal(body)
  380. require.NoError(t, err)
  381. rec := httptest.NewRecorder()
  382. c, _ := gin.CreateTestContext(rec)
  383. c.Request = httptest.NewRequest(http.MethodPut, "/api/v1/admin/settings", bytes.NewReader(rawBody))
  384. c.Request.Header.Set("Content-Type", "application/json")
  385. handler.UpdateSettings(c)
  386. require.Equal(t, http.StatusBadRequest, rec.Code)
  387. require.NotContains(t, repo.values, service.SettingPaymentVisibleMethodAlipaySource)
  388. }
  389. func TestSettingHandler_UpdateSettings_DoesNotPersistPartialSystemSettingsWhenAuthSourceDefaultsFail(t *testing.T) {
  390. gin.SetMode(gin.TestMode)
  391. repo := &failingAuthSourceSettingsRepoStub{
  392. values: map[string]string{
  393. service.SettingKeyRegistrationEnabled: "false",
  394. service.SettingKeyPromoCodeEnabled: "true",
  395. service.SettingKeyAuthSourceDefaultEmailBalance: "9.5",
  396. service.SettingKeyAuthSourceDefaultEmailConcurrency: "8",
  397. service.SettingKeyAuthSourceDefaultEmailSubscriptions: `[{"group_id":31,"validity_days":15}]`,
  398. },
  399. err: errors.New("write auth source defaults failed"),
  400. }
  401. svc := service.NewSettingService(repo, &config.Config{Default: config.DefaultConfig{UserConcurrency: 5}})
  402. handler := NewSettingHandler(svc, nil, nil, nil, nil, nil, nil)
  403. body := map[string]any{
  404. "registration_enabled": true,
  405. "promo_code_enabled": true,
  406. "auth_source_default_email_balance": 12.75,
  407. }
  408. rawBody, err := json.Marshal(body)
  409. require.NoError(t, err)
  410. rec := httptest.NewRecorder()
  411. c, _ := gin.CreateTestContext(rec)
  412. c.Request = httptest.NewRequest(http.MethodPut, "/api/v1/admin/settings", bytes.NewReader(rawBody))
  413. c.Request.Header.Set("Content-Type", "application/json")
  414. handler.UpdateSettings(c)
  415. require.Equal(t, http.StatusInternalServerError, rec.Code)
  416. require.Equal(t, "false", repo.values[service.SettingKeyRegistrationEnabled])
  417. require.Equal(t, "9.5", repo.values[service.SettingKeyAuthSourceDefaultEmailBalance])
  418. }
  419. func TestDiffSettings_IncludesAuthSourceDefaultsAndForceEmail(t *testing.T) {
  420. changed := diffSettings(
  421. &service.SystemSettings{},
  422. &service.SystemSettings{},
  423. &service.AuthSourceDefaultSettings{
  424. Email: service.ProviderDefaultGrantSettings{
  425. Balance: 0,
  426. Concurrency: 5,
  427. Subscriptions: nil,
  428. GrantOnSignup: true,
  429. GrantOnFirstBind: false,
  430. },
  431. ForceEmailOnThirdPartySignup: false,
  432. },
  433. &service.AuthSourceDefaultSettings{
  434. Email: service.ProviderDefaultGrantSettings{
  435. Balance: 12.5,
  436. Concurrency: 7,
  437. Subscriptions: []service.DefaultSubscriptionSetting{{GroupID: 21, ValidityDays: 30}},
  438. GrantOnSignup: false,
  439. GrantOnFirstBind: true,
  440. },
  441. ForceEmailOnThirdPartySignup: true,
  442. },
  443. UpdateSettingsRequest{},
  444. )
  445. require.Contains(t, changed, "auth_source_default_email_balance")
  446. require.Contains(t, changed, "auth_source_default_email_concurrency")
  447. require.Contains(t, changed, "auth_source_default_email_subscriptions")
  448. require.Contains(t, changed, "auth_source_default_email_grant_on_signup")
  449. require.Contains(t, changed, "auth_source_default_email_grant_on_first_bind")
  450. require.Contains(t, changed, "force_email_on_third_party_signup")
  451. }