package testutil import ( "encoding/json" "fmt" "net/http" "testing" "time" "wails-app/internal/dao" "wails-app/internal/global" "wails-app/internal/model/common/response" "wails-app/internal/utils" ) // ===================== 端到端测试用例 ===================== func TestLoginFlow(t *testing.T) { env := InitTestEnv(t) if env.Token == "" { t.Fatalf("Token is empty") } j := &utils.JWT{SigningKey: []byte(global.GVA_CONFIG.JWT.SigningKey)} claims, err := j.ParseToken(env.Token) if err != nil { t.Fatalf("Token parse failed: %v", err) } if claims.Username != "admin" || claims.AuthorityId != 888 { t.Fatalf("Token claims mismatch: username=%s, authorityId=%d", claims.Username, claims.AuthorityId) } fmt.Printf("PASS: Login flow verified, Token: %s...\n", env.Token[:min(20, len(env.Token))]) } func TestSystemManagement(t *testing.T) { env := InitTestEnv(t) resp, body := env.DoPost("/user/getUserList", map[string]interface{}{ "page": 1, "pageSize": 10, }) AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS) var result response.Response json.Unmarshal(body, &result) dataMap, ok := result.Data.(map[string]interface{}) if !ok { t.Fatalf("data is not map type: %T", result.Data) } listRaw, exists := dataMap["list"] if !exists { t.Fatalf("data.list missing, keys: %v", mapKeys(dataMap)) } list, ok := listRaw.([]interface{}) if !ok { t.Fatalf("data.list is not array: %T", listRaw) } if len(list) == 0 { t.Fatalf("user list is empty") } fmt.Printf("PASS: User list query successful, total %d records\n", len(list)) } func TestParkingInfrastructure(t *testing.T) { env := InitTestEnv(t) lotResp, lotBody := env.DoPost("/parking/lot/create", map[string]interface{}{ "lot_code": "TEST002", "lot_name": "Test Parking Lot B Zone", "capacity": 200, "description": "Automated test parking lot B zone", }) AssertResponse(t, lotResp, lotBody, http.StatusOK, response.SUCCESS) resp, body := env.DoGet("/parking/lot/list", map[string]string{ "page": "1", "page_size": "10", }) AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS) list := ParsePageList(body) if len(list) < 2 { t.Fatalf("Parking lot list should contain at least 2 items, actual %d", len(list)) } fmt.Printf("PASS: Parking lot create and query successful, total %d lots\n", len(list)) } func TestVehicleCRUD(t *testing.T) { env := InitTestEnv(t) createVehicle(t, env, "JingA12345", "RFID001", 1) resp, body := env.DoGet("/vehicle/get-by-plate", map[string]string{ "plate_number": "JingA12345", }) AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS) dataMap := ParseDataMap(body) if dataMap["plate_number"] != "JingA12345" { t.Fatalf("Vehicle info mismatch") } fmt.Println("PASS: Vehicle create and query successful") } func TestFeeConfig(t *testing.T) { env := InitTestEnv(t) resp, body := env.DoGet("/vehicle/fee-config/list", map[string]string{ "page": "1", "page_size": "10", }) AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS) list := ParsePageList(body) if len(list) == 0 { t.Fatalf("Fee config list is empty") } fmt.Printf("PASS: Fee config query successful, total %d records\n", len(list)) } func TestVehicleEntryAndExit(t *testing.T) { env := InitTestEnv(t) createVehicle(t, env, "JingB67890", "RFID002", 1) entryResp, entryBody := env.DoPost("/vehicle/entry", map[string]interface{}{ "plate_number": "JingB67890", "rfid_tag": "RFID002", "parking_lot_id": 1, "parking_space_id": 10, "entry_image": "test_entry.jpg", }) AssertResponse(t, entryResp, entryBody, http.StatusOK, response.SUCCESS) fmt.Println("PASS: Vehicle entry successful") // Set entry time to 2 hours ago to skip real waiting entryTime := time.Now().Add(-2 * time.Hour) global.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingB67890").Update("entry_time", entryTime) exitPreviewResp, exitPreviewBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{ "plate_number": "JingB67890", "rfid_tag": "RFID002", }) AssertResponse(t, exitPreviewResp, exitPreviewBody, http.StatusOK, response.SUCCESS) exitConfirmResp, exitConfirmBody := env.DoPost("/vehicle/exit/confirm", map[string]interface{}{ "plate_number": "JingB67890", "rfid_tag": "RFID002", "payment_method": "cash", "paid_amount": 10.0, }) AssertResponse(t, exitConfirmResp, exitConfirmBody, http.StatusOK, response.SUCCESS) fmt.Println("PASS: Vehicle exit and payment successful") paymentResp, paymentBody := env.DoGet("/payment/list", map[string]string{ "page": "1", "page_size": "10", "plate_number": "JingB67890", }) AssertResponse(t, paymentResp, paymentBody, http.StatusOK, response.SUCCESS) list := ParsePageList(paymentBody) if len(list) == 0 { t.Fatalf("Payment record query returned empty") } fmt.Printf("PASS: Payment record query successful, total %d records\n", len(list)) } func TestBlackWhiteList(t *testing.T) { env := InitTestEnv(t) createVehicle(t, env, "JingC11111", "RFID_WHITE", 1) var vehicle dao.Vehicle env.DB.Where("plate_number = ?", "JingC11111").First(&vehicle) shortlistResp, shortlistBody := env.DoPost("/shortlist/createShortlist", dao.Shortlist{ ListType: "白名单", VehicleId: int(vehicle.ID), ExpirationTime: nil, }) AssertResponse(t, shortlistResp, shortlistBody, http.StatusOK, response.SUCCESS) resp, body := env.DoGet("/shortlist/queryAllShortlists", nil) AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS) fmt.Println("PASS: Black/white list operation successful") } // TestBlackWhiteListEntryExitBehavior 测试黑白名单对进出场的影响 // 注意:当前后端 VehicleEntry 尚未集成 CheckVehicleShortlist, // 此测试记录当前行为,待业务逻辑接入后断言应反转。 func TestBlackWhiteListEntryExitBehavior(t *testing.T) { env := InitTestEnv(t) // --- 场景1:黑名单车辆尝试进场 --- createVehicle(t, env, "JingB99999", "RFID_BLACK", 1) var blackVehicle dao.Vehicle env.DB.Where("plate_number = ?", "JingB99999").First(&blackVehicle) _, _ = env.DoPost("/shortlist/createShortlist", dao.Shortlist{ ListType: "黑名单", VehicleId: int(blackVehicle.ID), ExpirationTime: nil, }) entryResp, entryBody := env.DoPost("/vehicle/entry", map[string]interface{}{ "plate_number": "JingB99999", "rfid_tag": "RFID_BLACK", "parking_lot_id": 1, "parking_space_id": 99, "entry_image": "test_black_entry.jpg", }) // 黑名单车辆应该被拦截,进场请求应返回失败 if entryResp.StatusCode == http.StatusOK { var result struct { Code int `json:"code"` Msg string `json:"msg"` } json.Unmarshal(entryBody, &result) if result.Code == response.SUCCESS { t.Fatalf("BUG: Blacklisted vehicle entry was NOT blocked! Response: code=%d msg=%s", result.Code, result.Msg) } } AssertResponse(t, entryResp, entryBody, http.StatusOK, response.ERROR) // --- 场景2:白名单车辆进场 --- createVehicle(t, env, "JingW00001", "RFID_WHITELIST", 1) var whiteVehicle dao.Vehicle env.DB.Where("plate_number = ?", "JingW00001").First(&whiteVehicle) _, _ = env.DoPost("/shortlist/createShortlist", dao.Shortlist{ ListType: "白名单", VehicleId: int(whiteVehicle.ID), ExpirationTime: nil, }) entryResp2, entryBody2 := env.DoPost("/vehicle/entry", map[string]interface{}{ "plate_number": "JingW00001", "rfid_tag": "RFID_WHITELIST", "parking_lot_id": 1, "parking_space_id": 88, "entry_image": "test_white_entry.jpg", }) AssertResponse(t, entryResp2, entryBody2, http.StatusOK, response.SUCCESS) fmt.Println("PASS: Whitelisted vehicle entry successful") // --- 场景3:查询黑白名单列表确认两条记录存在 --- resp3, body3 := env.DoGet("/shortlist/queryAllShortlists", nil) AssertResponse(t, resp3, body3, http.StatusOK, response.SUCCESS) // queryAllShortlists 返回 data=[] 数组格式,不是分页结构 var result3 response.Response json.Unmarshal(body3, &result3) listRaw, ok := result3.Data.([]interface{}) if !ok { t.Fatalf("shortlist data is not array: %T", result3.Data) } if len(listRaw) < 2 { t.Fatalf("Expected at least 2 shortlist records, got %d", len(listRaw)) } fmt.Printf("PASS: Shortlist query returned %d records\n", len(listRaw)) // --- 场景4:白名单车辆出场,验证费用计算(VIP/白名单免单逻辑)--- exitPreviewResp, exitPreviewBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{ "plate_number": "JingW00001", "rfid_tag": "RFID_WHITELIST", }) AssertResponse(t, exitPreviewResp, exitPreviewBody, http.StatusOK, response.SUCCESS) // 修改入场时间为 1 小时前 entryTime := time.Now().Add(-1 * time.Hour) global.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingW00001").Update("entry_time", entryTime) exitConfirmResp, exitConfirmBody := env.DoPost("/vehicle/exit/confirm", map[string]interface{}{ "plate_number": "JingW00001", "rfid_tag": "RFID_WHITELIST", "payment_method": "cash", "paid_amount": 0.0, }) AssertResponse(t, exitConfirmResp, exitConfirmBody, http.StatusOK, response.SUCCESS) fmt.Println("PASS: Whitelisted vehicle exit and payment successful") } func TestOwnerManagement(t *testing.T) { env := InitTestEnv(t) ownerResp, ownerBody := env.DoPost("/owner/create", map[string]interface{}{ "owner_name": "Zhang San", "owner_surname": "Zhang", "owner_phone": "13800138000", "is_vip": true, "vip_expire_time": time.Now().Add(365 * 24 * time.Hour).Format(time.RFC3339), }) AssertResponse(t, ownerResp, ownerBody, http.StatusOK, response.SUCCESS) resp, body := env.DoGet("/owner/list", map[string]string{ "page": "1", "page_size": "10", }) AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS) list := ParsePageList(body) if len(list) == 0 { t.Fatalf("Owner list is empty after creation") } fmt.Printf("PASS: Owner management successful, total %d owners\n", len(list)) } func TestDashboardMonitoring(t *testing.T) { env := InitTestEnv(t) resp, body := env.DoGetRaw("/dashboard/monitor", nil) AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS) dataMap := ParseDataMap(body) if dataMap == nil { t.Fatalf("Dashboard data is empty") } fmt.Println("PASS: Dashboard monitoring data query successful") } // ===================== Helper functions ===================== func createVehicle(t *testing.T, env *TestEnv, plateNumber, rfidTag string, vehicleTypeID uint) { t.Helper() _, body := env.DoPost("/vehicle/create", map[string]interface{}{ "plate_number": plateNumber, "vehicle_type_id": vehicleTypeID, "vehicle_brand": "TestBrand", "vehicle_color": "TestColor", "rfid_tag": rfidTag, }) 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: %s", result.Msg) } }