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 -} 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/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) }) 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, diff --git a/packages/server/src/api/routes/tests/plugin.spec.ts b/packages/server/src/api/routes/tests/plugin.spec.ts index 788d3cf349..70bbfd3cea 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,108 @@ 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", + 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", + }, + }, + }, }) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) - expect(res.body).toBeDefined() - expect(res.body.plugin._id).toEqual("plg_budibase-component") + .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", + }) + 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/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) + }) +}) 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/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/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) + }) +}) diff --git a/packages/server/src/automations/tests/zapier.spec.ts b/packages/server/src/automations/tests/zapier.spec.ts index 994df3dc99..a7dc7d3eae 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,44 +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.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) }) 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) diff --git a/packages/server/src/integrations/tests/googlesheets.spec.ts b/packages/server/src/integrations/tests/googlesheets.spec.ts index 97ac35787d..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,6 +63,13 @@ 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", + }) }) function createBasicTable(name: string, columns: string[]): Table { 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 () => { 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(() => { 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 +}