ops_runtime_logging_handler_test.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. package admin
  2. import (
  3. "bytes"
  4. "context"
  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/logger"
  11. "github.com/Wei-Shaw/sub2api/internal/server/middleware"
  12. "github.com/Wei-Shaw/sub2api/internal/service"
  13. "github.com/gin-gonic/gin"
  14. )
  15. type testSettingRepo struct {
  16. values map[string]string
  17. }
  18. func newTestSettingRepo() *testSettingRepo {
  19. return &testSettingRepo{values: map[string]string{}}
  20. }
  21. func (s *testSettingRepo) Get(ctx context.Context, key string) (*service.Setting, error) {
  22. v, err := s.GetValue(ctx, key)
  23. if err != nil {
  24. return nil, err
  25. }
  26. return &service.Setting{Key: key, Value: v}, nil
  27. }
  28. func (s *testSettingRepo) GetValue(ctx context.Context, key string) (string, error) {
  29. v, ok := s.values[key]
  30. if !ok {
  31. return "", service.ErrSettingNotFound
  32. }
  33. return v, nil
  34. }
  35. func (s *testSettingRepo) Set(ctx context.Context, key, value string) error {
  36. s.values[key] = value
  37. return nil
  38. }
  39. func (s *testSettingRepo) GetMultiple(ctx context.Context, keys []string) (map[string]string, error) {
  40. out := make(map[string]string, len(keys))
  41. for _, k := range keys {
  42. if v, ok := s.values[k]; ok {
  43. out[k] = v
  44. }
  45. }
  46. return out, nil
  47. }
  48. func (s *testSettingRepo) SetMultiple(ctx context.Context, settings map[string]string) error {
  49. for k, v := range settings {
  50. s.values[k] = v
  51. }
  52. return nil
  53. }
  54. func (s *testSettingRepo) GetAll(ctx context.Context) (map[string]string, error) {
  55. out := make(map[string]string, len(s.values))
  56. for k, v := range s.values {
  57. out[k] = v
  58. }
  59. return out, nil
  60. }
  61. func (s *testSettingRepo) Delete(ctx context.Context, key string) error {
  62. delete(s.values, key)
  63. return nil
  64. }
  65. func newOpsRuntimeRouter(handler *OpsHandler, withUser bool) *gin.Engine {
  66. gin.SetMode(gin.TestMode)
  67. r := gin.New()
  68. if withUser {
  69. r.Use(func(c *gin.Context) {
  70. c.Set(string(middleware.ContextKeyUser), middleware.AuthSubject{UserID: 7})
  71. c.Next()
  72. })
  73. }
  74. r.GET("/runtime/logging", handler.GetRuntimeLogConfig)
  75. r.PUT("/runtime/logging", handler.UpdateRuntimeLogConfig)
  76. r.POST("/runtime/logging/reset", handler.ResetRuntimeLogConfig)
  77. return r
  78. }
  79. func newRuntimeOpsService(t *testing.T) *service.OpsService {
  80. t.Helper()
  81. if err := logger.Init(logger.InitOptions{
  82. Level: "info",
  83. Format: "json",
  84. ServiceName: "sub2api",
  85. Environment: "test",
  86. Output: logger.OutputOptions{
  87. ToStdout: false,
  88. ToFile: false,
  89. },
  90. }); err != nil {
  91. t.Fatalf("init logger: %v", err)
  92. }
  93. settingRepo := newTestSettingRepo()
  94. cfg := &config.Config{
  95. Ops: config.OpsConfig{Enabled: true},
  96. Log: config.LogConfig{
  97. Level: "info",
  98. Caller: true,
  99. StacktraceLevel: "error",
  100. Sampling: config.LogSamplingConfig{
  101. Enabled: false,
  102. Initial: 100,
  103. Thereafter: 100,
  104. },
  105. },
  106. }
  107. return service.NewOpsService(nil, settingRepo, cfg, nil, nil, nil, nil, nil, nil, nil, nil)
  108. }
  109. func TestOpsRuntimeLoggingHandler_GetConfig(t *testing.T) {
  110. h := NewOpsHandler(newRuntimeOpsService(t))
  111. r := newOpsRuntimeRouter(h, false)
  112. w := httptest.NewRecorder()
  113. req := httptest.NewRequest(http.MethodGet, "/runtime/logging", nil)
  114. r.ServeHTTP(w, req)
  115. if w.Code != http.StatusOK {
  116. t.Fatalf("status=%d, want 200", w.Code)
  117. }
  118. }
  119. func TestOpsRuntimeLoggingHandler_UpdateUnauthorized(t *testing.T) {
  120. h := NewOpsHandler(newRuntimeOpsService(t))
  121. r := newOpsRuntimeRouter(h, false)
  122. body := `{"level":"debug","enable_sampling":false,"sampling_initial":100,"sampling_thereafter":100,"caller":true,"stacktrace_level":"error","retention_days":30}`
  123. w := httptest.NewRecorder()
  124. req := httptest.NewRequest(http.MethodPut, "/runtime/logging", bytes.NewBufferString(body))
  125. req.Header.Set("Content-Type", "application/json")
  126. r.ServeHTTP(w, req)
  127. if w.Code != http.StatusUnauthorized {
  128. t.Fatalf("status=%d, want 401", w.Code)
  129. }
  130. }
  131. func TestOpsRuntimeLoggingHandler_UpdateAndResetSuccess(t *testing.T) {
  132. h := NewOpsHandler(newRuntimeOpsService(t))
  133. r := newOpsRuntimeRouter(h, true)
  134. payload := map[string]any{
  135. "level": "debug",
  136. "enable_sampling": false,
  137. "sampling_initial": 100,
  138. "sampling_thereafter": 100,
  139. "caller": true,
  140. "stacktrace_level": "error",
  141. "retention_days": 30,
  142. }
  143. raw, _ := json.Marshal(payload)
  144. w := httptest.NewRecorder()
  145. req := httptest.NewRequest(http.MethodPut, "/runtime/logging", bytes.NewReader(raw))
  146. req.Header.Set("Content-Type", "application/json")
  147. r.ServeHTTP(w, req)
  148. if w.Code != http.StatusOK {
  149. t.Fatalf("update status=%d, want 200, body=%s", w.Code, w.Body.String())
  150. }
  151. w = httptest.NewRecorder()
  152. req = httptest.NewRequest(http.MethodPost, "/runtime/logging/reset", nil)
  153. r.ServeHTTP(w, req)
  154. if w.Code != http.StatusOK {
  155. t.Fatalf("reset status=%d, want 200, body=%s", w.Code, w.Body.String())
  156. }
  157. }