From 44aa26292de515bc5816f3e55810cfcafbca8935 Mon Sep 17 00:00:00 2001 From: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com> Date: Tue, 18 Nov 2025 05:34:24 +0530 Subject: [PATCH] fix: flaky integrations tests (#25218) * Revert "fix: resolve flaky integration tests (#25030)" This reverts commit 4e5d4f67d56edf7939f3fe96329bc2e7f1e49378. * update * test * Remove connection pool setup in Prisma index Set the connection pool to undefined, removing conditional pooling logic. * update --- .github/workflows/integration-tests.yml | 1 + apps/api/v1/lib/utils/isAdmin.ts | 4 +- .../bookings/[id]/_patch.integration-test.ts | 26 +++- .../lib/bookings/_get.integration-test.ts | 31 +++- .../lib/utils/isAdmin.integration-test.ts | 49 +++++- ...eScopedAccessibleUsers.integration-test.ts | 26 +++- packages/prisma/index.ts | 3 +- tests/integration/global-setup.ts | 58 ------- tests/lib/test-team-repository.ts | 144 ------------------ turbo.json | 1 + vitest.workspace.ts | 1 - 11 files changed, 128 insertions(+), 216 deletions(-) delete mode 100644 tests/integration/global-setup.ts delete mode 100644 tests/lib/test-team-repository.ts diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 6ccff66d9f..140d710179 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -5,6 +5,7 @@ permissions: contents: read env: NODE_OPTIONS: --max-old-space-size=4096 + INTEGRATION_TESTS: "true" ALLOWED_HOSTNAMES: ${{ vars.CI_ALLOWED_HOSTNAMES }} CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }} DAILY_API_KEY: ${{ secrets.CI_DAILY_API_KEY }} diff --git a/apps/api/v1/lib/utils/isAdmin.ts b/apps/api/v1/lib/utils/isAdmin.ts index 3198712041..38f3cc3099 100644 --- a/apps/api/v1/lib/utils/isAdmin.ts +++ b/apps/api/v1/lib/utils/isAdmin.ts @@ -19,9 +19,7 @@ export const isAdminGuard = async (req: NextApiRequest) => { team: { isOrganization: true, organizationSettings: { - is: { - isAdminAPIEnabled: true, - }, + isAdminAPIEnabled: true, }, }, OR: [{ role: MembershipRole.OWNER }, { role: MembershipRole.ADMIN }], diff --git a/apps/api/v1/test/lib/bookings/[id]/_patch.integration-test.ts b/apps/api/v1/test/lib/bookings/[id]/_patch.integration-test.ts index 5554d6411d..251583cb28 100644 --- a/apps/api/v1/test/lib/bookings/[id]/_patch.integration-test.ts +++ b/apps/api/v1/test/lib/bookings/[id]/_patch.integration-test.ts @@ -1,7 +1,7 @@ import type { Request, Response } from "express"; import type { NextApiRequest, NextApiResponse } from "next"; import { createMocks } from "node-mocks-http"; -import { describe, it, expect } from "vitest"; +import { describe, it, expect, beforeAll } from "vitest"; import prisma from "@calcom/prisma"; @@ -11,6 +11,30 @@ type CustomNextApiRequest = NextApiRequest & Request; type CustomNextApiResponse = NextApiResponse & Response; describe("PATCH /api/bookings", () => { + beforeAll(async () => { + const acmeOrg = await prisma.team.findFirst({ + where: { + slug: "acme", + isOrganization: true, + }, + }); + + if (acmeOrg) { + await prisma.organizationSettings.upsert({ + where: { + organizationId: acmeOrg.id, + }, + update: { + isAdminAPIEnabled: true, + }, + create: { + organizationId: acmeOrg.id, + orgAutoAcceptEmail: "acme.com", + isAdminAPIEnabled: true, + }, + }); + } + }); it("Returns 403 when user has no permission to the booking", async () => { const memberUser = await prisma.user.findFirstOrThrow({ where: { email: "member2-acme@example.com" } }); const proUser = await prisma.user.findFirstOrThrow({ where: { email: "pro@example.com" } }); diff --git a/apps/api/v1/test/lib/bookings/_get.integration-test.ts b/apps/api/v1/test/lib/bookings/_get.integration-test.ts index 18099ef3fe..2f2ba4f98e 100644 --- a/apps/api/v1/test/lib/bookings/_get.integration-test.ts +++ b/apps/api/v1/test/lib/bookings/_get.integration-test.ts @@ -16,14 +16,33 @@ const DefaultPagination = { skip: 0, }; -describe("GET /api/bookings", () => { - let proUser: Awaited>; - let proUserBooking: Awaited>; - +describe("GET /api/bookings", async () => { beforeAll(async () => { - proUser = await prisma.user.findFirstOrThrow({ where: { email: "pro@example.com" } }); - proUserBooking = await prisma.booking.findFirstOrThrow({ where: { userId: proUser.id } }); + const acmeOrg = await prisma.team.findFirst({ + where: { + slug: "acme", + isOrganization: true, + }, + }); + + if (acmeOrg) { + await prisma.organizationSettings.upsert({ + where: { + organizationId: acmeOrg.id, + }, + update: { + isAdminAPIEnabled: true, + }, + create: { + organizationId: acmeOrg.id, + orgAutoAcceptEmail: "acme.com", + isAdminAPIEnabled: true, + }, + }); + } }); + const proUser = await prisma.user.findFirstOrThrow({ where: { email: "pro@example.com" } }); + const proUserBooking = await prisma.booking.findFirstOrThrow({ where: { userId: proUser.id } }); it("Does not return bookings of other users when user has no permission", async () => { const memberUser = await prisma.user.findFirstOrThrow({ where: { email: "member2-acme@example.com" } }); diff --git a/apps/api/v1/test/lib/utils/isAdmin.integration-test.ts b/apps/api/v1/test/lib/utils/isAdmin.integration-test.ts index 2136617d23..f8485605cb 100644 --- a/apps/api/v1/test/lib/utils/isAdmin.integration-test.ts +++ b/apps/api/v1/test/lib/utils/isAdmin.integration-test.ts @@ -1,7 +1,7 @@ import type { Request, Response } from "express"; import type { NextApiRequest, NextApiResponse } from "next"; import { createMocks } from "node-mocks-http"; -import { describe, it, expect } from "vitest"; +import { describe, it, expect, beforeAll } from "vitest"; import prisma from "@calcom/prisma"; @@ -12,6 +12,53 @@ type CustomNextApiRequest = NextApiRequest & Request; type CustomNextApiResponse = NextApiResponse & Response; describe("isAdmin guard", () => { + beforeAll(async () => { + const acmeOrg = await prisma.team.findFirst({ + where: { + slug: "acme", + isOrganization: true, + }, + }); + + if (acmeOrg) { + await prisma.organizationSettings.upsert({ + where: { + organizationId: acmeOrg.id, + }, + update: { + isAdminAPIEnabled: true, + }, + create: { + organizationId: acmeOrg.id, + orgAutoAcceptEmail: "acme.com", + isAdminAPIEnabled: true, + }, + }); + } + + const dunderOrg = await prisma.team.findFirst({ + where: { + slug: "dunder-mifflin", + isOrganization: true, + }, + }); + + if (dunderOrg) { + await prisma.organizationSettings.upsert({ + where: { + organizationId: dunderOrg.id, + }, + update: { + isAdminAPIEnabled: false, + }, + create: { + organizationId: dunderOrg.id, + orgAutoAcceptEmail: "dunder-mifflin.com", + isAdminAPIEnabled: false, + }, + }); + } + }); it("Returns false when user does not exist in the system", async () => { const { req } = createMocks({ method: "POST", diff --git a/apps/api/v1/test/lib/utils/retrieveScopedAccessibleUsers.integration-test.ts b/apps/api/v1/test/lib/utils/retrieveScopedAccessibleUsers.integration-test.ts index d670defc72..66f180c4e0 100644 --- a/apps/api/v1/test/lib/utils/retrieveScopedAccessibleUsers.integration-test.ts +++ b/apps/api/v1/test/lib/utils/retrieveScopedAccessibleUsers.integration-test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect } from "vitest"; +import { describe, it, expect, beforeAll } from "vitest"; import prisma from "@calcom/prisma"; @@ -8,6 +8,30 @@ import { } from "../../../lib/utils/retrieveScopedAccessibleUsers"; describe("retrieveScopedAccessibleUsers tests", () => { + beforeAll(async () => { + const acmeOrg = await prisma.team.findFirst({ + where: { + slug: "acme", + isOrganization: true, + }, + }); + + if (acmeOrg) { + await prisma.organizationSettings.upsert({ + where: { + organizationId: acmeOrg.id, + }, + update: { + isAdminAPIEnabled: true, + }, + create: { + organizationId: acmeOrg.id, + orgAutoAcceptEmail: "acme.com", + isAdminAPIEnabled: true, + }, + }); + } + }); describe("getAccessibleUsers", () => { it("Does not return members when only admin user ID is supplied", async () => { const adminUser = await prisma.user.findFirstOrThrow({ where: { email: "owner1-acme@example.com" } }); diff --git a/packages/prisma/index.ts b/packages/prisma/index.ts index 1c7a844a55..6be7cc3164 100644 --- a/packages/prisma/index.ts +++ b/packages/prisma/index.ts @@ -8,8 +8,9 @@ import { excludePendingPaymentsExtension } from "./extensions/exclude-pending-pa import { PrismaClient, type Prisma } from "./generated/prisma/client"; const connectionString = process.env.DATABASE_URL || ""; +const isIntegrationTest = process.env.INTEGRATION_TESTS === "true"; const pool = - process.env.USE_POOL === "true" || process.env.USE_POOL === "1" + !isIntegrationTest && (process.env.USE_POOL === "true" || process.env.USE_POOL === "1") ? new Pool({ connectionString: connectionString, max: 5, diff --git a/tests/integration/global-setup.ts b/tests/integration/global-setup.ts deleted file mode 100644 index 5d9afe25bd..0000000000 --- a/tests/integration/global-setup.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Global setup for integration tests - * Runs once before all integration tests to seed org-admin state - */ -export default async function globalSetup() { - const module = await import("../lib/test-team-repository"); - const TestTeamRepository = module.TestTeamRepository; - const teamRepo = await TestTeamRepository.withGlobalPrisma(); - - const acmeOrg = await teamRepo.findOrganizationBySlug("acme"); - - if (acmeOrg) { - await teamRepo.upsertOrganizationSettings({ - organizationId: acmeOrg.id, - isAdminAPIEnabled: true, - orgAutoAcceptEmail: "acme.com", - }); - - await teamRepo.ensureMembership({ - userEmail: "owner1-acme@example.com", - organizationId: acmeOrg.id, - role: "OWNER", - accepted: true, - }); - - for (let i = 0; i < 10; i++) { - const memberEmail = `member${i}-acme@example.com`; - const memberUsername = `member${i}-acme`; - const memberName = `Member ${i}`; - - await teamRepo.ensureUser({ - email: memberEmail, - username: memberUsername, - name: memberName, - }); - - await teamRepo.ensureMembership({ - userEmail: memberEmail, - organizationId: acmeOrg.id, - role: "MEMBER", - accepted: true, - }); - } - } - - const dunderOrg = await teamRepo.findOrganizationBySlug("dunder-mifflin"); - - if (dunderOrg) { - await teamRepo.upsertOrganizationSettings({ - organizationId: dunderOrg.id, - isAdminAPIEnabled: false, - orgAutoAcceptEmail: "dunder-mifflin.com", - }); - } - - return async () => { - }; -} diff --git a/tests/lib/test-team-repository.ts b/tests/lib/test-team-repository.ts deleted file mode 100644 index 0fbc5f0ab5..0000000000 --- a/tests/lib/test-team-repository.ts +++ /dev/null @@ -1,144 +0,0 @@ -import type { PrismaClient } from "@calcom/prisma"; - -export class TestTeamRepository { - constructor(private prismaClient: PrismaClient) {} - - static async withGlobalPrisma() { - return new TestTeamRepository((await import("@calcom/prisma")).prisma); - } - - async findOrganizationBySlug(slug: string) { - return this.prismaClient.team.findFirst({ - where: { - slug, - isOrganization: true, - }, - select: { - id: true, - slug: true, - name: true, - }, - }); - } - - async upsertOrganizationSettings({ - organizationId, - isAdminAPIEnabled, - orgAutoAcceptEmail, - }: { - organizationId: number; - isAdminAPIEnabled: boolean; - orgAutoAcceptEmail?: string; - }) { - return this.prismaClient.organizationSettings.upsert({ - where: { - organizationId, - }, - update: { - isAdminAPIEnabled, - }, - create: { - organizationId, - orgAutoAcceptEmail: orgAutoAcceptEmail || "", - isAdminAPIEnabled, - }, - }); - } - - async findUserMembershipsInOrg({ - userEmail, - organizationId, - }: { - userEmail: string; - organizationId: number; - }) { - return this.prismaClient.membership.findMany({ - where: { - user: { - email: userEmail, - }, - teamId: organizationId, - }, - select: { - id: true, - role: true, - accepted: true, - user: { - select: { - id: true, - email: true, - }, - }, - }, - }); - } - - async ensureUser({ - email, - username, - name, - }: { - email: string; - username: string; - name: string; - }) { - return this.prismaClient.user.upsert({ - where: { email }, - update: { - name, - }, - create: { - email, - username, - name, - emailVerified: new Date(), - }, - }); - } - - async ensureMembership({ - userEmail, - organizationId, - role, - accepted, - }: { - userEmail: string; - organizationId: number; - role: "OWNER" | "ADMIN" | "MEMBER"; - accepted: boolean; - }) { - const user = await this.prismaClient.user.findFirst({ - where: { email: userEmail }, - }); - - if (!user) { - throw new Error(`User with email ${userEmail} not found`); - } - - const existingMembership = await this.prismaClient.membership.findFirst({ - where: { - userId: user.id, - teamId: organizationId, - }, - }); - - if (existingMembership) { - return this.prismaClient.membership.update({ - where: { id: existingMembership.id }, - data: { - role, - accepted, - }, - }); - } - - return this.prismaClient.membership.create({ - data: { - userId: user.id, - teamId: organizationId, - role, - accepted, - }, - }); - } -} diff --git a/turbo.json b/turbo.json index 4b199c0784..eae6690ea8 100644 --- a/turbo.json +++ b/turbo.json @@ -84,6 +84,7 @@ "HUBSPOT_CLIENT_SECRET", "IFFY_API_KEY", "INTEGRATION_TEST_MODE", + "INTEGRATION_TESTS", "INTERCOM_SECRET", "INTERCOM_SECRET", "INSIGHTS_DATABASE_URL", diff --git a/vitest.workspace.ts b/vitest.workspace.ts index 62260ef55f..498e907eef 100644 --- a/vitest.workspace.ts +++ b/vitest.workspace.ts @@ -27,7 +27,6 @@ const workspaces = packagedEmbedTestsOnly name: `IntegrationTests`, include: ["packages/**/*.integration-test.ts", "apps/**/*.integration-test.ts"], exclude: ["**/node_modules/**/*", "packages/embeds/**/*"], - globalSetup: new URL("./tests/integration/global-setup.ts", import.meta.url).pathname, setupFiles: ["setupVitest.ts"], }, resolve: {