cal/apps/web/lib/orgMigration.test.ts
Hariom Balhara 4aa0b4cc65
fix: Team URL and booker URLs (#13050)
Co-authored-by: Bailey Pumfleet <bailey@pumfleet.co.uk>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2024-01-08 15:25:48 +00:00

1513 lines
40 KiB
TypeScript

import prismock from "../../../tests/libs/__mocks__/prisma";
import { describe, expect, it } from "vitest";
import type { z } from "zod";
import { WEBSITE_URL } from "@calcom/lib/constants";
import type { MembershipRole, Prisma } from "@calcom/prisma/client";
import { RedirectType } from "@calcom/prisma/enums";
import type { teamMetadataSchema } from "@calcom/prisma/zod-utils";
import { moveTeamToOrg, moveUserToOrg, removeTeamFromOrg, removeUserFromOrg } from "./orgMigration";
const WEBSITE_PROTOCOL = new URL(WEBSITE_URL).protocol;
describe("orgMigration", () => {
describe("moveUserToOrg", () => {
describe("when user email does not match orgAutoAcceptEmail", () => {
it(`should migrate a user to become a part of an organization with ADMIN role
- username in the organization should be automatically derived from email when it isn't passed`, async () => {
const data = {
userToMigrate: {
username: "user-1",
email: "user-1@example.com",
// Because example.com isn't the orgAutoAcceptEmail
expectedUsernameInOrg: "user-1-example",
},
targetOrg: {
name: "Org 1",
slug: "org1",
},
membershipWeWant: {
role: "ADMIN",
} as const,
};
const dbUserToMigrate = await createUserOutsideOrg({
email: data.userToMigrate.email,
username: data.userToMigrate.username,
});
const dbOrg = await createOrg({
name: data.targetOrg.name,
slug: data.targetOrg.slug,
});
const team1 = await createTeamOutsideOrg({
name: "Team-1",
slug: "team-1",
});
// Make userToMigrate part of team-1
await addMemberShipOfUserWithTeam({
teamId: team1.id,
userId: dbUserToMigrate.id,
role: "MEMBER",
accepted: true,
});
await moveUserToOrg({
user: {
id: dbUserToMigrate.id,
},
targetOrg: {
id: dbOrg.id,
membership: {
role: data.membershipWeWant.role,
},
},
shouldMoveTeams: false,
});
await expectUserToBeAPartOfOrg({
userId: dbUserToMigrate.id,
orgId: dbOrg.id,
usernameInOrg: data.userToMigrate.expectedUsernameInOrg,
expectedMembership: { role: data.membershipWeWant.role, accepted: true },
});
await expectTeamToBeNotPartOfAnyOrganization({
teamId: team1.id,
});
expectUserRedirectToBeEnabled({
from: {
username: data.userToMigrate.username,
},
to: data.userToMigrate.expectedUsernameInOrg,
orgSlug: data.targetOrg.slug,
});
});
it("should migrate a user to become a part of an organization(which has slug set) with MEMBER role", async () => {
const data = {
userToMigrate: {
username: "user-1",
email: "user-1@example.com",
// Because example.com isn't the orgAutoAcceptEmail
expectedUsernameInOrg: "user-1-example",
},
targetOrg: {
id: 1,
name: "Org 1",
slug: "org1",
},
membershipWeWant: {
role: "MEMBER",
} as const,
};
const dbUserToMigrate = await createUserOutsideOrg({
email: data.userToMigrate.email,
username: data.userToMigrate.username,
});
const dbOrg = await createOrg({
slug: data.targetOrg.slug,
name: data.targetOrg.name,
});
await moveUserToOrg({
user: {
id: dbOrg.id,
},
targetOrg: {
id: data.targetOrg.id,
membership: {
role: data.membershipWeWant.role,
},
},
shouldMoveTeams: false,
});
await expectUserToBeAPartOfOrg({
userId: dbUserToMigrate.id,
orgId: data.targetOrg.id,
usernameInOrg: data.userToMigrate.expectedUsernameInOrg,
expectedMembership: { role: data.membershipWeWant.role, accepted: true },
});
});
it(`should migrate a user to become a part of an organization(which has no slug but requestedSlug) with MEMBER role
1. Should set the slug as requestedSlug for the organization(so that the redirect doesnt take to an unpublished organization page)`, async () => {
const data = {
userToMigrate: {
username: "user-1",
email: "user-1@example.com",
// Because example.com isn't the orgAutoAcceptEmail
expectedUsernameInOrg: "user-1-example",
},
targetOrg: {
name: "Org 1",
requestedSlug: "org1",
},
membershipWeWant: {
role: "MEMBER",
} as const,
};
const dbUserToMigrate = await createUserOutsideOrg({
email: "user-1@example.com",
username: "user-1",
});
const dbOrg = await createOrg({
name: data.targetOrg.name,
metadata: {
requestedSlug: data.targetOrg.requestedSlug,
},
});
await moveUserToOrg({
user: {
id: dbUserToMigrate.id,
},
targetOrg: {
id: dbOrg.id,
membership: {
role: data.membershipWeWant.role,
},
},
shouldMoveTeams: false,
});
const organization = await prismock.team.findUnique({
where: {
id: dbOrg.id,
},
});
expect(organization?.slug).toBe(data.targetOrg.requestedSlug);
await expectUserToBeAPartOfOrg({
userId: dbUserToMigrate.id,
orgId: dbOrg.id,
usernameInOrg: data.userToMigrate.expectedUsernameInOrg,
expectedMembership: { role: data.membershipWeWant.role, accepted: true },
});
});
it("should migrate a user along with its teams(without other members)", async () => {
const data = {
userToMigrate: {
username: "user-1",
email: "user-1@example.com",
// Because example.com isn't the orgAutoAcceptEmail
expectedUsernameInOrg: "user-1-example",
teams: [
{
name: "Team 100",
slug: "team-100",
},
{
name: "Team 101",
slug: "team-101",
},
],
},
targetOrg: {
name: "Org 1",
slug: "org1",
},
membershipWeWant: {
role: "MEMBER",
} as const,
userPartOfTeam1: {
username: "user-2",
email: "user-2@example.com",
},
userPartOfTeam2: {
username: "user-3",
email: "user-3@example.com",
},
};
// Create user to migrate
const dbUserToMigrate = await createUserOutsideOrg({
email: data.userToMigrate.email,
username: data.userToMigrate.username,
});
// Create another user that would be part of team-1 along with userToMigrate
const userPartOfTeam1 = await createUserOutsideOrg({
email: data.userPartOfTeam1.email,
username: data.userPartOfTeam1.username,
});
// Create another user that would be part of team-2 along with userToMigrate
const userPartOfTeam2 = await createUserOutsideOrg({
email: data.userPartOfTeam2.email,
username: data.userPartOfTeam2.username,
});
const team1 = await createTeamOutsideOrg({
name: data.userToMigrate.teams[0].name,
slug: data.userToMigrate.teams[0].slug,
});
const team2 = await createTeamOutsideOrg({
name: data.userToMigrate.teams[1].name,
slug: data.userToMigrate.teams[1].slug,
});
// Make userToMigrate part of team-1
await addMemberShipOfUserWithTeam({
teamId: team1.id,
userId: dbUserToMigrate.id,
role: "MEMBER",
accepted: true,
});
// Make userToMigrate part of team-2
await addMemberShipOfUserWithTeam({
teamId: team2.id,
userId: dbUserToMigrate.id,
role: "MEMBER",
accepted: true,
});
// Make userPartofTeam1 part of team-1
await addMemberShipOfUserWithTeam({
teamId: team1.id,
userId: userPartOfTeam1.id,
role: "MEMBER",
accepted: true,
});
// Make userPartofTeam2 part of team2
await addMemberShipOfUserWithTeam({
teamId: team2.id,
userId: userPartOfTeam2.id,
role: "MEMBER",
accepted: true,
});
const dbOrg = await createOrg({
name: data.targetOrg.name,
slug: data.targetOrg.slug,
});
await moveUserToOrg({
user: {
id: dbUserToMigrate.id,
},
targetOrg: {
id: dbOrg.id,
membership: {
role: data.membershipWeWant.role,
},
},
shouldMoveTeams: true,
});
await expectUserToBeAPartOfOrg({
userId: dbUserToMigrate.id,
orgId: dbOrg.id,
usernameInOrg: data.userToMigrate.expectedUsernameInOrg,
expectedMembership: { role: data.membershipWeWant.role, accepted: true },
});
await expectTeamToBeAPartOfOrg({
teamId: team1.id,
orgId: dbOrg.id,
teamSlugInOrg: team1.slug,
});
await expectTeamToBeAPartOfOrg({
teamId: team2.id,
orgId: dbOrg.id,
teamSlugInOrg: team2.slug,
});
await expectUserToBeNotAPartOfTheOrg({
userId: userPartOfTeam1.id,
orgId: dbOrg.id,
username: data.userPartOfTeam1.username,
});
await expectUserToBeNotAPartOfTheOrg({
userId: userPartOfTeam2.id,
orgId: dbOrg.id,
username: data.userPartOfTeam2.username,
});
});
it(`should migrate a user to become a part of an organization
- username in the organization should same as provided to moveUserToOrg`, async () => {
const data = {
userToMigrate: {
username: "user-1",
email: "user-1@example.com",
usernameInOrgThatWeWant: "user-1-in-org",
},
targetOrg: {
name: "Org 1",
slug: "org1",
},
membershipWeWant: {
role: "ADMIN",
} as const,
};
const dbUserToMigrate = await createUserOutsideOrg({
email: data.userToMigrate.email,
username: data.userToMigrate.username,
});
const dbOrg = await createOrg({
name: data.targetOrg.name,
slug: data.targetOrg.slug,
});
await moveUserToOrg({
user: {
id: dbUserToMigrate.id,
},
targetOrg: {
id: dbOrg.id,
username: data.userToMigrate.usernameInOrgThatWeWant,
membership: {
role: data.membershipWeWant.role,
},
},
shouldMoveTeams: false,
});
await expectUserToBeAPartOfOrg({
userId: dbUserToMigrate.id,
orgId: dbOrg.id,
usernameInOrg: data.userToMigrate.usernameInOrgThatWeWant,
expectedMembership: { role: data.membershipWeWant.role, accepted: true },
});
expectUserRedirectToBeEnabled({
from: {
username: data.userToMigrate.username,
},
to: data.userToMigrate.usernameInOrgThatWeWant,
orgSlug: data.targetOrg.slug,
});
});
it(`should be able to re-migrate an already migrated user fixing things as we do it
- Redirect should correctly determine the nonOrgUsername so that on repeat migrations, the original user link doesn't break`, async () => {
const data = {
userToMigrate: {
username: "user-1",
email: "user-1@example.com",
// Because example.com isn't the orgAutoAcceptEmail
usernameWeWantInOrg: "user-1-in-org",
},
targetOrg: {
name: "Org 1",
slug: "org1",
},
membershipWeWant: {
role: "MEMBER",
} as const,
};
const dbOrg = await createOrg({
name: data.targetOrg.name,
slug: data.targetOrg.slug,
});
const dbUserToMigrate = await createUserInsideTheOrg(
{
email: data.userToMigrate.email,
username: data.userToMigrate.username,
},
dbOrg.id
);
await moveUserToOrg({
user: {
id: dbUserToMigrate.id,
},
targetOrg: {
id: dbOrg.id,
username: data.userToMigrate.usernameWeWantInOrg,
membership: {
role: data.membershipWeWant.role,
},
},
shouldMoveTeams: false,
});
await expectUserToBeAPartOfOrg({
userId: dbUserToMigrate.id,
orgId: dbOrg.id,
usernameInOrg: data.userToMigrate.usernameWeWantInOrg,
// membership role should be updated
expectedMembership: { role: data.membershipWeWant.role, accepted: true },
});
await moveUserToOrg({
user: {
id: dbUserToMigrate.id,
},
targetOrg: {
id: dbOrg.id,
username: data.userToMigrate.usernameWeWantInOrg,
membership: {
role: data.membershipWeWant.role,
},
},
shouldMoveTeams: false,
});
await expectUserToBeAPartOfOrg({
userId: dbUserToMigrate.id,
orgId: dbOrg.id,
usernameInOrg: data.userToMigrate.usernameWeWantInOrg,
// membership role should be updated
expectedMembership: { role: data.membershipWeWant.role, accepted: true },
});
expectUserRedirectToBeEnabled({
from: {
username: data.userToMigrate.username,
},
to: data.userToMigrate.usernameWeWantInOrg,
orgSlug: data.targetOrg.slug,
});
console.log(await prismock.tempOrgRedirect.findMany({}));
});
describe("Failures handling", () => {
it("migration should fail if the username already exists in the organization", async () => {
const data = {
userToMigrate: {
username: "user-1",
email: "user-1@example.com",
// Because example.com isn't the orgAutoAcceptEmail
expectedUsernameInOrg: "user-1-example",
},
targetOrg: {
name: "Org 1",
slug: "org1",
},
membershipWeWant: {
role: "MEMBER",
} as const,
};
const dbOrg = await createOrg({
name: data.targetOrg.name,
slug: data.targetOrg.slug,
});
await createUserInsideTheOrg(
{
email: data.userToMigrate.email,
username: data.userToMigrate.username,
},
dbOrg.id
);
// User with same username exists outside the org as well as inside the org
const dbUserToMigrate = await createUserOutsideOrg({
email: data.userToMigrate.email,
username: data.userToMigrate.username,
});
expect(() => {
return moveUserToOrg({
user: {
id: dbUserToMigrate.id,
},
targetOrg: {
id: dbOrg.id,
username: data.userToMigrate.username,
membership: {
role: data.membershipWeWant.role,
},
},
shouldMoveTeams: false,
});
}).rejects.toThrowError("already exists");
});
it("should fail the migration if the target org doesn't exist", async () => {
const data = {
userToMigrate: {
username: "user-1",
email: "user-1@example.com",
// Because example.com isn't the orgAutoAcceptEmail
expectedUsernameInOrg: "user-1-example",
},
targetOrg: {
name: "Org 1",
slug: "org1",
},
membershipWeWant: {
role: "MEMBER",
} as const,
};
const dbOrg = await createOrg({
name: data.targetOrg.name,
slug: data.targetOrg.slug,
});
await createUserInsideTheOrg(
{
email: data.userToMigrate.email,
username: data.userToMigrate.username,
},
dbOrg.id
);
// User with same username exists outside the org as well as inside the org
const dbUserToMigrate = await createUserOutsideOrg({
email: data.userToMigrate.email,
username: data.userToMigrate.username,
});
expect(() => {
return moveUserToOrg({
user: {
id: dbUserToMigrate.id,
},
targetOrg: {
// Non existent teamId
id: 1001,
username: data.userToMigrate.username,
membership: {
role: data.membershipWeWant.role,
},
},
shouldMoveTeams: false,
});
}).rejects.toThrowError(/Org .* not found/);
});
it("should fail the migration if the target teamId is not an organization", async () => {
const data = {
userToMigrate: {
username: "user-1",
email: "user-1@example.com",
// Because example.com isn't the orgAutoAcceptEmail
expectedUsernameInOrg: "user-1-example",
},
targetOrg: {
name: "Org 1",
slug: "org1",
},
membershipWeWant: {
role: "MEMBER",
} as const,
};
// Create a team instead of an organization
const dbOrgWhichIsActuallyATeam = await createTeamOutsideOrg({
name: data.targetOrg.name,
slug: data.targetOrg.slug,
});
const dbUserToMigrate = await createUserOutsideOrg({
email: data.userToMigrate.email,
username: data.userToMigrate.username,
});
expect(() => {
return moveUserToOrg({
user: {
id: dbUserToMigrate.id,
},
targetOrg: {
// Non existent teamId
id: dbOrgWhichIsActuallyATeam.id,
username: data.userToMigrate.username,
membership: {
role: data.membershipWeWant.role,
},
},
shouldMoveTeams: false,
});
}).rejects.toThrowError(/is not an Org/);
});
it("should fail the migration if the user is part of any other organization", async () => {
const data = {
userToMigrate: {
username: "user-1",
email: "user-1@example.com",
// Because example.com isn't the orgAutoAcceptEmail
expectedUsernameInOrg: "user-1-example",
},
targetOrg: {
name: "Org 1",
slug: "org1",
},
membershipWeWant: {
role: "MEMBER",
} as const,
};
const dbOrg = await createOrg({
name: data.targetOrg.name,
slug: data.targetOrg.slug,
});
const dbOrgOther = await createOrg({
name: data.targetOrg.name,
slug: data.targetOrg.slug,
});
const dbUserToMigrate = await createUserInsideTheOrg(
{
email: data.userToMigrate.email,
username: data.userToMigrate.username,
},
dbOrgOther.id
);
expect(() => {
return moveUserToOrg({
user: {
id: dbUserToMigrate.id,
},
targetOrg: {
// Non existent teamId
id: dbOrg.id,
username: data.userToMigrate.username,
membership: {
role: data.membershipWeWant.role,
},
},
shouldMoveTeams: false,
});
}).rejects.toThrowError(/already a part of different organization/);
});
});
});
describe("when user email matches orgAutoAcceptEmail", () => {
const orgMetadata = {
orgAutoAcceptEmail: "org1.com",
} as const;
it(`should migrate a user to become a part of an organization with ADMIN role`, async () => {
const data = {
userToMigrate: {
username: "user-1",
email: "user-1@org1.com",
// Because example.com isn't the orgAutoAcceptEmail
expectedUsernameInOrg: "user-1",
},
targetOrg: {
name: "Org 1",
slug: "org1",
},
membershipWeWant: {
role: "ADMIN",
} as const,
};
const dbUserToMigrate = await createUserOutsideOrg({
email: data.userToMigrate.email,
username: data.userToMigrate.username,
});
const dbOrg = await createOrg({
slug: data.targetOrg.slug,
name: data.targetOrg.name,
metadata: {
...orgMetadata,
},
});
await moveUserToOrg({
user: {
id: dbUserToMigrate.id,
},
targetOrg: {
id: dbOrg.id,
membership: {
role: data.membershipWeWant.role,
},
},
shouldMoveTeams: false,
});
await expectUserToBeAPartOfOrg({
userId: dbUserToMigrate.id,
orgId: dbOrg.id,
usernameInOrg: data.userToMigrate.expectedUsernameInOrg,
expectedMembership: { role: data.membershipWeWant.role, accepted: true },
});
expectUserRedirectToBeEnabled({
from: {
username: data.userToMigrate.username,
},
to: data.userToMigrate.expectedUsernameInOrg,
orgSlug: data.targetOrg.slug,
});
});
it("should migrate a user to become a part of an organization(which has slug set) with MEMBER role", async () => {
const data = {
userToMigrate: {
username: "user-1",
email: "user-1@org1.com",
// Because example.com isn't the orgAutoAcceptEmail
expectedUsernameInOrg: "user-1",
},
targetOrg: {
name: "Org 1",
slug: "org1",
},
membershipWeWant: {
role: "MEMBER",
} as const,
};
const dbUserToMigrate = await createUserOutsideOrg({
email: data.userToMigrate.email,
username: data.userToMigrate.username,
});
const dbOrg = await createOrg({
slug: data.targetOrg.slug,
name: data.targetOrg.name,
metadata: {
...orgMetadata,
},
});
await moveUserToOrg({
user: {
id: dbUserToMigrate.id,
},
targetOrg: {
id: dbOrg.id,
membership: {
role: data.membershipWeWant.role,
},
},
shouldMoveTeams: false,
});
await expectUserToBeAPartOfOrg({
userId: dbUserToMigrate.id,
orgId: dbOrg.id,
usernameInOrg: data.userToMigrate.expectedUsernameInOrg,
expectedMembership: { role: data.membershipWeWant.role, accepted: true },
});
});
it(`should migrate a user to become a part of an organization(which has no slug but requestedSlug) with MEMBER role
1. Should set the slug as requestedSlug for the organization(so that the redirect doesnt take to an unpublished organization page)`, async () => {
const data = {
userToMigrate: {
username: "user-1",
email: "user-1@org1.com",
// Because example.com isn't the orgAutoAcceptEmail
expectedUsernameInOrg: "user-1",
},
targetOrg: {
name: "Org 1",
requestedSlug: "org1",
},
membershipWeWant: {
role: "MEMBER",
} as const,
};
const dbUserToMigrate = await createUserOutsideOrg({
email: data.userToMigrate.email,
username: data.userToMigrate.username,
});
const dbOrg = await createOrg({
name: data.targetOrg.name,
metadata: {
requestedSlug: data.targetOrg.requestedSlug,
...orgMetadata,
},
});
await moveUserToOrg({
user: {
id: dbUserToMigrate.id,
},
targetOrg: {
id: dbOrg.id,
membership: {
role: data.membershipWeWant.role,
},
},
shouldMoveTeams: false,
});
const organization = await prismock.team.findUnique({
where: {
id: dbOrg.id,
},
});
expect(organization?.slug).toBe(data.targetOrg.requestedSlug);
await expectUserToBeAPartOfOrg({
userId: dbUserToMigrate.id,
orgId: dbOrg.id,
usernameInOrg: data.userToMigrate.expectedUsernameInOrg,
expectedMembership: { role: data.membershipWeWant.role, accepted: true },
});
});
});
});
describe("moveTeamToOrg", () => {
it(`should migrate a team to become a part of an organization`, async () => {
const data = {
teamToMigrate: {
id: 1,
name: "Team 1",
slug: "team1",
newSlug: "team1-new-slug",
},
targetOrg: {
id: 2,
name: "Org 1",
slug: "org1",
},
};
await prismock.team.create({
data: {
id: data.teamToMigrate.id,
slug: data.teamToMigrate.slug,
name: data.teamToMigrate.name,
},
});
await prismock.team.create({
data: {
id: data.targetOrg.id,
slug: data.targetOrg.slug,
name: data.targetOrg.name,
metadata: {
isOrganization: true,
},
},
});
await moveTeamToOrg({
teamId: data.teamToMigrate.id,
targetOrg: {
id: data.targetOrg.id,
teamSlug: data.teamToMigrate.newSlug,
},
});
await expectTeamToBeAPartOfOrg({
teamId: data.teamToMigrate.id,
orgId: data.targetOrg.id,
teamSlugInOrg: data.teamToMigrate.newSlug,
});
expectTeamRedirectToBeEnabled({
from: {
teamSlug: data.teamToMigrate.slug,
},
to: data.teamToMigrate.newSlug,
orgSlug: data.targetOrg.slug,
});
});
it.todo("should migrate a team with members");
it.todo("Try migrating an already migrated team");
});
describe("removeUserFromOrg", () => {
it(`should remove a user from an organization but he should remain in team`, async () => {
const data = {
userToUnmigrate: {
username: "user1-in-org1",
email: "user-1@example.com",
usernameBeforeMovingToOrg: "user1",
},
targetOrg: {
name: "Org 1",
slug: "org1",
},
membershipWeWant: {
role: "ADMIN",
} as const,
};
const dbOrg = await createOrg({
slug: data.targetOrg.slug,
name: data.targetOrg.name,
});
const dbTeamOutsideOrg = await createTeamOutsideOrg({
slug: "team-1",
name: "Team 1",
});
const dbUser = await createUserInsideTheOrg(
{
email: data.userToUnmigrate.email,
username: data.userToUnmigrate.username,
metadata: {
migratedToOrgFrom: {
username: data.userToUnmigrate.usernameBeforeMovingToOrg,
},
},
},
dbOrg.id
);
await addMemberShipOfUserWithOrg({
userId: dbUser.id,
teamId: dbTeamOutsideOrg.id,
role: "MEMBER",
accepted: true,
});
await addMemberShipOfUserWithTeam({
userId: dbUser.id,
teamId: dbOrg.id,
role: data.membershipWeWant.role,
accepted: true,
});
const userToUnmigrate = await prismock.user.findUnique({
where: {
id: dbUser.id,
},
include: {
organization: true,
},
});
if (!userToUnmigrate?.organizationId || !userToUnmigrate.organization) {
throw new Error(
`Couldn't setup user to unmigrate properly userToUnMigrate: ${{
organizationId: userToUnmigrate?.organizationId,
organization: !!userToUnmigrate?.organization,
}}`
);
}
await removeUserFromOrg({
userId: dbUser.id,
targetOrgId: dbOrg.id,
});
await expectUserToBeNotAPartOfTheOrg({
userId: dbUser.id,
orgId: dbOrg.id,
username: data.userToUnmigrate.usernameBeforeMovingToOrg,
});
await expectUserToBeAPartOfTeam({
userId: dbUser.id,
teamId: dbTeamOutsideOrg.id,
expectedMembership: {
role: "MEMBER",
accepted: true,
},
});
expectUserRedirectToBeNotEnabled({
from: {
username: data.userToUnmigrate.username,
},
});
});
});
describe("removeTeamFromOrg", () => {
it(`should remove a team from an organization`, async () => {
const data = {
teamToUnmigrate: {
name: "Team 1",
slug: "team1",
},
targetOrg: {
name: "Org 1",
slug: "org1",
},
};
const targetOrg = await prismock.team.create({
data: {
slug: data.targetOrg.slug,
name: data.targetOrg.name,
metadata: {
isOrganization: true,
},
},
});
const { id: teamToUnMigrateId } = await prismock.team.create({
data: {
slug: data.teamToUnmigrate.slug,
name: data.teamToUnmigrate.name,
parent: {
connect: {
id: targetOrg.id,
},
},
},
});
const teamToUnmigrate = await prismock.team.findUnique({
where: {
id: teamToUnMigrateId,
},
include: {
parent: true,
},
});
if (!teamToUnmigrate?.parent || !teamToUnmigrate.parentId) {
throw new Error(`Couldn't setup team to unmigrate properly ID:${teamToUnMigrateId}`);
}
await removeTeamFromOrg({
teamId: teamToUnMigrateId,
targetOrgId: targetOrg.id,
});
await expectTeamToBeNotPartOfAnyOrganization({
teamId: teamToUnMigrateId,
});
expectTeamRedirectToBeNotEnabled({
from: {
teamSlug: data.teamToUnmigrate.slug,
},
to: data.teamToUnmigrate.slug,
orgSlug: data.targetOrg.slug,
});
});
});
});
async function expectUserToBeAPartOfOrg({
userId,
orgId,
expectedMembership,
usernameInOrg,
}: {
userId: number;
orgId: number;
usernameInOrg: string;
expectedMembership: {
role: MembershipRole;
accepted: boolean;
};
}) {
const migratedUser = await prismock.user.findUnique({
where: {
id: userId,
},
include: {
teams: true,
},
});
if (!migratedUser) {
throw new Error(`User with id ${userId} does not exist`);
}
expect(migratedUser.username).toBe(usernameInOrg);
expect(migratedUser.organizationId).toBe(orgId);
const membership = migratedUser.teams.find(
(membership) => membership.teamId === orgId && membership.userId === userId
);
expect(membership).not.toBeNull();
if (!membership) {
throw new Error(`User with id ${userId} is not a part of org with id ${orgId}`);
}
expect(membership.role).toBe(expectedMembership.role);
expect(membership.accepted).toBe(expectedMembership.accepted);
}
async function expectUserToBeAPartOfTeam({
userId,
teamId,
expectedMembership,
}: {
userId: number;
teamId: number;
expectedMembership: {
role: MembershipRole;
accepted: boolean;
};
}) {
const user = await prismock.user.findUnique({
where: {
id: userId,
},
include: {
teams: true,
},
});
if (!user) {
throw new Error(`User with id ${userId} does not exist`);
}
const membership = user.teams.find(
(membership) => membership.teamId === teamId && membership.userId === userId
);
expect(membership).not.toBeNull();
if (!membership) {
throw new Error(`User with id ${userId} is not a part of team with id ${teamId}`);
}
expect(membership.role).toBe(expectedMembership.role);
expect(membership.accepted).toBe(expectedMembership.accepted);
}
async function expectUserToBeNotAPartOfTheOrg({
userId,
orgId,
username,
}: {
userId: number;
orgId: number;
username: string;
}) {
const user = await prismock.user.findUnique({
where: {
id: userId,
},
include: {
teams: true,
},
});
if (!user) {
throw new Error(`User with id ${userId} does not exist`);
}
expect(user.username).toBe(username);
expect(user.organizationId).toBe(null);
const membership = user.teams.find(
(membership) => membership.teamId === orgId && membership.userId === userId
);
expect(membership).toBeUndefined();
}
async function expectTeamToBeAPartOfOrg({
teamId,
orgId,
teamSlugInOrg,
}: {
teamId: number;
orgId: number;
teamSlugInOrg: string | null;
}) {
const migratedTeam = await prismock.team.findUnique({
where: {
id: teamId,
},
});
if (!migratedTeam) {
throw new Error(`Team with id ${teamId} does not exist`);
}
if (!teamSlugInOrg) {
throw new Error(`teamSlugInOrg should be defined`);
}
expect(migratedTeam.parentId).toBe(orgId);
expect(migratedTeam.slug).toBe(teamSlugInOrg);
}
async function expectTeamToBeNotPartOfAnyOrganization({ teamId }: { teamId: number }) {
const team = await prismock.team.findUnique({
where: {
id: teamId,
},
});
if (!team) {
throw new Error(`Team with id ${teamId} does not exist`);
}
expect(team.parentId).toBe(null);
}
async function expectUserRedirectToBeEnabled({
from,
to,
orgSlug,
}: {
from: { username: string } | { teamSlug: string };
to: string;
orgSlug: string;
}) {
expectRedirectToBeEnabled({
from,
to,
orgSlug,
redirectType: RedirectType.User,
});
}
async function expectTeamRedirectToBeEnabled({
from,
to,
orgSlug,
}: {
from: { username: string } | { teamSlug: string };
to: string;
orgSlug: string;
}) {
expectRedirectToBeEnabled({
from,
to,
orgSlug,
redirectType: RedirectType.Team,
});
}
async function expectUserRedirectToBeNotEnabled({
from,
}: {
from: { username: string } | { teamSlug: string };
}) {
expectRedirectToBeNotEnabled({
from,
});
}
async function expectTeamRedirectToBeNotEnabled({
from,
}: {
from: { username: string } | { teamSlug: string };
to: string;
orgSlug: string;
}) {
expectRedirectToBeNotEnabled({
from,
});
}
async function expectRedirectToBeEnabled({
from,
to,
orgSlug,
redirectType,
}: {
from: { username: string } | { teamSlug: string };
to: string;
orgSlug: string;
redirectType: RedirectType;
}) {
let tempOrgRedirectWhere = null;
let tempOrgRedirectThatShouldNotExistWhere = null;
if ("username" in from) {
tempOrgRedirectWhere = {
from_type_fromOrgId: {
from: from.username,
type: RedirectType.User,
fromOrgId: 0,
},
};
// Normally with user migration `from.username != to`
if (from.username !== to) {
// There must not be a redirect setup from=To to something else as that would cause double redirection
tempOrgRedirectThatShouldNotExistWhere = {
from_type_fromOrgId: {
from: to,
type: RedirectType.User,
fromOrgId: 0,
},
};
}
} else if ("teamSlug" in from) {
tempOrgRedirectWhere = {
from_type_fromOrgId: {
from: from.teamSlug,
type: RedirectType.Team,
fromOrgId: 0,
},
};
if (from.teamSlug !== to) {
// There must not be a redirect setup from=To to something else as that would cause double redirection
tempOrgRedirectThatShouldNotExistWhere = {
from_type_fromOrgId: {
from: to,
type: RedirectType.Team,
fromOrgId: 0,
},
};
}
} else {
throw new Error("Atleast one of userId or teamId should be present in from");
}
const redirect = await prismock.tempOrgRedirect.findUnique({
where: tempOrgRedirectWhere,
});
if (tempOrgRedirectThatShouldNotExistWhere) {
const redirectThatShouldntBeThere = await prismock.tempOrgRedirect.findUnique({
where: tempOrgRedirectThatShouldNotExistWhere,
});
expect(redirectThatShouldntBeThere).toBeNull();
}
expect(redirect).not.toBeNull();
expect(redirect?.toUrl).toBe(`${WEBSITE_PROTOCOL}//${orgSlug}.cal.local:3000/${to}`);
if (!redirect) {
throw new Error(`Redirect doesn't exist for ${JSON.stringify(tempOrgRedirectWhere)}`);
}
expect(redirect.type).toBe(redirectType);
}
async function expectRedirectToBeNotEnabled({ from }: { from: { username: string } | { teamSlug: string } }) {
let tempOrgRedirectWhere = null;
if ("username" in from) {
tempOrgRedirectWhere = {
from_type_fromOrgId: {
from: from.username,
type: RedirectType.User,
fromOrgId: 0,
},
};
} else if ("teamSlug" in from) {
tempOrgRedirectWhere = {
from_type_fromOrgId: {
from: from.teamSlug,
type: RedirectType.Team,
fromOrgId: 0,
},
};
} else {
throw new Error("Atleast one of userId or teamId should be present in from");
}
const redirect = await prismock.tempOrgRedirect.findUnique({
where: tempOrgRedirectWhere,
});
expect(redirect).toBeNull();
}
async function createOrg(
data: Omit<Prisma.TeamCreateArgs["data"], "metadata" | "parent"> & {
metadata?: z.infer<typeof teamMetadataSchema>;
}
) {
return await prismock.team.create({
data: {
...data,
metadata: {
...(data.metadata || {}),
isOrganization: true,
},
},
});
}
async function createTeamOutsideOrg(
data: Omit<Prisma.TeamCreateArgs["data"], "metadata" | "parent"> & {
metadata?: z.infer<typeof teamMetadataSchema>;
}
) {
return await prismock.team.create({
data: {
...data,
parentId: null,
metadata: {
...(data.metadata || {}),
isOrganization: false,
},
},
});
}
async function createUserOutsideOrg(data: Omit<Prisma.UserCreateArgs["data"], "organization">) {
return await prismock.user.create({
data: {
...data,
organizationId: null,
},
});
}
async function createUserInsideTheOrg(
data: Omit<Prisma.UserUncheckedCreateInput, "organization" | "organizationId" | "id">,
orgId: number
) {
const org = await prismock.team.findUnique({
where: {
id: orgId,
},
});
if (!org) {
throw new Error(`Org with id ${orgId} does not exist`);
}
return await prismock.user.create({
data: {
...data,
organization: {
connect: {
id: orgId,
},
},
},
});
}
async function addMemberShipOfUserWithTeam({
teamId,
userId,
role,
accepted,
}: {
teamId: number;
userId: number;
role: MembershipRole;
accepted: boolean;
}) {
await prismock.membership.create({
data: {
role,
accepted,
team: {
connect: {
id: teamId,
},
},
user: {
connect: {
id: userId,
},
},
},
});
const membership = await prismock.membership.findUnique({
where: {
userId_teamId: {
teamId,
userId,
},
},
});
if (!membership) {
throw new Error(`Membership between teamId ${teamId} and userId ${userId} couldn't be created`);
}
}
const addMemberShipOfUserWithOrg = addMemberShipOfUserWithTeam;