user_platform_quotas_handler_test.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. //go:build unit
  2. package admin
  3. import (
  4. "context"
  5. "encoding/json"
  6. "net/http"
  7. "net/http/httptest"
  8. "strings"
  9. "testing"
  10. "time"
  11. infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
  12. "github.com/Wei-Shaw/sub2api/internal/service"
  13. "github.com/gin-gonic/gin"
  14. )
  15. type fakeQuotaRepoForAdmin struct {
  16. service.UserPlatformQuotaRepository
  17. records []service.UserPlatformQuotaRecord
  18. err error
  19. }
  20. func (f *fakeQuotaRepoForAdmin) ListByUser(_ context.Context, _ int64) ([]service.UserPlatformQuotaRecord, error) {
  21. return f.records, f.err
  22. }
  23. func newAdminQuotaTestContext(w *httptest.ResponseRecorder) *gin.Context {
  24. c, _ := gin.CreateTestContext(w)
  25. req, _ := http.NewRequest(http.MethodGet, "/", nil)
  26. c.Request = req
  27. return c
  28. }
  29. func TestAdminGetUserPlatformQuotas_IncludesWindowStart(t *testing.T) {
  30. start := time.Now().Add(-1 * time.Hour)
  31. repo := &fakeQuotaRepoForAdmin{records: []service.UserPlatformQuotaRecord{{
  32. UserID: 99, Platform: "anthropic",
  33. DailyUsageUSD: 1.0, DailyWindowStart: &start,
  34. }}}
  35. h := &UserHandler{userPlatformQuotaRepo: repo, adminService: newStubAdminService()}
  36. gin.SetMode(gin.TestMode)
  37. w := httptest.NewRecorder()
  38. c := newAdminQuotaTestContext(w)
  39. c.Params = []gin.Param{{Key: "id", Value: "99"}}
  40. h.GetUserPlatformQuotas(c)
  41. if w.Code != 200 {
  42. t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
  43. }
  44. if !strings.Contains(w.Body.String(), `"daily_window_start"`) {
  45. t.Errorf("admin response missing daily_window_start, got: %s", w.Body.String())
  46. }
  47. }
  48. func TestAdminGetUserPlatformQuotas_InvalidIDReturns400(t *testing.T) {
  49. h := &UserHandler{userPlatformQuotaRepo: &fakeQuotaRepoForAdmin{}}
  50. gin.SetMode(gin.TestMode)
  51. w := httptest.NewRecorder()
  52. c := newAdminQuotaTestContext(w)
  53. c.Params = []gin.Param{{Key: "id", Value: "abc"}}
  54. h.GetUserPlatformQuotas(c)
  55. if w.Code < 400 || w.Code >= 500 {
  56. t.Errorf("invalid id should yield 4xx, got %d", w.Code)
  57. }
  58. }
  59. func TestAdminGetUserPlatformQuotas_EmptyReturnsEmptyArray(t *testing.T) {
  60. repo := &fakeQuotaRepoForAdmin{records: nil}
  61. h := &UserHandler{userPlatformQuotaRepo: repo, adminService: newStubAdminService()}
  62. gin.SetMode(gin.TestMode)
  63. w := httptest.NewRecorder()
  64. c := newAdminQuotaTestContext(w)
  65. c.Params = []gin.Param{{Key: "id", Value: "99"}}
  66. h.GetUserPlatformQuotas(c)
  67. if w.Code != 200 {
  68. t.Errorf("empty list should be 200, got %d", w.Code)
  69. }
  70. var body map[string]any
  71. if err := json.Unmarshal(w.Body.Bytes(), &body); err != nil {
  72. t.Fatalf("response is not valid JSON: %v", err)
  73. }
  74. data, ok := body["data"].(map[string]any)
  75. if !ok {
  76. t.Fatalf("response missing data object: %v", body)
  77. }
  78. quotas, ok := data["platform_quotas"].([]any)
  79. if !ok {
  80. t.Fatalf("data.platform_quotas missing or wrong type: %v", data)
  81. }
  82. if len(quotas) != 0 {
  83. t.Errorf("expected empty platform_quotas, got %d entries: %v", len(quotas), quotas)
  84. }
  85. }
  86. func TestAdminGetUserPlatformQuotas_NilRepoReturnsEmpty(t *testing.T) {
  87. h := &UserHandler{userPlatformQuotaRepo: nil}
  88. gin.SetMode(gin.TestMode)
  89. w := httptest.NewRecorder()
  90. c := newAdminQuotaTestContext(w)
  91. c.Params = []gin.Param{{Key: "id", Value: "1"}}
  92. h.GetUserPlatformQuotas(c)
  93. if w.Code != 200 {
  94. t.Errorf("nil repo should return 200 empty, got %d", w.Code)
  95. }
  96. }
  97. // TestAdminGetUserPlatformQuotas_UserNotFoundReturns404 验证 GET 在用户不存在时返回 404
  98. // (与 PUT / POST reset 端点行为一致;review fix:原实现返回空数组会让 admin 界面误判用户存在)
  99. func TestAdminGetUserPlatformQuotas_UserNotFoundReturns404(t *testing.T) {
  100. adminSvc := newStubAdminService()
  101. adminSvc.getUserErr = infraerrors.NotFound("USER_NOT_FOUND", "user not found")
  102. repo := &fakeQuotaRepoForAdmin{records: nil}
  103. h := &UserHandler{userPlatformQuotaRepo: repo, adminService: adminSvc}
  104. gin.SetMode(gin.TestMode)
  105. w := httptest.NewRecorder()
  106. c := newAdminQuotaTestContext(w)
  107. c.Params = []gin.Param{{Key: "id", Value: "999"}}
  108. h.GetUserPlatformQuotas(c)
  109. if w.Code != http.StatusNotFound {
  110. t.Errorf("expected 404 for non-existent user, got %d: %s", w.Code, w.Body.String())
  111. }
  112. }