|
|
@@ -27,7 +27,10 @@ func TestVehicleReEntry(t *testing.T) {
|
|
|
"plate_number": "JingR00001", "rfid_tag": "RFID_REENTRY",
|
|
|
"parking_lot_id": 1, "parking_space_id": 2, "entry_image": "test2.jpg",
|
|
|
})
|
|
|
- var result struct { Code int `json:"code"`; Msg string `json:"msg"` }
|
|
|
+ var result struct {
|
|
|
+ Code int `json:"code"`
|
|
|
+ Msg string `json:"msg"`
|
|
|
+ }
|
|
|
json.Unmarshal(entryBody2, &result)
|
|
|
if result.Code == response.SUCCESS {
|
|
|
t.Fatalf("BUG: Vehicle re-entry was NOT blocked! code=%d msg=%s", result.Code, result.Msg)
|
|
|
@@ -41,7 +44,10 @@ func TestExitWithoutEntry(t *testing.T) {
|
|
|
_, exitPreviewBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{
|
|
|
"plate_number": "JingZ99999", "rfid_tag": "RFID_NOENTRY",
|
|
|
})
|
|
|
- var result struct { Code int `json:"code"`; Msg string `json:"msg"` }
|
|
|
+ var result struct {
|
|
|
+ Code int `json:"code"`
|
|
|
+ Msg string `json:"msg"`
|
|
|
+ }
|
|
|
json.Unmarshal(exitPreviewBody, &result)
|
|
|
if result.Code == response.SUCCESS {
|
|
|
t.Fatalf("BUG: Exit preview succeeded for non-entered vehicle!")
|
|
|
@@ -63,7 +69,10 @@ func TestPaymentInsufficientAmount(t *testing.T) {
|
|
|
"plate_number": "JingP00001", "rfid_tag": "RFID_PAY",
|
|
|
"payment_method": "cash", "paid_amount": 0.0,
|
|
|
})
|
|
|
- var result struct { Code int `json:"code"`; Msg string `json:"msg"` }
|
|
|
+ var result struct {
|
|
|
+ Code int `json:"code"`
|
|
|
+ Msg string `json:"msg"`
|
|
|
+ }
|
|
|
json.Unmarshal(exitConfirmBody, &result)
|
|
|
fmt.Printf("INFO: Insufficient payment response: code=%d msg=%s\n", result.Code, result.Msg)
|
|
|
}
|
|
|
@@ -74,7 +83,7 @@ func TestVIPExpired(t *testing.T) {
|
|
|
ownerResp, ownerBody := env.DoPost("/owner/create", map[string]interface{}{
|
|
|
"owner_name": "VIP Expired Test", "owner_surname": "VIP",
|
|
|
"owner_phone": "13900000001", "is_vip": true,
|
|
|
- "vip_expire_time": time.Now().Add(-24*time.Hour).Format(time.RFC3339),
|
|
|
+ "vip_expire_time": time.Now().Add(-24 * time.Hour).Format(time.RFC3339),
|
|
|
})
|
|
|
AssertResponse(t, ownerResp, ownerBody, http.StatusOK, response.SUCCESS)
|
|
|
|
|
|
@@ -119,7 +128,9 @@ func TestFreeDuration(t *testing.T) {
|
|
|
"plate_number": "JingF00001", "rfid_tag": "RFID_FREE",
|
|
|
})
|
|
|
AssertResponse(t, exitResp, exitBody, http.StatusOK, response.SUCCESS)
|
|
|
- var preview struct { Fee float64 `json:"fee"` }
|
|
|
+ var preview struct {
|
|
|
+ Fee float64 `json:"fee"`
|
|
|
+ }
|
|
|
json.Unmarshal(exitBody, &preview)
|
|
|
if preview.Fee != 0 {
|
|
|
t.Fatalf("BUG: Vehicle within free duration should have zero fee, got %.2f", preview.Fee)
|
|
|
@@ -141,7 +152,9 @@ func TestDailyCap(t *testing.T) {
|
|
|
"plate_number": "JingD00001", "rfid_tag": "RFID_CAP",
|
|
|
})
|
|
|
AssertResponse(t, exitResp, exitBody, http.StatusOK, response.SUCCESS)
|
|
|
- var preview struct { Fee float64 `json:"fee"` }
|
|
|
+ var preview struct {
|
|
|
+ Fee float64 `json:"fee"`
|
|
|
+ }
|
|
|
json.Unmarshal(exitBody, &preview)
|
|
|
if preview.Fee > 50.0 {
|
|
|
t.Fatalf("BUG: Fee exceeds daily cap! Expected <= 50, got %.2f", preview.Fee)
|
|
|
@@ -197,7 +210,9 @@ func TestShortlistByPlateOrRFID(t *testing.T) {
|
|
|
})
|
|
|
AssertResponse(t, resp1, body1, http.StatusOK, response.SUCCESS)
|
|
|
list1 := ParsePageList(body1)
|
|
|
- if len(list1) < 1 { t.Fatalf("Expected at least 1 shortlist record by plate number") }
|
|
|
+ if len(list1) < 1 {
|
|
|
+ t.Fatalf("Expected at least 1 shortlist record by plate number")
|
|
|
+ }
|
|
|
fmt.Printf("PASS: Shortlist query by plate number returned %d records\n", len(list1))
|
|
|
|
|
|
resp2, body2 := env.DoPost("/shortlist/queryShortlistList", map[string]interface{}{
|
|
|
@@ -205,7 +220,9 @@ func TestShortlistByPlateOrRFID(t *testing.T) {
|
|
|
})
|
|
|
AssertResponse(t, resp2, body2, http.StatusOK, response.SUCCESS)
|
|
|
list2 := ParsePageList(body2)
|
|
|
- if len(list2) < 1 { t.Fatalf("Expected at least 1 shortlist record by RFID") }
|
|
|
+ if len(list2) < 1 {
|
|
|
+ t.Fatalf("Expected at least 1 shortlist record by RFID")
|
|
|
+ }
|
|
|
fmt.Printf("PASS: Shortlist query by RFID returned %d records\n", len(list2))
|
|
|
}
|
|
|
|
|
|
@@ -220,14 +237,21 @@ func TestShortlistUpdate(t *testing.T) {
|
|
|
})
|
|
|
queryResp, queryBody := env.DoGet("/shortlist/queryAllShortlists", nil)
|
|
|
AssertResponse(t, queryResp, queryBody, http.StatusOK, response.SUCCESS)
|
|
|
- var result struct { Data []map[string]interface{} `json:"data"` }
|
|
|
+ var result struct {
|
|
|
+ Data []map[string]interface{} `json:"data"`
|
|
|
+ }
|
|
|
json.Unmarshal(queryBody, &result)
|
|
|
- if len(result.Data) == 0 { t.Fatalf("No shortlist found") }
|
|
|
- idVal, ok := result.Data[0]["ID"]; if !ok { idVal = result.Data[0]["id"] }
|
|
|
+ if len(result.Data) == 0 {
|
|
|
+ t.Fatalf("No shortlist found")
|
|
|
+ }
|
|
|
+ idVal, ok := result.Data[0]["ID"]
|
|
|
+ if !ok {
|
|
|
+ idVal = result.Data[0]["id"]
|
|
|
+ }
|
|
|
id := int(idVal.(float64))
|
|
|
updateResp, updateBody := env.DoPut("/shortlist/updateShortlist", map[string]interface{}{
|
|
|
"ID": id, "list_type": "黑名单", "vehicle_id": vehicle.ID,
|
|
|
- "expiration_time": time.Now().Add(30*24*time.Hour).Format(time.RFC3339),
|
|
|
+ "expiration_time": time.Now().Add(30 * 24 * time.Hour).Format(time.RFC3339),
|
|
|
})
|
|
|
AssertResponse(t, updateResp, updateBody, http.StatusOK, response.SUCCESS)
|
|
|
updatedResp, updatedBody := env.DoGet("/shortlist/queryAllShortlists", nil)
|
|
|
@@ -246,10 +270,17 @@ func TestShortlistDelete(t *testing.T) {
|
|
|
})
|
|
|
queryResp, queryBody := env.DoGet("/shortlist/queryAllShortlists", nil)
|
|
|
AssertResponse(t, queryResp, queryBody, http.StatusOK, response.SUCCESS)
|
|
|
- var result struct { Data []map[string]interface{} `json:"data"` }
|
|
|
+ var result struct {
|
|
|
+ Data []map[string]interface{} `json:"data"`
|
|
|
+ }
|
|
|
json.Unmarshal(queryBody, &result)
|
|
|
- if len(result.Data) == 0 { t.Fatalf("No shortlist found") }
|
|
|
- idVal, ok := result.Data[0]["ID"]; if !ok { idVal = result.Data[0]["id"] }
|
|
|
+ if len(result.Data) == 0 {
|
|
|
+ t.Fatalf("No shortlist found")
|
|
|
+ }
|
|
|
+ idVal, ok := result.Data[0]["ID"]
|
|
|
+ if !ok {
|
|
|
+ idVal = result.Data[0]["id"]
|
|
|
+ }
|
|
|
id := int(idVal.(float64))
|
|
|
deleteResp, deleteBody := env.DoDelete(fmt.Sprintf("/shortlist/deleteShortlist?id=%d", id))
|
|
|
AssertResponse(t, deleteResp, deleteBody, http.StatusOK, response.SUCCESS)
|
|
|
@@ -273,10 +304,17 @@ func TestOwnerVehicleRelationship(t *testing.T) {
|
|
|
found := false
|
|
|
for _, item := range list {
|
|
|
m, ok := item.(map[string]interface{})
|
|
|
- if !ok { continue }
|
|
|
- if m["owner_phone"] == "13911111111" { found = true; break }
|
|
|
+ if !ok {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if m["owner_phone"] == "13911111111" {
|
|
|
+ found = true
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if !found {
|
|
|
+ t.Fatalf("Owner not found in list")
|
|
|
}
|
|
|
- if !found { t.Fatalf("Owner not found in list") }
|
|
|
fmt.Println("PASS: Owner-vehicle relationship verified")
|
|
|
}
|
|
|
|
|
|
@@ -293,7 +331,10 @@ func TestParkingLotCapacity(t *testing.T) {
|
|
|
"plate_number": "JingC00001", "rfid_tag": "RFID_FULL",
|
|
|
"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",
|
|
|
})
|
|
|
- var result struct { Code int `json:"code"`; Msg string `json:"msg"` }
|
|
|
+ var result struct {
|
|
|
+ Code int `json:"code"`
|
|
|
+ Msg string `json:"msg"`
|
|
|
+ }
|
|
|
json.Unmarshal(entryBody, &result)
|
|
|
fmt.Printf("INFO: Entry into zero-capacity lot: code=%d msg=%s\n", result.Code, result.Msg)
|
|
|
}
|
|
|
@@ -309,7 +350,9 @@ func TestBoothCRUD(t *testing.T) {
|
|
|
listResp, listBody := env.DoGet("/parking/booth/list", map[string]string{"page": "1", "page_size": "10"})
|
|
|
AssertResponse(t, listResp, listBody, http.StatusOK, response.SUCCESS)
|
|
|
list := ParsePageList(listBody)
|
|
|
- if len(list) < 1 { t.Fatalf("Booth list is empty") }
|
|
|
+ if len(list) < 1 {
|
|
|
+ t.Fatalf("Booth list is empty")
|
|
|
+ }
|
|
|
fmt.Printf("PASS: Booth CRUD successful, total %d booths\n", len(list))
|
|
|
}
|
|
|
|
|
|
@@ -325,7 +368,9 @@ func TestChannelCRUD(t *testing.T) {
|
|
|
listResp, listBody := env.DoGet("/parking/channel/list", map[string]string{"page": "1", "page_size": "10"})
|
|
|
AssertResponse(t, listResp, listBody, http.StatusOK, response.SUCCESS)
|
|
|
list := ParsePageList(listBody)
|
|
|
- if len(list) < 1 { t.Fatalf("Channel list is empty") }
|
|
|
+ if len(list) < 1 {
|
|
|
+ t.Fatalf("Channel list is empty")
|
|
|
+ }
|
|
|
fmt.Printf("PASS: Channel CRUD successful, total %d channels\n", len(list))
|
|
|
}
|
|
|
|
|
|
@@ -342,7 +387,9 @@ func TestDeviceCRUD(t *testing.T) {
|
|
|
listResp, listBody := env.DoGet("/parking/device/list", map[string]string{"page": "1", "page_size": "10"})
|
|
|
AssertResponse(t, listResp, listBody, http.StatusOK, response.SUCCESS)
|
|
|
list := ParsePageList(listBody)
|
|
|
- if len(list) < 1 { t.Fatalf("Device list is empty") }
|
|
|
+ if len(list) < 1 {
|
|
|
+ t.Fatalf("Device list is empty")
|
|
|
+ }
|
|
|
fmt.Printf("PASS: Device CRUD successful, total %d devices\n", len(list))
|
|
|
}
|
|
|
|
|
|
@@ -356,13 +403,14 @@ func TestVehicleTypeCRUD(t *testing.T) {
|
|
|
listResp, listBody := env.DoGet("/vehicle/type/all", nil)
|
|
|
AssertResponse(t, listResp, listBody, http.StatusOK, response.SUCCESS)
|
|
|
list := ParseDataList(listBody)
|
|
|
- if len(list) < 1 { t.Fatalf("Vehicle type list is empty") }
|
|
|
+ if len(list) < 1 {
|
|
|
+ t.Fatalf("Vehicle type list is empty")
|
|
|
+ }
|
|
|
fmt.Printf("PASS: Vehicle type CRUD successful, total %d types\n", len(list))
|
|
|
}
|
|
|
|
|
|
// ===================== Helper functions =====================
|
|
|
|
|
|
-
|
|
|
// TestVehicleUpdate 车辆信息更新
|
|
|
func TestVehicleUpdate(t *testing.T) {
|
|
|
env := InitTestEnv(t)
|
|
|
@@ -396,7 +444,9 @@ func TestVehicleList(t *testing.T) {
|
|
|
resp, body := env.DoGet("/vehicle/list", map[string]string{"page": "1", "page_size": "10"})
|
|
|
AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
|
|
|
list := ParsePageList(body)
|
|
|
- if len(list) < 1 { t.Fatalf("Vehicle list is empty") }
|
|
|
+ if len(list) < 1 {
|
|
|
+ t.Fatalf("Vehicle list is empty")
|
|
|
+ }
|
|
|
fmt.Printf("PASS: Vehicle list returned %d records\n", len(list))
|
|
|
}
|
|
|
|
|
|
@@ -461,7 +511,6 @@ func TestParkingLotUpdate(t *testing.T) {
|
|
|
fmt.Println("PASS: Parking lot update successful")
|
|
|
}
|
|
|
|
|
|
-
|
|
|
// ===================== 月租车管理模块测试 =====================
|
|
|
|
|
|
// TestMonthlyCardCreate 办理月卡
|
|
|
@@ -478,13 +527,19 @@ func TestMonthlyCardCreate(t *testing.T) {
|
|
|
|
|
|
var card dao.MonthlyCard
|
|
|
env.DB.Where("vehicle_id = ?", v.ID).First(&card)
|
|
|
- if card.ID == 0 { t.Fatalf("Monthly card not created in DB") }
|
|
|
- if card.PaymentStatus != "paid" { t.Fatalf("Expected paid, got %s", card.PaymentStatus) }
|
|
|
+ if card.ID == 0 {
|
|
|
+ t.Fatalf("Monthly card not created in DB")
|
|
|
+ }
|
|
|
+ if card.PaymentStatus != "paid" {
|
|
|
+ t.Fatalf("Expected paid, got %s", card.PaymentStatus)
|
|
|
+ }
|
|
|
|
|
|
// Verify shortlist (whitelist) was created
|
|
|
var sl dao.Shortlist
|
|
|
env.DB.Where("vehicle_id = ? AND list_type = ?", v.ID, "白名单").First(&sl)
|
|
|
- if sl.ID == 0 { t.Fatalf("Shortlist not created") }
|
|
|
+ if sl.ID == 0 {
|
|
|
+ t.Fatalf("Shortlist not created")
|
|
|
+ }
|
|
|
|
|
|
fmt.Println("PASS: Monthly card created successfully")
|
|
|
}
|
|
|
@@ -531,11 +586,15 @@ func TestMonthlyCardRefund(t *testing.T) {
|
|
|
AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
|
|
|
|
|
|
env.DB.First(&card, card.ID)
|
|
|
- if card.PaymentStatus != "refunded" { t.Fatalf("Expected refunded, got %s", card.PaymentStatus) }
|
|
|
+ if card.PaymentStatus != "refunded" {
|
|
|
+ t.Fatalf("Expected refunded, got %s", card.PaymentStatus)
|
|
|
+ }
|
|
|
|
|
|
var count int64
|
|
|
env.DB.Model(&dao.Shortlist{}).Where("vehicle_id = ? AND list_type = ?", v.ID, "白名单").Count(&count)
|
|
|
- if count != 0 { t.Fatalf("Shortlist should be removed after refund, found %d", count) }
|
|
|
+ if count != 0 {
|
|
|
+ t.Fatalf("Shortlist should be removed after refund, found %d", count)
|
|
|
+ }
|
|
|
|
|
|
fmt.Println("PASS: Monthly card refunded successfully")
|
|
|
}
|
|
|
@@ -553,14 +612,18 @@ func TestMonthlyCardList(t *testing.T) {
|
|
|
resp, body := env.DoGet("/monthly-card/list", map[string]string{"page": "1", "page_size": "10"})
|
|
|
AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
|
|
|
list := ParsePageList(body)
|
|
|
- if len(list) < 1 { t.Fatalf("Monthly card list is empty") }
|
|
|
+ if len(list) < 1 {
|
|
|
+ t.Fatalf("Monthly card list is empty")
|
|
|
+ }
|
|
|
|
|
|
resp, body2 := env.DoGet("/monthly-card/list", map[string]string{
|
|
|
"page": "1", "page_size": "10", "card_type": "quarter",
|
|
|
})
|
|
|
AssertResponse(t, resp, body2, http.StatusOK, response.SUCCESS)
|
|
|
list2 := ParsePageList(body2)
|
|
|
- if len(list2) < 1 { t.Fatalf("Filtered list by card_type is empty") }
|
|
|
+ if len(list2) < 1 {
|
|
|
+ t.Fatalf("Filtered list by card_type is empty")
|
|
|
+ }
|
|
|
|
|
|
fmt.Printf("PASS: Monthly card list returned %d records\\n", len(list))
|
|
|
}
|
|
|
@@ -579,7 +642,10 @@ func TestMonthlyCardDuplicate(t *testing.T) {
|
|
|
_, body2 := env.DoPost("/monthly-card/create", map[string]interface{}{
|
|
|
"vehicle_id": v.ID, "card_type": "year", "fee": 3000.0,
|
|
|
})
|
|
|
- var result struct { Code int `json:"code"`; Msg string `json:"msg"` }
|
|
|
+ var result struct {
|
|
|
+ Code int `json:"code"`
|
|
|
+ Msg string `json:"msg"`
|
|
|
+ }
|
|
|
json.Unmarshal(body2, &result)
|
|
|
if result.Code == response.SUCCESS {
|
|
|
t.Fatalf("BUG: Duplicate monthly card creation was NOT blocked!")
|
|
|
@@ -592,7 +658,10 @@ func TestMonthlyCardNonexistentRefund(t *testing.T) {
|
|
|
env := InitTestEnv(t)
|
|
|
_, body := env.DoDelete("/monthly-card/refund?id=99999")
|
|
|
// Should fail with error code
|
|
|
- var result struct { Code int `json:"code"`; Msg string `json:"msg"` }
|
|
|
+ var result struct {
|
|
|
+ Code int `json:"code"`
|
|
|
+ Msg string `json:"msg"`
|
|
|
+ }
|
|
|
json.Unmarshal(body, &result)
|
|
|
if result.Code == response.SUCCESS {
|
|
|
t.Fatalf("BUG: Refunding non-existent card was NOT blocked!")
|
|
|
@@ -622,7 +691,6 @@ func TestMonthlyCardDifferentTypes(t *testing.T) {
|
|
|
fmt.Printf("PASS: Year card duration = %.0f days\\n", duration)
|
|
|
}
|
|
|
|
|
|
-
|
|
|
// ===================== 数字票模块测试 =====================
|
|
|
|
|
|
func createTicket(t *testing.T, env *TestEnv, plate, trigger string) (string, uint) {
|
|
|
@@ -663,18 +731,34 @@ func TestDigitalTicketDetail(t *testing.T) {
|
|
|
// payment entry is explicit because entry and method are independent settings.
|
|
|
func TestDigitalTicketPay(t *testing.T) {
|
|
|
env := InitTestEnv(t)
|
|
|
- ticketNo, _ := createTicket(t, env, "DT002", "plate_recognition")
|
|
|
+ ticketNo, ticketID := createTicket(t, env, "DT002", "plate_recognition")
|
|
|
resp, body := env.DoPost("/digital-ticket/"+ticketNo+"/pay", map[string]interface{}{
|
|
|
"payment_entry": "ticket", "payment_method": "cash", "payment_order_no": "PO002", "paid_amount": 20.0,
|
|
|
})
|
|
|
AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
|
|
|
+ var ticket dao.DigitalTicket
|
|
|
+ if err := env.DB.First(&ticket, ticketID).Error; err != nil {
|
|
|
+ t.Fatalf("Failed to reload paid ticket: %v", err)
|
|
|
+ }
|
|
|
+ var record dao.VehicleRecord
|
|
|
+ if err := env.DB.First(&record, ticket.VehicleRecordID).Error; err != nil {
|
|
|
+ t.Fatalf("Failed to reload paid vehicle record: %v", err)
|
|
|
+ }
|
|
|
+ if record.PaymentStatus != "paid" {
|
|
|
+ t.Fatalf("Expected vehicle record payment status=paid, got %s", record.PaymentStatus)
|
|
|
+ }
|
|
|
+ var paymentCount int64
|
|
|
+ env.DB.Model(&dao.PaymentRecord{}).Where("record_id = ?", record.ID).Count(&paymentCount)
|
|
|
+ if paymentCount != 1 {
|
|
|
+ t.Fatalf("Expected one unified payment record, got %d", paymentCount)
|
|
|
+ }
|
|
|
fmt.Println("PASS: Pay API works correctly")
|
|
|
}
|
|
|
|
|
|
// TestDigitalTicketExit DT03: 离场验证
|
|
|
func TestDigitalTicketExit(t *testing.T) {
|
|
|
env := InitTestEnv(t)
|
|
|
- ticketNo, _ := createTicket(t, env, "DT003", "rfid")
|
|
|
+ ticketNo, ticketID := createTicket(t, env, "DT003", "rfid")
|
|
|
// 支付
|
|
|
_, _ = env.DoPost("/digital-ticket/"+ticketNo+"/pay", map[string]interface{}{
|
|
|
"payment_entry": "ticket", "payment_method": "cash", "payment_order_no": "PO003", "paid_amount": 15.0,
|
|
|
@@ -682,6 +766,22 @@ func TestDigitalTicketExit(t *testing.T) {
|
|
|
// 支付成功后离场
|
|
|
resp, body := env.DoPost("/digital-ticket/"+ticketNo+"/exit", nil)
|
|
|
AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
|
|
|
+ var ticket dao.DigitalTicket
|
|
|
+ if err := env.DB.First(&ticket, ticketID).Error; err != nil {
|
|
|
+ t.Fatalf("Failed to reload exited ticket: %v", err)
|
|
|
+ }
|
|
|
+ var record dao.VehicleRecord
|
|
|
+ if err := env.DB.First(&record, ticket.VehicleRecordID).Error; err != nil {
|
|
|
+ t.Fatalf("Failed to reload exited vehicle record: %v", err)
|
|
|
+ }
|
|
|
+ if record.ExitTime == nil {
|
|
|
+ t.Fatal("Digital ticket exit must update the vehicle record exit time")
|
|
|
+ }
|
|
|
+ var paymentCount int64
|
|
|
+ env.DB.Model(&dao.PaymentRecord{}).Where("record_id = ?", record.ID).Count(&paymentCount)
|
|
|
+ if paymentCount != 1 {
|
|
|
+ t.Fatalf("Digital ticket exit must not duplicate payment records, got %d", paymentCount)
|
|
|
+ }
|
|
|
fmt.Println("PASS: Pay → Exit flow works correctly")
|
|
|
}
|
|
|
|
|
|
@@ -725,7 +825,10 @@ func TestDigitalTicketInvalidExit(t *testing.T) {
|
|
|
env := InitTestEnv(t)
|
|
|
ticketNo, _ := createTicket(t, env, "DT006", "manual")
|
|
|
_, body := env.DoPost("/digital-ticket/"+ticketNo+"/exit", nil)
|
|
|
- var result struct{ Code int `json:"code"`; Msg string `json:"msg"` }
|
|
|
+ var result struct {
|
|
|
+ Code int `json:"code"`
|
|
|
+ Msg string `json:"msg"`
|
|
|
+ }
|
|
|
json.Unmarshal(body, &result)
|
|
|
if result.Code == response.SUCCESS {
|
|
|
t.Fatalf("BUG: Exit without payment was NOT blocked!")
|
|
|
@@ -737,7 +840,10 @@ func TestDigitalTicketInvalidExit(t *testing.T) {
|
|
|
func TestDigitalTicketNonexistent(t *testing.T) {
|
|
|
env := InitTestEnv(t)
|
|
|
_, body := env.DoGet("/digital-ticket/FAKE", nil)
|
|
|
- var result struct{ Code int `json:"code"`; Msg string `json:"msg"` }
|
|
|
+ var result struct {
|
|
|
+ Code int `json:"code"`
|
|
|
+ Msg string `json:"msg"`
|
|
|
+ }
|
|
|
json.Unmarshal(body, &result)
|
|
|
if result.Code == response.SUCCESS {
|
|
|
t.Fatalf("BUG: Non-existent ticket returned success!")
|
|
|
@@ -780,7 +886,10 @@ func createVehicleWithOwner(t *testing.T, env *TestEnv, plateNumber, rfidTag str
|
|
|
"vehicle_brand": "TestBrand", "vehicle_color": "TestColor",
|
|
|
"rfid_tag": rfidTag, "owner_id": ownerID,
|
|
|
})
|
|
|
- var result struct { Code int `json:"code"`; Msg string `json:"msg"` }
|
|
|
+ var result struct {
|
|
|
+ Code int `json:"code"`
|
|
|
+ Msg string `json:"msg"`
|
|
|
+ }
|
|
|
json.Unmarshal(body, &result)
|
|
|
if result.Code != response.SUCCESS {
|
|
|
t.Fatalf("Failed to create vehicle with owner: %s", result.Msg)
|
|
|
@@ -929,9 +1038,9 @@ func TestShiftCurrent(t *testing.T) {
|
|
|
AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
|
|
|
|
|
|
var result struct {
|
|
|
- Code int `json:"code"`
|
|
|
- Data map[string]interface{} `json:"data"`
|
|
|
- Msg string `json:"msg"`
|
|
|
+ Code int `json:"code"`
|
|
|
+ Data map[string]interface{} `json:"data"`
|
|
|
+ Msg string `json:"msg"`
|
|
|
}
|
|
|
json.Unmarshal(body, &result)
|
|
|
|
|
|
@@ -1014,9 +1123,9 @@ func TestRevenueReportBasic(t *testing.T) {
|
|
|
}
|
|
|
|
|
|
var result struct {
|
|
|
- Code int `json:"code"`
|
|
|
- Data json.RawMessage `json:"data"`
|
|
|
- Msg string `json:"msg"`
|
|
|
+ Code int `json:"code"`
|
|
|
+ Data json.RawMessage `json:"data"`
|
|
|
+ Msg string `json:"msg"`
|
|
|
}
|
|
|
json.Unmarshal(body, &result)
|
|
|
|
|
|
@@ -1025,8 +1134,8 @@ func TestRevenueReportBasic(t *testing.T) {
|
|
|
t.Fatalf("Revenue report failed: code=%d msg=%s", result.Code, result.Msg)
|
|
|
}
|
|
|
if result.Data == nil || string(result.Data) == "null" {
|
|
|
- t.Fatalf("BUG: Revenue report with no date params returns null data! "+
|
|
|
- "Cause: SQL condition 'paid_at >= \"\" AND paid_at <= \" 23:59:59\"' filters out all records. "+
|
|
|
+ t.Fatalf("BUG: Revenue report with no date params returns null data! " +
|
|
|
+ "Cause: SQL condition 'paid_at >= \"\" AND paid_at <= \" 23:59:59\"' filters out all records. " +
|
|
|
"Fix: when start/end_date are empty, omit the WHERE clause or default to today.")
|
|
|
}
|
|
|
fmt.Printf("PASS: Revenue report basic query successful, data=%s\n", string(result.Data))
|
|
|
@@ -1046,9 +1155,9 @@ func TestRevenueReportDateRange(t *testing.T) {
|
|
|
AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
|
|
|
|
|
|
var result struct {
|
|
|
- Code int `json:"code"`
|
|
|
- Data json.RawMessage `json:"data"`
|
|
|
- Msg string `json:"msg"`
|
|
|
+ Code int `json:"code"`
|
|
|
+ Data json.RawMessage `json:"data"`
|
|
|
+ Msg string `json:"msg"`
|
|
|
}
|
|
|
json.Unmarshal(body, &result)
|
|
|
if len(result.Data) == 0 || string(result.Data) == "null" {
|
|
|
@@ -1072,9 +1181,9 @@ func TestRevenueReportWithLotFilter(t *testing.T) {
|
|
|
AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
|
|
|
|
|
|
var result struct {
|
|
|
- Code int `json:"code"`
|
|
|
- Data json.RawMessage `json:"data"`
|
|
|
- Msg string `json:"msg"`
|
|
|
+ Code int `json:"code"`
|
|
|
+ Data json.RawMessage `json:"data"`
|
|
|
+ Msg string `json:"msg"`
|
|
|
}
|
|
|
json.Unmarshal(body, &result)
|
|
|
if len(result.Data) == 0 || string(result.Data) == "null" {
|
|
|
@@ -1082,4 +1191,3 @@ func TestRevenueReportWithLotFilter(t *testing.T) {
|
|
|
}
|
|
|
fmt.Printf("PASS: Revenue report with lot filter successful, data=%s\n", string(result.Data))
|
|
|
}
|
|
|
-
|