From 3d20d4ccb7760ccff61da9e3f12b3b4f2dde1aed Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 1 Aug 2024 10:37:54 +0100 Subject: [PATCH 01/12] Fix application.spec.ts's reliance on the node-fetch mock. --- .../{node-fetch.ts => _node-fetch.ts} | 0 .../src/api/routes/tests/application.spec.ts | 19 ++++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) rename packages/server/__mocks__/{node-fetch.ts => _node-fetch.ts} (100%) diff --git a/packages/server/__mocks__/node-fetch.ts b/packages/server/__mocks__/_node-fetch.ts similarity index 100% rename from packages/server/__mocks__/node-fetch.ts rename to packages/server/__mocks__/_node-fetch.ts diff --git a/packages/server/src/api/routes/tests/application.spec.ts b/packages/server/src/api/routes/tests/application.spec.ts index 13b7451a7e..6ae598ee84 100644 --- a/packages/server/src/api/routes/tests/application.spec.ts +++ b/packages/server/src/api/routes/tests/application.spec.ts @@ -20,6 +20,7 @@ import { type App } from "@budibase/types" import tk from "timekeeper" import * as uuid from "uuid" import { structures } from "@budibase/backend-core/tests" +import nock from "nock" describe("/applications", () => { let config = setup.getConfig() @@ -35,6 +36,7 @@ describe("/applications", () => { throw new Error("Failed to publish app") } jest.clearAllMocks() + nock.cleanAll() }) // These need to go first for the app totals to make sense @@ -324,18 +326,33 @@ describe("/applications", () => { describe("delete", () => { it("should delete published app and dev apps with dev app ID", async () => { + const prodAppId = app.appId.replace("_dev", "") + nock("http://localhost:10000") + .delete(`/api/global/roles/${prodAppId}`) + .reply(200, {}) + await config.api.application.delete(app.appId) expect(events.app.deleted).toHaveBeenCalledTimes(1) expect(events.app.unpublished).toHaveBeenCalledTimes(1) }) it("should delete published app and dev app with prod app ID", async () => { - await config.api.application.delete(app.appId.replace("_dev", "")) + const prodAppId = app.appId.replace("_dev", "") + nock("http://localhost:10000") + .delete(`/api/global/roles/${prodAppId}`) + .reply(200, {}) + + await config.api.application.delete(prodAppId) expect(events.app.deleted).toHaveBeenCalledTimes(1) expect(events.app.unpublished).toHaveBeenCalledTimes(1) }) it("should be able to delete an app after SQS_SEARCH_ENABLE has been set but app hasn't been migrated", async () => { + const prodAppId = app.appId.replace("_dev", "") + nock("http://localhost:10000") + .delete(`/api/global/roles/${prodAppId}`) + .reply(200, {}) + await config.withCoreEnv({ SQS_SEARCH_ENABLE: "true" }, async () => { await config.api.application.delete(app.appId) }) From 9a2e8031bcc40808d61a7a147155faf3695116c9 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 1 Aug 2024 11:43:37 +0100 Subject: [PATCH 02/12] Fix plugin.spec.ts's reliance on the node-fetch mock. --- .../src/api/controllers/plugin/index.ts | 18 +-- .../src/api/routes/tests/plugin.spec.ts | 116 +++++++++++------- .../integrations/tests/googlesheets.spec.ts | 22 ++++ .../server/src/tests/utilities/api/index.ts | 3 + .../server/src/tests/utilities/api/plugin.ts | 11 ++ packages/types/src/api/web/index.ts | 1 + packages/types/src/api/web/plugins.ts | 12 ++ 7 files changed, 129 insertions(+), 54 deletions(-) create mode 100644 packages/server/src/tests/utilities/api/plugin.ts create mode 100644 packages/types/src/api/web/plugins.ts diff --git a/packages/server/src/api/controllers/plugin/index.ts b/packages/server/src/api/controllers/plugin/index.ts index c7d4912db3..e1c51f0219 100644 --- a/packages/server/src/api/controllers/plugin/index.ts +++ b/packages/server/src/api/controllers/plugin/index.ts @@ -1,6 +1,13 @@ import { npmUpload, urlUpload, githubUpload } from "./uploaders" import { plugins as pluginCore } from "@budibase/backend-core" -import { PluginType, FileType, PluginSource } from "@budibase/types" +import { + PluginType, + FileType, + PluginSource, + Ctx, + CreatePluginRequest, + CreatePluginResponse, +} from "@budibase/types" import env from "../../../environment" import { clientAppSocket } from "../../../websockets" import sdk from "../../../sdk" @@ -29,7 +36,9 @@ export async function upload(ctx: any) { } } -export async function create(ctx: any) { +export async function create( + ctx: Ctx +) { const { source, url, headers, githubToken } = ctx.request.body try { @@ -75,14 +84,9 @@ export async function create(ctx: any) { const doc = await pro.plugins.storePlugin(metadata, directory, source) clientAppSocket?.emit("plugins-update", { name, hash: doc.hash }) - ctx.body = { - message: "Plugin uploaded successfully", - plugins: [doc], - } ctx.body = { plugin: doc } } catch (err: any) { const errMsg = err?.message ? err?.message : err - ctx.throw(400, `Failed to import plugin: ${errMsg}`) } } diff --git a/packages/server/src/api/routes/tests/plugin.spec.ts b/packages/server/src/api/routes/tests/plugin.spec.ts index 788d3cf349..e592772909 100644 --- a/packages/server/src/api/routes/tests/plugin.spec.ts +++ b/packages/server/src/api/routes/tests/plugin.spec.ts @@ -15,6 +15,8 @@ jest.mock("@budibase/backend-core", () => { import { events, objectStore } from "@budibase/backend-core" import * as setup from "./utilities" +import nock from "nock" +import { PluginSource } from "@budibase/types" const mockUploadDirectory = objectStore.uploadDirectory as jest.Mock const mockDeleteFolder = objectStore.deleteFolder as jest.Mock @@ -28,6 +30,7 @@ describe("/plugins", () => { beforeEach(async () => { await config.init() jest.clearAllMocks() + nock.cleanAll() }) const createPlugin = async (status?: number) => { @@ -112,67 +115,86 @@ describe("/plugins", () => { }) describe("github", () => { - const createGithubPlugin = async (status?: number, url?: string) => { - return await request - .post(`/api/plugin`) - .send({ - source: "Github", - url, - githubToken: "token", + beforeEach(async () => { + nock("https://api.github.com") + .get("/repos/my-repo/budibase-comment-box") + .reply(200, { + name: "budibase-comment-box", + releases_url: + "https://api.github.com/repos/my-repo/budibase-comment-box{/id}", }) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(status ? status : 200) - } - it("should be able to create a plugin from github", async () => { - const res = await createGithubPlugin( - 200, - "https://github.com/my-repo/budibase-comment-box.git" - ) - expect(res.body).toBeDefined() - expect(res.body.plugin).toBeDefined() - expect(res.body.plugin._id).toEqual("plg_comment-box") + .get("/repos/my-repo/budibase-comment-box/latest") + .reply(200, { + assets: [ + { + content_type: "application/gzip", + browser_download_url: + "https://github.com/my-repo/budibase-comment-box/releases/download/v1.0.2/comment-box-1.0.2.tar.gz", + }, + ], + }) + + nock("https://github.com") + .get( + "/my-repo/budibase-comment-box/releases/download/v1.0.2/comment-box-1.0.2.tar.gz" + ) + .replyWithFile( + 200, + "src/api/routes/tests/data/comment-box-1.0.2.tar.gz" + ) }) + + it("should be able to create a plugin from github", async () => { + const { plugin } = await config.api.plugin.create({ + source: PluginSource.GITHUB, + url: "https://github.com/my-repo/budibase-comment-box.git", + githubToken: "token", + }) + expect(plugin._id).toEqual("plg_comment-box") + }) + it("should fail if the url is not from github", async () => { - const res = await createGithubPlugin( - 400, - "https://notgithub.com/my-repo/budibase-comment-box" - ) - expect(res.body.message).toEqual( - "Failed to import plugin: The plugin origin must be from Github" + await config.api.plugin.create( + { + source: PluginSource.GITHUB, + url: "https://notgithub.com/my-repo/budibase-comment-box", + githubToken: "token", + }, + { + status: 400, + body: { + message: + "Failed to import plugin: The plugin origin must be from Github", + }, + } ) }) }) describe("npm", () => { it("should be able to create a plugin from npm", async () => { - const res = await request - .post(`/api/plugin`) - .send({ - source: "NPM", - url: "https://www.npmjs.com/package/budibase-component", - }) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) - expect(res.body).toBeDefined() - expect(res.body.plugin._id).toEqual("plg_budibase-component") + const { plugin } = await config.api.plugin.create({ + source: PluginSource.NPM, + url: "https://www.npmjs.com/package/budibase-component", + }) + expect(plugin._id).toEqual("plg_budibase-component") expect(events.plugin.imported).toHaveBeenCalled() }) }) describe("url", () => { it("should be able to create a plugin from a URL", async () => { - const res = await request - .post(`/api/plugin`) - .send({ - source: "URL", - url: "https://www.someurl.com/comment-box/comment-box-1.0.2.tar.gz", - }) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) - expect(res.body).toBeDefined() - expect(res.body.plugin._id).toEqual("plg_comment-box") + nock("https://www.someurl.com") + .get("/comment-box/comment-box-1.0.2.tar.gz") + .replyWithFile( + 200, + "src/api/routes/tests/data/comment-box-1.0.2.tar.gz" + ) + + const { plugin } = await config.api.plugin.create({ + source: PluginSource.URL, + url: "https://www.someurl.com/comment-box/comment-box-1.0.2.tar.gz", + }) + expect(plugin._id).toEqual("plg_comment-box") expect(events.plugin.imported).toHaveBeenCalledTimes(1) }) }) diff --git a/packages/server/src/integrations/tests/googlesheets.spec.ts b/packages/server/src/integrations/tests/googlesheets.spec.ts index 97ac35787d..2252a8bb9b 100644 --- a/packages/server/src/integrations/tests/googlesheets.spec.ts +++ b/packages/server/src/integrations/tests/googlesheets.spec.ts @@ -64,6 +64,28 @@ describe("Google Sheets Integration", () => { jest.clearAllMocks() }) + // nock("https://www.googleapis.com/").post("/oauth2/v4/token").reply(200, { + // grant_type: "client_credentials", + // client_id: "your-client-id", + // client_secret: "your-client-secret", + // }) + + // nock("https://oauth2.googleapis.com").post("/token").reply(200, { + // access_token: "your-access-token", + // }) + + // nock("https://sheets.googleapis.com") + // .get("/v4/spreadsheets/randomId/") + // .reply(200, { + // sheets: [ + // { + // properties: { + // title: "test", + // }, + // }, + // ], + // }) + function createBasicTable(name: string, columns: string[]): Table { return { type: "table", diff --git a/packages/server/src/tests/utilities/api/index.ts b/packages/server/src/tests/utilities/api/index.ts index 36a6ed0222..79514d4ece 100644 --- a/packages/server/src/tests/utilities/api/index.ts +++ b/packages/server/src/tests/utilities/api/index.ts @@ -15,6 +15,7 @@ import { RoleAPI } from "./role" import { TemplateAPI } from "./template" import { RowActionAPI } from "./rowAction" import { AutomationAPI } from "./automation" +import { PluginAPI } from "./plugin" export default class API { table: TableAPI @@ -33,6 +34,7 @@ export default class API { templates: TemplateAPI rowAction: RowActionAPI automation: AutomationAPI + plugin: PluginAPI constructor(config: TestConfiguration) { this.table = new TableAPI(config) @@ -51,5 +53,6 @@ export default class API { this.templates = new TemplateAPI(config) this.rowAction = new RowActionAPI(config) this.automation = new AutomationAPI(config) + this.plugin = new PluginAPI(config) } } diff --git a/packages/server/src/tests/utilities/api/plugin.ts b/packages/server/src/tests/utilities/api/plugin.ts new file mode 100644 index 0000000000..c2b3a3269d --- /dev/null +++ b/packages/server/src/tests/utilities/api/plugin.ts @@ -0,0 +1,11 @@ +import { Expectations, TestAPI } from "./base" +import { CreatePluginRequest, CreatePluginResponse } from "@budibase/types" + +export class PluginAPI extends TestAPI { + create = async (body: CreatePluginRequest, expectations?: Expectations) => { + return await this._post(`/api/plugin`, { + body, + expectations, + }) + } +} diff --git a/packages/types/src/api/web/index.ts b/packages/types/src/api/web/index.ts index 8a091afdba..27d51ce1b7 100644 --- a/packages/types/src/api/web/index.ts +++ b/packages/types/src/api/web/index.ts @@ -15,3 +15,4 @@ export * from "./automation" export * from "./layout" export * from "./query" export * from "./role" +export * from "./plugins" diff --git a/packages/types/src/api/web/plugins.ts b/packages/types/src/api/web/plugins.ts new file mode 100644 index 0000000000..458ad3f6ce --- /dev/null +++ b/packages/types/src/api/web/plugins.ts @@ -0,0 +1,12 @@ +import { PluginSource } from "../../documents" + +export interface CreatePluginRequest { + source: PluginSource + url: string + githubToken?: string + headers?: { [key: string]: string } +} + +export interface CreatePluginResponse { + plugin: any +} From 3657067337cef5c66e5ded487d4d17a60d5c910c Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 1 Aug 2024 11:44:58 +0100 Subject: [PATCH 03/12] Fix googlesheets.spec.ts's reliance on the node-fetch mock. --- .../integrations/tests/googlesheets.spec.ts | 30 +++++-------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/packages/server/src/integrations/tests/googlesheets.spec.ts b/packages/server/src/integrations/tests/googlesheets.spec.ts index 2252a8bb9b..9b1ea815f5 100644 --- a/packages/server/src/integrations/tests/googlesheets.spec.ts +++ b/packages/server/src/integrations/tests/googlesheets.spec.ts @@ -1,4 +1,5 @@ import type { GoogleSpreadsheetWorksheet } from "google-spreadsheet" +import nock from "nock" jest.mock("google-auth-library") const { OAuth2Client } = require("google-auth-library") @@ -62,30 +63,15 @@ describe("Google Sheets Integration", () => { await config.init() jest.clearAllMocks() + + nock.cleanAll() + nock("https://www.googleapis.com/").post("/oauth2/v4/token").reply(200, { + grant_type: "client_credentials", + client_id: "your-client-id", + client_secret: "your-client-secret", + }) }) - // nock("https://www.googleapis.com/").post("/oauth2/v4/token").reply(200, { - // grant_type: "client_credentials", - // client_id: "your-client-id", - // client_secret: "your-client-secret", - // }) - - // nock("https://oauth2.googleapis.com").post("/token").reply(200, { - // access_token: "your-access-token", - // }) - - // nock("https://sheets.googleapis.com") - // .get("/v4/spreadsheets/randomId/") - // .reply(200, { - // sheets: [ - // { - // properties: { - // title: "test", - // }, - // }, - // ], - // }) - function createBasicTable(name: string, columns: string[]): Table { return { type: "table", From e530400f461c996636d6bd4ec88d9103cb178ca2 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 1 Aug 2024 11:57:04 +0100 Subject: [PATCH 04/12] Fix n8n.spec.ts's reliance on the node-fetch mock. --- .../server/src/automations/tests/n8n.spec.ts | 25 +++++++++++-------- packages/server/src/tests/jestSetup.ts | 10 ++++++++ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/packages/server/src/automations/tests/n8n.spec.ts b/packages/server/src/automations/tests/n8n.spec.ts index d60a08b53b..0c18f313b1 100644 --- a/packages/server/src/automations/tests/n8n.spec.ts +++ b/packages/server/src/automations/tests/n8n.spec.ts @@ -1,4 +1,5 @@ import { getConfig, afterAll, runStep, actions } from "./utilities" +import nock from "nock" describe("test the outgoing webhook action", () => { let config = getConfig() @@ -9,31 +10,33 @@ describe("test the outgoing webhook action", () => { afterAll() + beforeEach(() => { + nock.cleanAll() + }) + it("should be able to run the action and default to 'get'", async () => { + nock("http://www.example.com/").get("/").reply(200, { foo: "bar" }) const res = await runStep(actions.n8n.stepId, { url: "http://www.example.com", body: { test: "IGNORE_ME", }, }) - expect(res.response.url).toEqual("http://www.example.com") - expect(res.response.method).toEqual("GET") - expect(res.response.body).toBeUndefined() + expect(res.response.foo).toEqual("bar") expect(res.success).toEqual(true) }) it("should add the payload props when a JSON string is provided", async () => { - const payload = `{ "name": "Adam", "age": 9 }` + nock("http://www.example.com/") + .post("/", { name: "Adam", age: 9 }) + .reply(200) const res = await runStep(actions.n8n.stepId, { body: { - value: payload, + value: JSON.stringify({ name: "Adam", age: 9 }), }, method: "POST", url: "http://www.example.com", }) - expect(res.response.url).toEqual("http://www.example.com") - expect(res.response.method).toEqual("POST") - expect(res.response.body).toEqual(`{"name":"Adam","age":9}`) expect(res.success).toEqual(true) }) @@ -53,6 +56,9 @@ describe("test the outgoing webhook action", () => { }) it("should not append the body if the method is HEAD", async () => { + nock("http://www.example.com/") + .head("/", body => body === "") + .reply(200) const res = await runStep(actions.n8n.stepId, { url: "http://www.example.com", method: "HEAD", @@ -60,9 +66,6 @@ describe("test the outgoing webhook action", () => { test: "IGNORE_ME", }, }) - expect(res.response.url).toEqual("http://www.example.com") - expect(res.response.method).toEqual("HEAD") - expect(res.response.body).toBeUndefined() expect(res.success).toEqual(true) }) }) diff --git a/packages/server/src/tests/jestSetup.ts b/packages/server/src/tests/jestSetup.ts index c01f415f9e..bc6384e4cd 100644 --- a/packages/server/src/tests/jestSetup.ts +++ b/packages/server/src/tests/jestSetup.ts @@ -1,6 +1,7 @@ import env from "../environment" import { env as coreEnv, timers } from "@budibase/backend-core" import { testContainerUtils } from "@budibase/backend-core/tests" +import nock from "nock" if (!process.env.CI) { // set a longer timeout in dev for debugging 100 seconds @@ -9,6 +10,15 @@ if (!process.env.CI) { jest.setTimeout(30 * 1000) } +nock.disableNetConnect() +nock.enableNetConnect(host => { + return ( + host.includes("localhost") || + host.includes("127.0.0.1") || + host.includes("::1") + ) +}) + testContainerUtils.setupEnv(env, coreEnv) afterAll(() => { From f16f1fb7bad1477a862526a9c13cdb14e8912712 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 1 Aug 2024 12:03:29 +0100 Subject: [PATCH 05/12] Fix zapier.spec.ts's reliance on the node-fetch mock. --- .../src/automations/tests/zapier.spec.ts | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/packages/server/src/automations/tests/zapier.spec.ts b/packages/server/src/automations/tests/zapier.spec.ts index 994df3dc99..a1406e4818 100644 --- a/packages/server/src/automations/tests/zapier.spec.ts +++ b/packages/server/src/automations/tests/zapier.spec.ts @@ -1,4 +1,5 @@ import { getConfig, afterAll, runStep, actions } from "./utilities" +import nock from "nock" describe("test the outgoing webhook action", () => { let config = getConfig() @@ -9,34 +10,39 @@ describe("test the outgoing webhook action", () => { afterAll() + beforeEach(() => { + nock.cleanAll() + }) + it("should be able to run the action", async () => { + nock("http://www.example.com/").post("/").reply(200, { foo: "bar" }) const res = await runStep(actions.zapier.stepId, { - value1: "test", url: "http://www.example.com", }) - expect(res.response.url).toEqual("http://www.example.com") - expect(res.response.method).toEqual("post") + expect(res.response.foo).toEqual("bar") expect(res.success).toEqual(true) }) it("should add the payload props when a JSON string is provided", async () => { - const payload = `{ "value1": 1, "value2": 2, "value3": 3, "value4": 4, "value5": 5, "name": "Adam", "age": 9 }` + const payload = { + value1: 1, + value2: 2, + value3: 3, + value4: 4, + value5: 5, + name: "Adam", + age: 9, + } + + nock("http://www.example.com/") + .post("/", { ...payload, platform: "budibase" }) + .reply(200, { foo: "bar" }) + const res = await runStep(actions.zapier.stepId, { - value1: "ONE", - value2: "TWO", - value3: "THREE", - value4: "FOUR", - value5: "FIVE", - body: { - value: payload, - }, + body: { value: JSON.stringify(payload) }, url: "http://www.example.com", }) - expect(res.response.url).toEqual("http://www.example.com") - expect(res.response.method).toEqual("post") - expect(res.response.body).toEqual( - `{"platform":"budibase","value1":1,"value2":2,"value3":3,"value4":4,"value5":5,"name":"Adam","age":9}` - ) + expect(res.response.foo).toEqual("bar") expect(res.success).toEqual(true) }) From b39875fb4b6495f816ffa270458f6f3d61f8282d Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 1 Aug 2024 12:07:01 +0100 Subject: [PATCH 06/12] Fix make.spec.ts's reliance on the node-fetch mock. --- .../server/src/automations/tests/make.spec.ts | 44 ++++++++++--------- .../src/automations/tests/zapier.spec.ts | 6 +-- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/packages/server/src/automations/tests/make.spec.ts b/packages/server/src/automations/tests/make.spec.ts index 62474ae2c0..388b197c7f 100644 --- a/packages/server/src/automations/tests/make.spec.ts +++ b/packages/server/src/automations/tests/make.spec.ts @@ -1,4 +1,5 @@ import { getConfig, afterAll, runStep, actions } from "./utilities" +import nock from "nock" describe("test the outgoing webhook action", () => { let config = getConfig() @@ -9,42 +10,45 @@ describe("test the outgoing webhook action", () => { afterAll() + beforeEach(() => { + nock.cleanAll() + }) + it("should be able to run the action", async () => { + nock("http://www.example.com/").post("/").reply(200, { foo: "bar" }) const res = await runStep(actions.integromat.stepId, { - value1: "test", url: "http://www.example.com", }) - expect(res.response.url).toEqual("http://www.example.com") - expect(res.response.method).toEqual("post") + expect(res.response.foo).toEqual("bar") expect(res.success).toEqual(true) }) it("should add the payload props when a JSON string is provided", async () => { - const payload = `{"value1":1,"value2":2,"value3":3,"value4":4,"value5":5,"name":"Adam","age":9}` + const payload = { + value1: 1, + value2: 2, + value3: 3, + value4: 4, + value5: 5, + name: "Adam", + age: 9, + } + + nock("http://www.example.com/") + .post("/", payload) + .reply(200, { foo: "bar" }) + const res = await runStep(actions.integromat.stepId, { - value1: "ONE", - value2: "TWO", - value3: "THREE", - value4: "FOUR", - value5: "FIVE", - body: { - value: payload, - }, + body: { value: JSON.stringify(payload) }, url: "http://www.example.com", }) - expect(res.response.url).toEqual("http://www.example.com") - expect(res.response.method).toEqual("post") - expect(res.response.body).toEqual(payload) + expect(res.response.foo).toEqual("bar") expect(res.success).toEqual(true) }) it("should return a 400 if the JSON payload string is malformed", async () => { - const payload = `{ value1 1 }` const res = await runStep(actions.integromat.stepId, { - value1: "ONE", - body: { - value: payload, - }, + body: { value: "{ invalid json }" }, url: "http://www.example.com", }) expect(res.httpStatus).toEqual(400) diff --git a/packages/server/src/automations/tests/zapier.spec.ts b/packages/server/src/automations/tests/zapier.spec.ts index a1406e4818..a7dc7d3eae 100644 --- a/packages/server/src/automations/tests/zapier.spec.ts +++ b/packages/server/src/automations/tests/zapier.spec.ts @@ -47,12 +47,8 @@ describe("test the outgoing webhook action", () => { }) it("should return a 400 if the JSON payload string is malformed", async () => { - const payload = `{ value1 1 }` const res = await runStep(actions.zapier.stepId, { - value1: "ONE", - body: { - value: payload, - }, + body: { value: "{ invalid json }" }, url: "http://www.example.com", }) expect(res.httpStatus).toEqual(400) From 661e1f241d335b7ce3285ebbf223c19c0ee66658 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 1 Aug 2024 12:09:11 +0100 Subject: [PATCH 07/12] Fix startup.spec.ts's reliance on the node-fetch mock. --- packages/server/src/startup/tests/startup.spec.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/server/src/startup/tests/startup.spec.ts b/packages/server/src/startup/tests/startup.spec.ts index fef9270bb5..fd2e0df61a 100644 --- a/packages/server/src/startup/tests/startup.spec.ts +++ b/packages/server/src/startup/tests/startup.spec.ts @@ -1,6 +1,7 @@ import TestConfiguration from "../../tests/utilities/TestConfiguration" import { startup } from "../index" import { users, utils, tenancy } from "@budibase/backend-core" +import nock from "nock" describe("check BB_ADMIN environment variables", () => { const config = new TestConfiguration() @@ -8,7 +9,17 @@ describe("check BB_ADMIN environment variables", () => { await config.init() }) + beforeEach(() => { + nock.cleanAll() + }) + it("should be able to create a user with the BB_ADMIN environment variables", async () => { + nock("http://localhost:10000") + .get("/api/global/configs/checklist") + .reply(200, {}) + .get("/api/global/self/api_key") + .reply(200, {}) + const EMAIL = "budibase@budibase.com", PASSWORD = "budibase" await tenancy.doInTenant(tenancy.DEFAULT_TENANT_ID, async () => { From 6d70dd1924b6e9e2539e035834c2942f20dd48c0 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 1 Aug 2024 12:19:44 +0100 Subject: [PATCH 08/12] Fix outgoingWebhook.spec.ts's reliance on the node-fetch mock. --- .../automations/tests/outgoingWebhook.spec.js | 41 ------------------- .../automations/tests/outgoingWebhook.spec.ts | 37 +++++++++++++++++ 2 files changed, 37 insertions(+), 41 deletions(-) delete mode 100644 packages/server/src/automations/tests/outgoingWebhook.spec.js create mode 100644 packages/server/src/automations/tests/outgoingWebhook.spec.ts diff --git a/packages/server/src/automations/tests/outgoingWebhook.spec.js b/packages/server/src/automations/tests/outgoingWebhook.spec.js deleted file mode 100644 index 06fe2e0a38..0000000000 --- a/packages/server/src/automations/tests/outgoingWebhook.spec.js +++ /dev/null @@ -1,41 +0,0 @@ -const setup = require("./utilities") -const fetch = require("node-fetch") - -jest.mock("node-fetch") - -describe("test the outgoing webhook action", () => { - let inputs - let config = setup.getConfig() - - beforeAll(async () => { - await config.init() - inputs = { - requestMethod: "POST", - url: "www.example.com", - requestBody: JSON.stringify({ - a: 1, - }), - } - }) - - afterAll(setup.afterAll) - - it("should be able to run the action", async () => { - const res = await setup.runStep( - setup.actions.OUTGOING_WEBHOOK.stepId, - inputs - ) - expect(res.success).toEqual(true) - expect(res.response.url).toEqual("http://www.example.com") - expect(res.response.method).toEqual("POST") - expect(JSON.parse(res.response.body).a).toEqual(1) - }) - - it("should return an error if something goes wrong in fetch", async () => { - const res = await setup.runStep(setup.actions.OUTGOING_WEBHOOK.stepId, { - requestMethod: "GET", - url: "www.invalid.com", - }) - expect(res.success).toEqual(false) - }) -}) diff --git a/packages/server/src/automations/tests/outgoingWebhook.spec.ts b/packages/server/src/automations/tests/outgoingWebhook.spec.ts new file mode 100644 index 0000000000..0e26927c55 --- /dev/null +++ b/packages/server/src/automations/tests/outgoingWebhook.spec.ts @@ -0,0 +1,37 @@ +import { getConfig, afterAll as _afterAll, runStep, actions } from "./utilities" +import nock from "nock" + +describe("test the outgoing webhook action", () => { + const config = getConfig() + + beforeAll(async () => { + await config.init() + }) + + afterAll(_afterAll) + + beforeEach(() => { + nock.cleanAll() + }) + + it("should be able to run the action", async () => { + nock("http://www.example.com") + .post("/", { a: 1 }) + .reply(200, { foo: "bar" }) + const res = await runStep(actions.OUTGOING_WEBHOOK.stepId, { + requestMethod: "POST", + url: "www.example.com", + requestBody: JSON.stringify({ a: 1 }), + }) + expect(res.success).toEqual(true) + expect(res.response.foo).toEqual("bar") + }) + + it("should return an error if something goes wrong in fetch", async () => { + const res = await runStep(actions.OUTGOING_WEBHOOK.stepId, { + requestMethod: "GET", + url: "www.invalid.com", + }) + expect(res.success).toEqual(false) + }) +}) From c8fadc33d9e67d73a49ed607140483deb9a13584 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 1 Aug 2024 12:21:42 +0100 Subject: [PATCH 09/12] Fix discorfd.spec.ts's reliance on the node-fetch mock. --- .../src/automations/tests/discord.spec.js | 26 ------------------- .../src/automations/tests/discord.spec.ts | 26 +++++++++++++++++++ 2 files changed, 26 insertions(+), 26 deletions(-) delete mode 100644 packages/server/src/automations/tests/discord.spec.js create mode 100644 packages/server/src/automations/tests/discord.spec.ts diff --git a/packages/server/src/automations/tests/discord.spec.js b/packages/server/src/automations/tests/discord.spec.js deleted file mode 100644 index 84c7e6f46e..0000000000 --- a/packages/server/src/automations/tests/discord.spec.js +++ /dev/null @@ -1,26 +0,0 @@ -const setup = require("./utilities") -const fetch = require("node-fetch") - -jest.mock("node-fetch") - -describe("test the outgoing webhook action", () => { - let inputs - let config = setup.getConfig() - - beforeAll(async () => { - await config.init() - inputs = { - username: "joe_bloggs", - url: "http://www.example.com", - } - }) - - afterAll(setup.afterAll) - - it("should be able to run the action", async () => { - const res = await setup.runStep(setup.actions.discord.stepId, inputs) - expect(res.response.url).toEqual("http://www.example.com") - expect(res.response.method).toEqual("post") - expect(res.success).toEqual(true) - }) -}) diff --git a/packages/server/src/automations/tests/discord.spec.ts b/packages/server/src/automations/tests/discord.spec.ts new file mode 100644 index 0000000000..07eab7205c --- /dev/null +++ b/packages/server/src/automations/tests/discord.spec.ts @@ -0,0 +1,26 @@ +import { getConfig, afterAll as _afterAll, runStep, actions } from "./utilities" +import nock from "nock" + +describe("test the outgoing webhook action", () => { + let config = getConfig() + + beforeAll(async () => { + await config.init() + }) + + afterAll(_afterAll) + + beforeEach(() => { + nock.cleanAll() + }) + + it("should be able to run the action", async () => { + nock("http://www.example.com/").post("/").reply(200, { foo: "bar" }) + const res = await runStep(actions.discord.stepId, { + url: "http://www.example.com", + username: "joe_bloggs", + }) + expect(res.response.foo).toEqual("bar") + expect(res.success).toEqual(true) + }) +}) From a973b65a7231dfc6462d825b77301448f53db7d3 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 1 Aug 2024 12:36:32 +0100 Subject: [PATCH 10/12] Fix plugni.spec.ts's reliance on the node-fetch mock (again?). --- .../src/api/routes/tests/plugin.spec.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/server/src/api/routes/tests/plugin.spec.ts b/packages/server/src/api/routes/tests/plugin.spec.ts index e592772909..70bbfd3cea 100644 --- a/packages/server/src/api/routes/tests/plugin.spec.ts +++ b/packages/server/src/api/routes/tests/plugin.spec.ts @@ -172,6 +172,28 @@ describe("/plugins", () => { }) describe("npm", () => { it("should be able to create a plugin from npm", async () => { + nock("https://registry.npmjs.org") + .get("/budibase-component") + .reply(200, { + name: "budibase-component", + "dist-tags": { + latest: "1.0.0", + }, + versions: { + "1.0.0": { + dist: { + tarball: + "https://registry.npmjs.org/budibase-component/-/budibase-component-1.0.1.tgz", + }, + }, + }, + }) + .get("/budibase-component/-/budibase-component-1.0.1.tgz") + .replyWithFile( + 200, + "src/api/routes/tests/data/budibase-component-1.0.1.tgz" + ) + const { plugin } = await config.api.plugin.create({ source: PluginSource.NPM, url: "https://www.npmjs.com/package/budibase-component", From a38dc3d16356e2b4113f3d3371622df49789e2c5 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 1 Aug 2024 12:45:37 +0100 Subject: [PATCH 11/12] Fix datasource.spec.ts's reliance on the node-fetch mock. --- .../server/src/api/routes/tests/datasource.spec.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/server/src/api/routes/tests/datasource.spec.ts b/packages/server/src/api/routes/tests/datasource.spec.ts index 255e46167f..4ca766247b 100644 --- a/packages/server/src/api/routes/tests/datasource.spec.ts +++ b/packages/server/src/api/routes/tests/datasource.spec.ts @@ -19,6 +19,7 @@ import { } from "@budibase/types" import { DatabaseName, getDatasource } from "../../../integrations/tests/utils" import { tableForDatasource } from "../../../tests/utilities/structures" +import nock from "nock" describe("/datasources", () => { const config = setup.getConfig() @@ -37,6 +38,7 @@ describe("/datasources", () => { config: {}, }) jest.clearAllMocks() + nock.cleanAll() }) describe("create", () => { @@ -71,6 +73,12 @@ describe("/datasources", () => { describe("dynamic variables", () => { it("should invalidate changed or removed variables", async () => { + nock("http://www.example.com/") + .get("/") + .reply(200, [{ value: "test" }]) + .get("/?test=test") + .reply(200, [{ value: 1 }]) + let datasource = await config.api.datasource.create({ type: "datasource", name: "Rest", @@ -81,7 +89,7 @@ describe("/datasources", () => { const query = await config.api.query.save({ datasourceId: datasource._id!, fields: { - path: "www.google.com", + path: "www.example.com", }, parameters: [], transformer: null, From 97e142a1d8132a06dcb895c26360abe07548d517 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 1 Aug 2024 12:46:01 +0100 Subject: [PATCH 12/12] Delete node-fetch.ts mock. --- packages/server/__mocks__/_node-fetch.ts | 206 ----------------------- 1 file changed, 206 deletions(-) delete mode 100644 packages/server/__mocks__/_node-fetch.ts diff --git a/packages/server/__mocks__/_node-fetch.ts b/packages/server/__mocks__/_node-fetch.ts deleted file mode 100644 index c556d0f2e9..0000000000 --- a/packages/server/__mocks__/_node-fetch.ts +++ /dev/null @@ -1,206 +0,0 @@ -// @ts-ignore -import fs from "fs" - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -module FetchMock { - // @ts-ignore - const fetch = jest.requireActual("node-fetch") - let failCount = 0 - let mockSearch = false - - const func = async (url: any, opts: any) => { - const { host, pathname } = new URL(url) - function json(body: any, status = 200) { - return { - status, - headers: { - raw: () => { - return { "content-type": ["application/json"] } - }, - get: (name: string) => { - if (name.toLowerCase() === "content-type") { - return ["application/json"] - } - }, - }, - json: async () => { - //x-www-form-encoded body is a URLSearchParams - //The call to stringify it leaves it blank - if (body?.opts?.body instanceof URLSearchParams) { - const paramArray = Array.from(body.opts.body.entries()) - body.opts.body = paramArray.reduce((acc: any, pair: any) => { - acc[pair[0]] = pair[1] - return acc - }, {}) - } - return body - }, - } - } - - if (pathname.includes("/api/global")) { - const user = { - email: "test@example.com", - _id: "us_test@example.com", - status: "active", - roles: {}, - builder: { - global: false, - }, - admin: { - global: false, - }, - } - return pathname.endsWith("/users") && opts.method === "GET" - ? json([user]) - : json(user) - } - // mocked data based on url - else if (pathname.includes("api/apps")) { - return json({ - app1: { - url: "/app1", - }, - }) - } else if (host.includes("example.com")) { - return json({ - body: opts.body, - url, - method: opts.method, - }) - } else if (host.includes("invalid.com")) { - return json( - { - invalid: true, - }, - 404 - ) - } else if (mockSearch && pathname.includes("_search")) { - const body = opts.body - const parts = body.split("tableId:") - let tableId - if (parts && parts[1]) { - tableId = parts[1].split('"')[0] - } - return json({ - rows: [ - { - doc: { - _id: "test", - tableId: tableId, - query: opts.body, - }, - }, - ], - bookmark: "test", - }) - } else if (host.includes("google.com")) { - return json({ - url, - opts, - value: - '', - }) - } else if ( - url === "https://api.github.com/repos/my-repo/budibase-comment-box" - ) { - return Promise.resolve({ - json: () => { - return { - name: "budibase-comment-box", - releases_url: - "https://api.github.com/repos/my-repo/budibase-comment-box{/id}", - } - }, - }) - } else if ( - url === "https://api.github.com/repos/my-repo/budibase-comment-box/latest" - ) { - return Promise.resolve({ - json: () => { - return { - assets: [ - { - content_type: "application/gzip", - browser_download_url: - "https://github.com/my-repo/budibase-comment-box/releases/download/v1.0.2/comment-box-1.0.2.tar.gz", - }, - ], - } - }, - }) - } else if ( - url === - "https://github.com/my-repo/budibase-comment-box/releases/download/v1.0.2/comment-box-1.0.2.tar.gz" - ) { - return Promise.resolve({ - body: fs.createReadStream( - "src/api/routes/tests/data/comment-box-1.0.2.tar.gz" - ), - ok: true, - }) - } else if (url === "https://www.npmjs.com/package/budibase-component") { - return Promise.resolve({ - status: 200, - json: () => { - return { - name: "budibase-component", - "dist-tags": { - latest: "1.0.0", - }, - versions: { - "1.0.0": { - dist: { - tarball: - "https://registry.npmjs.org/budibase-component/-/budibase-component-1.0.2.tgz", - }, - }, - }, - } - }, - }) - } else if ( - url === - "https://registry.npmjs.org/budibase-component/-/budibase-component-1.0.2.tgz" - ) { - return Promise.resolve({ - body: fs.createReadStream( - "src/api/routes/tests/data/budibase-component-1.0.2.tgz" - ), - ok: true, - }) - } else if ( - url === "https://www.someurl.com/comment-box/comment-box-1.0.2.tar.gz" - ) { - return Promise.resolve({ - body: fs.createReadStream( - "src/api/routes/tests/data/comment-box-1.0.2.tar.gz" - ), - ok: true, - }) - } else if (url === "https://www.googleapis.com/oauth2/v4/token") { - // any valid response - return json({}) - } else if (host.includes("failonce.com")) { - failCount++ - if (failCount === 1) { - return json({ message: "error" }, 500) - } else { - return json({ - fails: failCount - 1, - url, - opts, - }) - } - } - return fetch(url, opts) - } - - func.Headers = fetch.Headers - - func.mockSearch = () => { - mockSearch = true - } - - module.exports = func -}