111 lines
3.4 KiB
JavaScript
111 lines
3.4 KiB
JavaScript
jest.mock("../middleware/auth", () => (req, res, next) => {
|
|
req.user = { id: 42, role: "user" };
|
|
next();
|
|
});
|
|
|
|
jest.mock("../middleware/optional-auth", () => (req, res, next) => next());
|
|
|
|
jest.mock("../services/group-invites.service", () => {
|
|
const actual = jest.requireActual("../services/group-invites.service");
|
|
return {
|
|
...actual,
|
|
acceptInviteLink: jest.fn(),
|
|
createInviteLink: jest.fn(),
|
|
deleteInviteLink: jest.fn(),
|
|
decideJoinRequest: jest.fn(),
|
|
getGroupJoinPolicy: jest.fn(),
|
|
getInviteLinkSummaryByToken: jest.fn(),
|
|
listPendingJoinRequests: jest.fn(),
|
|
listInviteLinks: jest.fn(),
|
|
resolveManagedGroupId: jest.fn(),
|
|
revokeInviteLink: jest.fn(),
|
|
reviveInviteLink: jest.fn(),
|
|
setGroupJoinPolicy: jest.fn(),
|
|
};
|
|
});
|
|
|
|
const request = require("supertest");
|
|
const invitesService = require("../services/group-invites.service");
|
|
const app = require("../app");
|
|
|
|
describe("group invites routes", () => {
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
invitesService.resolveManagedGroupId.mockResolvedValue(1);
|
|
invitesService.listInviteLinks.mockResolvedValue([]);
|
|
invitesService.listPendingJoinRequests.mockResolvedValue([]);
|
|
invitesService.createInviteLink.mockResolvedValue({
|
|
id: 1,
|
|
token: "abcd",
|
|
status: "ACTIVE",
|
|
});
|
|
invitesService.getInviteLinkSummaryByToken.mockResolvedValue({
|
|
id: 1,
|
|
token: "abcd",
|
|
group_name: "Test Group",
|
|
status: "ACTIVE",
|
|
active_policy: "AUTO_ACCEPT",
|
|
});
|
|
});
|
|
|
|
test("admin-only checks are enforced on invite management routes", async () => {
|
|
invitesService.createInviteLink.mockRejectedValue(
|
|
new invitesService.InviteServiceError(
|
|
"FORBIDDEN",
|
|
"Admin or owner role required",
|
|
403
|
|
)
|
|
);
|
|
|
|
const response = await request(app).post("/api/groups/invites").send({
|
|
policy: "AUTO_ACCEPT",
|
|
singleUse: false,
|
|
ttlDays: 3,
|
|
});
|
|
|
|
expect(response.status).toBe(403);
|
|
expect(response.body.error.code).toBe("FORBIDDEN");
|
|
expect(response.body.request_id).toBeTruthy();
|
|
});
|
|
|
|
test("request_id is present in invite responses", async () => {
|
|
const response = await request(app).get("/api/invite-links/abcd1234");
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.request_id).toBeTruthy();
|
|
expect(response.body.link).toBeTruthy();
|
|
});
|
|
|
|
test("pending join requests can be listed with request_id", async () => {
|
|
invitesService.listPendingJoinRequests.mockResolvedValue([
|
|
{ id: 12, user_id: 77, username: "pending-user", status: "PENDING" },
|
|
]);
|
|
|
|
const response = await request(app).get("/api/groups/join-requests");
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.request_id).toBeTruthy();
|
|
expect(response.body.requests).toEqual([
|
|
{ id: 12, user_id: 77, username: "pending-user", status: "PENDING" },
|
|
]);
|
|
});
|
|
|
|
test("decision route maps service validation errors", async () => {
|
|
invitesService.decideJoinRequest.mockRejectedValue(
|
|
new invitesService.InviteServiceError(
|
|
"JOIN_REQUEST_NOT_FOUND",
|
|
"Pending join request not found",
|
|
404
|
|
)
|
|
);
|
|
|
|
const response = await request(app)
|
|
.post("/api/groups/join-requests/decision")
|
|
.send({ requestId: 99, decision: "APPROVE" });
|
|
|
|
expect(response.status).toBe(404);
|
|
expect(response.body.request_id).toBeTruthy();
|
|
expect(response.body.error.code).toBe("JOIN_REQUEST_NOT_FOUND");
|
|
});
|
|
});
|