usage_cleanup_repo_ent_test.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. package repository
  2. import (
  3. "context"
  4. "database/sql"
  5. "encoding/json"
  6. "testing"
  7. "time"
  8. dbent "github.com/Wei-Shaw/sub2api/ent"
  9. "github.com/Wei-Shaw/sub2api/ent/enttest"
  10. dbusagecleanuptask "github.com/Wei-Shaw/sub2api/ent/usagecleanuptask"
  11. "github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
  12. "github.com/Wei-Shaw/sub2api/internal/service"
  13. "github.com/stretchr/testify/require"
  14. "entgo.io/ent/dialect"
  15. entsql "entgo.io/ent/dialect/sql"
  16. _ "modernc.org/sqlite"
  17. )
  18. func newUsageCleanupEntRepo(t *testing.T) (*usageCleanupRepository, *dbent.Client) {
  19. t.Helper()
  20. db, err := sql.Open("sqlite", "file:usage_cleanup?mode=memory&cache=shared")
  21. require.NoError(t, err)
  22. t.Cleanup(func() { _ = db.Close() })
  23. _, err = db.Exec("PRAGMA foreign_keys = ON")
  24. require.NoError(t, err)
  25. drv := entsql.OpenDB(dialect.SQLite, db)
  26. client := enttest.NewClient(t, enttest.WithOptions(dbent.Driver(drv)))
  27. t.Cleanup(func() { _ = client.Close() })
  28. repo := &usageCleanupRepository{client: client, sql: db}
  29. return repo, client
  30. }
  31. func TestUsageCleanupRepositoryEntCreateAndList(t *testing.T) {
  32. repo, _ := newUsageCleanupEntRepo(t)
  33. start := time.Date(2024, 1, 2, 0, 0, 0, 0, time.UTC)
  34. end := start.Add(24 * time.Hour)
  35. task := &service.UsageCleanupTask{
  36. Status: service.UsageCleanupStatusPending,
  37. Filters: service.UsageCleanupFilters{StartTime: start, EndTime: end},
  38. CreatedBy: 9,
  39. }
  40. require.NoError(t, repo.CreateTask(context.Background(), task))
  41. require.NotZero(t, task.ID)
  42. task2 := &service.UsageCleanupTask{
  43. Status: service.UsageCleanupStatusRunning,
  44. Filters: service.UsageCleanupFilters{StartTime: start.Add(-24 * time.Hour), EndTime: end.Add(-24 * time.Hour)},
  45. CreatedBy: 10,
  46. }
  47. require.NoError(t, repo.CreateTask(context.Background(), task2))
  48. tasks, result, err := repo.ListTasks(context.Background(), pagination.PaginationParams{Page: 1, PageSize: 10})
  49. require.NoError(t, err)
  50. require.Len(t, tasks, 2)
  51. require.Equal(t, int64(2), result.Total)
  52. require.Greater(t, tasks[0].ID, tasks[1].ID)
  53. require.Equal(t, start, tasks[1].Filters.StartTime)
  54. require.Equal(t, end, tasks[1].Filters.EndTime)
  55. }
  56. func TestUsageCleanupRepositoryEntListEmpty(t *testing.T) {
  57. repo, _ := newUsageCleanupEntRepo(t)
  58. tasks, result, err := repo.ListTasks(context.Background(), pagination.PaginationParams{Page: 1, PageSize: 10})
  59. require.NoError(t, err)
  60. require.Empty(t, tasks)
  61. require.Equal(t, int64(0), result.Total)
  62. }
  63. func TestUsageCleanupRepositoryEntGetStatusAndProgress(t *testing.T) {
  64. repo, client := newUsageCleanupEntRepo(t)
  65. task := &service.UsageCleanupTask{
  66. Status: service.UsageCleanupStatusPending,
  67. Filters: service.UsageCleanupFilters{StartTime: time.Now().UTC(), EndTime: time.Now().UTC().Add(time.Hour)},
  68. CreatedBy: 3,
  69. }
  70. require.NoError(t, repo.CreateTask(context.Background(), task))
  71. status, err := repo.GetTaskStatus(context.Background(), task.ID)
  72. require.NoError(t, err)
  73. require.Equal(t, service.UsageCleanupStatusPending, status)
  74. _, err = repo.GetTaskStatus(context.Background(), task.ID+99)
  75. require.ErrorIs(t, err, sql.ErrNoRows)
  76. require.NoError(t, repo.UpdateTaskProgress(context.Background(), task.ID, 42))
  77. loaded, err := client.UsageCleanupTask.Get(context.Background(), task.ID)
  78. require.NoError(t, err)
  79. require.Equal(t, int64(42), loaded.DeletedRows)
  80. }
  81. func TestUsageCleanupRepositoryEntCancelAndFinish(t *testing.T) {
  82. repo, client := newUsageCleanupEntRepo(t)
  83. task := &service.UsageCleanupTask{
  84. Status: service.UsageCleanupStatusPending,
  85. Filters: service.UsageCleanupFilters{StartTime: time.Now().UTC(), EndTime: time.Now().UTC().Add(time.Hour)},
  86. CreatedBy: 5,
  87. }
  88. require.NoError(t, repo.CreateTask(context.Background(), task))
  89. ok, err := repo.CancelTask(context.Background(), task.ID, 7)
  90. require.NoError(t, err)
  91. require.True(t, ok)
  92. loaded, err := client.UsageCleanupTask.Get(context.Background(), task.ID)
  93. require.NoError(t, err)
  94. require.Equal(t, service.UsageCleanupStatusCanceled, loaded.Status)
  95. require.NotNil(t, loaded.CanceledBy)
  96. require.NotNil(t, loaded.CanceledAt)
  97. require.NotNil(t, loaded.FinishedAt)
  98. loaded.Status = service.UsageCleanupStatusSucceeded
  99. _, err = client.UsageCleanupTask.Update().Where(dbusagecleanuptask.IDEQ(task.ID)).SetStatus(loaded.Status).Save(context.Background())
  100. require.NoError(t, err)
  101. ok, err = repo.CancelTask(context.Background(), task.ID, 7)
  102. require.NoError(t, err)
  103. require.False(t, ok)
  104. }
  105. func TestUsageCleanupRepositoryEntCancelError(t *testing.T) {
  106. repo, client := newUsageCleanupEntRepo(t)
  107. task := &service.UsageCleanupTask{
  108. Status: service.UsageCleanupStatusPending,
  109. Filters: service.UsageCleanupFilters{StartTime: time.Now().UTC(), EndTime: time.Now().UTC().Add(time.Hour)},
  110. CreatedBy: 5,
  111. }
  112. require.NoError(t, repo.CreateTask(context.Background(), task))
  113. require.NoError(t, client.Close())
  114. _, err := repo.CancelTask(context.Background(), task.ID, 7)
  115. require.Error(t, err)
  116. }
  117. func TestUsageCleanupRepositoryEntMarkResults(t *testing.T) {
  118. repo, client := newUsageCleanupEntRepo(t)
  119. task := &service.UsageCleanupTask{
  120. Status: service.UsageCleanupStatusRunning,
  121. Filters: service.UsageCleanupFilters{StartTime: time.Now().UTC(), EndTime: time.Now().UTC().Add(time.Hour)},
  122. CreatedBy: 12,
  123. }
  124. require.NoError(t, repo.CreateTask(context.Background(), task))
  125. require.NoError(t, repo.MarkTaskSucceeded(context.Background(), task.ID, 6))
  126. loaded, err := client.UsageCleanupTask.Get(context.Background(), task.ID)
  127. require.NoError(t, err)
  128. require.Equal(t, service.UsageCleanupStatusSucceeded, loaded.Status)
  129. require.Equal(t, int64(6), loaded.DeletedRows)
  130. require.NotNil(t, loaded.FinishedAt)
  131. task2 := &service.UsageCleanupTask{
  132. Status: service.UsageCleanupStatusRunning,
  133. Filters: service.UsageCleanupFilters{StartTime: time.Now().UTC(), EndTime: time.Now().UTC().Add(time.Hour)},
  134. CreatedBy: 12,
  135. }
  136. require.NoError(t, repo.CreateTask(context.Background(), task2))
  137. require.NoError(t, repo.MarkTaskFailed(context.Background(), task2.ID, 4, "boom"))
  138. loaded2, err := client.UsageCleanupTask.Get(context.Background(), task2.ID)
  139. require.NoError(t, err)
  140. require.Equal(t, service.UsageCleanupStatusFailed, loaded2.Status)
  141. require.Equal(t, "boom", *loaded2.ErrorMessage)
  142. }
  143. func TestUsageCleanupRepositoryEntInvalidStatus(t *testing.T) {
  144. repo, _ := newUsageCleanupEntRepo(t)
  145. task := &service.UsageCleanupTask{
  146. Status: "invalid",
  147. Filters: service.UsageCleanupFilters{StartTime: time.Now().UTC(), EndTime: time.Now().UTC().Add(time.Hour)},
  148. CreatedBy: 1,
  149. }
  150. require.Error(t, repo.CreateTask(context.Background(), task))
  151. }
  152. func TestUsageCleanupRepositoryEntListInvalidFilters(t *testing.T) {
  153. repo, client := newUsageCleanupEntRepo(t)
  154. now := time.Now().UTC()
  155. driver, ok := client.Driver().(*entsql.Driver)
  156. require.True(t, ok)
  157. _, err := driver.DB().ExecContext(
  158. context.Background(),
  159. `INSERT INTO usage_cleanup_tasks (status, filters, created_by, deleted_rows, created_at, updated_at)
  160. VALUES (?, ?, ?, ?, ?, ?)`,
  161. service.UsageCleanupStatusPending,
  162. []byte("invalid-json"),
  163. int64(1),
  164. int64(0),
  165. now,
  166. now,
  167. )
  168. require.NoError(t, err)
  169. _, _, err = repo.ListTasks(context.Background(), pagination.PaginationParams{Page: 1, PageSize: 10})
  170. require.Error(t, err)
  171. }
  172. func TestUsageCleanupTaskFromEntFull(t *testing.T) {
  173. start := time.Date(2024, 1, 2, 0, 0, 0, 0, time.UTC)
  174. end := start.Add(24 * time.Hour)
  175. errMsg := "failed"
  176. canceledBy := int64(2)
  177. canceledAt := start.Add(time.Minute)
  178. startedAt := start.Add(2 * time.Minute)
  179. finishedAt := start.Add(3 * time.Minute)
  180. filters := service.UsageCleanupFilters{StartTime: start, EndTime: end}
  181. filtersJSON, err := json.Marshal(filters)
  182. require.NoError(t, err)
  183. task, err := usageCleanupTaskFromEnt(&dbent.UsageCleanupTask{
  184. ID: 10,
  185. Status: service.UsageCleanupStatusFailed,
  186. Filters: filtersJSON,
  187. CreatedBy: 11,
  188. DeletedRows: 7,
  189. ErrorMessage: &errMsg,
  190. CanceledBy: &canceledBy,
  191. CanceledAt: &canceledAt,
  192. StartedAt: &startedAt,
  193. FinishedAt: &finishedAt,
  194. CreatedAt: start,
  195. UpdatedAt: end,
  196. })
  197. require.NoError(t, err)
  198. require.Equal(t, int64(10), task.ID)
  199. require.Equal(t, service.UsageCleanupStatusFailed, task.Status)
  200. require.NotNil(t, task.ErrorMsg)
  201. require.NotNil(t, task.CanceledBy)
  202. require.NotNil(t, task.CanceledAt)
  203. require.NotNil(t, task.StartedAt)
  204. require.NotNil(t, task.FinishedAt)
  205. }
  206. func TestUsageCleanupTaskFromEntInvalidFilters(t *testing.T) {
  207. task, err := usageCleanupTaskFromEnt(&dbent.UsageCleanupTask{
  208. Filters: json.RawMessage("invalid-json"),
  209. })
  210. require.Error(t, err)
  211. require.Empty(t, task)
  212. }