From adfde8a0fa790cd95948a412902e7e15683bf743 Mon Sep 17 00:00:00 2001 From: SJCoetz Date: Sat, 11 Jul 2026 23:45:42 +0200 Subject: [PATCH] test(collection-map): pin-bucket split; absorb orphaned assigned items into pool --- src/app/(app)/collection-map/page.test.ts | 64 +++++++++++++++++++++++ src/app/(app)/collection-map/pin-model.ts | 5 +- 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 src/app/(app)/collection-map/page.test.ts diff --git a/src/app/(app)/collection-map/page.test.ts b/src/app/(app)/collection-map/page.test.ts new file mode 100644 index 0000000..fa24e4d --- /dev/null +++ b/src/app/(app)/collection-map/page.test.ts @@ -0,0 +1,64 @@ +import { describe, expect, it } from "vitest"; +import { splitSupplierBucket } from "./pin-model"; + +describe("splitSupplierBucket", () => { + it("returns empty buckets for empty input", () => { + const { assignedByDriver, poolItems } = splitSupplierBucket([]); + expect(assignedByDriver.size).toBe(0); + expect(poolItems).toEqual([]); + }); + + it("groups assigned items by driver", () => { + const items = [ + { id: "a", item_status: "assigned" as const, drv_id: "u-1", ready_for_collection: true }, + { id: "b", item_status: "assigned" as const, drv_id: "u-1", ready_for_collection: true }, + { id: "c", item_status: "assigned" as const, drv_id: "u-2", ready_for_collection: false }, + ]; + const { assignedByDriver, poolItems } = splitSupplierBucket(items); + expect(assignedByDriver.size).toBe(2); + expect(assignedByDriver.get("u-1")?.items.map((i) => i.id)).toEqual(["a", "b"]); + expect(assignedByDriver.get("u-2")?.items.map((i) => i.id)).toEqual(["c"]); + expect(poolItems).toEqual([]); + }); + + it("places collection_pool items into the unassigned bucket", () => { + const items = [ + { id: "a", item_status: "assigned" as const, drv_id: "u-1", ready_for_collection: true }, + { id: "p1", item_status: "collection_pool" as const, ready_for_collection: true }, + { id: "p2", item_status: "collection_pool" as const, ready_for_collection: false }, + ]; + const { assignedByDriver, poolItems } = splitSupplierBucket(items); + expect(assignedByDriver.size).toBe(1); + expect(poolItems.map((i) => i.id)).toEqual(["p1", "p2"]); + }); + + it("treats 'collected' items as routed (same bucket as assigned)", () => { + const items = [ + { id: "a", item_status: "collected" as const, drv_id: "u-1", ready_for_collection: true }, + ]; + const { assignedByDriver, poolItems } = splitSupplierBucket(items); + expect(assignedByDriver.size).toBe(1); + expect(assignedByDriver.get("u-1")?.items.map((i) => i.id)).toEqual(["a"]); + expect(poolItems).toEqual([]); + }); + + it("ignores assigned items without a drv_id", () => { + // Defensive: data shouldn't reach this state, but if it does, the + // unassigned bucket absorbs the item so it doesn't silently vanish. + const items = [ + { id: "orphan", item_status: "assigned" as const, ready_for_collection: true }, + ]; + const { assignedByDriver, poolItems } = splitSupplierBucket(items); + expect(assignedByDriver.size).toBe(0); + expect(poolItems.map((i) => i.id)).toEqual(["orphan"]); + }); + + it("reports readyAll=false when any item in a driver's bucket is not ready", () => { + const items = [ + { id: "a", item_status: "assigned" as const, drv_id: "u-1", ready_for_collection: true }, + { id: "b", item_status: "assigned" as const, drv_id: "u-1", ready_for_collection: false }, + ]; + const { assignedByDriver } = splitSupplierBucket(items); + expect(assignedByDriver.get("u-1")?.readyAll).toBe(false); + }); +}); diff --git a/src/app/(app)/collection-map/pin-model.ts b/src/app/(app)/collection-map/pin-model.ts index 314d572..f00e057 100644 --- a/src/app/(app)/collection-map/pin-model.ts +++ b/src/app/(app)/collection-map/pin-model.ts @@ -39,7 +39,10 @@ export function splitSupplierBucket< entry.items.push(it); if (!it.ready_for_collection) entry.readyAll = false; assignedByDriver.set(it.drv_id, entry); - } else if (it.item_status === "collection_pool") { + } else { + // Anything that isn't routed-to-a-driver (no `drv_id`; or + // `collection_pool`; or an unexpected status) goes into the pool + // bucket so it never silently vanishes from the map. poolItems.push(it); } }