From a12045917e6d52a955fb66257ed1e5c5310aeae3 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 27 Oct 2022 15:15:08 +0100 Subject: [PATCH] Minor test rework after improving startup, a lot of mocks weren't being used correctly. --- packages/server/__mocks__/node-fetch.ts | 14 +- packages/server/src/app.ts | 115 +-------------- .../functions/tests/syncQuotas.spec.js | 4 +- .../tests/userEmailViewCasing.spec.js | 10 +- packages/server/src/startup.ts | 138 ++++++++++++++++++ .../src/tests/utilities/TestConfiguration.js | 7 + 6 files changed, 167 insertions(+), 121 deletions(-) create mode 100644 packages/server/src/startup.ts diff --git a/packages/server/__mocks__/node-fetch.ts b/packages/server/__mocks__/node-fetch.ts index dfffa7eb58..0e32c39edd 100644 --- a/packages/server/__mocks__/node-fetch.ts +++ b/packages/server/__mocks__/node-fetch.ts @@ -30,11 +30,21 @@ module FetchMock { } if (url.includes("/api/global")) { - return json({ + const user = { email: "test@test.com", _id: "us_test@test.com", status: "active", - }) + roles: {}, + builder: { + global: false, + }, + admin: { + global: false, + }, + } + return url.endsWith("/users") && opts.method === "GET" + ? json([user]) + : json(user) } // mocked data based on url else if (url.includes("api/apps")) { diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts index a5dfcc6e80..9253186498 100644 --- a/packages/server/src/app.ts +++ b/packages/server/src/app.ts @@ -15,30 +15,16 @@ db.init() const Koa = require("koa") const destroyable = require("server-destroy") const koaBody = require("koa-body") -const pino = require("koa-pino-logger") const http = require("http") const api = require("./api") -const eventEmitter = require("./events") const automations = require("./automations/index") const Sentry = require("@sentry/node") -const fileSystem = require("./utilities/fileSystem") -const bullboard = require("./automations/bullboard") const { logAlert } = require("@budibase/backend-core/logging") -const { pinoSettings } = require("@budibase/backend-core") const { Thread } = require("./threads") -const fs = require("fs") import redis from "./utilities/redis" -import * as migrations from "./migrations" -import { events, installation, tenancy } from "@budibase/backend-core" -import { - createAdminUser, - generateApiKey, - getChecklist, -} from "./utilities/workerRequests" -import { watch } from "./watch" +import { events } from "@budibase/backend-core" import { initialise as initialiseWebsockets } from "./websocket" -import sdk from "./sdk" -import * as pro from "@budibase/pro" +import { startup } from "./startup" const app = new Koa() @@ -91,103 +77,8 @@ server.on("close", async () => { } }) -async function initRoutes() { - app.use(pino(pinoSettings())) - - if (!env.isTest()) { - const plugin = await bullboard.init() - app.use(plugin) - } - - app.context.eventEmitter = eventEmitter - app.context.auth = {} - - // api routes - app.use(api.router.routes()) -} - -async function initPro() { - await pro.init({ - backups: { - processing: { - exportAppFn: sdk.backups.exportApp, - importAppFn: sdk.backups.importApp, - statsFn: sdk.backups.calculateBackupStats, - }, - }, - }) -} - module.exports = server.listen(env.PORT || 0, async () => { - console.log(`Budibase running on ${JSON.stringify(server.address())}`) - env._set("PORT", server.address().port) - eventEmitter.emitPort(env.PORT) - fileSystem.init() - await redis.init() - - // run migrations on startup if not done via http - // not recommended in a clustered environment - if (!env.HTTP_MIGRATIONS && !env.isTest()) { - try { - await migrations.migrate() - } catch (e) { - logAlert("Error performing migrations. Exiting.", e) - shutdown() - } - } - - // check and create admin user if required - if ( - env.SELF_HOSTED && - !env.MULTI_TENANCY && - env.BB_ADMIN_USER_EMAIL && - env.BB_ADMIN_USER_PASSWORD - ) { - const checklist = await getChecklist() - if (!checklist?.adminUser?.checked) { - try { - const tenantId = tenancy.getTenantId() - const user = await createAdminUser( - env.BB_ADMIN_USER_EMAIL, - env.BB_ADMIN_USER_PASSWORD, - tenantId - ) - // Need to set up an API key for automated integration tests - if (env.isTest()) { - await generateApiKey(user._id) - } - - console.log( - "Admin account automatically created for", - env.BB_ADMIN_USER_EMAIL - ) - } catch (e) { - logAlert("Error creating initial admin user. Exiting.", e) - shutdown() - } - } - } - - // monitor plugin directory if required - if ( - env.SELF_HOSTED && - !env.MULTI_TENANCY && - env.PLUGINS_DIR && - fs.existsSync(env.PLUGINS_DIR) - ) { - watch() - } - - // check for version updates - await installation.checkInstallVersion() - - // get the references to the queue promises, don't await as - // they will never end, unless the processing stops - let queuePromises = [] - queuePromises.push(automations.init()) - queuePromises.push(initPro()) - // bring routes online as final step once everything ready - await initRoutes() + await startup(app, server) }) const shutdown = () => { diff --git a/packages/server/src/migrations/functions/tests/syncQuotas.spec.js b/packages/server/src/migrations/functions/tests/syncQuotas.spec.js index 76a40e4b28..cdffeea8bd 100644 --- a/packages/server/src/migrations/functions/tests/syncQuotas.spec.js +++ b/packages/server/src/migrations/functions/tests/syncQuotas.spec.js @@ -1,13 +1,11 @@ -const TestConfig = require("../../../tests/utilities/TestConfiguration") - const syncApps = jest.fn() const syncRows = jest.fn() const syncPlugins = jest.fn() - jest.mock("../usageQuotas/syncApps", () => ({ run: syncApps }) ) jest.mock("../usageQuotas/syncRows", () => ({ run: syncRows }) ) jest.mock("../usageQuotas/syncPlugins", () => ({ run: syncPlugins }) ) +const TestConfig = require("../../../tests/utilities/TestConfiguration") const migration = require("../syncQuotas") describe("run", () => { diff --git a/packages/server/src/migrations/functions/tests/userEmailViewCasing.spec.js b/packages/server/src/migrations/functions/tests/userEmailViewCasing.spec.js index a58f8d9114..3c8894f8e5 100644 --- a/packages/server/src/migrations/functions/tests/userEmailViewCasing.spec.js +++ b/packages/server/src/migrations/functions/tests/userEmailViewCasing.spec.js @@ -1,11 +1,13 @@ +jest.mock("@budibase/backend-core/db", () => ({ + ...jest.requireActual("@budibase/backend-core/db"), + createNewUserEmailView: jest.fn(), +})) +const coreDb = require("@budibase/backend-core/db") const TestConfig = require("../../../tests/utilities/TestConfiguration") const { TENANT_ID } = require("../../../tests/utilities/structures") const { getGlobalDB, doInTenant } = require("@budibase/backend-core/tenancy") // mock email view creation -const coreDb = require("@budibase/backend-core/db") -const createNewUserEmailView = jest.fn() -coreDb.createNewUserEmailView = createNewUserEmailView const migration = require("../userEmailViewCasing") @@ -22,7 +24,7 @@ describe("run", () => { await doInTenant(TENANT_ID, async () => { const globalDb = getGlobalDB() await migration.run(globalDb) - expect(createNewUserEmailView).toHaveBeenCalledTimes(1) + expect(coreDb.createNewUserEmailView).toHaveBeenCalledTimes(1) }) }) }) diff --git a/packages/server/src/startup.ts b/packages/server/src/startup.ts new file mode 100644 index 0000000000..54d8baf098 --- /dev/null +++ b/packages/server/src/startup.ts @@ -0,0 +1,138 @@ +import * as env from "./environment" +import redis from "./utilities/redis" +import { + createAdminUser, + generateApiKey, + getChecklist, +} from "./utilities/workerRequests" +import { + installation, + pinoSettings, + tenancy, + logging, +} from "@budibase/backend-core" +import fs from "fs" +import { watch } from "./watch" +import automations from "./automations" +import fileSystem from "./utilities/fileSystem" +import eventEmitter from "./events" +import * as migrations from "./migrations" +import bullboard from "./automations/bullboard" +import * as pro from "../../../../budibase-pro/packages/pro" +import api from "./api" +import sdk from "./sdk" +const pino = require("koa-pino-logger") + +let STARTUP_RAN = false + +async function initRoutes(app: any) { + app.use(pino(pinoSettings())) + + if (!env.isTest()) { + const plugin = await bullboard.init() + app.use(plugin) + } + + app.context.eventEmitter = eventEmitter + app.context.auth = {} + + // api routes + app.use(api.router.routes()) +} + +async function initPro() { + await pro.init({ + backups: { + processing: { + exportAppFn: sdk.backups.exportApp, + importAppFn: sdk.backups.importApp, + statsFn: sdk.backups.calculateBackupStats, + }, + }, + }) +} + +function shutdown(server?: any) { + server.close() + server.destroy() +} + +export async function startup(app?: any, server?: any) { + if (STARTUP_RAN) { + return + } + STARTUP_RAN = true + if (server) { + console.log(`Budibase running on ${JSON.stringify(server.address())}`) + env._set("PORT", server.address().port) + } + eventEmitter.emitPort(env.PORT) + fileSystem.init() + await redis.init() + + // run migrations on startup if not done via http + // not recommended in a clustered environment + if (!env.HTTP_MIGRATIONS && !env.isTest()) { + try { + await migrations.migrate() + } catch (e) { + logging.logAlert("Error performing migrations. Exiting.", e) + shutdown() + } + } + + // check and create admin user if required + if ( + env.SELF_HOSTED && + !env.MULTI_TENANCY && + env.BB_ADMIN_USER_EMAIL && + env.BB_ADMIN_USER_PASSWORD + ) { + const checklist = await getChecklist() + if (!checklist?.adminUser?.checked) { + try { + const tenantId = tenancy.getTenantId() + const user = await createAdminUser( + env.BB_ADMIN_USER_EMAIL, + env.BB_ADMIN_USER_PASSWORD, + tenantId + ) + // Need to set up an API key for automated integration tests + if (env.isTest()) { + await generateApiKey(user._id) + } + + console.log( + "Admin account automatically created for", + env.BB_ADMIN_USER_EMAIL + ) + } catch (e) { + logging.logAlert("Error creating initial admin user. Exiting.", e) + shutdown() + } + } + } + + // monitor plugin directory if required + if ( + env.SELF_HOSTED && + !env.MULTI_TENANCY && + env.PLUGINS_DIR && + fs.existsSync(env.PLUGINS_DIR) + ) { + watch() + } + + // check for version updates + await installation.checkInstallVersion() + + // get the references to the queue promises, don't await as + // they will never end, unless the processing stops + let queuePromises = [] + queuePromises.push(automations.init()) + queuePromises.push(initPro()) + if (app) { + // bring routes online as final step once everything ready + await initRoutes(app) + } +} diff --git a/packages/server/src/tests/utilities/TestConfiguration.js b/packages/server/src/tests/utilities/TestConfiguration.js index 097b2eabaf..1b529054f5 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.js +++ b/packages/server/src/tests/utilities/TestConfiguration.js @@ -26,6 +26,7 @@ const context = require("@budibase/backend-core/context") const { generateDevInfoID, SEPARATOR } = require("@budibase/backend-core/db") const { encrypt } = require("@budibase/backend-core/encryption") const { DocumentType, generateUserMetadataID } = require("../../db/utils") +const { startup } = require("../../startup") const GLOBAL_USER_ID = "us_uuid1" const EMAIL = "babs@babs.com" @@ -41,6 +42,9 @@ class TestConfiguration { this.server = require("../../app") // we need the request for logging in, involves cookies, hard to fake this.request = supertest(this.server) + this.started = true + } else { + this.started = false } this.appId = null this.allApps = [] @@ -95,6 +99,9 @@ class TestConfiguration { // use a new id as the name to avoid name collisions async init(appName = newid()) { + if (!this.started) { + await startup() + } this.user = await this.globalUser() this.globalUserId = this.user._id this.userMetadataId = generateUserMetadataID(this.globalUserId)