api_test.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. package printer
  2. import (
  3. "fmt"
  4. "strings"
  5. "testing"
  6. "time"
  7. "github.com/gin-gonic/gin"
  8. "github.com/glebarez/sqlite"
  9. "github.com/stretchr/testify/require"
  10. "gorm.io/gorm"
  11. "wails-app/internal/dao"
  12. "wails-app/internal/global"
  13. commonModel "wails-app/internal/model/common"
  14. )
  15. type fakeTicketMachinePassage struct {
  16. req commonModel.PassageRequest
  17. result *commonModel.PassageResult
  18. err error
  19. }
  20. func (f *fakeTicketMachinePassage) HandlePassage(req commonModel.PassageRequest) (*commonModel.PassageResult, error) {
  21. f.req = req
  22. return f.result, f.err
  23. }
  24. type fakeTicketMachinePrinter struct {
  25. printerID uint
  26. lotName string
  27. channelCode string
  28. plateNo string
  29. ticketID uint
  30. ticketNo string
  31. err error
  32. }
  33. func (f *fakeTicketMachinePrinter) PrintTicket(printerID uint, lotName, channelCode, plateNo string, ticketID uint, ticketNo string) error {
  34. f.printerID = printerID
  35. f.lotName = lotName
  36. f.channelCode = channelCode
  37. f.plateNo = plateNo
  38. f.ticketID = ticketID
  39. f.ticketNo = ticketNo
  40. return f.err
  41. }
  42. func setupTicketMachineTest(t *testing.T) (*dao.ParkingLot, *dao.Channel) {
  43. t.Helper()
  44. dsn := fmt.Sprintf("file:ticket-machine-%d?mode=memory&cache=shared", time.Now().UnixNano())
  45. db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
  46. require.NoError(t, err)
  47. sqlDB, err := db.DB()
  48. require.NoError(t, err)
  49. t.Cleanup(func() {
  50. _ = sqlDB.Close()
  51. if global.GVA_DB == db {
  52. global.GVA_DB = nil
  53. }
  54. })
  55. global.GVA_DB = db
  56. require.NoError(t, db.AutoMigrate(&dao.ParkingLot{}, &dao.Channel{}, &dao.UHFReader{}))
  57. lot := &dao.ParkingLot{LotCode: "LOT-TICKET", LotName: "票机停车场", Capacity: 50, Available: 50}
  58. require.NoError(t, db.Create(lot).Error)
  59. channel := &dao.Channel{
  60. ChannelCode: "TICKET-IN", ChannelName: "票机入口", Direction: "in",
  61. AllowTemporary: true, ParkingLotID: lot.ID,
  62. }
  63. require.NoError(t, db.Create(channel).Error)
  64. return lot, channel
  65. }
  66. func createTicketMachineDevice(t *testing.T, channelID uint, code string) *dao.UHFReader {
  67. t.Helper()
  68. device := &dao.UHFReader{
  69. DeviceCode: code, DeviceName: code + "名称", DeviceType: "gate",
  70. ConnectType: "tcp", IsActive: true, ChannelID: channelID,
  71. }
  72. require.NoError(t, global.GVA_DB.Create(device).Error)
  73. return device
  74. }
  75. func TestProcessTicketMachineButtonUsesUnifiedPassageAndReturnedTicket(t *testing.T) {
  76. lot, channel := setupTicketMachineTest(t)
  77. device := createTicketMachineDevice(t, channel.ID, "GATE-TICKET-01")
  78. passage := &fakeTicketMachinePassage{result: &commonModel.PassageResult{
  79. Success: true, Direction: "in", PlateNumber: "TKT-RESULT",
  80. SessionID: 41, TicketID: 73, TicketNo: "ticket-no-73", EntryTime: 123456,
  81. BusinessCompleted: true, GateOpened: true, GateStatus: "opened",
  82. }}
  83. printer := &fakeTicketMachinePrinter{}
  84. result, err := processTicketMachineButton(buttonReq{
  85. PrinterID: 9, ChannelCode: channel.ChannelCode,
  86. }, passage, printer)
  87. require.NoError(t, err)
  88. require.True(t, result.AnonymousVehicle)
  89. require.Equal(t, lot.ID, result.ParkingLotID)
  90. require.Equal(t, channel.ChannelCode, result.ChannelCode)
  91. require.Equal(t, device.DeviceCode, result.DeviceCode)
  92. require.EqualValues(t, 41, result.SessionID)
  93. require.EqualValues(t, 73, result.TicketID)
  94. require.Equal(t, "ticket-no-73", result.TicketNo)
  95. require.Equal(t, "printed", result.PrintStatus)
  96. require.Equal(t, device.DeviceCode, passage.req.DeviceCode)
  97. require.Equal(t, "in", passage.req.Direction)
  98. require.Equal(t, "ticket_machine", passage.req.TriggerSource)
  99. require.True(t, strings.HasPrefix(passage.req.PlateNumber, "TKT"))
  100. require.Len(t, passage.req.PlateNumber, 19)
  101. require.EqualValues(t, 9, printer.printerID)
  102. require.Equal(t, lot.LotName, printer.lotName)
  103. require.Equal(t, channel.ChannelCode, printer.channelCode)
  104. require.Empty(t, printer.plateNo)
  105. require.EqualValues(t, 73, printer.ticketID)
  106. require.Equal(t, "ticket-no-73", printer.ticketNo)
  107. }
  108. func TestProcessTicketMachineButtonRequiresDeviceWhenChannelHasMultiple(t *testing.T) {
  109. _, channel := setupTicketMachineTest(t)
  110. createTicketMachineDevice(t, channel.ID, "GATE-A")
  111. createTicketMachineDevice(t, channel.ID, "GATE-B")
  112. result, err := processTicketMachineButton(buttonReq{
  113. PrinterID: 1, ChannelCode: channel.ChannelCode,
  114. }, &fakeTicketMachinePassage{}, &fakeTicketMachinePrinter{})
  115. require.Nil(t, result)
  116. require.EqualError(t, err, "票机通道绑定了多个启用设备,必须指定 device_code")
  117. }
  118. func TestProcessTicketMachineButtonKeepsBusinessResultWhenPrintFails(t *testing.T) {
  119. _, channel := setupTicketMachineTest(t)
  120. createTicketMachineDevice(t, channel.ID, "GATE-PRINT")
  121. passage := &fakeTicketMachinePassage{result: &commonModel.PassageResult{
  122. SessionID: 8, TicketID: 9, TicketNo: "ticket-9", PlateNumber: "TEST-PLATE",
  123. EntryTime: 100, BusinessCompleted: true, GateStatus: "opened",
  124. }}
  125. printer := &fakeTicketMachinePrinter{err: fmt.Errorf("paper out")}
  126. result, err := processTicketMachineButton(buttonReq{
  127. PrinterID: 1, ChannelCode: channel.ChannelCode, PlateNo: "TEST-PLATE",
  128. }, passage, printer)
  129. require.ErrorContains(t, err, "打印失败")
  130. require.EqualValues(t, 8, result.SessionID)
  131. require.EqualValues(t, 9, result.TicketID)
  132. require.Equal(t, "completed", result.BusinessStatus)
  133. require.Equal(t, "failed", result.PrintStatus)
  134. }
  135. func TestPrinterDebugRoutesAreDisabledByDefault(t *testing.T) {
  136. gin.SetMode(gin.TestMode)
  137. router := gin.New()
  138. group := router.Group("")
  139. SetupPrinterRouter(group)
  140. SetupPrinterDebugRoutes(group, false)
  141. routes := router.Routes()
  142. require.Len(t, routes, 1)
  143. require.Equal(t, "POST", routes[0].Method)
  144. require.Equal(t, "/ticket-machine/button", routes[0].Path)
  145. }
  146. func TestPrinterDebugRoutesUseSafeMethodsWhenEnabled(t *testing.T) {
  147. gin.SetMode(gin.TestMode)
  148. router := gin.New()
  149. SetupPrinterDebugRoutes(router.Group(""), true)
  150. methods := map[string]string{}
  151. for _, route := range router.Routes() {
  152. methods[route.Path] = route.Method
  153. }
  154. require.Equal(t, "POST", methods["/ticket-machine/test"])
  155. require.Equal(t, "POST", methods["/ticket-machine/debug"])
  156. require.Equal(t, "GET", methods["/ticket-machine/usb-list"])
  157. }