2
0

first commit

This commit is contained in:
2024-08-09 00:39:27 +02:00
commit 79688abe2e
5698 changed files with 497838 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { TestingModule } from "@nestjs/testing";
import { randomBytes, createHash } from "crypto";
export class ApiKeysRepositoryFixture {
private primaReadClient: PrismaReadService["prisma"];
private prismaWriteClient: PrismaWriteService["prisma"];
constructor(private readonly module: TestingModule) {
this.primaReadClient = module.get(PrismaReadService).prisma;
this.prismaWriteClient = module.get(PrismaWriteService).prisma;
}
async createApiKey(userId: number, expiresAt: Date | null, teamId?: number) {
const keyString = randomBytes(16).toString("hex");
const apiKey = await this.prismaWriteClient.apiKey.create({
data: {
userId,
teamId,
hashedKey: createHash("sha256").update(keyString).digest("hex"),
expiresAt: expiresAt,
},
});
return { apiKey, keyString };
}
}

View File

@@ -0,0 +1,38 @@
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { TestingModule } from "@nestjs/testing";
export class PlatformBillingRepositoryFixture {
private primaReadClient: PrismaReadService["prisma"];
private prismaWriteClient: PrismaWriteService["prisma"];
constructor(private readonly module: TestingModule) {
this.primaReadClient = module.get(PrismaReadService).prisma;
this.prismaWriteClient = module.get(PrismaWriteService).prisma;
}
async create(orgId: number) {
const randomString = Date.now().toString(36);
return this.prismaWriteClient.platformBilling.create({
data: {
id: orgId,
customerId: `cus_123_${randomString}`,
subscriptionId: `sub_123_${randomString}`,
plan: "STARTER",
},
});
}
async deleteSubscriptionForTeam(teamId: number) {
// silently try to delete the subscription
try {
await this.prismaWriteClient.platformBilling.delete({
where: {
id: teamId,
},
});
} catch (err) {
console.error(err);
}
}
}

View File

@@ -0,0 +1,26 @@
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { TestingModule } from "@nestjs/testing";
import { Booking, User } from "@prisma/client";
export class BookingsRepositoryFixture {
private primaReadClient: PrismaReadService["prisma"];
private prismaWriteClient: PrismaWriteService["prisma"];
constructor(private readonly module: TestingModule) {
this.primaReadClient = module.get(PrismaReadService).prisma;
this.prismaWriteClient = module.get(PrismaWriteService).prisma;
}
async getById(bookingId: Booking["id"]) {
return this.primaReadClient.booking.findFirst({ where: { id: bookingId } });
}
async deleteById(bookingId: Booking["id"]) {
return this.prismaWriteClient.booking.delete({ where: { id: bookingId } });
}
async deleteAllBookings(userId: User["id"], userEmail: User["email"]) {
return this.prismaWriteClient.booking.deleteMany({ where: { userId, userPrimaryEmail: userEmail } });
}
}

View File

@@ -0,0 +1,33 @@
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { TestingModule } from "@nestjs/testing";
import { Prisma } from "@prisma/client";
export class CredentialsRepositoryFixture {
private primaReadClient: PrismaReadService["prisma"];
private prismaWriteClient: PrismaWriteService["prisma"];
constructor(private readonly module: TestingModule) {
this.primaReadClient = module.get(PrismaReadService).prisma;
this.prismaWriteClient = module.get(PrismaWriteService).prisma;
}
create(type: string, key: Prisma.InputJsonValue, userId: number, appId: string) {
return this.prismaWriteClient.credential.create({
data: {
type,
key,
userId,
appId,
},
});
}
delete(id: number) {
return this.prismaWriteClient.credential.delete({
where: {
id,
},
});
}
}

View File

@@ -0,0 +1,47 @@
import { CreateEventTypeInput_2024_04_15 } from "@/ee/event-types/event-types_2024_04_15/inputs/create-event-type.input";
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { TestingModule } from "@nestjs/testing";
import { EventType } from "@prisma/client";
export class EventTypesRepositoryFixture {
private primaReadClient: PrismaReadService["prisma"];
private prismaWriteClient: PrismaWriteService["prisma"];
constructor(private readonly module: TestingModule) {
this.primaReadClient = module.get(PrismaReadService).prisma;
this.prismaWriteClient = module.get(PrismaWriteService).prisma;
}
async getAllUserEventTypes(userId: number) {
return this.prismaWriteClient.eventType.findMany({
where: {
userId,
},
});
}
async getAllTeamEventTypes(teamId: number) {
return this.prismaWriteClient.eventType.findMany({
where: {
teamId,
},
});
}
async create(
data: Pick<CreateEventTypeInput_2024_04_15, "title" | "slug" | "length" | "hidden">,
userId: number
) {
return this.prismaWriteClient.eventType.create({
data: {
...data,
userId,
},
});
}
async delete(eventTypeId: EventType["id"]) {
return this.prismaWriteClient.eventType.delete({ where: { id: eventTypeId } });
}
}

View File

@@ -0,0 +1,38 @@
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { TestingModule } from "@nestjs/testing";
import { Membership, MembershipRole, Prisma, Team, User } from "@prisma/client";
export class MembershipRepositoryFixture {
private primaReadClient: PrismaReadService["prisma"];
private prismaWriteClient: PrismaWriteService["prisma"];
constructor(private readonly module: TestingModule) {
this.primaReadClient = module.get(PrismaReadService).prisma;
this.prismaWriteClient = module.get(PrismaWriteService).prisma;
}
async create(data: Prisma.MembershipCreateInput) {
return this.prismaWriteClient.membership.create({ data });
}
async delete(membershipId: Membership["id"]) {
return this.prismaWriteClient.membership.delete({ where: { id: membershipId } });
}
async get(membershipId: Membership["id"]) {
return this.primaReadClient.membership.findFirst({ where: { id: membershipId } });
}
async getUserMembershipByTeamId(userId: User["id"], teamId: Team["id"]) {
return this.primaReadClient.membership.findFirst({ where: { teamId, userId } });
}
async addUserToOrg(user: User, org: Team, role: MembershipRole, accepted: boolean) {
const membership = await this.prismaWriteClient.membership.create({
data: { teamId: org.id, userId: user.id, role, accepted },
});
await this.prismaWriteClient.user.update({ where: { id: user.id }, data: { organizationId: org.id } });
return membership;
}
}

View File

@@ -0,0 +1,49 @@
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { TestingModule } from "@nestjs/testing";
import { PlatformOAuthClient } from "@prisma/client";
import { CreateOAuthClientInput } from "@calcom/platform-types";
export class OAuthClientRepositoryFixture {
private prismaReadClient: PrismaReadService["prisma"];
private prismaWriteClient: PrismaWriteService["prisma"];
constructor(private readonly module: TestingModule) {
this.prismaReadClient = module.get(PrismaReadService).prisma;
this.prismaWriteClient = module.get(PrismaWriteService).prisma;
}
async get(clientId: PlatformOAuthClient["id"]) {
return this.prismaReadClient.platformOAuthClient.findFirst({ where: { id: clientId } });
}
async getUsers(clientId: PlatformOAuthClient["id"]) {
const response = await this.prismaReadClient.platformOAuthClient.findFirst({
where: { id: clientId },
include: {
users: true,
},
});
return response?.users;
}
async create(organizationId: number, data: CreateOAuthClientInput, secret: string) {
return this.prismaWriteClient.platformOAuthClient.create({
data: {
...data,
secret,
organizationId,
},
});
}
async delete(clientId: PlatformOAuthClient["id"]) {
return this.prismaWriteClient.platformOAuthClient.delete({ where: { id: clientId } });
}
async deleteByClientId(clientId: PlatformOAuthClient["id"]) {
return this.prismaWriteClient.platformOAuthClient.delete({ where: { id: clientId } });
}
}

View File

@@ -0,0 +1,26 @@
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { TestingModule } from "@nestjs/testing";
import { Prisma } from "@prisma/client";
export class ProfileRepositoryFixture {
private primaReadClient: PrismaReadService["prisma"];
private prismaWriteClient: PrismaWriteService["prisma"];
constructor(private readonly module: TestingModule) {
this.primaReadClient = module.get(PrismaReadService).prisma;
this.prismaWriteClient = module.get(PrismaWriteService).prisma;
}
async get(profileId: number) {
return this.primaReadClient.profile.findFirst({ where: { id: profileId } });
}
async create(data: Prisma.ProfileCreateInput) {
return this.prismaWriteClient.profile.create({ data });
}
async delete(profileId: number) {
return this.prismaWriteClient.profile.delete({ where: { id: profileId } });
}
}

View File

@@ -0,0 +1,32 @@
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { TestingModule } from "@nestjs/testing";
import { Schedule } from "@prisma/client";
import { Prisma } from "@calcom/prisma/client";
export class SchedulesRepositoryFixture {
private primaReadClient: PrismaReadService["prisma"];
private prismaWriteClient: PrismaWriteService["prisma"];
constructor(private readonly module: TestingModule) {
this.primaReadClient = module.get(PrismaReadService).prisma;
this.prismaWriteClient = module.get(PrismaWriteService).prisma;
}
async create(schedule: Prisma.ScheduleCreateArgs["data"]) {
return this.prismaWriteClient.schedule.create({ data: schedule });
}
async getById(scheduleId: Schedule["id"]) {
return this.primaReadClient.schedule.findFirst({ where: { id: scheduleId } });
}
async deleteById(scheduleId: Schedule["id"]) {
return this.prismaWriteClient.schedule.delete({ where: { id: scheduleId } });
}
async deleteAvailabilities(scheduleId: Schedule["id"]) {
return this.prismaWriteClient.availability.deleteMany({ where: { scheduleId } });
}
}

View File

@@ -0,0 +1,26 @@
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { TestingModule } from "@nestjs/testing";
import { Prisma, Team } from "@prisma/client";
export class TeamRepositoryFixture {
private primaReadClient: PrismaReadService["prisma"];
private prismaWriteClient: PrismaWriteService["prisma"];
constructor(private readonly module: TestingModule) {
this.primaReadClient = module.get(PrismaReadService).prisma;
this.prismaWriteClient = module.get(PrismaWriteService).prisma;
}
async get(teamId: Team["id"]) {
return this.primaReadClient.team.findFirst({ where: { id: teamId } });
}
async create(data: Prisma.TeamCreateInput) {
return this.prismaWriteClient.team.create({ data });
}
async delete(teamId: Team["id"]) {
return this.prismaWriteClient.team.delete({ where: { id: teamId } });
}
}

View File

@@ -0,0 +1,47 @@
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { TestingModule } from "@nestjs/testing";
import * as crypto from "crypto";
import { DateTime } from "luxon";
export class TokensRepositoryFixture {
private primaReadClient: PrismaReadService["prisma"];
private prismaWriteClient: PrismaWriteService["prisma"];
constructor(private readonly module: TestingModule) {
this.primaReadClient = module.get(PrismaReadService).prisma;
this.prismaWriteClient = module.get(PrismaWriteService).prisma;
}
async createTokens(userId: number, clientId: string) {
const accessExpiry = DateTime.now().plus({ days: 1 }).startOf("day").toJSDate();
const refreshExpiry = DateTime.now().plus({ year: 1 }).startOf("day").toJSDate();
const accessTokenBuffer = crypto.randomBytes(48);
const accessTokenSecret = accessTokenBuffer.toString("hex");
const refreshTokenBuffer = crypto.randomBytes(48);
const refreshTokenSecret = refreshTokenBuffer.toString("hex");
const [accessToken, refreshToken] = await this.prismaWriteClient.$transaction([
this.prismaWriteClient.accessToken.create({
data: {
secret: accessTokenSecret,
expiresAt: accessExpiry,
client: { connect: { id: clientId } },
owner: { connect: { id: userId } },
},
}),
this.prismaWriteClient.refreshToken.create({
data: {
secret: refreshTokenSecret,
expiresAt: refreshExpiry,
client: { connect: { id: clientId } },
owner: { connect: { id: userId } },
},
}),
]);
return {
accessToken: accessToken.secret,
refreshToken: refreshToken.secret,
};
}
}

View File

@@ -0,0 +1,51 @@
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { TestingModule } from "@nestjs/testing";
import { Prisma, User } from "@prisma/client";
export class UserRepositoryFixture {
private primaReadClient: PrismaReadService["prisma"];
private prismaWriteClient: PrismaWriteService["prisma"];
constructor(private readonly module: TestingModule) {
this.primaReadClient = module.get(PrismaReadService).prisma;
this.prismaWriteClient = module.get(PrismaWriteService).prisma;
}
async get(userId: User["id"]) {
return this.primaReadClient.user.findFirst({ where: { id: userId } });
}
async create(data: Prisma.UserCreateInput) {
try {
// avoid uniq constraint in tests
await this.deleteByEmail(data.email);
} catch {}
return this.prismaWriteClient.user.create({ data });
}
async createOAuthManagedUser(email: Prisma.UserCreateInput["email"], oAuthClientId: string) {
try {
// avoid uniq constraint in tests
await this.deleteByEmail(email);
} catch {}
return this.prismaWriteClient.user.create({
data: {
email,
platformOAuthClients: {
connect: { id: oAuthClientId },
},
},
});
}
async delete(userId: User["id"]) {
return this.prismaWriteClient.user.delete({ where: { id: userId } });
}
async deleteByEmail(email: User["email"]) {
return this.prismaWriteClient.user.delete({ where: { email } });
}
}

View File

@@ -0,0 +1,25 @@
import { BaseStrategy } from "@/lib/passport/strategies/types";
import { UsersRepository } from "@/modules/users/users.repository";
import { Injectable } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
@Injectable()
export class ApiAuthMockStrategy extends PassportStrategy(BaseStrategy, "api-auth") {
constructor(private readonly email: string, private readonly usersRepository: UsersRepository) {
super();
}
async authenticate() {
try {
const user = await this.usersRepository.findByEmailWithProfile(this.email);
if (!user) {
throw new Error("User with the provided ID not found");
}
return this.success(user);
} catch (error) {
console.error(error);
if (error instanceof Error) return this.error(error);
}
}
}

View File

@@ -0,0 +1,58 @@
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
export class CalendarsServiceMock {
async getCalendars() {
return {
connectedCalendars: [
{
integration: {
installed: false,
type: "google_calendar",
title: "",
name: "",
description: "",
variant: "calendar",
slug: "",
locationOption: null,
categories: ["calendar"],
logo: "",
publisher: "",
url: "",
email: "",
},
credentialId: 1,
error: { message: "" },
},
{
integration: {
installed: false,
type: "office365_calendar",
title: "",
name: "",
description: "",
variant: "calendar",
slug: "",
locationOption: null,
categories: ["calendar"],
logo: "",
publisher: "",
url: "",
email: "",
},
credentialId: 2,
error: { message: "" },
},
],
destinationCalendar: {
name: "destinationCalendar",
eventTypeId: 1,
credentialId: 1,
primaryEmail: "primaryEmail",
integration: "google_calendar",
externalId: "externalId",
userId: null,
id: 0,
},
} satisfies Awaited<ReturnType<typeof CalendarsService.prototype.getCalendars>>;
}
}

View File

@@ -0,0 +1,15 @@
import { RedisService } from "@/modules/redis/redis.service";
import { Provider } from "@nestjs/common";
export const MockedRedisService = {
provide: RedisService,
useValue: {
redis: {
get: jest.fn(),
hgetall: jest.fn(),
set: jest.fn(),
hmset: jest.fn(),
expireat: jest.fn(),
},
},
} as Provider;

View File

@@ -0,0 +1,24 @@
import { NextAuthPassportStrategy } from "@/lib/passport/strategies/types";
import { UsersRepository } from "@/modules/users/users.repository";
import { Injectable } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
@Injectable()
export class NextAuthMockStrategy extends PassportStrategy(NextAuthPassportStrategy, "next-auth") {
constructor(private readonly email: string, private readonly userRepository: UsersRepository) {
super();
}
async authenticate() {
try {
const user = await this.userRepository.findByEmailWithProfile(this.email);
if (!user) {
throw new Error("User with the provided email not found");
}
return this.success(user);
} catch (error) {
console.error(error);
if (error instanceof Error) return this.error(error);
}
}
}

View File

@@ -0,0 +1,25 @@
import type { Environment } from "@/env";
const env: Partial<Omit<Environment, "NODE_ENV">> = {
API_URL: "http://localhost",
API_PORT: "5555",
DATABASE_URL: "postgresql://postgres:@localhost:5450/calendso",
DATABASE_READ_URL: "postgresql://postgres:@localhost:5450/calendso",
DATABASE_WRITE_URL: "postgresql://postgres:@localhost:5450/calendso",
NEXTAUTH_SECRET: "XF+Hws3A5g2eyWA5uGYYVJ74X+wrCWJ8oWo6kAfU6O8=",
JWT_SECRET: "XF+Hws3A5g2eyWA5uGYYVJ74X+wrCWJ8oWo6kAfU6O8=",
LOG_LEVEL: "trace",
REDIS_URL: "redis://localhost:6379",
STRIPE_API_KEY: "sk_test_51J4",
STRIPE_WEBHOOK_SECRET: "whsec_51J4",
IS_E2E: true,
API_KEY_PREFIX: "cal_test_",
GET_LICENSE_KEY_URL: " https://console.cal.com/api/license",
CALCOM_LICENSE_KEY: "c4234812-12ab-42s6-a1e3-55bedd4a5bb7",
};
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
process.env = {
...env,
...process.env,
};

View File

@@ -0,0 +1,10 @@
import { ApiAuthStrategy } from "@/modules/auth/strategies/api-auth/api-auth.strategy";
import { UsersRepository } from "@/modules/users/users.repository";
import { TestingModuleBuilder } from "@nestjs/testing";
import { ApiAuthMockStrategy } from "test/mocks/api-auth-mock.strategy";
export const withApiAuth = (email: string, module: TestingModuleBuilder) =>
module.overrideProvider(ApiAuthStrategy).useFactory({
factory: (usersRepository: UsersRepository) => new ApiAuthMockStrategy(email, usersRepository),
inject: [UsersRepository],
});

View File

@@ -0,0 +1,10 @@
import { NextAuthStrategy } from "@/modules/auth/strategies/next-auth/next-auth.strategy";
import { UsersRepository } from "@/modules/users/users.repository";
import { TestingModuleBuilder } from "@nestjs/testing";
import { NextAuthMockStrategy } from "test/mocks/next-auth-mock.strategy";
export const withNextAuth = (email: string, module: TestingModuleBuilder) =>
module.overrideProvider(NextAuthStrategy).useFactory({
factory: (userRepository: UsersRepository) => new NextAuthMockStrategy(email, userRepository),
inject: [UsersRepository],
});