diff --git a/visionA-frontend/src/stores/device-store.test.ts b/visionA-frontend/src/stores/device-store.test.ts index b996fda..b9a316d 100644 --- a/visionA-frontend/src/stores/device-store.test.ts +++ b/visionA-frontend/src/stores/device-store.test.ts @@ -175,7 +175,7 @@ describe("useDeviceStore", () => { }); }); - it("serial_number 正規化:snake_case / camelCase 都吃、缺欄位與空字串 → null(WP-C)", async () => { + it("serial_number 正規化:snake_case / camelCase 都吃、缺欄位與空字串/純空白 → null(WP-C)", async () => { vi.spyOn(globalThis, "fetch").mockResolvedValueOnce( jsonResponse({ success: true, @@ -188,6 +188,8 @@ describe("useDeviceStore", () => { { id: "dev-3", name: "C", type: "kl520", status: "connected" }, // 空字串(防禦性;無法路由)→ null { id: "dev-4", name: "D", type: "kl520", status: "connected", serial_number: "" }, + // 純空白(防禦性;trim 後為空、無法路由)→ null + { id: "dev-5", name: "E", type: "kl520", status: "connected", serial_number: " " }, ], }), ); @@ -198,6 +200,7 @@ describe("useDeviceStore", () => { expect(devices[1]?.serialNumber).toBe("KN99887766"); expect(devices[2]?.serialNumber).toBeNull(); expect(devices[3]?.serialNumber).toBeNull(); + expect(devices[4]?.serialNumber).toBeNull(); }); it("fetchDevices 遇到 501 NOT_IMPLEMENTED 時視為空 list,不記錯誤", async () => { diff --git a/visionA-frontend/src/stores/device-store.ts b/visionA-frontend/src/stores/device-store.ts index ed03cf1..8114168 100644 --- a/visionA-frontend/src/stores/device-store.ts +++ b/visionA-frontend/src/stores/device-store.ts @@ -112,14 +112,16 @@ function normalizeDevice(raw: unknown): Device { const rawRemoteStatus = pick("remote_status", "remoteStatus") as | RemoteStatus | undefined; - // serial_number 為 omitempty:缺欄位 → null;防禦性把空字串也視為 null - // (空字串無法路由,語意上等同「未回報序號」)。 + // serial_number 為 omitempty:缺欄位 → null;防禦性把空字串/純空白也視為 null + // (空字串與空白皆無法路由,語意上等同「未回報序號」)。先 trim 再判定, + // 避免純空白(" ")誤判為 truthy 而以空白 serial 組出必失敗的請求路由。 const rawSerial = pick("serial_number", "serialNumber"); + const serial = rawSerial != null ? String(rawSerial).trim() : ""; return { id: String(pick("id") ?? ""), name: String(pick("name") ?? pick("device_name") ?? ""), alias: pick("alias") ?? undefined, - serialNumber: rawSerial ? String(rawSerial) : null, + serialNumber: serial !== "" ? serial : null, type: String(pick("type", "device_type") ?? ""), status: (pick("status") as DeviceHardwareStatus) ?? "disconnected", remoteStatus: