jest.mock("../models/household.model", () => ({ getUserRole: jest.fn(), reorderUserHouseholds: jest.fn(), transferOwnership: jest.fn(), updateMemberRole: jest.fn(), })); jest.mock("../utils/logger", () => ({ logError: jest.fn(), })); const householdModel = require("../models/household.model"); const controller = require("../controllers/households.controller"); function createResponse() { const res = {}; res.status = jest.fn().mockReturnValue(res); res.json = jest.fn().mockReturnValue(res); return res; } describe("households.controller updateMemberRole", () => { beforeEach(() => { jest.clearAllMocks(); householdModel.getUserRole.mockResolvedValue("member"); householdModel.transferOwnership.mockResolvedValue({ user_id: 7, role: "owner" }); householdModel.updateMemberRole.mockResolvedValue({ user_id: 7, role: "admin" }); }); test("owner can transfer household ownership", async () => { const req = { params: { householdId: "3", userId: "7" }, body: { role: "owner" }, user: { id: 1 }, household: { id: 3, role: "owner" }, }; const res = createResponse(); await controller.updateMemberRole(req, res); expect(householdModel.transferOwnership).toHaveBeenCalledWith("3", 1, 7); expect(householdModel.updateMemberRole).not.toHaveBeenCalled(); expect(res.json).toHaveBeenCalledWith({ message: "Household ownership transferred successfully", member: { user_id: 7, role: "owner" }, }); }); test("admin cannot transfer household ownership", async () => { const req = { params: { householdId: "3", userId: "7" }, body: { role: "owner" }, user: { id: 1 }, household: { id: 3, role: "admin" }, }; const res = createResponse(); await controller.updateMemberRole(req, res); expect(householdModel.transferOwnership).not.toHaveBeenCalled(); expect(res.status).toHaveBeenCalledWith(403); expect(res.json).toHaveBeenCalledWith( expect.objectContaining({ error: expect.objectContaining({ message: "Only the household owner can transfer ownership", }), }) ); }); test("owner can still update a member to admin without transfer flow", async () => { const req = { params: { householdId: "3", userId: "7" }, body: { role: "admin" }, user: { id: 1 }, household: { id: 3, role: "owner" }, }; const res = createResponse(); await controller.updateMemberRole(req, res); expect(householdModel.updateMemberRole).toHaveBeenCalledWith("3", "7", "admin"); expect(householdModel.transferOwnership).not.toHaveBeenCalled(); expect(res.json).toHaveBeenCalledWith({ message: "Member role updated successfully", member: { user_id: 7, role: "admin" }, }); }); }); describe("households.controller reorderHouseholds", () => { beforeEach(() => { jest.clearAllMocks(); householdModel.reorderUserHouseholds.mockResolvedValue([ { id: 3, name: "Third" }, { id: 1, name: "First" }, ]); }); test("updates the current user's household order", async () => { const req = { body: { household_ids: [3, 1] }, user: { id: 9 }, }; const res = createResponse(); await controller.reorderHouseholds(req, res); expect(householdModel.reorderUserHouseholds).toHaveBeenCalledWith(9, [3, 1]); expect(res.json).toHaveBeenCalledWith({ message: "Household order updated successfully", households: [ { id: 3, name: "Third" }, { id: 1, name: "First" }, ], }); }); test("rejects duplicate household IDs", async () => { const req = { body: { household_ids: [3, 3] }, user: { id: 9 }, }; const res = createResponse(); await controller.reorderHouseholds(req, res); expect(householdModel.reorderUserHouseholds).not.toHaveBeenCalled(); expect(res.status).toHaveBeenCalledWith(400); expect(res.json).toHaveBeenCalledWith( expect.objectContaining({ error: expect.objectContaining({ message: "household_ids must contain unique positive household IDs", }), }) ); }); test("rejects orders that do not match current memberships", async () => { householdModel.reorderUserHouseholds.mockResolvedValue(null); const req = { body: { household_ids: [999] }, user: { id: 9 }, }; const res = createResponse(); await controller.reorderHouseholds(req, res); expect(res.status).toHaveBeenCalledWith(400); expect(res.json).toHaveBeenCalledWith( expect.objectContaining({ error: expect.objectContaining({ message: "Household order must include every household you belong to", }), }) ); }); });