e2e_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. package testutil
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/http"
  6. "testing"
  7. "time"
  8. "wails-app/internal/dao"
  9. "wails-app/internal/global"
  10. "wails-app/internal/model/common/response"
  11. "wails-app/internal/utils"
  12. )
  13. // ===================== 端到端测试用例 =====================
  14. func TestLoginFlow(t *testing.T) {
  15. env := InitTestEnv(t)
  16. if env.Token == "" {
  17. t.Fatalf("Token is empty")
  18. }
  19. j := &utils.JWT{SigningKey: []byte(global.GVA_CONFIG.JWT.SigningKey)}
  20. claims, err := j.ParseToken(env.Token)
  21. if err != nil {
  22. t.Fatalf("Token parse failed: %v", err)
  23. }
  24. if claims.Username != "admin" || claims.AuthorityId != 888 {
  25. t.Fatalf("Token claims mismatch: username=%s, authorityId=%d", claims.Username, claims.AuthorityId)
  26. }
  27. fmt.Printf("PASS: Login flow verified, Token: %s...\n", env.Token[:min(20, len(env.Token))])
  28. }
  29. func TestSystemManagement(t *testing.T) {
  30. env := InitTestEnv(t)
  31. resp, body := env.DoPost("/user/getUserList", map[string]interface{}{
  32. "page": 1,
  33. "pageSize": 10,
  34. })
  35. AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
  36. var result response.Response
  37. json.Unmarshal(body, &result)
  38. dataMap, ok := result.Data.(map[string]interface{})
  39. if !ok {
  40. t.Fatalf("data is not map type: %T", result.Data)
  41. }
  42. listRaw, exists := dataMap["list"]
  43. if !exists {
  44. t.Fatalf("data.list missing, keys: %v", mapKeys(dataMap))
  45. }
  46. list, ok := listRaw.([]interface{})
  47. if !ok {
  48. t.Fatalf("data.list is not array: %T", listRaw)
  49. }
  50. if len(list) == 0 {
  51. t.Fatalf("user list is empty")
  52. }
  53. fmt.Printf("PASS: User list query successful, total %d records\n", len(list))
  54. }
  55. func TestParkingInfrastructure(t *testing.T) {
  56. env := InitTestEnv(t)
  57. lotResp, lotBody := env.DoPost("/parking/lot/create", map[string]interface{}{
  58. "lot_code": "TEST002",
  59. "lot_name": "Test Parking Lot B Zone",
  60. "capacity": 200,
  61. "description": "Automated test parking lot B zone",
  62. })
  63. AssertResponse(t, lotResp, lotBody, http.StatusOK, response.SUCCESS)
  64. resp, body := env.DoGet("/parking/lot/list", map[string]string{
  65. "page": "1",
  66. "page_size": "10",
  67. })
  68. AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
  69. list := ParsePageList(body)
  70. if len(list) < 2 {
  71. t.Fatalf("Parking lot list should contain at least 2 items, actual %d", len(list))
  72. }
  73. fmt.Printf("PASS: Parking lot create and query successful, total %d lots\n", len(list))
  74. }
  75. func TestVehicleCRUD(t *testing.T) {
  76. env := InitTestEnv(t)
  77. createVehicle(t, env, "JingA12345", "RFID001", 1)
  78. resp, body := env.DoGet("/vehicle/get-by-plate", map[string]string{
  79. "plate_number": "JingA12345",
  80. })
  81. AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
  82. dataMap := ParseDataMap(body)
  83. if dataMap["plate_number"] != "JingA12345" {
  84. t.Fatalf("Vehicle info mismatch")
  85. }
  86. fmt.Println("PASS: Vehicle create and query successful")
  87. }
  88. func TestFeeConfig(t *testing.T) {
  89. env := InitTestEnv(t)
  90. resp, body := env.DoGet("/vehicle/fee-config/list", map[string]string{
  91. "page": "1",
  92. "page_size": "10",
  93. })
  94. AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
  95. list := ParsePageList(body)
  96. if len(list) == 0 {
  97. t.Fatalf("Fee config list is empty")
  98. }
  99. fmt.Printf("PASS: Fee config query successful, total %d records\n", len(list))
  100. }
  101. func TestVehicleEntryAndExit(t *testing.T) {
  102. env := InitTestEnv(t)
  103. createVehicle(t, env, "JingB67890", "RFID002", 1)
  104. entryResp, entryBody := env.DoPost("/vehicle/entry", map[string]interface{}{
  105. "plate_number": "JingB67890",
  106. "rfid_tag": "RFID002",
  107. "parking_lot_id": 1,
  108. "parking_space_id": 10,
  109. "entry_image": "test_entry.jpg",
  110. })
  111. AssertResponse(t, entryResp, entryBody, http.StatusOK, response.SUCCESS)
  112. fmt.Println("PASS: Vehicle entry successful")
  113. // Set entry time to 2 hours ago to skip real waiting
  114. entryTime := time.Now().Add(-2 * time.Hour)
  115. global.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingB67890").Update("entry_time", entryTime)
  116. exitPreviewResp, exitPreviewBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{
  117. "plate_number": "JingB67890",
  118. "rfid_tag": "RFID002",
  119. })
  120. AssertResponse(t, exitPreviewResp, exitPreviewBody, http.StatusOK, response.SUCCESS)
  121. exitConfirmResp, exitConfirmBody := env.DoPost("/vehicle/exit/confirm", map[string]interface{}{
  122. "plate_number": "JingB67890",
  123. "rfid_tag": "RFID002",
  124. "payment_method": "cash",
  125. "paid_amount": 10.0,
  126. })
  127. AssertResponse(t, exitConfirmResp, exitConfirmBody, http.StatusOK, response.SUCCESS)
  128. fmt.Println("PASS: Vehicle exit and payment successful")
  129. paymentResp, paymentBody := env.DoGet("/payment/list", map[string]string{
  130. "page": "1",
  131. "page_size": "10",
  132. "plate_number": "JingB67890",
  133. })
  134. AssertResponse(t, paymentResp, paymentBody, http.StatusOK, response.SUCCESS)
  135. list := ParsePageList(paymentBody)
  136. if len(list) == 0 {
  137. t.Fatalf("Payment record query returned empty")
  138. }
  139. fmt.Printf("PASS: Payment record query successful, total %d records\n", len(list))
  140. }
  141. func TestBlackWhiteList(t *testing.T) {
  142. env := InitTestEnv(t)
  143. createVehicle(t, env, "JingC11111", "RFID_WHITE", 1)
  144. var vehicle dao.Vehicle
  145. env.DB.Where("plate_number = ?", "JingC11111").First(&vehicle)
  146. shortlistResp, shortlistBody := env.DoPost("/shortlist/createShortlist", dao.Shortlist{
  147. ListType: "白名单",
  148. VehicleId: int(vehicle.ID),
  149. ExpirationTime: nil,
  150. })
  151. AssertResponse(t, shortlistResp, shortlistBody, http.StatusOK, response.SUCCESS)
  152. resp, body := env.DoGet("/shortlist/queryAllShortlists", nil)
  153. AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
  154. fmt.Println("PASS: Black/white list operation successful")
  155. }
  156. // TestBlackWhiteListEntryExitBehavior 测试黑白名单对进出场的影响
  157. // 注意:当前后端 VehicleEntry 尚未集成 CheckVehicleShortlist,
  158. // 此测试记录当前行为,待业务逻辑接入后断言应反转。
  159. func TestBlackWhiteListEntryExitBehavior(t *testing.T) {
  160. env := InitTestEnv(t)
  161. // --- 场景1:黑名单车辆尝试进场 ---
  162. createVehicle(t, env, "JingB99999", "RFID_BLACK", 1)
  163. var blackVehicle dao.Vehicle
  164. env.DB.Where("plate_number = ?", "JingB99999").First(&blackVehicle)
  165. _, _ = env.DoPost("/shortlist/createShortlist", dao.Shortlist{
  166. ListType: "黑名单",
  167. VehicleId: int(blackVehicle.ID),
  168. ExpirationTime: nil,
  169. })
  170. entryResp, entryBody := env.DoPost("/vehicle/entry", map[string]interface{}{
  171. "plate_number": "JingB99999",
  172. "rfid_tag": "RFID_BLACK",
  173. "parking_lot_id": 1,
  174. "parking_space_id": 99,
  175. "entry_image": "test_black_entry.jpg",
  176. })
  177. // 黑名单车辆应该被拦截,进场请求应返回失败
  178. if entryResp.StatusCode == http.StatusOK {
  179. var result struct {
  180. Code int `json:"code"`
  181. Msg string `json:"msg"`
  182. }
  183. json.Unmarshal(entryBody, &result)
  184. if result.Code == response.SUCCESS {
  185. t.Fatalf("BUG: Blacklisted vehicle entry was NOT blocked! Response: code=%d msg=%s", result.Code, result.Msg)
  186. }
  187. }
  188. AssertResponse(t, entryResp, entryBody, http.StatusOK, response.ERROR)
  189. // --- 场景2:白名单车辆进场 ---
  190. createVehicle(t, env, "JingW00001", "RFID_WHITELIST", 1)
  191. var whiteVehicle dao.Vehicle
  192. env.DB.Where("plate_number = ?", "JingW00001").First(&whiteVehicle)
  193. _, _ = env.DoPost("/shortlist/createShortlist", dao.Shortlist{
  194. ListType: "白名单",
  195. VehicleId: int(whiteVehicle.ID),
  196. ExpirationTime: nil,
  197. })
  198. entryResp2, entryBody2 := env.DoPost("/vehicle/entry", map[string]interface{}{
  199. "plate_number": "JingW00001",
  200. "rfid_tag": "RFID_WHITELIST",
  201. "parking_lot_id": 1,
  202. "parking_space_id": 88,
  203. "entry_image": "test_white_entry.jpg",
  204. })
  205. AssertResponse(t, entryResp2, entryBody2, http.StatusOK, response.SUCCESS)
  206. fmt.Println("PASS: Whitelisted vehicle entry successful")
  207. // --- 场景3:查询黑白名单列表确认两条记录存在 ---
  208. resp3, body3 := env.DoGet("/shortlist/queryAllShortlists", nil)
  209. AssertResponse(t, resp3, body3, http.StatusOK, response.SUCCESS)
  210. // queryAllShortlists 返回 data=[] 数组格式,不是分页结构
  211. var result3 response.Response
  212. json.Unmarshal(body3, &result3)
  213. listRaw, ok := result3.Data.([]interface{})
  214. if !ok {
  215. t.Fatalf("shortlist data is not array: %T", result3.Data)
  216. }
  217. if len(listRaw) < 2 {
  218. t.Fatalf("Expected at least 2 shortlist records, got %d", len(listRaw))
  219. }
  220. fmt.Printf("PASS: Shortlist query returned %d records\n", len(listRaw))
  221. // --- 场景4:白名单车辆出场,验证费用计算(VIP/白名单免单逻辑)---
  222. exitPreviewResp, exitPreviewBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{
  223. "plate_number": "JingW00001",
  224. "rfid_tag": "RFID_WHITELIST",
  225. })
  226. AssertResponse(t, exitPreviewResp, exitPreviewBody, http.StatusOK, response.SUCCESS)
  227. // 修改入场时间为 1 小时前
  228. entryTime := time.Now().Add(-1 * time.Hour)
  229. global.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingW00001").Update("entry_time", entryTime)
  230. exitConfirmResp, exitConfirmBody := env.DoPost("/vehicle/exit/confirm", map[string]interface{}{
  231. "plate_number": "JingW00001",
  232. "rfid_tag": "RFID_WHITELIST",
  233. "payment_method": "cash",
  234. "paid_amount": 0.0,
  235. })
  236. AssertResponse(t, exitConfirmResp, exitConfirmBody, http.StatusOK, response.SUCCESS)
  237. fmt.Println("PASS: Whitelisted vehicle exit and payment successful")
  238. }
  239. func TestOwnerManagement(t *testing.T) {
  240. env := InitTestEnv(t)
  241. ownerResp, ownerBody := env.DoPost("/owner/create", map[string]interface{}{
  242. "owner_name": "Zhang San",
  243. "owner_surname": "Zhang",
  244. "owner_phone": "13800138000",
  245. "is_vip": true,
  246. "vip_expire_time": time.Now().Add(365 * 24 * time.Hour).Format(time.RFC3339),
  247. })
  248. AssertResponse(t, ownerResp, ownerBody, http.StatusOK, response.SUCCESS)
  249. resp, body := env.DoGet("/owner/list", map[string]string{
  250. "page": "1",
  251. "page_size": "10",
  252. })
  253. AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
  254. list := ParsePageList(body)
  255. if len(list) == 0 {
  256. t.Fatalf("Owner list is empty after creation")
  257. }
  258. fmt.Printf("PASS: Owner management successful, total %d owners\n", len(list))
  259. }
  260. func TestDashboardMonitoring(t *testing.T) {
  261. env := InitTestEnv(t)
  262. resp, body := env.DoGetRaw("/dashboard/monitor", nil)
  263. AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
  264. dataMap := ParseDataMap(body)
  265. if dataMap == nil {
  266. t.Fatalf("Dashboard data is empty")
  267. }
  268. fmt.Println("PASS: Dashboard monitoring data query successful")
  269. }
  270. // ===================== Helper functions =====================
  271. func createVehicle(t *testing.T, env *TestEnv, plateNumber, rfidTag string, vehicleTypeID uint) {
  272. t.Helper()
  273. _, body := env.DoPost("/vehicle/create", map[string]interface{}{
  274. "plate_number": plateNumber,
  275. "vehicle_type_id": vehicleTypeID,
  276. "vehicle_brand": "TestBrand",
  277. "vehicle_color": "TestColor",
  278. "rfid_tag": rfidTag,
  279. })
  280. var result struct {
  281. Code int `json:"code"`
  282. Msg string `json:"msg"`
  283. }
  284. json.Unmarshal(body, &result)
  285. if result.Code != response.SUCCESS {
  286. t.Fatalf("Failed to create vehicle: %s", result.Msg)
  287. }
  288. }