jest.mock("../db/pool", () => ({ query: jest.fn(), })); const pool = require("../db/pool"); const AvailableItems = require("../models/available-item.model"); describe("available-item.model", () => { beforeEach(() => { pool.query.mockReset(); }); test("creates an available item using an existing catalog item", async () => { pool.query .mockResolvedValueOnce({ rowCount: 1, rows: [{ id: 55, name: "milk" }] }) .mockResolvedValueOnce({ rowCount: 1, rows: [] }) .mockResolvedValueOnce({ rowCount: 1, rows: [ { item_id: 55, item_name: "milk", item_image: null, image_mime_type: null, item_type: "dairy", item_group: "Milk", zone: "Dairy & Refrigerated", }, ], }); const result = await AvailableItems.createAvailableItem(1, 2, "Milk"); expect(result).toEqual( expect.objectContaining({ item_id: 55, item_name: "milk", }) ); expect(pool.query).toHaveBeenNthCalledWith( 1, "SELECT id, name FROM items WHERE name ILIKE $1", ["milk"] ); expect(pool.query).toHaveBeenNthCalledWith( 2, expect.stringContaining("INSERT INTO household_store_available_items"), [1, 2, 55, null, null] ); }); test("creates an available item and inserts a new master item when needed", async () => { pool.query .mockResolvedValueOnce({ rowCount: 0, rows: [] }) .mockResolvedValueOnce({ rowCount: 1, rows: [{ id: 77, name: "granola" }] }) .mockResolvedValueOnce({ rowCount: 1, rows: [] }) .mockResolvedValueOnce({ rowCount: 1, rows: [{ item_id: 77, item_name: "granola" }], }); const result = await AvailableItems.createAvailableItem(1, 2, "Granola"); expect(result).toEqual(expect.objectContaining({ item_id: 77, item_name: "granola" })); expect(pool.query).toHaveBeenNthCalledWith( 2, "INSERT INTO items (name) VALUES ($1) RETURNING id, name", ["granola"] ); }); test("updates available item images and returns refreshed data", async () => { const imageBuffer = Buffer.from("abc"); pool.query .mockResolvedValueOnce({ rowCount: 1, rows: [{ item_id: 55 }] }) .mockResolvedValueOnce({ rowCount: 1, rows: [{ item_id: 55, item_name: "milk", item_image: "YWJj", image_mime_type: "image/jpeg" }], }); const result = await AvailableItems.updateAvailableItem(1, 2, 55, { imageBuffer, mimeType: "image/jpeg", }); expect(result).toEqual(expect.objectContaining({ item_id: 55, image_mime_type: "image/jpeg" })); expect(pool.query).toHaveBeenNthCalledWith( 1, expect.stringContaining("UPDATE household_store_available_items"), [1, 2, 55, imageBuffer, "image/jpeg"] ); }); test("imports current household list items idempotently", async () => { pool.query.mockResolvedValueOnce({ rowCount: 2, rows: [{ item_id: 10 }, { item_id: 11 }], }); const result = await AvailableItems.importCurrentListItems(1, 2); expect(result).toBe(2); expect(pool.query).toHaveBeenCalledWith( expect.stringContaining("INSERT INTO household_store_available_items"), [1, 2] ); }); test("deletes only the catalog entry", async () => { pool.query.mockResolvedValueOnce({ rowCount: 1, rows: [] }); const deleted = await AvailableItems.deleteAvailableItem(1, 2, 55); expect(deleted).toBe(true); expect(pool.query).toHaveBeenCalledWith( expect.stringContaining("DELETE FROM household_store_available_items"), [1, 2, 55] ); }); });