Skip to content

Commit

Permalink
fix: remove from host when removed from org (calcom#17893)
Browse files Browse the repository at this point in the history
* fix: remove from host when removed from org

* test: add test for removing members

* fix: type errors, some unused params

* fix: test

* refactor: use prisma.count

---------

Co-authored-by: Alex van Andel <[email protected]>
  • Loading branch information
Udit-takkar and emrysal authored Nov 28, 2024
1 parent 4eac6c7 commit 589c1dc
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 4 deletions.
14 changes: 12 additions & 2 deletions apps/web/test/utils/bookingScenario/bookingScenario.ts
Original file line number Diff line number Diff line change
Expand Up @@ -810,12 +810,22 @@ export async function createBookingScenario(data: ScenarioData) {
};
}

type TeamCreateReturnType = Awaited<ReturnType<typeof prismock.team.create>>;

function assertNonNullableSlug<T extends { slug: string | null }>(
org: T
): asserts org is T & { slug: string } {
if (org.slug === null) {
throw new Error("Slug cannot be null");
}
}

export async function createOrganization(orgData: {
name: string;
slug: string;
metadata?: z.infer<typeof teamMetadataSchema>;
withTeam?: boolean;
}) {
}): Promise<TeamCreateReturnType & { slug: NonNullable<TeamCreateReturnType["slug"]> }> {
const org = await prismock.team.create({
data: {
name: orgData.name,
Expand All @@ -841,7 +851,7 @@ export async function createOrganization(orgData: {
},
});
}

assertNonNullableSlug(org);
return org;
}

Expand Down
10 changes: 10 additions & 0 deletions packages/features/ee/teams/lib/removeMember.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,16 @@ const removeMember = async ({
userId: membership.userId,
organizationId: team.id,
}),
prisma.host.deleteMany({
where: {
userId: memberId,
eventType: {
team: {
parentId: teamId,
},
},
},
}),
]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowE
import logger from "@calcom/lib/logger";
import { isTeamAdmin, isTeamOwner } from "@calcom/lib/server/queries/teams";
import { TeamRepository } from "@calcom/lib/server/repository/team";
import type { PrismaClient } from "@calcom/prisma";
import type { TrpcSessionUser } from "@calcom/trpc/server/trpc";

import { TRPCError } from "@trpc/server";
Expand All @@ -13,7 +12,6 @@ const log = logger.getSubLogger({ prefix: ["viewer/teams/removeMember.handler"]
type RemoveMemberOptions = {
ctx: {
user: NonNullable<TrpcSessionUser>;
prisma: PrismaClient;
sourceIp?: string;
};
input: TRemoveMemberInputSchema;
Expand Down
176 changes: 176 additions & 0 deletions packages/trpc/server/routers/viewer/teams/removeMember.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import prismaMock from "../../../../../../tests/libs/__mocks__/prisma";

import {
createBookingScenario,
TestData,
getOrganizer,
getScenarioData,
Timezones,
createOrganization,
} from "@calcom/web/test/utils/bookingScenario/bookingScenario";
import { setupAndTeardown } from "@calcom/web/test/utils/bookingScenario/setupAndTeardown";

import { describe, test, expect } from "vitest";

import { SchedulingType, MembershipRole } from "@calcom/prisma/enums";

import type { TrpcSessionUser } from "../../../trpc";
import removeMember from "./removeMember.handler";

describe("removeMember", () => {
setupAndTeardown();

describe("should remove a member from a team", () => {
test(`1) Should remove a member from a team
2) Should remove the member from hosts
`, async () => {
const org = await createOrganization({
name: "Test Org",
slug: "testorg",
});

// Create the child team
const childTeam = {
id: 202,
name: "Team 1",
slug: "team-1",
parentId: org.id,
};

const organizer = getOrganizer({
name: "Organizer",
email: "[email protected]",
id: 101,
organizationId: org.id,
schedules: [TestData.schedules.IstWorkHours],
teams: [
{
membership: {
accepted: true,
role: MembershipRole.ADMIN,
},
team: {
id: org.id,
name: "Test Org",
slug: "testorg",
},
},
{
membership: {
accepted: true,
role: MembershipRole.ADMIN,
},
team: {
id: childTeam.id,
name: "Team 1",
slug: "team-1",
parentId: org.id,
},
},
],
});

const otherTeamMembers = [
{
name: "Other Team Member 1",
username: "other-team-member-1",
timeZone: Timezones["+5:30"],
defaultScheduleId: null,
email: "[email protected]",
id: 102,
organizationId: org.id,
schedules: [TestData.schedules.IstEveningShift],
teams: [
{
membership: {
accepted: true,
role: MembershipRole.MEMBER,
},
team: {
id: org.id,
name: "Test Org",
slug: "testorg",
},
},
{
membership: {
accepted: true,
role: MembershipRole.MEMBER,
},
team: {
id: childTeam.id,
name: "Team 1",
slug: "team-1",
parentId: org.id,
},
},
],
},
];

await createBookingScenario(
getScenarioData(
{
eventTypes: [
{
id: 1,
teamId: childTeam.id,
schedulingType: SchedulingType.ROUND_ROBIN,
length: 30,
hosts: [
{
userId: 101,
isFixed: false,
},
{
userId: 102,
isFixed: false,
},
],
},
],
organizer,
usersApartFromOrganizer: otherTeamMembers,
apps: [TestData.apps["daily-video"]],
},
org
)
);

//Logged in user is admin of the org
const ctx = {
user: {
id: organizer.id,
name: organizer.name,
} as NonNullable<TrpcSessionUser>,
};

await removeMember({
ctx,
input: {
teamIds: [org.id],
memberIds: [102],
isOrg: true,
},
});

//Check if the remaining memberships are correct
const remainingMemberships = await prismaMock.membership.count({
where: {
teamId: childTeam.id,
},
});

expect(remainingMemberships).toBe(1);

//Check if the event type has the correct hosts
const remainingHostsCount = await prismaMock.host.count({
where: {
eventTypeId: 1,
},
});

expect(remainingHostsCount).toBe(1);
});
});
});

0 comments on commit 589c1dc

Please sign in to comment.