diff --git a/backend/src/server/app.ts b/backend/src/server/app.ts index 3ced74160..60b678f63 100644 --- a/backend/src/server/app.ts +++ b/backend/src/server/app.ts @@ -26,7 +26,6 @@ import { TSmtpService } from "@app/services/smtp/smtp-service"; import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; import { globalRateLimiterCfg } from "./config/rateLimiter"; -import { mockThirdPartyApiCalls } from "./mock-third-party-api-calls"; import { addErrorsToResponseSchemas } from "./plugins/add-errors-to-response-schemas"; import { apiMetrics } from "./plugins/api-metrics"; import { fastifyErrHandler } from "./plugins/error-handler"; @@ -67,12 +66,6 @@ export const main = async ({ }: TMain) => { const appCfg = getConfig(); - if (appCfg.shouldMockThirdPartyApiCalls) { - logger?.info("Mocking third party API calls for BDD"); - // Note: to make BDD tests much easier, we mock some third party API calls here - mockThirdPartyApiCalls(); - } - const server = fastify({ logger: appCfg.NODE_ENV === "test" ? false : logger, genReqId: () => `req-${alphaNumericNanoId(14)}`, diff --git a/backend/src/server/mock-third-party-api-calls.ts b/backend/src/server/mock-third-party-api-calls.ts deleted file mode 100644 index 97805f3f4..000000000 --- a/backend/src/server/mock-third-party-api-calls.ts +++ /dev/null @@ -1,95 +0,0 @@ -import nock from "nock"; - -const CLOUDFLARE_ACCOUNT_ID = "A2A6347F-88B5-442D-9798-95E408BC7701"; - -// We mock the Cloudflare account API calls to let adding app connection much easier in BDD tests. -// It's mainly for the DNS provider validation in ACME challenge validation. -const mockCloudflare = () => { - nock("https://api.cloudflare.com:443") - .get("/client/v4/accounts/MOCK_ACCOUNT_ID") - .reply(200, { - result: { - id: CLOUDFLARE_ACCOUNT_ID, - name: "Mock Account", - type: "standard", - settings: { - enforce_twofactor: true, - api_access_enabled: null, - access_approval_expiry: null, - abuse_contact_email: null, - user_groups_ui_beta: false - }, - legacy_flags: { - enterprise_zone_quota: { maximum: 0, current: 0, available: 0 } - }, - created_on: "2013-04-18T00:41:02.215243Z" - }, - success: true, - errors: [], - messages: [] - }); - - nock("https://api.cloudflare.com:443") - .get("/client/v4/zones") - .reply(200, { - result: [ - { - id: "2DF47D5E-7FE2-4BD9-8503-BF27ACE6EBE5", - name: "example.com", - status: "active", - paused: false, - type: "full", - development_mode: 0, - name_servers: ["abby.ns.cloudflare.com", "cody.ns.cloudflare.com"], - original_name_servers: ["ns1gmz.name.com", "ns2fgp.name.com", "ns3jwx.name.com", "ns4lny.name.com"], - original_registrar: "name.com, inc. (id: 625)", - original_dnshost: null, - modified_on: "2025-11-05T18:08:57.046348Z", - created_on: "2025-11-05T18:05:52.536690Z", - activated_on: "2025-11-05T18:08:57.046348Z", - vanity_name_servers: [], - vanity_name_servers_ips: null, - meta: { - step: 2, - custom_certificate_quota: 0, - page_rule_quota: 3, - phishing_detected: false - }, - owner: { id: null, type: "user", email: null }, - account: { - id: CLOUDFLARE_ACCOUNT_ID, - name: "Mock Account" - }, - tenant: { id: null, name: null }, - tenant_unit: { id: null }, - permissions: ["#dns_records:edit", "#dns_records:read", "#zone:read"], - plan: { - id: "0feeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - name: "Free Website", - price: 0, - currency: "USD", - frequency: "", - is_subscribed: false, - can_subscribe: false, - legacy_id: "free", - legacy_discount: false, - externally_managed: false - } - } - ], - result_info: { - page: 1, - per_page: 20, - total_pages: 1, - count: 1, - total_count: 1 - }, - success: true, - errors: [], - messages: [] - }); -}; - -export const mockThirdPartyApiCalls = () => { - mockCloudflare(); -}; diff --git a/backend/src/server/routes/v1/bdd-nock-router.ts b/backend/src/server/routes/v1/bdd-nock-router.ts new file mode 100644 index 000000000..5237ce6ae --- /dev/null +++ b/backend/src/server/routes/v1/bdd-nock-router.ts @@ -0,0 +1,33 @@ +import { z } from "zod"; + +import { getConfig } from "@app/lib/config/env"; +import { ForbiddenRequestError } from "@app/lib/errors"; +import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; +import { AuthMode } from "@app/services/auth/auth-type"; +import nock, { Definition } from "nock"; + +export const registerBddNockRouter = async (server: FastifyZodProvider) => { + server.route({ + method: "POST", + url: "/define", + schema: { + body: z.object({ definition: z.string() }), + response: { + 200: z.object({ status: z.string() }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const appCfg = getConfig(); + // Note: Please note that this API is only available in development mode and only for BDD tests. + // This endpoint should NEVER BE ENABLED IN PRODUCTION! + if (appCfg.NODE_ENV !== "development" || !appCfg.isBddNockApiEnabled) { + throw new ForbiddenRequestError({ message: "BDD Nock API is not enabled" }); + } + const { body } = req; + const { definition } = body; + nock.define(definition as unknown as Definition[]); + return { status: "ok" }; + } + }); +}; diff --git a/backend/src/server/routes/v1/index.ts b/backend/src/server/routes/v1/index.ts index b480a5144..4d8b87e60 100644 --- a/backend/src/server/routes/v1/index.ts +++ b/backend/src/server/routes/v1/index.ts @@ -6,8 +6,10 @@ import { registerCmekRouter } from "@app/server/routes/v1/cmek-router"; import { registerDashboardRouter } from "@app/server/routes/v1/dashboard-router"; import { registerSecretSyncRouter, SECRET_SYNC_REGISTER_ROUTER_MAP } from "@app/server/routes/v1/secret-sync-routers"; +import { getConfig } from "@app/lib/config/env"; import { registerAdminRouter } from "./admin-router"; import { registerAuthRoutes } from "./auth-router"; +import { registerBddNockRouter } from "./bdd-nock-router"; import { registerProjectBotRouter } from "./bot-router"; import { registerCaRouter } from "./certificate-authority-router"; import { CERTIFICATE_AUTHORITY_REGISTER_ROUTER_MAP } from "./certificate-authority-routers"; @@ -237,4 +239,10 @@ export const registerV1Routes = async (server: FastifyZodProvider) => { await server.register(registerEventRouter, { prefix: "/events" }); await server.register(registerUpgradePathRouter, { prefix: "/upgrade-path" }); + + // Note: This is a special route for BDD tests. It's only available in development mode and only for BDD tests. + // This route should NEVER BE ENABLED IN PRODUCTION! + if (getConfig().isBddNockApiEnabled) { + await server.register(registerBddNockRouter, { prefix: "/bdd-nock" }); + } };