mirror of
https://github.com/hanzoai/calendar.git
synced 2026-10-30 14:46:43 +00:00
* chore: Upgrade prisma to 6.7.0 * Build fixes * type fixes Signed-off-by: Omar López <[email protected]> * Update schema.prisma * Patching * Revert "Update schema.prisma" This reverts commit 47d8618bf89ef4d007b30084df766f17281e21a1. * Revert "Patching" This reverts commit a1d2e3040e71690a44d4324db95d73b4d68c6adb. * Revert schema changes Signed-off-by: Omar López <[email protected]> * WIP Signed-off-by: Omar López <[email protected]> * Update getPublicEvent.ts * Update imports Signed-off-by: Omar López <[email protected]> * Update gitignore Signed-off-by: Omar López <[email protected]> * update remaining imports Signed-off-by: Omar López <[email protected]> * Delete .cursor/config.json * Discard changes to packages/features/eventtypes/lib/getPublicEvent.ts * Update _get.ts * Update user.ts * Update .gitignore * update * Update WorkflowStepContainer.tsx * Update next-auth-custom-adapter.ts * Update getPublicEvent.ts * Update workflow.ts * Update next-auth-custom-adapter.ts * Update next-auth-options.ts * Update bookingScenario.ts * fix missing imports * upgrades prismock Signed-off-by: Omar López <[email protected]> * patches prismock Signed-off-by: Omar López <[email protected]> * Update reschedule.test.ts * Update prisma.ts * patch prismock Signed-off-by: Omar López <[email protected]> * fix enums imports Signed-off-by: Omar López <[email protected]> * Revert "Update prisma.ts" This reverts commit 64edcf8db54171ff4456c209d563b5d431d99619. * Revert "patch prismock" This reverts commit e95819113dc9d88e7130947aa120cd42710977c8. * fix patch * Fix test that overrun the boundary, it shouldn't test too much * Move prisma import to changeSMSLockState * Bring back broken test without illegal imports * Merge with main and fix filter hosts by same round robin host * Fixed buildDryRunBooking fn tests * Fix and move ooo create or update handler test * Fix packages/features/eventtypes/lib/isCurrentlyAvailable.test.ts * Fix packages/trpc/server/routers/viewer/organizations/listMembers.handler.test.ts * Mock @calcom/prisma * Fix: verify-email.test.ts * fix: Moved WebhookService test and fixed default import mock * Fix: Added missing prisma mock, handleNewBooking uses that of course * We're not testing createContext here * fix: Prisma mock fix for listMembers.test.ts * More fixes to broken testcases * Forgot to remove borked test * Prevent the need to mock a lot of dependencies by moving out buildBaseWhereCondition to its own file * Temporarily skip getCalendarEvents, needs a rewrite * Fix: turns out you can access protected in testcases * fix further mocks * Added packages/features/insights/server/buildBaseWhereCondition.ts, types * Always great to have a mock and then not use it * And one less again. * fix: confirm.handler.test, didn't mock prisma * fix: Address minor nit by @eunjae & fix ImpersonationProvider test * Updated isPrismaAvailableCheck that doesn't crash on import * fix: Get Prisma directly from the client, it usually involves the Validator and does not need 'local' inclusion * Add zod-prisma-types without the generator enabled (commented out) * Uncomment and see what happens * Change method of import as imports did not work in Input Schemas * Remove custom 'zod' booking model, it does not belong with Prisma * Fix all other global Model imports * Rewrite most schema includes AND remove barrel file * Add bookingCreateBodySchema to features/bookings * Flurry of type fixes for compatibility with new zod gen * Refactor out the custom prisma type createEventTypeInput * Work around nullable eventTypeLocations * HandlePayment type fix * More fixes, final fix remaining is CompleteEventType * Should fix a bunch more booking related type errors * Missed one * Some props missing from BookingCreateBodySchema * Fix location type in handleChildrenEventTypes * Little bit hacky imo but it works * Final type error \o/ * Forgot to include Prisma * Do not include zod-utils in booker/types * Oops, was already including Booker/types * Fix membership type, also disallow updating createdAt/updatedAt, make part of patch/post * Fix api v1 type errors * Fix EventTypeDescription typings * Remove getParserWithGeneric, use userBodySchema with UserSchema * use centralized timeZoneSchema * Implement feedback by @zomars * Couple of WIP pushes * Fix tests * Type fixes in `handleChildrenEventTypes` test * Try and parse metadata before use * Change zod-prisma-types configuration for optimal performance * Fix prisma validator error in `prisma/selects/credential` * Disable seperate relations model, hits a bug * Import absolute - this makes rollup work in @platform/libraries * Attempt at removing resolutions override * Refactor using `Prisma.validator` to `satisfies` * Build atoms using @calcom/prisma/client * Build atoms using @calcom/prisma/client * fixes * Update eventTypeSelect.ts * Adjust `eventTypeMetaDataSchemaWithUntypedApps` from `unknown` to `record(any)` * `EventTypeDescription` rely on `descriptionAsSafeHTML` instead of `description` * Add `seatsPerTimeSlot` to event type public select * Fix typing in `users-public-view` getServerSide props * Add missing `schedulingType` to prop * chore: bump platform libraries * Function return type is illegal, not sure how this passed eslint (#21567) * Merged with main * Update updateTokenObject.ts * Update handleResponse.ts * Update index.ts * Update handleChildrenEventTypes.ts * Update booking-idempotency-key.ts * Update WebhookService.test.ts * Update events.test.ts * Update queued-response.test.ts * Update events.test.ts * Update getRoutedUrl.test.ts * fix: type checks Signed-off-by: Omar López <[email protected]> * fixes Signed-off-by: Omar López <[email protected]> * chore: bump platform libraries * Update yarn.lock * more fixes Signed-off-by: Omar López <[email protected]> * fixes Signed-off-by: Omar López <[email protected]> * biuld fixes * chore: bump platform libraries * Update conferencing.repository.ts * Update conferencing.repository.ts * Update getCalendarsEvents.test.ts * Update vite.config.js * chore: bump platform libraries * Update users.ts * Discard changes to docs/api-reference/v2/openapi.json * Update vite.config.ts * updated platform libraries * Update get.handler.test.ts * Update get.handler.test.ts * Update schema.prisma * Discard changes to docs/api-reference/v2/openapi.json * Update next-auth-custom-adapter.ts * Update team.ts * Flurry of type fixes * Fix majority of insight related type errors * Type fixes for unlink of account * Make user nullable again * Fixed a bunch of unit tests and one type error * Attempted mock fix * Attempted fix for Attribute type * Ensure default import becomes prisma, but not direct usage * Import default as prisma in prisma.module * Add attributeOption to attribute type * Fix calcom/prisma mock * Refactor Prisma client imports to @calcom/prisma/client Updated all imports from '@prisma/client' to '@calcom/prisma/client' across tests and repository files for consistency and to use the correct Prisma client package. This change improves maintainability and ensures the correct client is referenced throughout the codebase. * Undo removal of max-warnings=0 to get main to merge * Remove unit tests for e2e fixtures, provide new prisma mock * Mock @calcom/prisma in event manager * Mock @calcom/prisma in event manager * Add correct format even with --no-verify * Mock prisma in CalendarManager * Add mock for permission-check.service * Better injection in PrismaApiKeyRepository imports * More mock fixes :) * Fix listMembers.handler.test * Fix User import * Appropriately adjust all types to be imported as types, there were a lot of types imported as normal deps * Why was this a thing? * Strictly speaking; Not using prismock anymore * Ditched patch file for prismock * Fix output.service.ts platform type imports, need concrete for plainToClass * Better typing and tests for unlinkConnectedAccount.handler * Small type fix * Disable calendar cache tests as they are dependent on prismock * chore: bump platform lib * getRoutedUrl test remove of unused import * Extract select to external const on getEventTypesFromDB * Direct select of userSelect from selects/user * fix type error from merging 23653 * Fixed integration tests by removing hardcoded values that were possible due to mocking, but as its now directly hitting the db no longer * fix: vite config atoms prisma client type location * revert: example app prisma client * revert: example app prisma client * bump platform libs * fix: use class instead of type for DI of PlatformBookingsService * update platform libs * remove unused variable * chore: generate prisma client for api v2 * fix: api v2 e2e * fix: atoms e2e * fix: atoms e2e * fix: atoms e2e * fix: api v2 e2e * fix: tsconfig apiv2 enums * publish libraries * Simplify check for existence teamId --------- Signed-off-by: Omar López <[email protected]> Co-authored-by: Alex van Andel <[email protected]> Co-authored-by: Joe Au-Yeung <[email protected]> Co-authored-by: supalarry <[email protected]> Co-authored-by: cal.com <[email protected]> Co-authored-by: Morgan <[email protected]> Co-authored-by: Benny Joo <[email protected]>
565 lines
16 KiB
TypeScript
565 lines
16 KiB
TypeScript
/**
|
|
* This file is deprecated in favour of inviteMember.handler.integration-test.ts
|
|
*
|
|
* It mocks a lot of things that are untested and integration tests make more sense for this handler
|
|
*/
|
|
import { scenarios as checkRateLimitAndThrowErrorScenarios } from "../../../../../../../tests/libs/__mocks__/checkRateLimitAndThrowError";
|
|
import { mock as getTranslationMock } from "../../../../../../../tests/libs/__mocks__/getTranslation";
|
|
import {
|
|
inviteMemberutilsScenarios as inviteMemberUtilsScenarios,
|
|
default as inviteMemberUtilsMock,
|
|
} from "./__mocks__/inviteMemberUtils";
|
|
import { constantsScenarios } from "@calcom/lib/__mocks__/constants";
|
|
|
|
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
|
|
import type { Profile } from "@calcom/prisma/client";
|
|
import { IdentityProvider, MembershipRole } from "@calcom/prisma/enums";
|
|
|
|
import { TRPCError } from "@trpc/server";
|
|
|
|
import type { TrpcSessionUser } from "../../../../types";
|
|
import inviteMemberHandler from "./inviteMember.handler";
|
|
import { INVITE_STATUS } from "./types";
|
|
|
|
vi.mock("@trpc/server", () => {
|
|
return {
|
|
TRPCError: class TRPCError {
|
|
code: string;
|
|
message: unknown;
|
|
constructor({ code, message }: { code: string; message: unknown }) {
|
|
this.code = code;
|
|
this.message = message;
|
|
}
|
|
},
|
|
};
|
|
});
|
|
|
|
vi.mock("@calcom/prisma", () => {
|
|
return {
|
|
prisma: vi.fn(),
|
|
};
|
|
});
|
|
|
|
function fakeNoUsersFoundMatchingInvitations(args: {
|
|
team: any;
|
|
invitations: {
|
|
role: MembershipRole;
|
|
usernameOrEmail: string;
|
|
}[];
|
|
}) {
|
|
inviteMemberUtilsScenarios.findUsersWithInviteStatus.useAdvancedMock([], args);
|
|
}
|
|
|
|
function getPersonalProfile({ username }: { username: string }) {
|
|
return {
|
|
id: null,
|
|
upId: "abc",
|
|
organization: null,
|
|
organizationId: null,
|
|
username,
|
|
startTime: 0,
|
|
endTime: 0,
|
|
avatarUrl: "",
|
|
name: "",
|
|
bufferTime: 0,
|
|
};
|
|
}
|
|
|
|
function getLoggedInUser() {
|
|
return {
|
|
id: 123,
|
|
name: "John Doe",
|
|
organization: {
|
|
id: 456,
|
|
isOrgAdmin: true,
|
|
metadata: null,
|
|
requestedSlug: null,
|
|
},
|
|
profile: getPersonalProfile({ username: "john_doe" }),
|
|
} as NonNullable<TrpcSessionUser>;
|
|
}
|
|
|
|
function buildExistingUser(user: { id: number; email: string; username: string }) {
|
|
return {
|
|
password: {
|
|
hash: "hash",
|
|
userId: 1,
|
|
},
|
|
identityProvider: IdentityProvider.CAL,
|
|
profiles: [] as Profile[],
|
|
completedOnboarding: false,
|
|
...user,
|
|
};
|
|
}
|
|
|
|
describe("inviteMemberHandler", () => {
|
|
beforeEach(async () => {
|
|
await inviteMemberUtilsScenarios.getOrgState.useActual();
|
|
await inviteMemberUtilsScenarios.getUniqueInvitationsOrThrowIfEmpty.useActual();
|
|
await inviteMemberUtilsScenarios.getOrgConnectionInfo.useActual();
|
|
checkRateLimitAndThrowErrorScenarios.fakeRateLimitPassed();
|
|
getTranslationMock.fakeIdentityFn();
|
|
inviteMemberUtilsScenarios.checkPermissions.fakePassed();
|
|
constantsScenarios.enableTeamBilling();
|
|
});
|
|
|
|
describe("Regular Team", () => {
|
|
describe("with 2 emails in input and when there are no users matching the emails", () => {
|
|
it("should call appropriate utilities to send email, add users and update in stripe. It should return `numUsersInvited`=2", async () => {
|
|
const usersToBeInvited = [
|
|
{
|
|
id: 1,
|
|
email: "[email protected]",
|
|
},
|
|
{
|
|
id: 2,
|
|
email: "[email protected]",
|
|
},
|
|
];
|
|
|
|
const loggedInUser = getLoggedInUser();
|
|
|
|
const input = {
|
|
teamId: 1,
|
|
role: MembershipRole.MEMBER,
|
|
isOrg: false,
|
|
language: "en",
|
|
usernameOrEmail: usersToBeInvited.map((u) => u.email),
|
|
};
|
|
|
|
const team = {
|
|
id: input.teamId,
|
|
name: "Team 1",
|
|
parent: null,
|
|
};
|
|
|
|
const retValueOfGetTeamOrThrowError = inviteMemberUtilsScenarios.getTeamOrThrow.fakeReturnTeam(team, {
|
|
teamId: input.teamId,
|
|
});
|
|
|
|
const allExpectedInvitations = [
|
|
{
|
|
role: input.role,
|
|
usernameOrEmail: usersToBeInvited[0].email,
|
|
},
|
|
{
|
|
role: input.role,
|
|
usernameOrEmail: usersToBeInvited[1].email,
|
|
},
|
|
];
|
|
fakeNoUsersFoundMatchingInvitations({
|
|
team,
|
|
invitations: allExpectedInvitations,
|
|
});
|
|
|
|
const ctx = {
|
|
user: loggedInUser,
|
|
};
|
|
|
|
// Call the inviteMemberHandler function
|
|
const result = await inviteMemberHandler({ ctx, input });
|
|
const expectedConnectionInfoMap = {
|
|
[usersToBeInvited[0].email]: {
|
|
orgId: undefined,
|
|
autoAccept: false,
|
|
},
|
|
[usersToBeInvited[1].email]: {
|
|
orgId: undefined,
|
|
autoAccept: false,
|
|
},
|
|
};
|
|
|
|
const inviter = {
|
|
name: loggedInUser.name,
|
|
};
|
|
|
|
expect(inviteMemberUtilsMock.handleNewUsersInvites).toHaveBeenCalledWith({
|
|
invitationsForNewUsers: allExpectedInvitations,
|
|
team: retValueOfGetTeamOrThrowError,
|
|
orgConnectInfoByUsernameOrEmail: expectedConnectionInfoMap,
|
|
teamId: input.teamId,
|
|
language: input.language,
|
|
inviter,
|
|
autoAcceptEmailDomain: null,
|
|
});
|
|
|
|
// TODO: Fix this test
|
|
// expect(paymentsMock.updateQuantitySubscriptionFromStripe).toHaveBeenCalledWith(input.teamId);
|
|
|
|
expect(inviteMemberUtilsMock.handleExistingUsersInvites).not.toHaveBeenCalled();
|
|
|
|
expect(inviteMemberUtilsMock.getUniqueInvitationsOrThrowIfEmpty).toHaveBeenCalledWith([
|
|
{
|
|
role: input.role,
|
|
usernameOrEmail: usersToBeInvited[0].email,
|
|
},
|
|
{
|
|
role: input.role,
|
|
usernameOrEmail: usersToBeInvited[1].email,
|
|
},
|
|
]);
|
|
// Assert the result
|
|
expect(result).toEqual({
|
|
usernameOrEmail: input.usernameOrEmail,
|
|
numUsersInvited: 2,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("with 2 emails in input and when there is one user matching the email", () => {
|
|
// TODO: Fix this test
|
|
it.skip("should call appropriate utilities to add users and update in stripe. It should return `numUsersInvited=2`", async () => {
|
|
const usersToBeInvited = [
|
|
buildExistingUser({
|
|
id: 1,
|
|
email: "[email protected]",
|
|
username: "user1",
|
|
}),
|
|
{
|
|
id: null,
|
|
email: "[email protected]",
|
|
},
|
|
] as const;
|
|
|
|
const loggedInUser = getLoggedInUser();
|
|
|
|
const input = {
|
|
teamId: 1,
|
|
role: MembershipRole.MEMBER,
|
|
language: "en",
|
|
usernameOrEmail: usersToBeInvited.map((u) => u.email),
|
|
};
|
|
|
|
const team = {
|
|
id: input.teamId,
|
|
name: "Team 1",
|
|
parent: null,
|
|
isOrganization: false,
|
|
};
|
|
|
|
const retValueOfGetTeamOrThrowError = inviteMemberUtilsScenarios.getTeamOrThrow.fakeReturnTeam(team, {
|
|
teamId: input.teamId,
|
|
});
|
|
|
|
const allExpectedInvitations = [
|
|
{
|
|
role: input.role,
|
|
usernameOrEmail: usersToBeInvited[0].email,
|
|
},
|
|
{
|
|
role: input.role,
|
|
usernameOrEmail: usersToBeInvited[1].email,
|
|
},
|
|
];
|
|
|
|
const retValueOfFindUsersWithInviteStatus =
|
|
await inviteMemberUtilsScenarios.findUsersWithInviteStatus.useAdvancedMock(
|
|
[
|
|
{
|
|
...usersToBeInvited[0],
|
|
canBeInvited: INVITE_STATUS.CAN_BE_INVITED,
|
|
newRole: input.role,
|
|
},
|
|
],
|
|
{
|
|
invitations: allExpectedInvitations,
|
|
team,
|
|
}
|
|
);
|
|
|
|
const ctx = {
|
|
user: loggedInUser,
|
|
};
|
|
|
|
const result = await inviteMemberHandler({ ctx, input });
|
|
const expectedConnectionInfoMap = {
|
|
[usersToBeInvited[0].email]: {
|
|
orgId: undefined,
|
|
autoAccept: false,
|
|
},
|
|
[usersToBeInvited[1].email]: {
|
|
orgId: undefined,
|
|
autoAccept: false,
|
|
},
|
|
};
|
|
const inviter = {
|
|
name: loggedInUser.name,
|
|
};
|
|
|
|
expect(inviteMemberUtilsMock.handleNewUsersInvites).toHaveBeenCalledWith({
|
|
invitationsForNewUsers: allExpectedInvitations.slice(1),
|
|
team: retValueOfGetTeamOrThrowError,
|
|
orgConnectInfoByUsernameOrEmail: expectedConnectionInfoMap,
|
|
teamId: input.teamId,
|
|
language: input.language,
|
|
inviter,
|
|
autoAcceptEmailDomain: null,
|
|
isOrg: false,
|
|
});
|
|
|
|
// TODO: Fix this test
|
|
// expect(paymentsMock.updateQuantitySubscriptionFromStripe).toHaveBeenCalledWith(input.teamId);
|
|
|
|
expect(inviteMemberUtilsMock.handleExistingUsersInvites).toHaveBeenCalledWith({
|
|
invitableExistingUsers: retValueOfFindUsersWithInviteStatus,
|
|
teamId: input.teamId,
|
|
language: input.language,
|
|
inviter,
|
|
orgConnectInfoByUsernameOrEmail: expectedConnectionInfoMap,
|
|
orgSlug: null,
|
|
team: retValueOfGetTeamOrThrowError,
|
|
isOrg: false,
|
|
});
|
|
|
|
// Assert the result
|
|
expect(result).toEqual({
|
|
usernameOrEmail: input.usernameOrEmail,
|
|
numUsersInvited: 2,
|
|
});
|
|
});
|
|
});
|
|
|
|
it("With one email in input and that email is already a member of the team, it should throw error", async () => {
|
|
const userToBeInvited = buildExistingUser({
|
|
id: 1,
|
|
email: "[email protected]",
|
|
username: "user1",
|
|
});
|
|
|
|
const loggedInUser = getLoggedInUser();
|
|
|
|
const input = {
|
|
teamId: 1,
|
|
role: MembershipRole.MEMBER,
|
|
isOrg: false,
|
|
language: "en",
|
|
usernameOrEmail: userToBeInvited.email,
|
|
};
|
|
|
|
const team = {
|
|
id: input.teamId,
|
|
name: "Team 1",
|
|
parent: null,
|
|
};
|
|
|
|
inviteMemberUtilsScenarios.getTeamOrThrow.fakeReturnTeam(team, {
|
|
teamId: input.teamId,
|
|
});
|
|
|
|
inviteMemberUtilsScenarios.findUsersWithInviteStatus.useAdvancedMock(
|
|
[
|
|
{
|
|
...userToBeInvited,
|
|
canBeInvited: INVITE_STATUS.USER_ALREADY_INVITED_OR_MEMBER,
|
|
newRole: input.role,
|
|
},
|
|
],
|
|
{
|
|
invitations: [
|
|
{
|
|
newRole: input.role,
|
|
usernameOrEmail: userToBeInvited.email,
|
|
},
|
|
],
|
|
team,
|
|
}
|
|
);
|
|
|
|
const ctx = {
|
|
user: loggedInUser,
|
|
};
|
|
try {
|
|
await inviteMemberHandler({ ctx, input });
|
|
throw new Error("Expected an error to be thrown");
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
} catch (e: any) {
|
|
expect(e).toBeInstanceOf(TRPCError);
|
|
expect(e.code).toEqual("BAD_REQUEST");
|
|
expect(e.message).toBe(INVITE_STATUS.USER_ALREADY_INVITED_OR_MEMBER);
|
|
}
|
|
});
|
|
|
|
it("With one email that is already a member and isDirectUserAction=false, it should NOT throw error", async () => {
|
|
const userToBeInvited = buildExistingUser({
|
|
id: 1,
|
|
email: "[email protected]",
|
|
username: "user1",
|
|
});
|
|
|
|
const loggedInUser = getLoggedInUser();
|
|
|
|
const team = {
|
|
id: 1,
|
|
name: "Team 1",
|
|
parent: null,
|
|
isOrganization: false,
|
|
};
|
|
|
|
inviteMemberUtilsScenarios.getTeamOrThrow.fakeReturnTeam(team, {
|
|
teamId: team.id,
|
|
});
|
|
|
|
inviteMemberUtilsScenarios.findUsersWithInviteStatus.useAdvancedMock(
|
|
[
|
|
{
|
|
...userToBeInvited,
|
|
canBeInvited: INVITE_STATUS.USER_ALREADY_INVITED_OR_MEMBER,
|
|
newRole: MembershipRole.MEMBER,
|
|
},
|
|
],
|
|
{
|
|
invitations: [
|
|
{
|
|
role: MembershipRole.MEMBER,
|
|
usernameOrEmail: userToBeInvited.email,
|
|
},
|
|
],
|
|
team,
|
|
}
|
|
);
|
|
|
|
// Call inviteMembersWithNoInviterPermissionCheck directly with isDirectUserAction=false
|
|
const { inviteMembersWithNoInviterPermissionCheck } = await import("./inviteMember.handler");
|
|
|
|
const result = await inviteMembersWithNoInviterPermissionCheck({
|
|
inviterName: loggedInUser.name,
|
|
teamId: team.id,
|
|
language: "en",
|
|
creationSource: "WEBAPP" as const,
|
|
orgSlug: null,
|
|
invitations: [
|
|
{
|
|
usernameOrEmail: userToBeInvited.email,
|
|
role: MembershipRole.MEMBER,
|
|
},
|
|
],
|
|
isDirectUserAction: false,
|
|
});
|
|
|
|
// Should not throw error, should return successfully with 0 users invited
|
|
expect(result).toEqual({
|
|
usernameOrEmail: userToBeInvited.email,
|
|
numUsersInvited: 0,
|
|
});
|
|
|
|
// Verify that handleNewUsersInvites and handleExistingUsersInvites were not called
|
|
// since the user is already a member
|
|
expect(inviteMemberUtilsMock.handleNewUsersInvites).not.toHaveBeenCalled();
|
|
expect(inviteMemberUtilsMock.handleExistingUsersInvites).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("With multiple emails where some are already members and isDirectUserAction=false, it should NOT throw error and invite only eligible users", async () => {
|
|
const existingMember = buildExistingUser({
|
|
id: 1,
|
|
email: "[email protected]",
|
|
username: "existing",
|
|
});
|
|
|
|
const newUser = {
|
|
id: null,
|
|
email: "[email protected]",
|
|
};
|
|
|
|
const team = {
|
|
id: 1,
|
|
name: "Team 1",
|
|
parent: null,
|
|
isOrganization: false,
|
|
};
|
|
|
|
inviteMemberUtilsScenarios.getTeamOrThrow.fakeReturnTeam(team, {
|
|
teamId: team.id,
|
|
});
|
|
|
|
const allInvitations = [
|
|
{
|
|
role: MembershipRole.MEMBER,
|
|
usernameOrEmail: existingMember.email,
|
|
},
|
|
{
|
|
role: MembershipRole.MEMBER,
|
|
usernameOrEmail: newUser.email,
|
|
},
|
|
];
|
|
|
|
inviteMemberUtilsScenarios.findUsersWithInviteStatus.useAdvancedMock(
|
|
[
|
|
{
|
|
...existingMember,
|
|
canBeInvited: INVITE_STATUS.USER_ALREADY_INVITED_OR_MEMBER,
|
|
newRole: MembershipRole.MEMBER,
|
|
},
|
|
],
|
|
{
|
|
invitations: allInvitations,
|
|
team,
|
|
}
|
|
);
|
|
|
|
// Call inviteMembersWithNoInviterPermissionCheck directly with isDirectUserAction=false
|
|
const { inviteMembersWithNoInviterPermissionCheck } = await import("./inviteMember.handler");
|
|
|
|
const result = await inviteMembersWithNoInviterPermissionCheck({
|
|
inviterName: "Test Inviter",
|
|
teamId: team.id,
|
|
language: "en",
|
|
creationSource: "WEBAPP" as const,
|
|
orgSlug: null,
|
|
invitations: allInvitations,
|
|
isDirectUserAction: false,
|
|
});
|
|
|
|
// Should not throw error, should return successfully with 1 user invited (the new user)
|
|
expect(result).toEqual({
|
|
usernameOrEmail: [existingMember.email, newUser.email],
|
|
numUsersInvited: 1,
|
|
});
|
|
|
|
// Verify that only new user was invited
|
|
expect(inviteMemberUtilsMock.handleNewUsersInvites).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
invitationsForNewUsers: [
|
|
{
|
|
role: MembershipRole.MEMBER,
|
|
usernameOrEmail: newUser.email,
|
|
},
|
|
],
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
it("When rate limit exceeded, it should throw error", async () => {
|
|
const userToBeInvited = buildExistingUser({
|
|
id: 1,
|
|
email: "[email protected]",
|
|
username: "user1",
|
|
});
|
|
|
|
const errorMessageForRateLimit = checkRateLimitAndThrowErrorScenarios.fakeRateLimitFailed();
|
|
|
|
const loggedInUser = getLoggedInUser();
|
|
|
|
const input = {
|
|
teamId: 1,
|
|
role: MembershipRole.MEMBER,
|
|
isOrg: false,
|
|
language: "en",
|
|
usernameOrEmail: userToBeInvited.email,
|
|
};
|
|
|
|
const ctx = {
|
|
user: loggedInUser,
|
|
};
|
|
try {
|
|
await inviteMemberHandler({ ctx, input });
|
|
throw new Error("Expected an error to be thrown");
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
} catch (e: any) {
|
|
expect(e).toBeInstanceOf(Error);
|
|
expect(e.message).toEqual(errorMessageForRateLimit);
|
|
}
|
|
});
|
|
});
|