diff --git a/packages/features/auth/signup/utils/createOrUpdateMemberships.integration-test.ts b/packages/features/auth/signup/utils/createOrUpdateMemberships.integration-test.ts deleted file mode 100644 index 9c8e9fcb12..0000000000 --- a/packages/features/auth/signup/utils/createOrUpdateMemberships.integration-test.ts +++ /dev/null @@ -1,472 +0,0 @@ -import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; - -import { prisma } from "@calcom/prisma"; -import type { Team, User, OrganizationSettings } from "@calcom/prisma/client"; -import { MembershipRole } from "@calcom/prisma/enums"; - -import { createOrUpdateMemberships } from "./createOrUpdateMemberships"; - -function generateUniqueId() { - return `${Date.now()}-${Math.random().toString(36).substring(7)}`; -} - -async function createTestUser(data: { - email: string; - username: string; - organizationId?: number | null; -}): Promise { - const uniqueId = generateUniqueId(); - const uniqueEmail = data.email.includes("@") - ? data.email.replace("@", `-${uniqueId}@`) - : `${data.email}-${uniqueId}@example.com`; - const uniqueUsername = `${data.username}-${uniqueId}`; - - return await prisma.user.create({ - data: { - email: uniqueEmail, - username: uniqueUsername, - organizationId: data.organizationId ?? undefined, - }, - }); -} - -async function createTestOrganization(data: { - name: string; - slug: string; - orgAutoAcceptEmail?: string; -}): Promise { - const uniqueId = generateUniqueId(); - const uniqueSlug = `${data.slug}-${uniqueId}`; - - const team = await prisma.team.create({ - data: { - name: `${data.name} ${uniqueId}`, - slug: uniqueSlug, - isOrganization: true, - }, - }); - - let organizationSettings: OrganizationSettings | null = null; - if (data.orgAutoAcceptEmail) { - organizationSettings = await prisma.organizationSettings.create({ - data: { - organizationId: team.id, - orgAutoAcceptEmail: data.orgAutoAcceptEmail, - isOrganizationVerified: true, - }, - }); - } - - return { ...team, organizationSettings }; -} - -async function createTestSubteam(data: { name: string; slug: string; parentId: number }): Promise { - const uniqueId = generateUniqueId(); - const uniqueSlug = `${data.slug}-${uniqueId}`; - - return await prisma.team.create({ - data: { - name: `${data.name} ${uniqueId}`, - slug: uniqueSlug, - isOrganization: false, - parentId: data.parentId, - }, - }); -} - -describe("createOrUpdateMemberships Integration Tests", () => { - let testUsers: User[] = []; - let testTeams: Team[] = []; - - beforeEach(() => { - testUsers = []; - testTeams = []; - }); - - afterEach(async () => { - const userIds = testUsers.map((u) => u.id); - const teamIds = testTeams.map((t) => t.id); - - if (userIds.length > 0 || teamIds.length > 0) { - // Clean up in reverse dependency order - await prisma.profile.deleteMany({ - where: { - OR: [{ userId: { in: userIds } }, { organizationId: { in: teamIds } }], - }, - }); - await prisma.membership.deleteMany({ - where: { - OR: [{ userId: { in: userIds } }, { teamId: { in: teamIds } }], - }, - }); - await prisma.organizationSettings.deleteMany({ - where: { organizationId: { in: teamIds } }, - }); - await prisma.team.deleteMany({ - where: { id: { in: teamIds } }, - }); - await prisma.user.deleteMany({ - where: { id: { in: userIds } }, - }); - } - }); - - function trackUser(user: User): User { - testUsers.push(user); - return user; - } - - function trackTeam(team: Team): Team { - testTeams.push(team); - return team; - } - - describe("Organization Direct Membership", () => { - it("should create membership and profile when joining an organization directly", async () => { - // Setup - const org = trackTeam( - await createTestOrganization({ - name: "Test Org", - slug: "test-org", - orgAutoAcceptEmail: "example.com", - }) - ); - - const user = trackUser( - await createTestUser({ - email: "user@example.com", - username: "testuser", - organizationId: org.id, - }) - ); - - // Act - await createOrUpdateMemberships({ - user: { id: user.id }, - team: { - id: org.id, - parentId: org.parentId, - isOrganization: org.isOrganization ?? false, - organizationSettings: { orgAutoAcceptEmail: "example.com" }, - }, - }); - - // Assert - membership created with accepted: true - const membership = await prisma.membership.findUnique({ - where: { userId_teamId: { userId: user.id, teamId: org.id } }, - }); - expect(membership).toBeTruthy(); - expect(membership?.accepted).toBe(true); - expect(membership?.role).toBe(MembershipRole.MEMBER); - - // Assert - profile created - const profile = await prisma.profile.findUnique({ - where: { - userId_organizationId: { userId: user.id, organizationId: org.id }, - }, - }); - expect(profile).toBeTruthy(); - expect(profile?.organizationId).toBe(org.id); - }); - }); - - describe("Sub-Team Membership (within organization)", () => { - it("should create memberships for both team and parent org when joining a sub-team", async () => { - // Setup - const org = trackTeam( - await createTestOrganization({ - name: "Test Org", - slug: "test-org", - orgAutoAcceptEmail: "example.com", - }) - ); - - const subteam = trackTeam( - await createTestSubteam({ - name: "Sub Team", - slug: "sub-team", - parentId: org.id, - }) - ); - - const user = trackUser( - await createTestUser({ - email: "user@example.com", - username: "testuser", - organizationId: org.id, // User already has organizationId set (as done in calcomSignupHandler) - }) - ); - - // Act - await createOrUpdateMemberships({ - user: { id: user.id }, - team: { - id: subteam.id, - parentId: subteam.parentId, - isOrganization: subteam.isOrganization ?? false, - organizationSettings: null, - }, - }); - - // Assert - team membership created - const teamMembership = await prisma.membership.findUnique({ - where: { userId_teamId: { userId: user.id, teamId: subteam.id } }, - }); - expect(teamMembership).toBeTruthy(); - expect(teamMembership?.accepted).toBe(true); - - // Assert - org membership created - const orgMembership = await prisma.membership.findUnique({ - where: { userId_teamId: { userId: user.id, teamId: org.id } }, - }); - expect(orgMembership).toBeTruthy(); - expect(orgMembership?.accepted).toBe(true); - }); - - it("should create profile for parent org when joining a sub-team", async () => { - // Setup - const org = trackTeam( - await createTestOrganization({ - name: "Test Org", - slug: "test-org", - orgAutoAcceptEmail: "example.com", - }) - ); - - const subteam = trackTeam( - await createTestSubteam({ - name: "Sub Team", - slug: "sub-team", - parentId: org.id, - }) - ); - - const user = trackUser( - await createTestUser({ - email: "user@example.com", - username: "testuser", - organizationId: org.id, // User already has organizationId set - }) - ); - - // Act - NOTE: We need to pass parent data for profile creation - await createOrUpdateMemberships({ - user: { id: user.id }, - team: { - id: subteam.id, - parentId: subteam.parentId, - isOrganization: subteam.isOrganization ?? false, - organizationSettings: null, - parent: { - id: org.id, - organizationSettings: { orgAutoAcceptEmail: "example.com" }, - }, - }, - }); - - // Assert - profile should be created for the parent organization - const profile = await prisma.profile.findUnique({ - where: { - userId_organizationId: { userId: user.id, organizationId: org.id }, - }, - }); - - // BUG: This assertion will FAIL because profile is not created for sub-team signup - expect(profile).toBeTruthy(); - expect(profile?.organizationId).toBe(org.id); - }); - - it("should handle user invited to sub-team via invite link (new user flow)", async () => { - // This simulates the API v2 invite link flow: - // 1. Token created with identifier: "invite-link-for-teamId-${teamId}" - // 2. User fills in their email and signs up - // 3. User should be added to both team and org with proper profile - - // Setup - const org = trackTeam( - await createTestOrganization({ - name: "Acme Corp", - slug: "acme", - orgAutoAcceptEmail: "acme.com", - }) - ); - - const salesTeam = trackTeam( - await createTestSubteam({ - name: "Sales Team", - slug: "sales", - parentId: org.id, - }) - ); - - // User is created during signup with organizationId already set - // (this happens in calcomSignupHandler.ts:200-232) - const newUser = trackUser( - await createTestUser({ - email: "newuser@acme.com", - username: "newuser", - organizationId: org.id, - }) - ); - - // Act - simulate createOrUpdateMemberships call from calcomSignupHandler - await createOrUpdateMemberships({ - user: { id: newUser.id }, - team: { - id: salesTeam.id, - parentId: salesTeam.parentId, - isOrganization: salesTeam.isOrganization ?? false, - organizationSettings: null, - parent: { - id: org.id, - organizationSettings: { orgAutoAcceptEmail: "acme.com" }, - }, - }, - }); - - // Assert - team membership exists - const teamMembership = await prisma.membership.findUnique({ - where: { userId_teamId: { userId: newUser.id, teamId: salesTeam.id } }, - }); - expect(teamMembership).toBeTruthy(); - expect(teamMembership?.accepted).toBe(true); - - // Assert - org membership exists - const orgMembership = await prisma.membership.findUnique({ - where: { userId_teamId: { userId: newUser.id, teamId: org.id } }, - }); - expect(orgMembership).toBeTruthy(); - expect(orgMembership?.accepted).toBe(true); - - // Assert - profile exists for the organization - // BUG: This will FAIL - the profile is not created for sub-team invites - const profile = await prisma.profile.findUnique({ - where: { - userId_organizationId: { userId: newUser.id, organizationId: org.id }, - }, - }); - expect(profile).toBeTruthy(); - expect(profile?.organizationId).toBe(org.id); - expect(profile?.username).toBe(newUser.username); - }); - - it("should upsert existing memberships when user was previously invited", async () => { - // Setup - user was invited via email (membership created with accepted: false) - const org = trackTeam( - await createTestOrganization({ - name: "Test Org", - slug: "test-org", - orgAutoAcceptEmail: "example.com", - }) - ); - - const subteam = trackTeam( - await createTestSubteam({ - name: "Sub Team", - slug: "sub-team", - parentId: org.id, - }) - ); - - const user = trackUser( - await createTestUser({ - email: "invited@other.com", - username: "inviteduser", - organizationId: org.id, - }) - ); - - // Pre-create memberships as they would be created during invite - await prisma.membership.create({ - data: { - userId: user.id, - teamId: subteam.id, - role: MembershipRole.MEMBER, - accepted: false, - }, - }); - await prisma.membership.create({ - data: { - userId: user.id, - teamId: org.id, - role: MembershipRole.MEMBER, - accepted: false, - }, - }); - - // Act - user completes signup - await createOrUpdateMemberships({ - user: { id: user.id }, - team: { - id: subteam.id, - parentId: subteam.parentId, - isOrganization: subteam.isOrganization ?? false, - organizationSettings: null, - parent: { - id: org.id, - organizationSettings: null, - }, - }, - }); - - // Assert - memberships are now accepted - const teamMembership = await prisma.membership.findUnique({ - where: { userId_teamId: { userId: user.id, teamId: subteam.id } }, - }); - expect(teamMembership?.accepted).toBe(true); - - const orgMembership = await prisma.membership.findUnique({ - where: { userId_teamId: { userId: user.id, teamId: org.id } }, - }); - expect(orgMembership?.accepted).toBe(true); - }); - }); - - describe("Regular Team Membership (not in organization)", () => { - it("should create only team membership for regular teams", async () => { - // Setup - regular team without parent org - const uniqueId = generateUniqueId(); - const regularTeam = trackTeam( - await prisma.team.create({ - data: { - name: `Regular Team ${uniqueId}`, - slug: `regular-team-${uniqueId}`, - isOrganization: false, - parentId: null, - }, - }) - ); - - const user = trackUser( - await createTestUser({ - email: "user@example.com", - username: "testuser", - }) - ); - - // Act - await createOrUpdateMemberships({ - user: { id: user.id }, - team: { - id: regularTeam.id, - parentId: regularTeam.parentId, - isOrganization: regularTeam.isOrganization ?? false, - organizationSettings: null, - }, - }); - - // Assert - team membership created - const teamMembership = await prisma.membership.findUnique({ - where: { userId_teamId: { userId: user.id, teamId: regularTeam.id } }, - }); - expect(teamMembership).toBeTruthy(); - expect(teamMembership?.accepted).toBe(true); - - // Assert - no profile should be created for regular teams - const profiles = await prisma.profile.findMany({ - where: { userId: user.id }, - }); - expect(profiles.length).toBe(0); - }); - }); -});