grocery-app/backend/tests/available-items.controller.test.js
2026-03-28 22:35:34 -07:00

138 lines
4.0 KiB
JavaScript

jest.mock("../models/available-item.model", () => ({
createAvailableItem: jest.fn(),
deleteAvailableItem: jest.fn(),
getAvailableItemById: jest.fn(),
importCurrentListItems: jest.fn(),
listAvailableItems: jest.fn(),
updateAvailableItem: jest.fn(),
}));
jest.mock("../models/list.model.v2", () => ({
deleteClassification: jest.fn(),
upsertClassification: jest.fn(),
}));
jest.mock("../utils/logger", () => ({
logError: jest.fn(),
}));
const AvailableItems = require("../models/available-item.model");
const List = require("../models/list.model.v2");
const controller = require("../controllers/available-items.controller");
function createResponse() {
const res = {};
res.status = jest.fn().mockReturnValue(res);
res.json = jest.fn().mockReturnValue(res);
return res;
}
describe("available-items.controller", () => {
beforeEach(() => {
jest.clearAllMocks();
AvailableItems.createAvailableItem.mockResolvedValue({ item_id: 99, item_name: "milk" });
AvailableItems.getAvailableItemById.mockResolvedValue({
item_id: 99,
item_name: "milk",
item_type: "dairy",
item_group: "Milk",
zone: "Dairy & Refrigerated",
});
AvailableItems.updateAvailableItem.mockResolvedValue({ item_id: 99, item_name: "milk" });
AvailableItems.deleteAvailableItem.mockResolvedValue(true);
AvailableItems.importCurrentListItems.mockResolvedValue(2);
AvailableItems.listAvailableItems.mockResolvedValue([]);
List.upsertClassification.mockResolvedValue(undefined);
List.deleteClassification.mockResolvedValue(undefined);
});
test("creates an available item and persists classification metadata", async () => {
const req = {
params: { householdId: "1", storeId: "2" },
body: {
item_name: "milk",
classification: JSON.stringify({
item_type: "dairy",
item_group: "Milk",
zone: "Dairy & Refrigerated",
}),
},
processedImage: null,
};
const res = createResponse();
await controller.createAvailableItem(req, res);
expect(AvailableItems.createAvailableItem).toHaveBeenCalledWith("1", "2", "milk", null, null);
expect(List.upsertClassification).toHaveBeenCalledWith(
"1",
"2",
99,
expect.objectContaining({
item_type: "dairy",
item_group: "Milk",
zone: "Dairy & Refrigerated",
})
);
expect(res.status).toHaveBeenCalledWith(201);
});
test("rejects invalid item_group values", async () => {
const req = {
params: { householdId: "1", storeId: "2" },
body: {
item_name: "milk",
classification: JSON.stringify({
item_type: "dairy",
item_group: "Bread",
}),
},
};
const res = createResponse();
await controller.createAvailableItem(req, res);
expect(AvailableItems.createAvailableItem).not.toHaveBeenCalled();
expect(res.status).toHaveBeenCalledWith(400);
expect(res.json).toHaveBeenCalledWith(
expect.objectContaining({
error: expect.objectContaining({
message: "Invalid item_group for selected item_type",
}),
})
);
});
test("clears classification on update when classification is explicitly empty", async () => {
const req = {
params: { householdId: "1", storeId: "2", itemId: "99" },
body: {
classification: "null",
},
processedImage: null,
};
const res = createResponse();
await controller.updateAvailableItem(req, res);
expect(List.deleteClassification).toHaveBeenCalledWith("1", "2", 99);
expect(res.status).not.toHaveBeenCalledWith(400);
});
test("imports current list items and reports the import count", async () => {
const req = {
params: { householdId: "1", storeId: "2" },
};
const res = createResponse();
await controller.importCurrentItems(req, res);
expect(AvailableItems.importCurrentListItems).toHaveBeenCalledWith("1", "2");
expect(res.json).toHaveBeenCalledWith(
expect.objectContaining({
imported_count: 2,
})
);
});
});