diff --git a/backend/tests/location-map.model.test.js b/backend/tests/location-map.model.test.js index a8af49e..5121aad 100644 --- a/backend/tests/location-map.model.test.js +++ b/backend/tests/location-map.model.test.js @@ -1,8 +1,14 @@ jest.mock("../db/pool", () => ({ + query: jest.fn(), connect: jest.fn(), })); +jest.mock("../models/store.model", () => ({ + getLocationById: jest.fn(), +})); + const pool = require("../db/pool"); +const storeModel = require("../models/store.model"); const LocationMap = require("../models/location-map.model"); function createClient() { @@ -17,6 +23,134 @@ describe("location-map.model", () => { jest.clearAllMocks(); }); + function mockMapStateQueries() { + const location = { + id: 10, + household_id: 1, + name: "Costco", + location_name: "Eastvale", + }; + const draftMap = { + id: 900, + household_id: 1, + store_location_id: 10, + name: "Draft Map", + width: 1000, + height: 700, + status: "draft", + version: 3, + }; + const publishedMap = { + id: 901, + household_id: 1, + store_location_id: 10, + name: "Published Map", + width: 1000, + height: 700, + status: "published", + version: 2, + }; + const draftObject = { + id: 1100, + location_map_id: 900, + zone_id: 501, + type: "zone", + label: "Draft Bakery", + x: 10, + y: 20, + width: 200, + height: 120, + rotation: 0, + locked: false, + visible: true, + sort_order: 1, + }; + const publishedObject = { + ...draftObject, + id: 1200, + location_map_id: 901, + label: "Published Bakery", + }; + const zones = [{ id: 501, name: "Bakery", sort_order: 10, item_count: 1 }]; + const items = [{ + id: 3001, + item_id: 2001, + item_name: "sourdough", + quantity: 1, + bought: false, + zone_id: 501, + zone: "Bakery", + added_by_users: ["Owner"], + }]; + + storeModel.getLocationById.mockResolvedValue(location); + pool.query.mockImplementation(async (sql, params = []) => { + const query = String(sql); + if (query.includes("FROM location_maps") && query.includes("status = ANY")) { + const statuses = params[2] || []; + return { + rows: [draftMap, publishedMap].filter((map) => statuses.includes(map.status)), + }; + } + if (query.includes("FROM location_map_objects")) { + const mapId = params[0]; + return { + rows: mapId === 900 ? [draftObject] : mapId === 901 ? [publishedObject] : [], + }; + } + if (query.includes("FROM store_location_zones")) { + return { rows: zones }; + } + if (query.includes("FROM household_lists hl")) { + return { rows: items }; + } + return { rows: [], rowCount: 0 }; + }); + + return { draftMap, publishedMap, draftObject, publishedObject, location, zones, items }; + } + + test("hides draft map state from non-managers", async () => { + const { publishedMap, publishedObject, location, zones, items } = mockMapStateQueries(); + + const state = await LocationMap.getMapState(1, 10, false); + + expect(storeModel.getLocationById).toHaveBeenCalledWith(1, 10); + expect(pool.query).toHaveBeenCalledWith( + expect.stringContaining("FROM location_maps"), + [1, 10, ["published"]] + ); + expect(state).toEqual(expect.objectContaining({ + location, + map: expect.objectContaining({ id: publishedMap.id, status: "published" }), + draft_map: null, + published_map: expect.objectContaining({ id: publishedMap.id, status: "published" }), + objects: [expect.objectContaining({ id: publishedObject.id, label: "Published Bakery" })], + draft_objects: [], + published_objects: [expect.objectContaining({ id: publishedObject.id })], + zones, + items, + unmapped_count: 0, + })); + }); + + test("includes draft and published map state for managers", async () => { + const { draftMap, publishedMap, draftObject, publishedObject } = mockMapStateQueries(); + + const state = await LocationMap.getMapState(1, 10, true); + + expect(pool.query).toHaveBeenCalledWith( + expect.stringContaining("FROM location_maps"), + [1, 10, ["draft", "published"]] + ); + expect(state.map).toEqual(expect.objectContaining({ id: draftMap.id, status: "draft" })); + expect(state.draft_map).toEqual(expect.objectContaining({ id: draftMap.id, status: "draft" })); + expect(state.published_map).toEqual(expect.objectContaining({ id: publishedMap.id, status: "published" })); + expect(state.objects).toEqual([expect.objectContaining({ id: draftObject.id, label: "Draft Bakery" })]); + expect(state.draft_objects).toEqual([expect.objectContaining({ id: draftObject.id })]); + expect(state.published_objects).toEqual([expect.objectContaining({ id: publishedObject.id })]); + }); + test("unlinks saved map objects from zones outside the current location", async () => { const client = createClient(); pool.connect.mockResolvedValue(client);