setting_panel_rate_limit_test.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. package service
  2. import (
  3. "context"
  4. "sync"
  5. "testing"
  6. "github.com/Wei-Shaw/sub2api/internal/config"
  7. "github.com/stretchr/testify/require"
  8. )
  9. type panelRateLimitSettingRepo struct {
  10. mu sync.Mutex
  11. values map[string]string
  12. getValueErr error
  13. getValueCalls int
  14. }
  15. func (r *panelRateLimitSettingRepo) Get(_ context.Context, key string) (*Setting, error) {
  16. r.mu.Lock()
  17. defer r.mu.Unlock()
  18. value, ok := r.values[key]
  19. if !ok {
  20. return nil, ErrSettingNotFound
  21. }
  22. return &Setting{Key: key, Value: value}, nil
  23. }
  24. func (r *panelRateLimitSettingRepo) GetValue(_ context.Context, key string) (string, error) {
  25. r.mu.Lock()
  26. defer r.mu.Unlock()
  27. r.getValueCalls++
  28. if r.getValueErr != nil {
  29. return "", r.getValueErr
  30. }
  31. value, ok := r.values[key]
  32. if !ok {
  33. return "", ErrSettingNotFound
  34. }
  35. return value, nil
  36. }
  37. func (r *panelRateLimitSettingRepo) Set(_ context.Context, key, value string) error {
  38. r.mu.Lock()
  39. defer r.mu.Unlock()
  40. if r.values == nil {
  41. r.values = make(map[string]string)
  42. }
  43. r.values[key] = value
  44. return nil
  45. }
  46. func (r *panelRateLimitSettingRepo) GetMultiple(_ context.Context, keys []string) (map[string]string, error) {
  47. r.mu.Lock()
  48. defer r.mu.Unlock()
  49. out := make(map[string]string, len(keys))
  50. for _, key := range keys {
  51. if value, ok := r.values[key]; ok {
  52. out[key] = value
  53. }
  54. }
  55. return out, nil
  56. }
  57. func (r *panelRateLimitSettingRepo) SetMultiple(_ context.Context, settings map[string]string) error {
  58. r.mu.Lock()
  59. defer r.mu.Unlock()
  60. if r.values == nil {
  61. r.values = make(map[string]string)
  62. }
  63. for key, value := range settings {
  64. r.values[key] = value
  65. }
  66. return nil
  67. }
  68. func (r *panelRateLimitSettingRepo) GetAll(_ context.Context) (map[string]string, error) {
  69. r.mu.Lock()
  70. defer r.mu.Unlock()
  71. out := make(map[string]string, len(r.values))
  72. for key, value := range r.values {
  73. out[key] = value
  74. }
  75. return out, nil
  76. }
  77. func (r *panelRateLimitSettingRepo) Delete(_ context.Context, key string) error {
  78. r.mu.Lock()
  79. defer r.mu.Unlock()
  80. delete(r.values, key)
  81. return nil
  82. }
  83. func newPanelRateLimitTestService(repo SettingRepository) *SettingService {
  84. return NewSettingService(repo, &config.Config{})
  85. }
  86. func TestGetPanelRateLimitSettingsDefaults(t *testing.T) {
  87. svc := newPanelRateLimitTestService(&panelRateLimitSettingRepo{})
  88. settings, err := svc.GetPanelRateLimitSettings(context.Background())
  89. require.NoError(t, err)
  90. require.Equal(t, DefaultPanelRateLimitSettings(), settings)
  91. }
  92. func TestGetPanelRateLimitSettingsInvalidJSONFallsBack(t *testing.T) {
  93. repo := &panelRateLimitSettingRepo{values: map[string]string{
  94. SettingKeyPanelRateLimitSettings: "{not-json",
  95. }}
  96. svc := newPanelRateLimitTestService(repo)
  97. settings, err := svc.GetPanelRateLimitSettings(context.Background())
  98. require.NoError(t, err)
  99. require.Equal(t, DefaultPanelRateLimitSettings(), settings)
  100. }
  101. func TestGetPanelRateLimitSettingsNormalizesValues(t *testing.T) {
  102. repo := &panelRateLimitSettingRepo{values: map[string]string{
  103. SettingKeyPanelRateLimitSettings: `{"enabled":true,"user_rpm":-5,"heavy_rpm":999999999,"exempt_admin":false,"public_ip_rpm":10}`,
  104. }}
  105. svc := newPanelRateLimitTestService(repo)
  106. settings, err := svc.GetPanelRateLimitSettings(context.Background())
  107. require.NoError(t, err)
  108. require.True(t, settings.Enabled)
  109. require.Equal(t, 0, settings.UserRPM)
  110. require.Equal(t, panelRateLimitRPMMax, settings.HeavyRPM)
  111. require.Equal(t, 10, settings.PublicIPRPM)
  112. require.False(t, settings.ExemptAdmin)
  113. }
  114. func TestSetPanelRateLimitSettingsValidation(t *testing.T) {
  115. svc := newPanelRateLimitTestService(&panelRateLimitSettingRepo{})
  116. require.Error(t, svc.SetPanelRateLimitSettings(context.Background(), nil))
  117. require.Error(t, svc.SetPanelRateLimitSettings(context.Background(), &PanelRateLimitSettings{UserRPM: -1}))
  118. require.Error(t, svc.SetPanelRateLimitSettings(context.Background(), &PanelRateLimitSettings{HeavyRPM: panelRateLimitRPMMax + 1}))
  119. }
  120. func TestSetPanelRateLimitSettingsRoundTripAndCacheRefresh(t *testing.T) {
  121. repo := &panelRateLimitSettingRepo{}
  122. svc := newPanelRateLimitTestService(repo)
  123. // 先填充缓存(默认值)
  124. cached := svc.GetPanelRateLimitSettingsCached(context.Background())
  125. require.Equal(t, *DefaultPanelRateLimitSettings(), cached)
  126. want := &PanelRateLimitSettings{
  127. Enabled: true,
  128. UserRPM: 120,
  129. HeavyRPM: 30,
  130. ExemptAdmin: false,
  131. PublicIPRPM: 60,
  132. }
  133. require.NoError(t, svc.SetPanelRateLimitSettings(context.Background(), want))
  134. // 写入后无需等待 TTL,缓存立即反映新值
  135. cached = svc.GetPanelRateLimitSettingsCached(context.Background())
  136. require.Equal(t, *want, cached)
  137. // DB 中持久化的值可直读
  138. stored, err := svc.GetPanelRateLimitSettings(context.Background())
  139. require.NoError(t, err)
  140. require.Equal(t, want, stored)
  141. }
  142. func TestGetPanelRateLimitSettingsCachedAvoidsRepeatedDBReads(t *testing.T) {
  143. repo := &panelRateLimitSettingRepo{values: map[string]string{
  144. SettingKeyPanelRateLimitSettings: `{"enabled":true,"user_rpm":100,"heavy_rpm":20,"exempt_admin":true,"public_ip_rpm":50}`,
  145. }}
  146. svc := newPanelRateLimitTestService(repo)
  147. for i := 0; i < 5; i++ {
  148. settings := svc.GetPanelRateLimitSettingsCached(context.Background())
  149. require.Equal(t, 100, settings.UserRPM)
  150. }
  151. repo.mu.Lock()
  152. calls := repo.getValueCalls
  153. repo.mu.Unlock()
  154. require.Equal(t, 1, calls, "TTL 内应只读一次 DB")
  155. }