import os path = r"test\testutil\e2e_edge_cases_test.go" BT = chr(96) L = [] def a(s=""): L.append(s) a("package testutil") a() a("import (") a('\t"encoding/json"') a('\t"fmt"') a('\t"net/http"') a('\t"testing"') a('\t"time"') a() a('\t"wails-app/internal/dao"') a('\t"wails-app/internal/global"') a('\t"wails-app/internal/model/common/response"') a(")") a() a("// TestVehicleReEntry 同一车辆重复入场应被拦截") a("func TestVehicleReEntry(t *testing.T) {") a('\tenv := InitTestEnv(t)') a('\tcreateVehicle(t, env, "JingR00001", "RFID_REENTRY", 1)') a('\tentryResp, _ := env.DoPost("/vehicle/entry", map[string]interface{}{') a('\t\t"plate_number": "JingR00001", "rfid_tag": "RFID_REENTRY",') a('\t\t"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",') a('\t})') a('\tAssertResponse(t, entryResp, nil, http.StatusOK, response.SUCCESS)') a() a('\t_, entryBody2 := env.DoPost("/vehicle/entry", map[string]interface{}{') a('\t\t"plate_number": "JingR00001", "rfid_tag": "RFID_REENTRY",') a('\t\t"parking_lot_id": 1, "parking_space_id": 2, "entry_image": "test2.jpg",') a('\t})') a('\tvar result struct { Code int '+BT+'json:"code"'+BT+'; Msg string '+BT+'json:"msg"'+BT+' }') a('\tjson.Unmarshal(entryBody2, &result)') a('\tif result.Code == response.SUCCESS {') a('\t\tt.Fatalf("BUG: Vehicle re-entry was NOT blocked! code=%d msg=%s", result.Code, result.Msg)') a('\t}') a('\tfmt.Println("PASS: Re-entry attempt correctly rejected")') a("}") a() a("// TestExitWithoutEntry 未入场车辆尝试出场应返回错误") a("func TestExitWithoutEntry(t *testing.T) {") a('\tenv := InitTestEnv(t)') a('\t_, exitPreviewBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{') a('\t\t"plate_number": "JingZ99999", "rfid_tag": "RFID_NOENTRY",') a('\t})') a('\tvar result struct { Code int '+BT+'json:"code"'+BT+'; Msg string '+BT+'json:"msg"'+BT+' }') a('\tjson.Unmarshal(exitPreviewBody, &result)') a('\tif result.Code == response.SUCCESS {') a('\t\tt.Fatalf("BUG: Exit preview succeeded for non-entered vehicle!")') a('\t}') a('\tfmt.Println("PASS: Exit without entry correctly rejected")') a("}") a() a("// TestPaymentInsufficientAmount 支付金额不足时的行为") a("func TestPaymentInsufficientAmount(t *testing.T) {") a('\tenv := InitTestEnv(t)') a('\tcreateVehicle(t, env, "JingP00001", "RFID_PAY", 1)') a('\t_, _ = env.DoPost("/vehicle/entry", map[string]interface{}{') a('\t\t"plate_number": "JingP00001", "rfid_tag": "RFID_PAY",') a('\t\t"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",') a('\t})') a('\tentryTime := time.Now().Add(-3 * time.Hour)') a('\tglobal.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingP00001").Update("entry_time", entryTime)') a('\t_, exitConfirmBody := env.DoPost("/vehicle/exit/confirm", map[string]interface{}{') a('\t\t"plate_number": "JingP00001", "rfid_tag": "RFID_PAY",') a('\t\t"payment_method": "cash", "paid_amount": 0.0,') a('\t})') a('\tvar result struct { Code int '+BT+'json:"code"'+BT+'; Msg string '+BT+'json:"msg"'+BT+' }') a('\tjson.Unmarshal(exitConfirmBody, &result)') a('\tfmt.Printf("INFO: Insufficient payment response: code=%d msg=%s\\n", result.Code, result.Msg)') a("}") a() a("// TestVIPExpired 过期 VIP 应恢复普通计费") a("func TestVIPExpired(t *testing.T) {") a('\tenv := InitTestEnv(t)') a('\townerResp, ownerBody := env.DoPost("/owner/create", map[string]interface{}{') a('\t\t"owner_name": "VIP Expired Test", "owner_surname": "VIP",') a('\t\t"owner_phone": "13900000001", "is_vip": true,') a('\t\t"vip_expire_time": time.Now().Add(-24*time.Hour).Format(time.RFC3339),') a('\t})') a('\tAssertResponse(t, ownerResp, ownerBody, http.StatusOK, response.SUCCESS)') a() a('\tvar owner dao.Owner') a('\tenv.DB.Where("owner_phone = ?", "13900000001").First(&owner)') a('\tcreateVehicleWithOwner(t, env, "JingV00001", "RFID_VIP_EXP", owner.ID)') a() a('\t_, _ = env.DoPost("/vehicle/entry", map[string]interface{}{') a('\t\t"plate_number": "JingV00001", "rfid_tag": "RFID_VIP_EXP",') a('\t\t"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",') a('\t})') a('\tentryTime := time.Now().Add(-3 * time.Hour)') a('\tglobal.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingV00001").Update("entry_time", entryTime)') a() a('\texitResp, exitBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{') a('\t\t"plate_number": "JingV00001", "rfid_tag": "RFID_VIP_EXP",') a('\t})') a('\tAssertResponse(t, exitResp, exitBody, http.StatusOK, response.SUCCESS)') a('\tvar preview struct { Fee float64 '+BT+'json:"fee"'+BT+' }') a('\tjson.Unmarshal(exitBody, &preview)') a('\tif preview.Fee == 0 {') a('\t\tt.Fatalf("BUG: Expired VIP still getting free parking! Fee should be > 0")') a('\t}') a('\tfmt.Printf("PASS: Expired VIP charged correctly, fee=%.2f\\n", preview.Fee)') a("}") a() a("// TestFreeDuration 免费时长内不应收费") a("func TestFreeDuration(t *testing.T) {") a('\tenv := InitTestEnv(t)') a('\tcreateVehicle(t, env, "JingF00001", "RFID_FREE", 1)') a('\t_, _ = env.DoPost("/vehicle/entry", map[string]interface{}{') a('\t\t"plate_number": "JingF00001", "rfid_tag": "RFID_FREE",') a('\t\t"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",') a('\t})') a('\tentryTime := time.Now().Add(-10 * time.Minute)') a('\tglobal.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingF00001").Update("entry_time", entryTime)') a('\texitResp, exitBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{') a('\t\t"plate_number": "JingF00001", "rfid_tag": "RFID_FREE",') a('\t})') a('\tAssertResponse(t, exitResp, exitBody, http.StatusOK, response.SUCCESS)') a('\tvar preview struct { Fee float64 '+BT+'json:"fee"'+BT+' }') a('\tjson.Unmarshal(exitBody, &preview)') a('\tif preview.Fee != 0 {') a('\t\tt.Fatalf("BUG: Vehicle within free duration should have zero fee, got %.2f", preview.Fee)') a('\t}') a('\tfmt.Println("PASS: Free duration correctly applied")') a("}") a() a("// TestDailyCap 超过每日封顶费用应封顶") a("func TestDailyCap(t *testing.T) {") a('\tenv := InitTestEnv(t)') a('\tcreateVehicle(t, env, "JingD00001", "RFID_CAP", 1)') a('\t_, _ = env.DoPost("/vehicle/entry", map[string]interface{}{') a('\t\t"plate_number": "JingD00001", "rfid_tag": "RFID_CAP",') a('\t\t"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",') a('\t})') a('\tentryTime := time.Now().Add(-24 * time.Hour)') a('\tglobal.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingD00001").Update("entry_time", entryTime)') a('\texitResp, exitBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{') a('\t\t"plate_number": "JingD00001", "rfid_tag": "RFID_CAP",') a('\t})') a('\tAssertResponse(t, exitResp, exitBody, http.StatusOK, response.SUCCESS)') a('\tvar preview struct { Fee float64 '+BT+'json:"fee"'+BT+' }') a('\tjson.Unmarshal(exitBody, &preview)') a('\tif preview.Fee > 50.0 {') a('\t\tt.Fatalf("BUG: Fee exceeds daily cap! Expected <= 50, got %.2f", preview.Fee)') a('\t}') a('\tfmt.Printf("PASS: Daily cap applied correctly, fee=%.2f\\n", preview.Fee)') a("}") a() a("// TestNoFeeConfig 无收费配置时默认计费") a("func TestNoFeeConfig(t *testing.T) {") a('\tenv := InitTestEnv(t)') a('\tvar normalType dao.VehicleType') a('\tenv.DB.Where("is_system = ? AND name != ?", true, "临时车").First(&normalType)') a('\tif normalType.ID == 0 {') a('\t\tnormalType = dao.VehicleType{Name: "特殊车", Remarks: "无收费配置"}') a('\t\tenv.DB.Create(&normalType)') a('\t}') a('\tenv.DB.Where("vehicle_type_id = ?", normalType.ID).Delete(&dao.FeeConfig{})') a('\tcreateVehicleWithOwner(t, env, "JingN00001", "RFID_NOFEE", normalType.ID)') a('\t_, _ = env.DoPost("/vehicle/entry", map[string]interface{}{') a('\t\t"plate_number": "JingN00001", "rfid_tag": "RFID_NOFEE",') a('\t\t"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",') a('\t})') a('\tentryTime := time.Now().Add(-1 * time.Hour)') a('\tglobal.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingN00001").Update("entry_time", entryTime)') a('\texitResp, exitBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{') a('\t\t"plate_number": "JingN00001", "rfid_tag": "RFID_NOFEE",') a('\t})') a('\tAssertResponse(t, exitResp, exitBody, http.StatusOK, response.SUCCESS)') a('\tvar preview struct { Fee float64 '+BT+'json:"fee"'+BT+' }') a('\tjson.Unmarshal(exitBody, &preview)') a('\tif preview.Fee <= 0 {') a('\t\tt.Fatalf("BUG: No fee config should use default rate, got fee=%.2f", preview.Fee)') a('\t}') a('\tfmt.Printf("PASS: Default fee applied correctly, fee=%.2f\\n", preview.Fee)') a("}") a() a("// TestShortlistByPlateOrRFID 黑白名单按车牌/RFID查询") a("func TestShortlistByPlateOrRFID(t *testing.T) {") a('\tenv := InitTestEnv(t)') a('\tcreateVehicle(t, env, "JingS00001", "RFID_FILTER", 1)') a('\tvar vehicle dao.Vehicle') a('\tenv.DB.Where("plate_number = ?", "JingS00001").First(&vehicle)') a('\t_, _ = env.DoPost("/shortlist/createShortlist", dao.Shortlist{') a('\t\tListType: "黑名单", VehicleId: int(vehicle.ID), ExpirationTime: nil,') a('\t})') a() a('\tresp1, body1 := env.DoPost("/shortlist/queryShortlistList", map[string]interface{}{') a('\t\t"page": 1, "pageSize": 10, "list_type": "", "plate_number": "JingS00001",') a('\t})') a('\tAssertResponse(t, resp1, body1, http.StatusOK, response.SUCCESS)') a('\tlist1 := ParsePageList(body1)') a('\tif len(list1) < 1 { t.Fatalf("Expected at least 1 shortlist record by plate number") }') a('\tfmt.Printf("PASS: Shortlist query by plate number returned %d records\\n", len(list1))') a() a('\tresp2, body2 := env.DoPost("/shortlist/queryShortlistList", map[string]interface{}{') a('\t\t"page": 1, "pageSize": 10, "list_type": "", "rfid_tag": "RFID_FILTER",') a('\t})') a('\tAssertResponse(t, resp2, body2, http.StatusOK, response.SUCCESS)') a('\tlist2 := ParsePageList(body2)') a('\tif len(list2) < 1 { t.Fatalf("Expected at least 1 shortlist record by RFID") }') a('\tfmt.Printf("PASS: Shortlist query by RFID returned %d records\\n", len(list2))') a("}") a() a("// TestShortlistUpdate 黑白名单更新") a("func TestShortlistUpdate(t *testing.T) {") a('\tenv := InitTestEnv(t)') a('\tcreateVehicle(t, env, "JingU00001", "RFID_UPDATE", 1)') a('\tvar vehicle dao.Vehicle') a('\tenv.DB.Where("plate_number = ?", "JingU00001").First(&vehicle)') a('\t_, _ = env.DoPost("/shortlist/createShortlist", dao.Shortlist{') a('\t\tListType: "白名单", VehicleId: int(vehicle.ID), ExpirationTime: nil,') a('\t})') a('\tqueryResp, queryBody := env.DoGet("/shortlist/queryAllShortlists", nil)') a('\tAssertResponse(t, queryResp, queryBody, http.StatusOK, response.SUCCESS)') a('\tvar result struct { Data []map[string]interface{} '+BT+'json:"data"'+BT+' }') a('\tjson.Unmarshal(queryBody, &result)') a('\tif len(result.Data) == 0 { t.Fatalf("No shortlist found") }') a('\tid := int(result.Data[0]["ID"].(float64))') a('\tupdateResp, updateBody := env.DoPut("/shortlist/updateShortlist", map[string]interface{}{') a('\t\t"ID": id, "list_type": "黑名单", "vehicle_id": vehicle.ID,') a('\t\t"expiration_time": time.Now().Add(30*24*time.Hour).Format(time.RFC3339),') a('\t})') a('\tAssertResponse(t, updateResp, updateBody, http.StatusOK, response.SUCCESS)') a('\tupdatedResp, updatedBody := env.DoGet("/shortlist/queryAllShortlists", nil)') a('\tAssertResponse(t, updatedResp, updatedBody, http.StatusOK, response.SUCCESS)') a('\tfmt.Println("PASS: Shortlist update successful")') a("}") a() a("// TestShortlistDelete 黑白名单删除") a("func TestShortlistDelete(t *testing.T) {") a('\tenv := InitTestEnv(t)') a('\tcreateVehicle(t, env, "JingDel001", "RFID_DELETE", 1)') a('\tvar vehicle dao.Vehicle') a('\tenv.DB.Where("plate_number = ?", "JingDel001").First(&vehicle)') a('\t_, _ = env.DoPost("/shortlist/createShortlist", dao.Shortlist{') a('\t\tListType: "黑名单", VehicleId: int(vehicle.ID), ExpirationTime: nil,') a('\t})') a('\tqueryResp, queryBody := env.DoGet("/shortlist/queryAllShortlists", nil)') a('\tAssertResponse(t, queryResp, queryBody, http.StatusOK, response.SUCCESS)') a('\tvar result struct { Data []map[string]interface{} '+BT+'json:"data"'+BT+' }') a('\tjson.Unmarshal(queryBody, &result)') a('\tif len(result.Data) == 0 { t.Fatalf("No shortlist found") }') a('\tid := int(result.Data[0]["ID"].(float64))') a('\tdeleteResp, deleteBody := env.DoDelete(fmt.Sprintf("/shortlist/deleteShortlist?id=%d", id))') a('\tAssertResponse(t, deleteResp, deleteBody, http.StatusOK, response.SUCCESS)') a('\tfmt.Println("PASS: Shortlist delete successful")') a("}") a() a("// TestOwnerVehicleRelationship 车主关联车辆查询") a("func TestOwnerVehicleRelationship(t *testing.T) {") a('\tenv := InitTestEnv(t)') a('\townerResp, ownerBody := env.DoPost("/owner/create", map[string]interface{}{') a('\t\t"owner_name": "Rel Test Owner", "owner_surname": "REL",') a('\t\t"owner_phone": "13911111111", "is_vip": false, "vip_expire_time": nil,') a('\t})') a('\tAssertResponse(t, ownerResp, ownerBody, http.StatusOK, response.SUCCESS)') a('\tvar owner dao.Owner') a('\tenv.DB.Where("owner_phone = ?", "13911111111").First(&owner)') a('\tcreateVehicleWithOwner(t, env, "JingO00001", "RFID_OWNER", owner.ID)') a('\townerListResp, ownerListBody := env.DoGet("/owner/list", map[string]string{"page": "1", "page_size": "10"})') a('\tAssertResponse(t, ownerListResp, ownerListBody, http.StatusOK, response.SUCCESS)') a('\tlist := ParsePageList(ownerListBody)') a('\tfound := false') a('\tfor _, item := range list {') a('\t\tm, ok := item.(map[string]interface{})') a('\t\tif !ok { continue }') a('\t\tif m["owner_phone"] == "13911111111" { found = true; break }') a('\t}') a('\tif !found { t.Fatalf("Owner not found in list") }') a('\tfmt.Println("PASS: Owner-vehicle relationship verified")') a("}") a() a("// TestParkingLotCapacity 停车场容量检查") a("func TestParkingLotCapacity(t *testing.T) {") a('\tenv := InitTestEnv(t)') a('\tlotResp, lotBody := env.DoPost("/parking/lot/create", map[string]interface{}{') a('\t\t"lot_code": "TEST_EMPTY", "lot_name": "Empty Lot",') a('\t\t"capacity": 0, "description": "Zero capacity test",') a('\t})') a('\tAssertResponse(t, lotResp, lotBody, http.StatusOK, response.SUCCESS)') a('\tcreateVehicle(t, env, "JingC00001", "RFID_FULL", 1)') a('\t_, entryBody := env.DoPost("/vehicle/entry", map[string]interface{}{') a('\t\t"plate_number": "JingC00001", "rfid_tag": "RFID_FULL",') a('\t\t"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",') a('\t})') a('\tvar result struct { Code int '+BT+'json:"code"'+BT+'; Msg string '+BT+'json:"msg"'+BT+' }') a('\tjson.Unmarshal(entryBody, &result)') a('\tfmt.Printf("INFO: Entry into zero-capacity lot: code=%d msg=%s\\n", result.Code, result.Msg)') a("}") a() a("// TestBoothCRUD 岗亭CRUD") a("func TestBoothCRUD(t *testing.T) {") a('\tenv := InitTestEnv(t)') a('\tboothResp, boothBody := env.DoPost("/parking/booth/create", map[string]interface{}{') a('\t\t"booth_code": "BT001", "booth_name": "Test Booth",') a('\t\t"parking_lot_id": 1, "description": "Automated test booth",') a('\t})') a('\tAssertResponse(t, boothResp, boothBody, http.StatusOK, response.SUCCESS)') a('\tlistResp, listBody := env.DoGet("/parking/booth/list", map[string]string{"page": "1", "page_size": "10"})') a('\tAssertResponse(t, listResp, listBody, http.StatusOK, response.SUCCESS)') a('\tlist := ParsePageList(listBody)') a('\tif len(list) < 1 { t.Fatalf("Booth list is empty") }') a('\tfmt.Printf("PASS: Booth CRUD successful, total %d booths\\n", len(list))') a("}") a() a("// TestChannelCRUD 通道CRUD") a("func TestChannelCRUD(t *testing.T) {") a('\tenv := InitTestEnv(t)') a('\tchannelResp, channelBody := env.DoPost("/parking/channel/create", map[string]interface{}{') a('\t\t"channel_code": "CH-IN-TEST", "channel_name": "Test Entrance",') a('\t\t"direction": "in", "parking_lot_id": 1, "booth_id": 1,') a('\t\t"allow_temporary": true, "description": "Automated test channel",') a('\t})') a('\tAssertResponse(t, channelResp, channelBody, http.StatusOK, response.SUCCESS)') a('\tlistResp, listBody := env.DoGet("/parking/channel/list", map[string]string{"page": "1", "page_size": "10"})') a('\tAssertResponse(t, listResp, listBody, http.StatusOK, response.SUCCESS)') a('\tlist := ParsePageList(listBody)') a('\tif len(list) < 1 { t.Fatalf("Channel list is empty") }') a('\tfmt.Printf("PASS: Channel CRUD successful, total %d channels\\n", len(list))') a("}") a() a("// TestDeviceCRUD 设备CRUD") a("func TestDeviceCRUD(t *testing.T) {") a('\tenv := InitTestEnv(t)') a('\tdeviceResp, deviceBody := env.DoPost("/parking/device/create", map[string]interface{}{') a('\t\t"device_code": "DEV001", "device_name": "Test Device",') a('\t\t"device_type": "TCP", "connect_type": "tcp", "ip_address": "127.0.0.1",') a('\t\t"port": 9000, "is_active": true, "parking_lot_id": 1, "channel_id": 1,') a('\t\t"description": "Automated test device",') a('\t})') a('\tAssertResponse(t, deviceResp, deviceBody, http.StatusOK, response.SUCCESS)') a('\tlistResp, listBody := env.DoGet("/parking/device/list", map[string]string{"page": "1", "page_size": "10"})') a('\tAssertResponse(t, listResp, listBody, http.StatusOK, response.SUCCESS)') a('\tlist := ParsePageList(listBody)') a('\tif len(list) < 1 { t.Fatalf("Device list is empty") }') a('\tfmt.Printf("PASS: Device CRUD successful, total %d devices\\n", len(list))') a("}") a() a("// TestVehicleTypeCRUD 自定义车辆类型CRUD") a("func TestVehicleTypeCRUD(t *testing.T) {") a('\tenv := InitTestEnv(t)') a('\ttypeResp, typeBody := env.DoPost("/vehicle/type/create", map[string]interface{}{') a('\t\t"name": "新能源车", "remarks": "测试新能源车辆类型", "is_system": false,') a('\t})') a('\tAssertResponse(t, typeResp, typeBody, http.StatusOK, response.SUCCESS)') a('\tlistResp, listBody := env.DoGet("/vehicle/type/all", nil)') a('\tAssertResponse(t, listResp, listBody, http.StatusOK, response.SUCCESS)') a('\tlist := ParsePageList(listBody)') a('\tif len(list) < 1 { t.Fatalf("Vehicle type list is empty") }') a('\tfmt.Printf("PASS: Vehicle type CRUD successful, total %d types\\n", len(list))') a("}") a() a("// ===================== Helper functions =====================") a() a("func createVehicleWithOwner(t *testing.T, env *TestEnv, plateNumber, rfidTag string, ownerID uint) {") a('\tt.Helper()') a('\t_, body := env.DoPost("/vehicle/create", map[string]interface{}{') a('\t\t"plate_number": plateNumber, "vehicle_type_id": 1,') a('\t\t"vehicle_brand": "TestBrand", "vehicle_color": "TestColor",') a('\t\t"rfid_tag": rfidTag, "owner_id": ownerID,') a('\t})') a('\tvar result struct { Code int '+BT+'json:"code"'+BT+'; Msg string '+BT+'json:"msg"'+BT+' }') a('\tjson.Unmarshal(body, &result)') a('\tif result.Code != response.SUCCESS {') a('\t\tt.Fatalf("Failed to create vehicle with owner: %s", result.Msg)') a('\t}') a("}") content = '\n'.join(L) + '\n' with open(path, 'w', encoding='utf-8') as f: f.write(content) print(f"Written {len(content)} bytes to {path}")