grocery-app/backend/tests/store-locations.routes.test.js

165 lines
5.6 KiB
JavaScript

jest.mock("../middleware/auth", () => (req, res, next) => {
req.user = { id: 42, role: "user" };
next();
});
jest.mock("../middleware/household", () => ({
householdAccess: (req, res, next) => {
req.household = {
id: Number.parseInt(req.params.householdId, 10),
role: req.headers["x-household-role"] || "member",
};
next();
},
locationAccess: (req, res, next) => {
req.storeLocation = { id: Number.parseInt(req.params.locationId, 10) };
next();
},
requireHouseholdAdmin: (req, res, next) => {
if (["owner", "admin"].includes(req.household?.role)) {
return next();
}
return res.status(403).json({
error: { code: "FORBIDDEN", message: "Admin role required" },
request_id: req.request_id,
});
},
storeAccess: (req, res, next) => next(),
}));
jest.mock("../middleware/image", () => ({
upload: {
single: () => (req, res, next) => next(),
},
processImage: (req, res, next) => next(),
}));
jest.mock("../controllers/households.controller", () => ({
createHousehold: jest.fn(),
deleteHousehold: jest.fn(),
getHousehold: jest.fn(),
getMembers: jest.fn(),
getUserHouseholds: jest.fn(),
joinHousehold: jest.fn(),
refreshInviteCode: jest.fn(),
removeMember: jest.fn(),
reorderHouseholds: jest.fn((req, res) => res.json({ message: "ordered" })),
updateHousehold: jest.fn(),
updateMemberRole: jest.fn(),
}));
jest.mock("../controllers/lists.controller.v2", () => ({
addItem: jest.fn(),
deleteItem: jest.fn(),
getClassification: jest.fn(),
getItemByName: jest.fn(),
getList: jest.fn(),
getRecentlyBought: jest.fn(),
getSuggestions: jest.fn(),
markBought: jest.fn(),
setClassification: jest.fn(),
updateItem: jest.fn(),
updateItemImage: jest.fn(),
}));
jest.mock("../controllers/available-items.controller", () => ({
createAvailableItem: jest.fn(),
deleteAvailableItem: jest.fn(),
getAvailableItems: jest.fn(),
importCurrentItems: jest.fn(),
updateAvailableItem: jest.fn(),
}));
jest.mock("../controllers/stores.controller", () => ({
addLocationToStore: jest.fn((req, res) => res.status(201).json({ message: "location" })),
createHouseholdStore: jest.fn((req, res) => res.status(201).json({ message: "store" })),
createZone: jest.fn((req, res) => res.status(201).json({ message: "zone" })),
deleteHouseholdStore: jest.fn((req, res) => res.json({ message: "deleted store" })),
deleteLocation: jest.fn((req, res) => res.json({ message: "deleted location" })),
deleteZone: jest.fn((req, res) => res.json({ message: "deleted zone" })),
getHouseholdStores: jest.fn((req, res) => res.json([{ id: 2, name: "Costco" }])),
getLocationZones: jest.fn((req, res) => res.json({ zones: [] })),
setDefaultLocation: jest.fn((req, res) => res.json({ message: "default" })),
updateHouseholdStore: jest.fn((req, res) => res.json({ message: "updated store" })),
updateLocation: jest.fn((req, res) => res.json({ message: "updated location" })),
updateZone: jest.fn((req, res) => res.json({ message: "updated zone" })),
}));
const express = require("express");
const request = require("supertest");
const router = require("../routes/households.routes");
const householdsController = require("../controllers/households.controller");
const storesController = require("../controllers/stores.controller");
describe("store location routes", () => {
let app;
beforeEach(() => {
app = express();
app.use(express.json());
app.use("/households", router);
jest.clearAllMocks();
});
test("members can list household store locations", async () => {
const response = await request(app).get("/households/1/stores");
expect(response.status).toBe(200);
expect(storesController.getHouseholdStores).toHaveBeenCalled();
});
test("users can reorder their household switcher list", async () => {
const response = await request(app)
.patch("/households/order")
.send({ household_ids: [3, 1, 2] });
expect(response.status).toBe(200);
expect(householdsController.reorderHouseholds).toHaveBeenCalled();
});
test("members cannot create household stores", async () => {
const response = await request(app)
.post("/households/1/stores")
.set("x-household-role", "member")
.send({ name: "Costco" });
expect(response.status).toBe(403);
expect(storesController.createHouseholdStore).not.toHaveBeenCalled();
});
test("admins can create household stores", async () => {
const response = await request(app)
.post("/households/1/stores")
.set("x-household-role", "admin")
.send({ name: "Costco", location_name: "Fontana" });
expect(response.status).toBe(201);
expect(storesController.createHouseholdStore).toHaveBeenCalled();
});
test("members can list zones but cannot create zones", async () => {
const listResponse = await request(app)
.get("/households/1/locations/2/zones")
.set("x-household-role", "member");
const createResponse = await request(app)
.post("/households/1/locations/2/zones")
.set("x-household-role", "member")
.send({ name: "Produce", sort_order: 10 });
expect(listResponse.status).toBe(200);
expect(createResponse.status).toBe(403);
expect(storesController.getLocationZones).toHaveBeenCalled();
expect(storesController.createZone).not.toHaveBeenCalled();
});
test("admins can update zone order", async () => {
const response = await request(app)
.patch("/households/1/locations/2/zones/9")
.set("x-household-role", "admin")
.send({ sort_order: 20 });
expect(response.status).toBe(200);
expect(storesController.updateZone).toHaveBeenCalled();
});
});