Files
calendar/packages/features/membership/repositories/MembershipRepository.integration-test.ts
sean-brydonGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
38ed00d71a fix: add server-side redirect for users with pending invites on onboarding page (#26829)
* fix: add server-side redirect for users with pending invites on onboarding page

When users sign up with an invite token, they were being redirected to
/onboarding/getting-started (plan selection page) instead of going
directly to /onboarding/personal/settings. This caused users to see
the payment prompt for team/org plans before being redirected.

This fix adds a server-side check in the /onboarding/getting-started
page to redirect users with pending invites directly to the personal
onboarding flow, preventing them from seeing the plan selection page.

Also refactored onboardingUtils.ts to use MembershipRepository instead
of direct prisma access.

Co-Authored-By: [email protected] <[email protected]>

* test: add integration tests for hasPendingInviteByUserId method

Co-Authored-By: [email protected] <[email protected]>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-01-14 09:22:05 -03:00

165 lines
4.8 KiB
TypeScript

import { describe, it, expect, beforeAll, afterAll, afterEach } from "vitest";
import { prisma } from "@calcom/prisma";
import { MembershipRole } from "@calcom/prisma/enums";
import { MembershipRepository } from "./MembershipRepository";
const createdMembershipIds: number[] = [];
let testTeamId: number;
let createdTeamId: number | null = null;
async function clearTestMemberships() {
if (createdMembershipIds.length > 0) {
await prisma.membership.deleteMany({
where: { id: { in: createdMembershipIds } },
});
createdMembershipIds.length = 0;
}
}
describe("MembershipRepository (Integration Tests)", () => {
beforeAll(async () => {
let testTeam = await prisma.team.findFirst({
where: { slug: { not: null } },
});
if (!testTeam) {
testTeam = await prisma.team.create({
data: {
name: "Test Team for MembershipRepository",
slug: `test-team-membership-repo-${Date.now()}`,
},
});
createdTeamId = testTeam.id;
}
testTeamId = testTeam.id;
});
afterAll(async () => {
if (createdTeamId) {
await prisma.team.delete({ where: { id: createdTeamId } });
}
});
afterEach(async () => {
await clearTestMemberships();
});
describe("hasPendingInviteByUserId", () => {
it("should return true when user has a pending invite (accepted: false)", async () => {
const newUser = await prisma.user.create({
data: {
email: `test-pending-invite-${Date.now()}@example.com`,
username: `test-pending-${Date.now()}`,
},
});
const membership = await prisma.membership.create({
data: {
userId: newUser.id,
teamId: testTeamId,
role: MembershipRole.MEMBER,
accepted: false,
},
});
createdMembershipIds.push(membership.id);
const result = await MembershipRepository.hasPendingInviteByUserId({ userId: newUser.id });
expect(result).toBe(true);
await prisma.membership.delete({ where: { id: membership.id } });
createdMembershipIds.length = 0;
await prisma.user.delete({ where: { id: newUser.id } });
});
it("should return false when user has no pending invites (all accepted)", async () => {
const newUser = await prisma.user.create({
data: {
email: `test-accepted-invite-${Date.now()}@example.com`,
username: `test-accepted-${Date.now()}`,
},
});
const membership = await prisma.membership.create({
data: {
userId: newUser.id,
teamId: testTeamId,
role: MembershipRole.MEMBER,
accepted: true,
},
});
createdMembershipIds.push(membership.id);
const result = await MembershipRepository.hasPendingInviteByUserId({ userId: newUser.id });
expect(result).toBe(false);
await prisma.membership.delete({ where: { id: membership.id } });
createdMembershipIds.length = 0;
await prisma.user.delete({ where: { id: newUser.id } });
});
it("should return false when user has no memberships at all", async () => {
const newUser = await prisma.user.create({
data: {
email: `test-no-membership-${Date.now()}@example.com`,
username: `test-no-membership-${Date.now()}`,
},
});
const result = await MembershipRepository.hasPendingInviteByUserId({ userId: newUser.id });
expect(result).toBe(false);
await prisma.user.delete({ where: { id: newUser.id } });
});
it("should return true when user has both accepted and pending invites", async () => {
const newUser = await prisma.user.create({
data: {
email: `test-mixed-invites-${Date.now()}@example.com`,
username: `test-mixed-${Date.now()}`,
},
});
const team2 = await prisma.team.findFirst({
where: {
slug: { not: null },
id: { not: testTeamId },
},
});
const acceptedMembership = await prisma.membership.create({
data: {
userId: newUser.id,
teamId: testTeamId,
role: MembershipRole.MEMBER,
accepted: true,
},
});
createdMembershipIds.push(acceptedMembership.id);
if (team2) {
const pendingMembership = await prisma.membership.create({
data: {
userId: newUser.id,
teamId: team2.id,
role: MembershipRole.MEMBER,
accepted: false,
},
});
createdMembershipIds.push(pendingMembership.id);
}
const result = await MembershipRepository.hasPendingInviteByUserId({ userId: newUser.id });
expect(result).toBe(team2 ? true : false);
await clearTestMemberships();
await prisma.user.delete({ where: { id: newUser.id } });
});
});
});