setting_handler_platform_quota_test.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. //go:build unit
  2. package admin
  3. import (
  4. "bytes"
  5. "encoding/json"
  6. "net/http"
  7. "net/http/httptest"
  8. "testing"
  9. "github.com/Wei-Shaw/sub2api/internal/config"
  10. "github.com/Wei-Shaw/sub2api/internal/pkg/response"
  11. "github.com/Wei-Shaw/sub2api/internal/service"
  12. "github.com/gin-gonic/gin"
  13. "github.com/stretchr/testify/require"
  14. )
  15. func TestDiffSettings_DetectsGlobalPlatformQuotaChange(t *testing.T) {
  16. five := 5.0
  17. ten := 10.0
  18. before := &service.SystemSettings{
  19. DefaultPlatformQuotas: map[string]*service.DefaultPlatformQuotaSetting{
  20. "anthropic": {DailyLimitUSD: &five},
  21. },
  22. }
  23. after := &service.SystemSettings{
  24. DefaultPlatformQuotas: map[string]*service.DefaultPlatformQuotaSetting{
  25. "anthropic": {DailyLimitUSD: &ten},
  26. },
  27. }
  28. changed := diffSettings(before, after, nil, nil, UpdateSettingsRequest{})
  29. found := false
  30. for _, key := range changed {
  31. if key == service.SettingKeyDefaultPlatformQuotas {
  32. found = true
  33. break
  34. }
  35. }
  36. if !found {
  37. t.Errorf("expected change detection for default platform quotas, got %v", changed)
  38. }
  39. }
  40. func TestDiffSettings_NoChangeWhenEqual(t *testing.T) {
  41. five := 5.0
  42. before := &service.SystemSettings{
  43. DefaultPlatformQuotas: map[string]*service.DefaultPlatformQuotaSetting{
  44. "anthropic": {DailyLimitUSD: &five},
  45. },
  46. }
  47. after := &service.SystemSettings{
  48. DefaultPlatformQuotas: map[string]*service.DefaultPlatformQuotaSetting{
  49. "anthropic": {DailyLimitUSD: &five},
  50. },
  51. }
  52. changed := diffSettings(before, after, nil, nil, UpdateSettingsRequest{})
  53. for _, key := range changed {
  54. if key == service.SettingKeyDefaultPlatformQuotas {
  55. t.Error("equal values should not be detected as changed")
  56. }
  57. }
  58. }
  59. func TestSettingsAuditRequestDoesNotInheritStoredTencentSecrets(t *testing.T) {
  60. req := UpdateSettingsRequest{
  61. TencentCaptchaAppSecretKey: " ",
  62. TencentCaptchaCloudSecretID: "\t",
  63. TencentCaptchaCloudSecretKey: "\n",
  64. }
  65. auditReq := settingsAuditRequest(req)
  66. req.TencentCaptchaAppSecretKey = "stored-app-secret"
  67. req.TencentCaptchaCloudSecretID = "stored-secret-id"
  68. req.TencentCaptchaCloudSecretKey = "stored-secret-key"
  69. require.Empty(t, auditReq.TencentCaptchaAppSecretKey)
  70. require.Empty(t, auditReq.TencentCaptchaCloudSecretID)
  71. require.Empty(t, auditReq.TencentCaptchaCloudSecretKey)
  72. }
  73. func TestDiffSettings_DetectsCompactHomeChange(t *testing.T) {
  74. changed := diffSettings(
  75. &service.SystemSettings{},
  76. &service.SystemSettings{CompactHomeEnabled: true},
  77. nil,
  78. nil,
  79. UpdateSettingsRequest{},
  80. )
  81. require.Contains(t, changed, service.SettingKeyCompactHomeEnabled)
  82. }
  83. func TestEqualNullableFloat(t *testing.T) {
  84. five := 5.0
  85. five2 := 5.0
  86. ten := 10.0
  87. cases := []struct {
  88. a, b *float64
  89. want bool
  90. }{
  91. {nil, nil, true},
  92. {&five, nil, false},
  93. {nil, &five, false},
  94. {&five, &five2, true},
  95. {&five, &ten, false},
  96. }
  97. for _, c := range cases {
  98. if got := equalNullableFloat(c.a, c.b); got != c.want {
  99. t.Errorf("equalNullableFloat(%v, %v) = %v, want %v", c.a, c.b, got, c.want)
  100. }
  101. }
  102. }
  103. func TestEqualPlatformQuotaSettings_DetectsPerWindowChange(t *testing.T) {
  104. five := 5.0
  105. ten := 10.0
  106. before := map[string]*service.DefaultPlatformQuotaSetting{
  107. "anthropic": {DailyLimitUSD: &five},
  108. }
  109. after := map[string]*service.DefaultPlatformQuotaSetting{
  110. "anthropic": {DailyLimitUSD: &ten},
  111. }
  112. if equalPlatformQuotaSettings(before, after) {
  113. t.Error("expected unequal")
  114. }
  115. }
  116. func TestAppendAuthSourceDefaultChanges_DetectsPerWindow(t *testing.T) {
  117. five := 5.0
  118. ten := 10.0
  119. before := &service.AuthSourceDefaultSettings{
  120. LinuxDo: service.ProviderDefaultGrantSettings{
  121. PlatformQuotas: map[string]*service.DefaultPlatformQuotaSetting{
  122. "anthropic": {DailyLimitUSD: &five},
  123. },
  124. },
  125. }
  126. after := &service.AuthSourceDefaultSettings{
  127. LinuxDo: service.ProviderDefaultGrantSettings{
  128. PlatformQuotas: map[string]*service.DefaultPlatformQuotaSetting{
  129. "anthropic": {DailyLimitUSD: &ten},
  130. },
  131. },
  132. }
  133. changed := appendAuthSourceDefaultChanges([]string{}, before, after)
  134. // 改动 B5:整体替换语义,审计 log 发单个 JSON key,而非展开 84 个扁平 key。
  135. key := service.SettingKeyAuthSourcePlatformQuotas("linuxdo")
  136. found := false
  137. for _, k := range changed {
  138. if k == key {
  139. found = true
  140. break
  141. }
  142. }
  143. if !found {
  144. t.Errorf("expected %q in changed, got %v", key, changed)
  145. }
  146. }
  147. // TestSettingHandler_AuthSourcePlatformQuotas_PutGetRoundTrip 验证 Bug A 修复:
  148. // PUT 发 auth_source_default_email_platform_quotas,GET 能读回相同值(端到端往返)。
  149. func TestSettingHandler_AuthSourcePlatformQuotas_PutGetRoundTrip(t *testing.T) {
  150. gin.SetMode(gin.TestMode)
  151. repo := &settingHandlerRepoStub{
  152. values: map[string]string{
  153. service.SettingKeyPromoCodeEnabled: "true",
  154. },
  155. }
  156. svc := service.NewSettingService(repo, &config.Config{Default: config.DefaultConfig{UserConcurrency: 5}})
  157. handler := NewSettingHandler(svc, nil, nil, nil, nil, nil, nil)
  158. // PUT:发 email platform quota(openai monthly=20)
  159. putBody := map[string]any{
  160. "auth_source_default_email_platform_quotas": map[string]any{
  161. "openai": map[string]any{
  162. "monthly": 20,
  163. },
  164. },
  165. }
  166. rawBody, err := json.Marshal(putBody)
  167. require.NoError(t, err)
  168. rec := httptest.NewRecorder()
  169. c, _ := gin.CreateTestContext(rec)
  170. c.Request = httptest.NewRequest(http.MethodPut, "/api/v1/admin/settings", bytes.NewReader(rawBody))
  171. c.Request.Header.Set("Content-Type", "application/json")
  172. handler.UpdateSettings(c)
  173. require.Equal(t, http.StatusOK, rec.Code)
  174. // 验证 DB 中写入了 JSON key
  175. jsonKey := service.SettingKeyAuthSourcePlatformQuotas("email")
  176. require.NotEmpty(t, repo.values[jsonKey], "expected JSON key to be written to DB")
  177. // GET:验证响应中 auth_source_default_email_platform_quotas.openai.monthly = 20
  178. rec2 := httptest.NewRecorder()
  179. c2, _ := gin.CreateTestContext(rec2)
  180. c2.Request = httptest.NewRequest(http.MethodGet, "/api/v1/admin/settings", nil)
  181. handler.GetSettings(c2)
  182. require.Equal(t, http.StatusOK, rec2.Code)
  183. var resp response.Response
  184. require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
  185. data, ok := resp.Data.(map[string]any)
  186. require.True(t, ok)
  187. emailPQ, ok := data["auth_source_default_email_platform_quotas"].(map[string]any)
  188. require.True(t, ok, "expected auth_source_default_email_platform_quotas to be a map")
  189. openaiPQ, ok := emailPQ["openai"].(map[string]any)
  190. require.True(t, ok, "expected openai entry in email platform quotas")
  191. monthly, ok := openaiPQ["monthly"].(float64)
  192. require.True(t, ok, "expected monthly to be float64")
  193. require.Equal(t, float64(20), monthly, "expected openai monthly=20")
  194. }