Mock third party calls

This commit is contained in:
Fang-Pen Lin
2025-11-12 19:56:33 -08:00
parent 8246483e8a
commit 1a18096ac8
5 changed files with 194 additions and 0 deletions

View File

@@ -177,6 +177,7 @@
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-simple-import-sort": "^10.0.0",
"nock": "^14.0.10",
"nodemon": "^3.0.2",
"pino-pretty": "^10.2.3",
"prompt-sync": "^4.2.0",
@@ -9705,6 +9706,24 @@
"win32"
]
},
"node_modules/@mswjs/interceptors": {
"version": "0.39.8",
"resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.39.8.tgz",
"integrity": "sha512-2+BzZbjRO7Ct61k8fMNHEtoKjeWI9pIlHFTqBwZ5icHpqszIgEZbjb1MW5Z0+bITTCTl3gk4PDBxs9tA/csXvA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@open-draft/deferred-promise": "^2.2.0",
"@open-draft/logger": "^0.3.0",
"@open-draft/until": "^2.0.0",
"is-node-process": "^1.2.0",
"outvariant": "^1.4.3",
"strict-event-emitter": "^0.5.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@next/env": {
"version": "15.5.2",
"resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.2.tgz",
@@ -10714,6 +10733,31 @@
"urijs": "^1.19.11"
}
},
"node_modules/@open-draft/deferred-promise": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz",
"integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==",
"dev": true,
"license": "MIT"
},
"node_modules/@open-draft/logger": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz",
"integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-node-process": "^1.2.0",
"outvariant": "^1.4.0"
}
},
"node_modules/@open-draft/until": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz",
"integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==",
"dev": true,
"license": "MIT"
},
"node_modules/@opentelemetry/api": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
@@ -22958,6 +23002,13 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-node-process": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz",
"integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==",
"dev": true,
"license": "MIT"
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -23507,6 +23558,13 @@
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
"dev": true
},
"node_modules/json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
"dev": true,
"license": "ISC"
},
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -25074,6 +25132,21 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/nock": {
"version": "14.0.10",
"resolved": "https://registry.npmjs.org/nock/-/nock-14.0.10.tgz",
"integrity": "sha512-Q7HjkpyPeLa0ZVZC5qpxBt5EyLczFJ91MEewQiIi9taWuA0KB/MDJlUWtON+7dGouVdADTQsf9RA7TZk6D8VMw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@mswjs/interceptors": "^0.39.5",
"json-stringify-safe": "^5.0.1",
"propagate": "^2.0.0"
},
"engines": {
"node": ">=18.20.0 <20 || >=20.12.1"
}
},
"node_modules/node-abi": {
"version": "3.65.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.65.0.tgz",
@@ -27702,6 +27775,13 @@
"@otplib/preset-v11": "^12.0.1"
}
},
"node_modules/outvariant": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz",
"integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==",
"dev": true,
"license": "MIT"
},
"node_modules/p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
@@ -29103,6 +29183,16 @@
"node": ">= 6"
}
},
"node_modules/propagate": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz",
"integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 8"
}
},
"node_modules/proto3-json-serializer": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz",
@@ -31601,6 +31691,13 @@
"node": ">=4.0.0"
}
},
"node_modules/strict-event-emitter": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz",
"integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==",
"dev": true,
"license": "MIT"
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",

View File

@@ -123,6 +123,7 @@
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-simple-import-sort": "^10.0.0",
"nock": "^14.0.10",
"nodemon": "^3.0.2",
"pino-pretty": "^10.2.3",
"prompt-sync": "^4.2.0",

View File

@@ -106,6 +106,7 @@ const envSchema = z
HTTPS_ENABLED: zodStrBool,
ROTATION_DEVELOPMENT_MODE: zodStrBool.default("false").optional(),
DAILY_RESOURCE_CLEAN_UP_DEVELOPMENT_MODE: zodStrBool.default("false").optional(),
BDD_MOCK_THIRD_PARTY_API_CALLS: zodStrBool.default("false").optional(),
ACME_DEVELOPMENT_MODE: zodStrBool.default("false").optional(),
ACME_DEVELOPMENT_HTTP01_CHALLENGE_HOST_OVERRIDES: zpStr(
z
@@ -398,6 +399,7 @@ const envSchema = z
isAcmeDevelopmentMode: data.NODE_ENV === "development" && data.ACME_DEVELOPMENT_MODE,
isProductionMode: data.NODE_ENV === "production" || IS_PACKAGED,
isRedisSentinelMode: Boolean(data.REDIS_SENTINEL_HOSTS),
shouldMockThirdPartyApiCalls: data.NODE_ENV === "development" && data.BDD_MOCK_THIRD_PARTY_API_CALLS,
REDIS_SENTINEL_HOSTS: data.REDIS_SENTINEL_HOSTS?.trim()
?.split(",")
.map((el) => {

View File

@@ -0,0 +1,87 @@
import nock from "nock";
export const mockThirdPartyApiCalls = () => {
nock("https://api.cloudflare.com:443")
.get("/client/v4/accounts/MOCK_ACCOUNT_ID")
.reply(200, {
result: {
id: "A2A6347F-88B5-442D-9798-95E408BC7701",
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: "A2A6347F-88B5-442D-9798-95E408BC7701",
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: []
});
};

View File

@@ -26,6 +26,7 @@ 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";
@@ -66,6 +67,12 @@ 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)}`,