service_test.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. package service
  2. import (
  3. "testing"
  4. "time"
  5. "github.com/glebarez/sqlite"
  6. "github.com/stretchr/testify/require"
  7. "gorm.io/gorm"
  8. "wails-app/internal/dao"
  9. "wails-app/internal/global"
  10. )
  11. func setupMonthlyServiceTest(t *testing.T) *MonthlyCardService {
  12. t.Helper()
  13. db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
  14. require.NoError(t, err)
  15. require.NoError(t, db.AutoMigrate(&dao.Vehicle{}, &dao.MonthlyCard{}, &dao.Shortlist{}, &dao.PaymentRecord{}))
  16. global.GVA_DB = db
  17. t.Cleanup(func() { global.GVA_DB = nil })
  18. return NewMonthlyCardService()
  19. }
  20. func createVehicle(t *testing.T, plate string) *dao.Vehicle {
  21. t.Helper()
  22. vehicle := dao.Vehicle{PlateNumber: plate, VehicleTypeID: 1}
  23. require.NoError(t, global.GVA_DB.Create(&vehicle).Error)
  24. return &vehicle
  25. }
  26. func countTable(t *testing.T, model interface{}) int64 {
  27. t.Helper()
  28. var count int64
  29. require.NoError(t, global.GVA_DB.Model(model).Count(&count).Error)
  30. return count
  31. }
  32. func TestCreateWritesCardWhitelistAndPayment(t *testing.T) {
  33. svc := setupMonthlyServiceTest(t)
  34. vehicle := createVehicle(t, "MC-001")
  35. card, err := svc.Create(vehicle.ID, "month", 300, 7, "测试办理")
  36. require.NoError(t, err)
  37. require.NotZero(t, card.ID)
  38. require.Equal(t, "paid", card.PaymentStatus)
  39. require.True(t, card.EndDate.After(time.Now().Add(29*24*time.Hour)))
  40. // 三张表全部写入
  41. require.Equal(t, int64(1), countTable(t, &dao.MonthlyCard{}))
  42. require.Equal(t, int64(1), countTable(t, &dao.Shortlist{}))
  43. require.Equal(t, int64(1), countTable(t, &dao.PaymentRecord{}))
  44. // 白名单到期日与月卡一致
  45. var shortlist dao.Shortlist
  46. require.NoError(t, global.GVA_DB.First(&shortlist).Error)
  47. require.Equal(t, "白名单", shortlist.ListType)
  48. require.NotNil(t, shortlist.ExpirationTime)
  49. require.True(t, shortlist.ExpirationTime.Equal(card.EndDate))
  50. // 支付流水
  51. var pay dao.PaymentRecord
  52. require.NoError(t, global.GVA_DB.First(&pay).Error)
  53. require.Equal(t, 300.0, pay.Amount)
  54. require.Equal(t, uint(7), pay.OperatorID)
  55. }
  56. func TestCreateRollsBackWhenWhitelistWriteFails(t *testing.T) {
  57. svc := setupMonthlyServiceTest(t)
  58. vehicle := createVehicle(t, "MC-002")
  59. // 模拟白名单写入失败:删除 shortlist 表
  60. require.NoError(t, global.GVA_DB.Exec("DROP TABLE shortlist").Error)
  61. _, err := svc.Create(vehicle.ID, "month", 300, 7, "")
  62. require.Error(t, err)
  63. require.Contains(t, err.Error(), "白名单")
  64. // 月卡与流水均回滚
  65. require.Equal(t, int64(0), countTable(t, &dao.MonthlyCard{}))
  66. require.Equal(t, int64(0), countTable(t, &dao.PaymentRecord{}))
  67. }
  68. func TestCreateRollsBackWhenPaymentWriteFails(t *testing.T) {
  69. svc := setupMonthlyServiceTest(t)
  70. vehicle := createVehicle(t, "MC-003")
  71. // 模拟支付流水写入失败:删除 payment_record 表
  72. require.NoError(t, global.GVA_DB.Exec("DROP TABLE payment_record").Error)
  73. _, err := svc.Create(vehicle.ID, "month", 300, 7, "")
  74. require.Error(t, err)
  75. require.Contains(t, err.Error(), "支付流水")
  76. // 月卡与白名单均回滚
  77. require.Equal(t, int64(0), countTable(t, &dao.MonthlyCard{}))
  78. require.Equal(t, int64(0), countTable(t, &dao.Shortlist{}))
  79. }
  80. func TestCreateRejectsDuplicateActiveCard(t *testing.T) {
  81. svc := setupMonthlyServiceTest(t)
  82. vehicle := createVehicle(t, "MC-004")
  83. _, err := svc.Create(vehicle.ID, "month", 300, 7, "")
  84. require.NoError(t, err)
  85. _, err = svc.Create(vehicle.ID, "month", 300, 7, "")
  86. require.Error(t, err)
  87. require.Contains(t, err.Error(), "已有有效月卡")
  88. require.Equal(t, int64(1), countTable(t, &dao.MonthlyCard{}))
  89. }
  90. func TestCreateRejectsUnknownVehicle(t *testing.T) {
  91. svc := setupMonthlyServiceTest(t)
  92. _, err := svc.Create(99999, "month", 300, 7, "")
  93. require.Error(t, err)
  94. require.Contains(t, err.Error(), "车辆不存在")
  95. }
  96. func TestRenewExtendsFromEndDateWhenActive(t *testing.T) {
  97. svc := setupMonthlyServiceTest(t)
  98. vehicle := createVehicle(t, "MC-005")
  99. now := time.Now()
  100. card := &dao.MonthlyCard{
  101. VehicleID: vehicle.ID, CardType: "month",
  102. StartDate: now.AddDate(0, -1, 0), EndDate: now.AddDate(0, 0, 10),
  103. Fee: 300, PaymentStatus: "paid",
  104. }
  105. require.NoError(t, global.GVA_DB.Create(card).Error)
  106. originalEnd := card.EndDate
  107. require.NoError(t, global.GVA_DB.Create(&dao.Shortlist{
  108. VehicleId: int(vehicle.ID), ListType: "白名单", ExpirationTime: &originalEnd,
  109. }).Error)
  110. err := svc.Renew(card.ID, "month", 300, 7)
  111. require.NoError(t, err)
  112. var renewed dao.MonthlyCard
  113. require.NoError(t, global.GVA_DB.First(&renewed, card.ID).Error)
  114. // 未过期:从原到期日顺延 30 天
  115. require.True(t, renewed.EndDate.After(now.AddDate(0, 0, 39)), "到期日应约为原到期日+30天")
  116. require.True(t, renewed.EndDate.Before(now.AddDate(0, 0, 41)))
  117. require.Equal(t, 600.0, renewed.Fee)
  118. // 白名单到期日同步
  119. var shortlist dao.Shortlist
  120. require.NoError(t, global.GVA_DB.Where("vehicle_id = ?", vehicle.ID).First(&shortlist).Error)
  121. require.True(t, shortlist.ExpirationTime.Equal(renewed.EndDate))
  122. }
  123. func TestRenewStartsFromNowWhenCardExpired(t *testing.T) {
  124. svc := setupMonthlyServiceTest(t)
  125. vehicle := createVehicle(t, "MC-006")
  126. now := time.Now()
  127. card := &dao.MonthlyCard{
  128. VehicleID: vehicle.ID, CardType: "month",
  129. StartDate: now.AddDate(0, -3, 0), EndDate: now.AddDate(0, -2, 0), // 已过期 2 个月
  130. Fee: 300, PaymentStatus: "paid",
  131. }
  132. require.NoError(t, global.GVA_DB.Create(card).Error)
  133. expiredEnd := card.EndDate
  134. require.NoError(t, global.GVA_DB.Create(&dao.Shortlist{
  135. VehicleId: int(vehicle.ID), ListType: "白名单", ExpirationTime: &expiredEnd,
  136. }).Error)
  137. err := svc.Renew(card.ID, "month", 300, 7)
  138. require.NoError(t, err)
  139. var renewed dao.MonthlyCard
  140. require.NoError(t, global.GVA_DB.First(&renewed, card.ID).Error)
  141. // 已过期:从今天起算约 30 天,而不是从两个月前的到期日续期
  142. require.True(t, renewed.EndDate.After(now.AddDate(0, 0, 29)), "过期卡应从今天起算")
  143. require.True(t, renewed.EndDate.Before(now.AddDate(0, 0, 31)))
  144. // 白名单到期日同步为新到期日(未过期卡同样适用)
  145. var shortlist dao.Shortlist
  146. require.NoError(t, global.GVA_DB.Where("vehicle_id = ?", vehicle.ID).First(&shortlist).Error)
  147. require.True(t, shortlist.ExpirationTime.Equal(renewed.EndDate))
  148. }
  149. func TestRenewRollsBackWhenPaymentWriteFails(t *testing.T) {
  150. svc := setupMonthlyServiceTest(t)
  151. vehicle := createVehicle(t, "MC-007")
  152. now := time.Now()
  153. card := &dao.MonthlyCard{
  154. VehicleID: vehicle.ID, CardType: "month",
  155. StartDate: now, EndDate: now.AddDate(0, 0, 10),
  156. Fee: 300, PaymentStatus: "paid",
  157. }
  158. require.NoError(t, global.GVA_DB.Create(card).Error)
  159. originalEnd := card.EndDate
  160. require.NoError(t, global.GVA_DB.Exec("DROP TABLE payment_record").Error)
  161. err := svc.Renew(card.ID, "month", 300, 7)
  162. require.Error(t, err)
  163. // 到期日与费用均未变(回滚)
  164. var after dao.MonthlyCard
  165. require.NoError(t, global.GVA_DB.First(&after, card.ID).Error)
  166. require.True(t, after.EndDate.Equal(originalEnd))
  167. require.Equal(t, 300.0, after.Fee)
  168. }
  169. func TestRefundMarksRefundedAndRemovesWhitelist(t *testing.T) {
  170. svc := setupMonthlyServiceTest(t)
  171. vehicle := createVehicle(t, "MC-008")
  172. card, err := svc.Create(vehicle.ID, "month", 300, 7, "")
  173. require.NoError(t, err)
  174. require.Equal(t, int64(1), countTable(t, &dao.Shortlist{}))
  175. err = svc.Refund(card.ID)
  176. require.NoError(t, err)
  177. var refunded dao.MonthlyCard
  178. require.NoError(t, global.GVA_DB.First(&refunded, card.ID).Error)
  179. require.Equal(t, "refunded", refunded.PaymentStatus)
  180. var whitelistCount int64
  181. require.NoError(t, global.GVA_DB.Model(&dao.Shortlist{}).
  182. Where("vehicle_id = ? AND list_type = ?", vehicle.ID, "白名单").Count(&whitelistCount).Error)
  183. require.Equal(t, int64(0), whitelistCount)
  184. }
  185. func TestRefundRejectsUnknownCard(t *testing.T) {
  186. svc := setupMonthlyServiceTest(t)
  187. err := svc.Refund(99999)
  188. require.Error(t, err)
  189. require.Contains(t, err.Error(), "月卡记录不存在")
  190. }