_write_edge.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. import os
  2. path = r"test\testutil\e2e_edge_cases_test.go"
  3. BT = chr(96)
  4. L = []
  5. def a(s=""):
  6. L.append(s)
  7. a("package testutil")
  8. a()
  9. a("import (")
  10. a('\t"encoding/json"')
  11. a('\t"fmt"')
  12. a('\t"net/http"')
  13. a('\t"testing"')
  14. a('\t"time"')
  15. a()
  16. a('\t"wails-app/internal/dao"')
  17. a('\t"wails-app/internal/global"')
  18. a('\t"wails-app/internal/model/common/response"')
  19. a(")")
  20. a()
  21. a("// TestVehicleReEntry 同一车辆重复入场应被拦截")
  22. a("func TestVehicleReEntry(t *testing.T) {")
  23. a('\tenv := InitTestEnv(t)')
  24. a('\tcreateVehicle(t, env, "JingR00001", "RFID_REENTRY", 1)')
  25. a('\tentryResp, _ := env.DoPost("/vehicle/entry", map[string]interface{}{')
  26. a('\t\t"plate_number": "JingR00001", "rfid_tag": "RFID_REENTRY",')
  27. a('\t\t"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",')
  28. a('\t})')
  29. a('\tAssertResponse(t, entryResp, nil, http.StatusOK, response.SUCCESS)')
  30. a()
  31. a('\t_, entryBody2 := env.DoPost("/vehicle/entry", map[string]interface{}{')
  32. a('\t\t"plate_number": "JingR00001", "rfid_tag": "RFID_REENTRY",')
  33. a('\t\t"parking_lot_id": 1, "parking_space_id": 2, "entry_image": "test2.jpg",')
  34. a('\t})')
  35. a('\tvar result struct { Code int '+BT+'json:"code"'+BT+'; Msg string '+BT+'json:"msg"'+BT+' }')
  36. a('\tjson.Unmarshal(entryBody2, &result)')
  37. a('\tif result.Code == response.SUCCESS {')
  38. a('\t\tt.Fatalf("BUG: Vehicle re-entry was NOT blocked! code=%d msg=%s", result.Code, result.Msg)')
  39. a('\t}')
  40. a('\tfmt.Println("PASS: Re-entry attempt correctly rejected")')
  41. a("}")
  42. a()
  43. a("// TestExitWithoutEntry 未入场车辆尝试出场应返回错误")
  44. a("func TestExitWithoutEntry(t *testing.T) {")
  45. a('\tenv := InitTestEnv(t)')
  46. a('\t_, exitPreviewBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{')
  47. a('\t\t"plate_number": "JingZ99999", "rfid_tag": "RFID_NOENTRY",')
  48. a('\t})')
  49. a('\tvar result struct { Code int '+BT+'json:"code"'+BT+'; Msg string '+BT+'json:"msg"'+BT+' }')
  50. a('\tjson.Unmarshal(exitPreviewBody, &result)')
  51. a('\tif result.Code == response.SUCCESS {')
  52. a('\t\tt.Fatalf("BUG: Exit preview succeeded for non-entered vehicle!")')
  53. a('\t}')
  54. a('\tfmt.Println("PASS: Exit without entry correctly rejected")')
  55. a("}")
  56. a()
  57. a("// TestPaymentInsufficientAmount 支付金额不足时的行为")
  58. a("func TestPaymentInsufficientAmount(t *testing.T) {")
  59. a('\tenv := InitTestEnv(t)')
  60. a('\tcreateVehicle(t, env, "JingP00001", "RFID_PAY", 1)')
  61. a('\t_, _ = env.DoPost("/vehicle/entry", map[string]interface{}{')
  62. a('\t\t"plate_number": "JingP00001", "rfid_tag": "RFID_PAY",')
  63. a('\t\t"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",')
  64. a('\t})')
  65. a('\tentryTime := time.Now().Add(-3 * time.Hour)')
  66. a('\tglobal.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingP00001").Update("entry_time", entryTime)')
  67. a('\t_, exitConfirmBody := env.DoPost("/vehicle/exit/confirm", map[string]interface{}{')
  68. a('\t\t"plate_number": "JingP00001", "rfid_tag": "RFID_PAY",')
  69. a('\t\t"payment_method": "cash", "paid_amount": 0.0,')
  70. a('\t})')
  71. a('\tvar result struct { Code int '+BT+'json:"code"'+BT+'; Msg string '+BT+'json:"msg"'+BT+' }')
  72. a('\tjson.Unmarshal(exitConfirmBody, &result)')
  73. a('\tfmt.Printf("INFO: Insufficient payment response: code=%d msg=%s\\n", result.Code, result.Msg)')
  74. a("}")
  75. a()
  76. a("// TestVIPExpired 过期 VIP 应恢复普通计费")
  77. a("func TestVIPExpired(t *testing.T) {")
  78. a('\tenv := InitTestEnv(t)')
  79. a('\townerResp, ownerBody := env.DoPost("/owner/create", map[string]interface{}{')
  80. a('\t\t"owner_name": "VIP Expired Test", "owner_surname": "VIP",')
  81. a('\t\t"owner_phone": "13900000001", "is_vip": true,')
  82. a('\t\t"vip_expire_time": time.Now().Add(-24*time.Hour).Format(time.RFC3339),')
  83. a('\t})')
  84. a('\tAssertResponse(t, ownerResp, ownerBody, http.StatusOK, response.SUCCESS)')
  85. a()
  86. a('\tvar owner dao.Owner')
  87. a('\tenv.DB.Where("owner_phone = ?", "13900000001").First(&owner)')
  88. a('\tcreateVehicleWithOwner(t, env, "JingV00001", "RFID_VIP_EXP", owner.ID)')
  89. a()
  90. a('\t_, _ = env.DoPost("/vehicle/entry", map[string]interface{}{')
  91. a('\t\t"plate_number": "JingV00001", "rfid_tag": "RFID_VIP_EXP",')
  92. a('\t\t"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",')
  93. a('\t})')
  94. a('\tentryTime := time.Now().Add(-3 * time.Hour)')
  95. a('\tglobal.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingV00001").Update("entry_time", entryTime)')
  96. a()
  97. a('\texitResp, exitBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{')
  98. a('\t\t"plate_number": "JingV00001", "rfid_tag": "RFID_VIP_EXP",')
  99. a('\t})')
  100. a('\tAssertResponse(t, exitResp, exitBody, http.StatusOK, response.SUCCESS)')
  101. a('\tvar preview struct { Fee float64 '+BT+'json:"fee"'+BT+' }')
  102. a('\tjson.Unmarshal(exitBody, &preview)')
  103. a('\tif preview.Fee == 0 {')
  104. a('\t\tt.Fatalf("BUG: Expired VIP still getting free parking! Fee should be > 0")')
  105. a('\t}')
  106. a('\tfmt.Printf("PASS: Expired VIP charged correctly, fee=%.2f\\n", preview.Fee)')
  107. a("}")
  108. a()
  109. a("// TestFreeDuration 免费时长内不应收费")
  110. a("func TestFreeDuration(t *testing.T) {")
  111. a('\tenv := InitTestEnv(t)')
  112. a('\tcreateVehicle(t, env, "JingF00001", "RFID_FREE", 1)')
  113. a('\t_, _ = env.DoPost("/vehicle/entry", map[string]interface{}{')
  114. a('\t\t"plate_number": "JingF00001", "rfid_tag": "RFID_FREE",')
  115. a('\t\t"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",')
  116. a('\t})')
  117. a('\tentryTime := time.Now().Add(-10 * time.Minute)')
  118. a('\tglobal.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingF00001").Update("entry_time", entryTime)')
  119. a('\texitResp, exitBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{')
  120. a('\t\t"plate_number": "JingF00001", "rfid_tag": "RFID_FREE",')
  121. a('\t})')
  122. a('\tAssertResponse(t, exitResp, exitBody, http.StatusOK, response.SUCCESS)')
  123. a('\tvar preview struct { Fee float64 '+BT+'json:"fee"'+BT+' }')
  124. a('\tjson.Unmarshal(exitBody, &preview)')
  125. a('\tif preview.Fee != 0 {')
  126. a('\t\tt.Fatalf("BUG: Vehicle within free duration should have zero fee, got %.2f", preview.Fee)')
  127. a('\t}')
  128. a('\tfmt.Println("PASS: Free duration correctly applied")')
  129. a("}")
  130. a()
  131. a("// TestDailyCap 超过每日封顶费用应封顶")
  132. a("func TestDailyCap(t *testing.T) {")
  133. a('\tenv := InitTestEnv(t)')
  134. a('\tcreateVehicle(t, env, "JingD00001", "RFID_CAP", 1)')
  135. a('\t_, _ = env.DoPost("/vehicle/entry", map[string]interface{}{')
  136. a('\t\t"plate_number": "JingD00001", "rfid_tag": "RFID_CAP",')
  137. a('\t\t"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",')
  138. a('\t})')
  139. a('\tentryTime := time.Now().Add(-24 * time.Hour)')
  140. a('\tglobal.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingD00001").Update("entry_time", entryTime)')
  141. a('\texitResp, exitBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{')
  142. a('\t\t"plate_number": "JingD00001", "rfid_tag": "RFID_CAP",')
  143. a('\t})')
  144. a('\tAssertResponse(t, exitResp, exitBody, http.StatusOK, response.SUCCESS)')
  145. a('\tvar preview struct { Fee float64 '+BT+'json:"fee"'+BT+' }')
  146. a('\tjson.Unmarshal(exitBody, &preview)')
  147. a('\tif preview.Fee > 50.0 {')
  148. a('\t\tt.Fatalf("BUG: Fee exceeds daily cap! Expected <= 50, got %.2f", preview.Fee)')
  149. a('\t}')
  150. a('\tfmt.Printf("PASS: Daily cap applied correctly, fee=%.2f\\n", preview.Fee)')
  151. a("}")
  152. a()
  153. a("// TestNoFeeConfig 无收费配置时默认计费")
  154. a("func TestNoFeeConfig(t *testing.T) {")
  155. a('\tenv := InitTestEnv(t)')
  156. a('\tvar normalType dao.VehicleType')
  157. a('\tenv.DB.Where("is_system = ? AND name != ?", true, "临时车").First(&normalType)')
  158. a('\tif normalType.ID == 0 {')
  159. a('\t\tnormalType = dao.VehicleType{Name: "特殊车", Remarks: "无收费配置"}')
  160. a('\t\tenv.DB.Create(&normalType)')
  161. a('\t}')
  162. a('\tenv.DB.Where("vehicle_type_id = ?", normalType.ID).Delete(&dao.FeeConfig{})')
  163. a('\tcreateVehicleWithOwner(t, env, "JingN00001", "RFID_NOFEE", normalType.ID)')
  164. a('\t_, _ = env.DoPost("/vehicle/entry", map[string]interface{}{')
  165. a('\t\t"plate_number": "JingN00001", "rfid_tag": "RFID_NOFEE",')
  166. a('\t\t"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",')
  167. a('\t})')
  168. a('\tentryTime := time.Now().Add(-1 * time.Hour)')
  169. a('\tglobal.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingN00001").Update("entry_time", entryTime)')
  170. a('\texitResp, exitBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{')
  171. a('\t\t"plate_number": "JingN00001", "rfid_tag": "RFID_NOFEE",')
  172. a('\t})')
  173. a('\tAssertResponse(t, exitResp, exitBody, http.StatusOK, response.SUCCESS)')
  174. a('\tvar preview struct { Fee float64 '+BT+'json:"fee"'+BT+' }')
  175. a('\tjson.Unmarshal(exitBody, &preview)')
  176. a('\tif preview.Fee <= 0 {')
  177. a('\t\tt.Fatalf("BUG: No fee config should use default rate, got fee=%.2f", preview.Fee)')
  178. a('\t}')
  179. a('\tfmt.Printf("PASS: Default fee applied correctly, fee=%.2f\\n", preview.Fee)')
  180. a("}")
  181. a()
  182. a("// TestShortlistByPlateOrRFID 黑白名单按车牌/RFID查询")
  183. a("func TestShortlistByPlateOrRFID(t *testing.T) {")
  184. a('\tenv := InitTestEnv(t)')
  185. a('\tcreateVehicle(t, env, "JingS00001", "RFID_FILTER", 1)')
  186. a('\tvar vehicle dao.Vehicle')
  187. a('\tenv.DB.Where("plate_number = ?", "JingS00001").First(&vehicle)')
  188. a('\t_, _ = env.DoPost("/shortlist/createShortlist", dao.Shortlist{')
  189. a('\t\tListType: "黑名单", VehicleId: int(vehicle.ID), ExpirationTime: nil,')
  190. a('\t})')
  191. a()
  192. a('\tresp1, body1 := env.DoPost("/shortlist/queryShortlistList", map[string]interface{}{')
  193. a('\t\t"page": 1, "pageSize": 10, "list_type": "", "plate_number": "JingS00001",')
  194. a('\t})')
  195. a('\tAssertResponse(t, resp1, body1, http.StatusOK, response.SUCCESS)')
  196. a('\tlist1 := ParsePageList(body1)')
  197. a('\tif len(list1) < 1 { t.Fatalf("Expected at least 1 shortlist record by plate number") }')
  198. a('\tfmt.Printf("PASS: Shortlist query by plate number returned %d records\\n", len(list1))')
  199. a()
  200. a('\tresp2, body2 := env.DoPost("/shortlist/queryShortlistList", map[string]interface{}{')
  201. a('\t\t"page": 1, "pageSize": 10, "list_type": "", "rfid_tag": "RFID_FILTER",')
  202. a('\t})')
  203. a('\tAssertResponse(t, resp2, body2, http.StatusOK, response.SUCCESS)')
  204. a('\tlist2 := ParsePageList(body2)')
  205. a('\tif len(list2) < 1 { t.Fatalf("Expected at least 1 shortlist record by RFID") }')
  206. a('\tfmt.Printf("PASS: Shortlist query by RFID returned %d records\\n", len(list2))')
  207. a("}")
  208. a()
  209. a("// TestShortlistUpdate 黑白名单更新")
  210. a("func TestShortlistUpdate(t *testing.T) {")
  211. a('\tenv := InitTestEnv(t)')
  212. a('\tcreateVehicle(t, env, "JingU00001", "RFID_UPDATE", 1)')
  213. a('\tvar vehicle dao.Vehicle')
  214. a('\tenv.DB.Where("plate_number = ?", "JingU00001").First(&vehicle)')
  215. a('\t_, _ = env.DoPost("/shortlist/createShortlist", dao.Shortlist{')
  216. a('\t\tListType: "白名单", VehicleId: int(vehicle.ID), ExpirationTime: nil,')
  217. a('\t})')
  218. a('\tqueryResp, queryBody := env.DoGet("/shortlist/queryAllShortlists", nil)')
  219. a('\tAssertResponse(t, queryResp, queryBody, http.StatusOK, response.SUCCESS)')
  220. a('\tvar result struct { Data []map[string]interface{} '+BT+'json:"data"'+BT+' }')
  221. a('\tjson.Unmarshal(queryBody, &result)')
  222. a('\tif len(result.Data) == 0 { t.Fatalf("No shortlist found") }')
  223. a('\tid := int(result.Data[0]["ID"].(float64))')
  224. a('\tupdateResp, updateBody := env.DoPut("/shortlist/updateShortlist", map[string]interface{}{')
  225. a('\t\t"ID": id, "list_type": "黑名单", "vehicle_id": vehicle.ID,')
  226. a('\t\t"expiration_time": time.Now().Add(30*24*time.Hour).Format(time.RFC3339),')
  227. a('\t})')
  228. a('\tAssertResponse(t, updateResp, updateBody, http.StatusOK, response.SUCCESS)')
  229. a('\tupdatedResp, updatedBody := env.DoGet("/shortlist/queryAllShortlists", nil)')
  230. a('\tAssertResponse(t, updatedResp, updatedBody, http.StatusOK, response.SUCCESS)')
  231. a('\tfmt.Println("PASS: Shortlist update successful")')
  232. a("}")
  233. a()
  234. a("// TestShortlistDelete 黑白名单删除")
  235. a("func TestShortlistDelete(t *testing.T) {")
  236. a('\tenv := InitTestEnv(t)')
  237. a('\tcreateVehicle(t, env, "JingDel001", "RFID_DELETE", 1)')
  238. a('\tvar vehicle dao.Vehicle')
  239. a('\tenv.DB.Where("plate_number = ?", "JingDel001").First(&vehicle)')
  240. a('\t_, _ = env.DoPost("/shortlist/createShortlist", dao.Shortlist{')
  241. a('\t\tListType: "黑名单", VehicleId: int(vehicle.ID), ExpirationTime: nil,')
  242. a('\t})')
  243. a('\tqueryResp, queryBody := env.DoGet("/shortlist/queryAllShortlists", nil)')
  244. a('\tAssertResponse(t, queryResp, queryBody, http.StatusOK, response.SUCCESS)')
  245. a('\tvar result struct { Data []map[string]interface{} '+BT+'json:"data"'+BT+' }')
  246. a('\tjson.Unmarshal(queryBody, &result)')
  247. a('\tif len(result.Data) == 0 { t.Fatalf("No shortlist found") }')
  248. a('\tid := int(result.Data[0]["ID"].(float64))')
  249. a('\tdeleteResp, deleteBody := env.DoDelete(fmt.Sprintf("/shortlist/deleteShortlist?id=%d", id))')
  250. a('\tAssertResponse(t, deleteResp, deleteBody, http.StatusOK, response.SUCCESS)')
  251. a('\tfmt.Println("PASS: Shortlist delete successful")')
  252. a("}")
  253. a()
  254. a("// TestOwnerVehicleRelationship 车主关联车辆查询")
  255. a("func TestOwnerVehicleRelationship(t *testing.T) {")
  256. a('\tenv := InitTestEnv(t)')
  257. a('\townerResp, ownerBody := env.DoPost("/owner/create", map[string]interface{}{')
  258. a('\t\t"owner_name": "Rel Test Owner", "owner_surname": "REL",')
  259. a('\t\t"owner_phone": "13911111111", "is_vip": false, "vip_expire_time": nil,')
  260. a('\t})')
  261. a('\tAssertResponse(t, ownerResp, ownerBody, http.StatusOK, response.SUCCESS)')
  262. a('\tvar owner dao.Owner')
  263. a('\tenv.DB.Where("owner_phone = ?", "13911111111").First(&owner)')
  264. a('\tcreateVehicleWithOwner(t, env, "JingO00001", "RFID_OWNER", owner.ID)')
  265. a('\townerListResp, ownerListBody := env.DoGet("/owner/list", map[string]string{"page": "1", "page_size": "10"})')
  266. a('\tAssertResponse(t, ownerListResp, ownerListBody, http.StatusOK, response.SUCCESS)')
  267. a('\tlist := ParsePageList(ownerListBody)')
  268. a('\tfound := false')
  269. a('\tfor _, item := range list {')
  270. a('\t\tm, ok := item.(map[string]interface{})')
  271. a('\t\tif !ok { continue }')
  272. a('\t\tif m["owner_phone"] == "13911111111" { found = true; break }')
  273. a('\t}')
  274. a('\tif !found { t.Fatalf("Owner not found in list") }')
  275. a('\tfmt.Println("PASS: Owner-vehicle relationship verified")')
  276. a("}")
  277. a()
  278. a("// TestParkingLotCapacity 停车场容量检查")
  279. a("func TestParkingLotCapacity(t *testing.T) {")
  280. a('\tenv := InitTestEnv(t)')
  281. a('\tlotResp, lotBody := env.DoPost("/parking/lot/create", map[string]interface{}{')
  282. a('\t\t"lot_code": "TEST_EMPTY", "lot_name": "Empty Lot",')
  283. a('\t\t"capacity": 0, "description": "Zero capacity test",')
  284. a('\t})')
  285. a('\tAssertResponse(t, lotResp, lotBody, http.StatusOK, response.SUCCESS)')
  286. a('\tcreateVehicle(t, env, "JingC00001", "RFID_FULL", 1)')
  287. a('\t_, entryBody := env.DoPost("/vehicle/entry", map[string]interface{}{')
  288. a('\t\t"plate_number": "JingC00001", "rfid_tag": "RFID_FULL",')
  289. a('\t\t"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",')
  290. a('\t})')
  291. a('\tvar result struct { Code int '+BT+'json:"code"'+BT+'; Msg string '+BT+'json:"msg"'+BT+' }')
  292. a('\tjson.Unmarshal(entryBody, &result)')
  293. a('\tfmt.Printf("INFO: Entry into zero-capacity lot: code=%d msg=%s\\n", result.Code, result.Msg)')
  294. a("}")
  295. a()
  296. a("// TestBoothCRUD 岗亭CRUD")
  297. a("func TestBoothCRUD(t *testing.T) {")
  298. a('\tenv := InitTestEnv(t)')
  299. a('\tboothResp, boothBody := env.DoPost("/parking/booth/create", map[string]interface{}{')
  300. a('\t\t"booth_code": "BT001", "booth_name": "Test Booth",')
  301. a('\t\t"parking_lot_id": 1, "description": "Automated test booth",')
  302. a('\t})')
  303. a('\tAssertResponse(t, boothResp, boothBody, http.StatusOK, response.SUCCESS)')
  304. a('\tlistResp, listBody := env.DoGet("/parking/booth/list", map[string]string{"page": "1", "page_size": "10"})')
  305. a('\tAssertResponse(t, listResp, listBody, http.StatusOK, response.SUCCESS)')
  306. a('\tlist := ParsePageList(listBody)')
  307. a('\tif len(list) < 1 { t.Fatalf("Booth list is empty") }')
  308. a('\tfmt.Printf("PASS: Booth CRUD successful, total %d booths\\n", len(list))')
  309. a("}")
  310. a()
  311. a("// TestChannelCRUD 通道CRUD")
  312. a("func TestChannelCRUD(t *testing.T) {")
  313. a('\tenv := InitTestEnv(t)')
  314. a('\tchannelResp, channelBody := env.DoPost("/parking/channel/create", map[string]interface{}{')
  315. a('\t\t"channel_code": "CH-IN-TEST", "channel_name": "Test Entrance",')
  316. a('\t\t"direction": "in", "parking_lot_id": 1, "booth_id": 1,')
  317. a('\t\t"allow_temporary": true, "description": "Automated test channel",')
  318. a('\t})')
  319. a('\tAssertResponse(t, channelResp, channelBody, http.StatusOK, response.SUCCESS)')
  320. a('\tlistResp, listBody := env.DoGet("/parking/channel/list", map[string]string{"page": "1", "page_size": "10"})')
  321. a('\tAssertResponse(t, listResp, listBody, http.StatusOK, response.SUCCESS)')
  322. a('\tlist := ParsePageList(listBody)')
  323. a('\tif len(list) < 1 { t.Fatalf("Channel list is empty") }')
  324. a('\tfmt.Printf("PASS: Channel CRUD successful, total %d channels\\n", len(list))')
  325. a("}")
  326. a()
  327. a("// TestDeviceCRUD 设备CRUD")
  328. a("func TestDeviceCRUD(t *testing.T) {")
  329. a('\tenv := InitTestEnv(t)')
  330. a('\tdeviceResp, deviceBody := env.DoPost("/parking/device/create", map[string]interface{}{')
  331. a('\t\t"device_code": "DEV001", "device_name": "Test Device",')
  332. a('\t\t"device_type": "TCP", "connect_type": "tcp", "ip_address": "127.0.0.1",')
  333. a('\t\t"port": 9000, "is_active": true, "parking_lot_id": 1, "channel_id": 1,')
  334. a('\t\t"description": "Automated test device",')
  335. a('\t})')
  336. a('\tAssertResponse(t, deviceResp, deviceBody, http.StatusOK, response.SUCCESS)')
  337. a('\tlistResp, listBody := env.DoGet("/parking/device/list", map[string]string{"page": "1", "page_size": "10"})')
  338. a('\tAssertResponse(t, listResp, listBody, http.StatusOK, response.SUCCESS)')
  339. a('\tlist := ParsePageList(listBody)')
  340. a('\tif len(list) < 1 { t.Fatalf("Device list is empty") }')
  341. a('\tfmt.Printf("PASS: Device CRUD successful, total %d devices\\n", len(list))')
  342. a("}")
  343. a()
  344. a("// TestVehicleTypeCRUD 自定义车辆类型CRUD")
  345. a("func TestVehicleTypeCRUD(t *testing.T) {")
  346. a('\tenv := InitTestEnv(t)')
  347. a('\ttypeResp, typeBody := env.DoPost("/vehicle/type/create", map[string]interface{}{')
  348. a('\t\t"name": "新能源车", "remarks": "测试新能源车辆类型", "is_system": false,')
  349. a('\t})')
  350. a('\tAssertResponse(t, typeResp, typeBody, http.StatusOK, response.SUCCESS)')
  351. a('\tlistResp, listBody := env.DoGet("/vehicle/type/all", nil)')
  352. a('\tAssertResponse(t, listResp, listBody, http.StatusOK, response.SUCCESS)')
  353. a('\tlist := ParsePageList(listBody)')
  354. a('\tif len(list) < 1 { t.Fatalf("Vehicle type list is empty") }')
  355. a('\tfmt.Printf("PASS: Vehicle type CRUD successful, total %d types\\n", len(list))')
  356. a("}")
  357. a()
  358. a("// ===================== Helper functions =====================")
  359. a()
  360. a("func createVehicleWithOwner(t *testing.T, env *TestEnv, plateNumber, rfidTag string, ownerID uint) {")
  361. a('\tt.Helper()')
  362. a('\t_, body := env.DoPost("/vehicle/create", map[string]interface{}{')
  363. a('\t\t"plate_number": plateNumber, "vehicle_type_id": 1,')
  364. a('\t\t"vehicle_brand": "TestBrand", "vehicle_color": "TestColor",')
  365. a('\t\t"rfid_tag": rfidTag, "owner_id": ownerID,')
  366. a('\t})')
  367. a('\tvar result struct { Code int '+BT+'json:"code"'+BT+'; Msg string '+BT+'json:"msg"'+BT+' }')
  368. a('\tjson.Unmarshal(body, &result)')
  369. a('\tif result.Code != response.SUCCESS {')
  370. a('\t\tt.Fatalf("Failed to create vehicle with owner: %s", result.Msg)')
  371. a('\t}')
  372. a("}")
  373. content = '\n'.join(L) + '\n'
  374. with open(path, 'w', encoding='utf-8') as f:
  375. f.write(content)
  376. print(f"Written {len(content)} bytes to {path}")