Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 20 additions & 11 deletions packages/features/bookings/lib/payment/handleNoShowFee.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { PaymentServiceMap } from "@calcom/app-store/payment.services.generated"
import { sendNoShowFeeChargedEmail } from "@calcom/emails/billing-email-service";
import { CredentialRepository } from "@calcom/features/credentials/repositories/CredentialRepository";
import { TeamRepository } from "@calcom/features/ee/teams/repositories/TeamRepository";
import { MembershipRepository } from "@calcom/features/membership/repositories/MembershipRepository";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { ErrorWithCode } from "@calcom/lib/errors";
import { getTranslation } from "@calcom/lib/server/i18n";
Expand All @@ -14,9 +13,11 @@ import { handleNoShowFee } from "./handleNoShowFee";
vi.mock("@calcom/app-store/payment.services.generated", () => ({
PaymentServiceMap: {
stripepayment: Promise.resolve({
PaymentService: vi.fn().mockImplementation(function() { return {
chargeCard: vi.fn(),
}; }),
PaymentService: vi.fn().mockImplementation(function () {
return {
chargeCard: vi.fn(),
};
}),
}),
},
}));
Expand All @@ -36,10 +37,18 @@ vi.mock("@calcom/features/credentials/repositories/CredentialRepository", () =>
},
}));

const { mockFindUniqueByUserIdAndTeamId, MockMembershipRepository } = vi.hoisted(() => {
const mockFindUniqueByUserIdAndTeamId = vi.fn();

class MockMembershipRepository {
findUniqueByUserIdAndTeamId = mockFindUniqueByUserIdAndTeamId;
}

return { mockFindUniqueByUserIdAndTeamId, MockMembershipRepository };
});

vi.mock("@calcom/features/membership/repositories/MembershipRepository", () => ({
MembershipRepository: {
findUniqueByUserIdAndTeamId: vi.fn(),
},
MembershipRepository: MockMembershipRepository,
}));

vi.mock("@calcom/features/ee/teams/repositories/TeamRepository", () => ({
Expand Down Expand Up @@ -154,7 +163,7 @@ describe("handleNoShowFee", () => {

mockPaymentService.chargeCard.mockResolvedValue({ success: true, paymentId: "pay_123" });

vi.mocked(MembershipRepository.findUniqueByUserIdAndTeamId).mockResolvedValue({
mockFindUniqueByUserIdAndTeamId.mockResolvedValue({
id: 1,
userId: 1,
teamId: 1,
Expand All @@ -175,7 +184,7 @@ describe("handleNoShowFee", () => {
});

expect(result).toEqual({ success: true, paymentId: "pay_123" });
expect(MembershipRepository.findUniqueByUserIdAndTeamId).toHaveBeenCalledWith({
expect(mockFindUniqueByUserIdAndTeamId).toHaveBeenCalledWith({
userId: 1,
teamId: 1,
});
Expand All @@ -192,7 +201,7 @@ describe("handleNoShowFee", () => {

mockPaymentService.chargeCard.mockResolvedValue({ success: true, paymentId: "pay_123" });

vi.mocked(MembershipRepository.findUniqueByUserIdAndTeamId).mockResolvedValue({
mockFindUniqueByUserIdAndTeamId.mockResolvedValue({
id: 1,
userId: 1,
teamId: 1,
Expand Down Expand Up @@ -251,7 +260,7 @@ describe("handleNoShowFee", () => {
},
};

vi.mocked(MembershipRepository.findUniqueByUserIdAndTeamId).mockResolvedValue(null);
mockFindUniqueByUserIdAndTeamId.mockResolvedValue(null);

await expect(
handleNoShowFee({
Expand Down
3 changes: 2 additions & 1 deletion packages/features/bookings/lib/payment/handleNoShowFee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ export const handleNoShowFee = async ({
};

if (teamId) {
const userIsInTeam = await MembershipRepository.findUniqueByUserIdAndTeamId({
const membershipRepository = new MembershipRepository();
const userIsInTeam = await membershipRepository.findUniqueByUserIdAndTeamId({
userId,
teamId,
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import { describe, expect, it, vi, beforeEach } from "vitest";

import { MembershipRepository } from "@calcom/features/membership/repositories/MembershipRepository";
import type { Payment } from "@calcom/prisma/client";

import { handleNoShowFee } from "./handleNoShowFee";
import { processNoShowFeeOnCancellation } from "./processNoShowFeeOnCancellation";
import { shouldChargeNoShowCancellationFee } from "./shouldChargeNoShowCancellationFee";

const { mockFindUniqueByUserIdAndTeamId, MockMembershipRepository } = vi.hoisted(() => {
const mockFindUniqueByUserIdAndTeamId = vi.fn();

class MockMembershipRepository {
findUniqueByUserIdAndTeamId = mockFindUniqueByUserIdAndTeamId;
}

return { mockFindUniqueByUserIdAndTeamId, MockMembershipRepository };
});

vi.mock("@calcom/features/membership/repositories/MembershipRepository", () => ({
MembershipRepository: {
findUniqueByUserIdAndTeamId: vi.fn(),
},
MembershipRepository: MockMembershipRepository,
}));

vi.mock("./handleNoShowFee", () => ({
Expand Down Expand Up @@ -165,7 +172,7 @@ describe("processNoShowFeeOnCancellation", () => {
},
};

vi.mocked(MembershipRepository.findUniqueByUserIdAndTeamId).mockResolvedValue({
mockFindUniqueByUserIdAndTeamId.mockResolvedValue({
id: 1,
userId: 999,
teamId: 1,
Expand All @@ -183,7 +190,7 @@ describe("processNoShowFeeOnCancellation", () => {
cancelledByUserId: 999,
});

expect(MembershipRepository.findUniqueByUserIdAndTeamId).toHaveBeenCalledWith({
expect(mockFindUniqueByUserIdAndTeamId).toHaveBeenCalledWith({
userId: 999,
teamId: 1,
});
Expand All @@ -200,7 +207,7 @@ describe("processNoShowFeeOnCancellation", () => {
},
};

vi.mocked(MembershipRepository.findUniqueByUserIdAndTeamId).mockResolvedValue({
mockFindUniqueByUserIdAndTeamId.mockResolvedValue({
id: 1,
userId: 999,
teamId: 1,
Expand Down Expand Up @@ -231,7 +238,7 @@ describe("processNoShowFeeOnCancellation", () => {
},
};

vi.mocked(MembershipRepository.findUniqueByUserIdAndTeamId).mockResolvedValue({
mockFindUniqueByUserIdAndTeamId.mockResolvedValue({
id: 1,
userId: 999,
teamId: 1,
Expand Down Expand Up @@ -264,7 +271,7 @@ describe("processNoShowFeeOnCancellation", () => {
cancelledByUserId: 999,
});

expect(MembershipRepository.findUniqueByUserIdAndTeamId).toHaveBeenCalledWith({
expect(mockFindUniqueByUserIdAndTeamId).toHaveBeenCalledWith({
userId: 999,
teamId: 1,
});
Expand All @@ -281,7 +288,7 @@ describe("processNoShowFeeOnCancellation", () => {
},
};

vi.mocked(MembershipRepository.findUniqueByUserIdAndTeamId).mockResolvedValue(null);
mockFindUniqueByUserIdAndTeamId.mockResolvedValue(null);
vi.mocked(shouldChargeNoShowCancellationFee).mockReturnValue(true);
vi.mocked(handleNoShowFee).mockResolvedValue({
id: 999,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ export const processNoShowFeeOnCancellation = async ({

// Skip no-show fee if the booking was cancelled by a team/org admin
if (cancelledByUserId && booking.eventType?.teamId) {
const membership = await MembershipRepository.findUniqueByUserIdAndTeamId({
const membershipRepository = new MembershipRepository();
const membership = await membershipRepository.findUniqueByUserIdAndTeamId({
userId: cancelledByUserId,
teamId: booking.eventType.teamId,
});
Expand Down
3 changes: 2 additions & 1 deletion packages/features/ee/dsync/lib/assignValueToUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,8 @@ export const assignValueToUserInOrgBulk = async ({
attributeLabelToValueMap: AttributeLabelToValueMap;
updater: BulkAttributeAssigner;
}) => {
const membership = await MembershipRepository.findUniqueByUserIdAndTeamId({ userId, teamId: orgId });
const membershipRepository = new MembershipRepository();
const membership = await membershipRepository.findUniqueByUserIdAndTeamId({ userId, teamId: orgId });
const defaultReturn = { numOfAttributeOptionsSet: 0, numOfAttributeOptionsDeleted: 0 };
if (!membership) {
console.error(`User ${userId} not a member of org ${orgId}, not assigning attribute options`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,8 +329,8 @@ export class MembershipRepository {
});
}

static async findUniqueByUserIdAndTeamId({ userId, teamId }: { userId: number; teamId: number }) {
return await prisma.membership.findUnique({
async findUniqueByUserIdAndTeamId({ userId, teamId }: { userId: number; teamId: number }) {
return await this.prismaClient.membership.findUnique({
where: {
userId_teamId: {
userId,
Expand Down
2 changes: 1 addition & 1 deletion packages/features/membership/services/membershipService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class MembershipService {
* Checks the membership status of a user within a specific team.
*/
async checkMembership(teamId: number, userId: number): Promise<MembershipCheckResult> {
const membership = await MembershipRepository.findUniqueByUserIdAndTeamId({ teamId, userId });
const membership = await this.membershipRepository.findUniqueByUserIdAndTeamId({ teamId, userId });

if (!membership || !membership.accepted) {
return {
Expand Down
3 changes: 2 additions & 1 deletion packages/features/pbac/lib/resource-permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,8 @@ export async function getRoutingFormPermissions({
};
}

const membership = await MembershipRepository.findUniqueByUserIdAndTeamId({
const membershipRepository = new MembershipRepository();
const membership = await membershipRepository.findUniqueByUserIdAndTeamId({
userId,
teamId: formTeamId,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ describe("PermissionCheckService", () => {
let mockRepository: MockRepository;
let mockFeaturesRepository: { checkIfTeamHasFeature: Mock };
let mockPermissionService: { validatePermission: Mock; validatePermissions: Mock };
let mockMembershipRepository: { findUniqueByUserIdAndTeamId: Mock };

beforeEach(() => {
vi.clearAllMocks();
Expand All @@ -56,14 +57,16 @@ describe("PermissionCheckService", () => {
validatePermissions: vi.fn().mockReturnValue({ isValid: true }),
};

mockMembershipRepository = {
findUniqueByUserIdAndTeamId: vi.fn(),
};

service = new PermissionCheckService(
mockRepository,
mockFeaturesRepository as unknown as FeaturesRepository,
mockPermissionService as unknown as PermissionService
mockPermissionService as unknown as PermissionService,
mockMembershipRepository as unknown as MembershipRepository
);

// Mock MembershipRepository static method
(MembershipRepository.findUniqueByUserIdAndTeamId as Mock) = vi.fn();
});

describe("checkPermission", () => {
Expand Down Expand Up @@ -104,7 +107,7 @@ describe("PermissionCheckService", () => {
updatedAt: new Date(),
};

(MembershipRepository.findUniqueByUserIdAndTeamId as Mock).mockResolvedValueOnce(membership);
mockMembershipRepository.findUniqueByUserIdAndTeamId.mockResolvedValueOnce(membership);
mockFeaturesRepository.checkIfTeamHasFeature.mockResolvedValueOnce(false);

const result = await service.checkPermission({
Expand All @@ -115,7 +118,7 @@ describe("PermissionCheckService", () => {
});

expect(result).toBe(true);
expect(MembershipRepository.findUniqueByUserIdAndTeamId).toHaveBeenCalledWith({
expect(mockMembershipRepository.findUniqueByUserIdAndTeamId).toHaveBeenCalledWith({
userId: 1,
teamId: 1,
});
Expand All @@ -125,7 +128,7 @@ describe("PermissionCheckService", () => {

it("should return false if no team or org membership found when PBAC disabled", async () => {
mockFeaturesRepository.checkIfTeamHasFeature.mockResolvedValueOnce(false);
(MembershipRepository.findUniqueByUserIdAndTeamId as Mock).mockResolvedValue(null);
mockMembershipRepository.findUniqueByUserIdAndTeamId.mockResolvedValue(null);
mockRepository.getTeamById.mockResolvedValueOnce({ id: 1, parentId: 100 });

const result = await service.checkPermission({
Expand All @@ -136,7 +139,7 @@ describe("PermissionCheckService", () => {
});

expect(result).toBe(false);
expect(MembershipRepository.findUniqueByUserIdAndTeamId).toHaveBeenCalledTimes(2);
expect(mockMembershipRepository.findUniqueByUserIdAndTeamId).toHaveBeenCalledTimes(2);
});

it("should use org membership when team membership not found (PBAC disabled)", async () => {
Expand All @@ -153,7 +156,7 @@ describe("PermissionCheckService", () => {
};

mockFeaturesRepository.checkIfTeamHasFeature.mockResolvedValueOnce(false);
(MembershipRepository.findUniqueByUserIdAndTeamId as Mock)
mockMembershipRepository.findUniqueByUserIdAndTeamId
.mockResolvedValueOnce(null) // No team membership
.mockResolvedValueOnce(orgMembership); // Has org membership
mockRepository.getTeamById.mockResolvedValueOnce({ id: 1, parentId: 100 });
Expand All @@ -167,11 +170,11 @@ describe("PermissionCheckService", () => {

expect(result).toBe(true);
expect(mockRepository.getTeamById).toHaveBeenCalledWith(1);
expect(MembershipRepository.findUniqueByUserIdAndTeamId).toHaveBeenNthCalledWith(1, {
expect(mockMembershipRepository.findUniqueByUserIdAndTeamId).toHaveBeenNthCalledWith(1, {
userId: 1,
teamId: 1,
});
expect(MembershipRepository.findUniqueByUserIdAndTeamId).toHaveBeenNthCalledWith(2, {
expect(mockMembershipRepository.findUniqueByUserIdAndTeamId).toHaveBeenNthCalledWith(2, {
userId: 1,
teamId: 100,
});
Expand Down Expand Up @@ -203,7 +206,7 @@ describe("PermissionCheckService", () => {
};

mockFeaturesRepository.checkIfTeamHasFeature.mockResolvedValueOnce(false);
(MembershipRepository.findUniqueByUserIdAndTeamId as Mock)
mockMembershipRepository.findUniqueByUserIdAndTeamId
.mockResolvedValueOnce(teamMembership) // Has team membership as MEMBER
.mockResolvedValueOnce(orgMembership); // Has org membership as ADMIN
mockRepository.getTeamById.mockResolvedValueOnce({ id: 1, parentId: 100 });
Expand All @@ -217,11 +220,11 @@ describe("PermissionCheckService", () => {

expect(result).toBe(true);
expect(mockRepository.getTeamById).toHaveBeenCalledWith(1);
expect(MembershipRepository.findUniqueByUserIdAndTeamId).toHaveBeenNthCalledWith(1, {
expect(mockMembershipRepository.findUniqueByUserIdAndTeamId).toHaveBeenNthCalledWith(1, {
userId: 1,
teamId: 1,
});
expect(MembershipRepository.findUniqueByUserIdAndTeamId).toHaveBeenNthCalledWith(2, {
expect(mockMembershipRepository.findUniqueByUserIdAndTeamId).toHaveBeenNthCalledWith(2, {
userId: 1,
teamId: 100,
});
Expand Down Expand Up @@ -362,7 +365,7 @@ describe("PermissionCheckService", () => {
updatedAt: new Date(),
};

(MembershipRepository.findUniqueByUserIdAndTeamId as Mock).mockResolvedValueOnce(membership);
mockMembershipRepository.findUniqueByUserIdAndTeamId.mockResolvedValueOnce(membership);
mockFeaturesRepository.checkIfTeamHasFeature.mockResolvedValueOnce(false);

const result = await service.checkPermissions({
Expand All @@ -373,7 +376,7 @@ describe("PermissionCheckService", () => {
});

expect(result).toBe(true);
expect(MembershipRepository.findUniqueByUserIdAndTeamId).toHaveBeenCalledWith({
expect(mockMembershipRepository.findUniqueByUserIdAndTeamId).toHaveBeenCalledWith({
userId: 1,
teamId: 1,
});
Expand All @@ -395,7 +398,7 @@ describe("PermissionCheckService", () => {
};

mockFeaturesRepository.checkIfTeamHasFeature.mockResolvedValueOnce(false);
(MembershipRepository.findUniqueByUserIdAndTeamId as Mock)
mockMembershipRepository.findUniqueByUserIdAndTeamId
.mockResolvedValueOnce(null) // No team membership
.mockResolvedValueOnce(orgMembership); // Has org membership
mockRepository.getTeamById.mockResolvedValueOnce({ id: 1, parentId: 100 });
Expand All @@ -409,11 +412,11 @@ describe("PermissionCheckService", () => {

expect(result).toBe(true);
expect(mockRepository.getTeamById).toHaveBeenCalledWith(1);
expect(MembershipRepository.findUniqueByUserIdAndTeamId).toHaveBeenNthCalledWith(1, {
expect(mockMembershipRepository.findUniqueByUserIdAndTeamId).toHaveBeenNthCalledWith(1, {
userId: 1,
teamId: 1,
});
expect(MembershipRepository.findUniqueByUserIdAndTeamId).toHaveBeenNthCalledWith(2, {
expect(mockMembershipRepository.findUniqueByUserIdAndTeamId).toHaveBeenNthCalledWith(2, {
userId: 1,
teamId: 100,
});
Expand Down
Loading
Loading