Server flaky tests fixes - improving tenancy config

This commit is contained in:
Rory Powell 2023-02-23 13:43:01 +00:00
parent 2d993adec8
commit 4e1bebe897
8 changed files with 62 additions and 61 deletions

View File

@ -10,7 +10,6 @@ import {
isCloudAccount, isCloudAccount,
isSSOAccount, isSSOAccount,
TenantGroup, TenantGroup,
SettingsConfig,
CloudAccount, CloudAccount,
UserIdentity, UserIdentity,
InstallationGroup, InstallationGroup,

View File

@ -58,7 +58,7 @@ export async function exportApps(ctx: Ctx) {
} }
async function checkHasBeenImported() { async function checkHasBeenImported() {
if (!env.SELF_HOSTED || env.MULTI_TENANCY) { if (!env.SELF_HOSTED) {
return true return true
} }
const apps = await dbCore.getAllApps({ all: true }) const apps = await dbCore.getAllApps({ all: true })
@ -72,7 +72,7 @@ export async function hasBeenImported(ctx: Ctx) {
} }
export async function importApps(ctx: Ctx) { export async function importApps(ctx: Ctx) {
if (!env.SELF_HOSTED || env.MULTI_TENANCY) { if (!env.SELF_HOSTED) {
ctx.throw(400, "Importing only allowed in self hosted environments.") ctx.throw(400, "Importing only allowed in self hosted environments.")
} }
const beenImported = await checkHasBeenImported() const beenImported = await checkHasBeenImported()

View File

@ -1,3 +1,5 @@
import { App } from "@budibase/types"
jest.setTimeout(30000) jest.setTimeout(30000)
import { AppStatus } from "../../../db/utils" import { AppStatus } from "../../../db/utils"
@ -5,6 +7,7 @@ import { AppStatus } from "../../../db/utils"
import * as setup from "./utilities" import * as setup from "./utilities"
import { wipeDb } from "./utilities/TestFunctions" import { wipeDb } from "./utilities/TestFunctions"
import { tenancy } from "@budibase/backend-core"
describe("/cloud", () => { describe("/cloud", () => {
let request = setup.getRequest()! let request = setup.getRequest()!
@ -12,18 +15,10 @@ describe("/cloud", () => {
afterAll(setup.afterAll) afterAll(setup.afterAll)
beforeAll(() => { beforeAll(async () => {
// Importing is only allowed in self hosted environments // Importing is only allowed in self hosted environments
config.modeSelf()
})
beforeEach(async () => {
await config.init() await config.init()
}) config.modeSelf()
afterEach(async () => {
// clear all mocks
jest.clearAllMocks()
}) })
describe("import", () => { describe("import", () => {
@ -32,30 +27,28 @@ describe("/cloud", () => {
// import will not run // import will not run
await wipeDb() await wipeDb()
// get a count of apps before the import
const preImportApps = await request
.get(`/api/applications?status=${AppStatus.ALL}`)
.set(config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(200)
// Perform the import // Perform the import
const res = await request const res = await request
.post(`/api/cloud/import`) .post(`/api/cloud/import`)
.set(config.publicHeaders())
.attach("importFile", "src/api/routes/tests/data/export-test.tar.gz") .attach("importFile", "src/api/routes/tests/data/export-test.tar.gz")
.set(config.defaultHeaders())
.expect(200) .expect(200)
expect(res.body.message).toEqual("Apps successfully imported.") expect(res.body.message).toEqual("Apps successfully imported.")
// get a count of apps after the import // get a count of apps after the import
const postImportApps = await request const postImportApps = await request
.get(`/api/applications?status=${AppStatus.ALL}`) .get(`/api/applications?status=${AppStatus.ALL}`)
.set(config.defaultHeaders()) .set(config.publicHeaders())
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
const apps = postImportApps.body as App[]
// There are two apps in the file that was imported so check for this // There are two apps in the file that was imported so check for this
expect(postImportApps.body.length).toEqual(2) expect(apps.length).toEqual(2)
// The new tenant id was assigned to the imported apps
expect(tenancy.getTenantIDFromAppID(apps[0].appId)).toBe(
config.getTenantId()
)
}) })
}) })
}) })

View File

@ -2,7 +2,6 @@ import * as rowController from "../../../controllers/row"
import * as appController from "../../../controllers/application" import * as appController from "../../../controllers/application"
import { AppStatus } from "../../../../db/utils" import { AppStatus } from "../../../../db/utils"
import { roles, tenancy, context } from "@budibase/backend-core" import { roles, tenancy, context } from "@budibase/backend-core"
import { TENANT_ID } from "../../../../tests/utilities/structures"
import env from "../../../../environment" import env from "../../../../environment"
import { db } from "@budibase/backend-core" import { db } from "@budibase/backend-core"
import Nano from "@budibase/nano" import Nano from "@budibase/nano"
@ -33,7 +32,7 @@ export const getAllTableRows = async (config: any) => {
} }
export const clearAllApps = async ( export const clearAllApps = async (
tenantId = TENANT_ID, tenantId: string,
exceptions: Array<string> = [] exceptions: Array<string> = []
) => { ) => {
await tenancy.doInTenant(tenantId, async () => { await tenancy.doInTenant(tenantId, async () => {

View File

@ -8,9 +8,8 @@ jest.mock("@budibase/backend-core", () => {
} }
} }
}) })
const { tenancy, db: dbCore } = require("@budibase/backend-core") const { context, db: dbCore } = require("@budibase/backend-core")
const TestConfig = require("../../../tests/utilities/TestConfiguration") const TestConfig = require("../../../tests/utilities/TestConfiguration")
const { TENANT_ID } = require("../../../tests/utilities/structures")
// mock email view creation // mock email view creation
@ -26,8 +25,8 @@ describe("run", () => {
afterAll(config.end) afterAll(config.end)
it("runs successfully", async () => { it("runs successfully", async () => {
await tenancy.doInTenant(TENANT_ID, async () => { await config.doInTenant(async () => {
const globalDb = tenancy.getGlobalDB() const globalDb = context.getGlobalDB()
await migration.run(globalDb) await migration.run(globalDb)
expect(dbCore.createNewUserEmailView).toHaveBeenCalledTimes(1) expect(dbCore.createNewUserEmailView).toHaveBeenCalledTimes(1)
}) })

View File

@ -8,3 +8,4 @@ process.env.BUDIBASE_DIR = tmpdir("budibase-unittests")
process.env.LOG_LEVEL = process.env.LOG_LEVEL || "error" process.env.LOG_LEVEL = process.env.LOG_LEVEL || "error"
process.env.ENABLE_4XX_HTTP_LOGGING = "0" process.env.ENABLE_4XX_HTTP_LOGGING = "0"
process.env.MOCK_REDIS = "1" process.env.MOCK_REDIS = "1"
process.env.PLATFORM_URL = "http://localhost:10000"

View File

@ -21,7 +21,6 @@ import {
basicScreen, basicScreen,
basicLayout, basicLayout,
basicWebhook, basicWebhook,
TENANT_ID,
} from "./structures" } from "./structures"
import { import {
constants, constants,
@ -41,8 +40,8 @@ import { generateUserMetadataID } from "../../db/utils"
import { startup } from "../../startup" import { startup } from "../../startup"
import supertest from "supertest" import supertest from "supertest"
import { import {
App,
AuthToken, AuthToken,
Database,
Datasource, Datasource,
Row, Row,
SourceName, SourceName,
@ -63,7 +62,7 @@ class TestConfiguration {
started: boolean started: boolean
appId: string | null appId: string | null
allApps: any[] allApps: any[]
app: any app?: App
prodApp: any prodApp: any
prodAppId: any prodAppId: any
user: any user: any
@ -73,7 +72,7 @@ class TestConfiguration {
linkedTable: any linkedTable: any
automation: any automation: any
datasource: any datasource: any
tenantId: string | null tenantId?: string
defaultUserValues: DefaultUserValues defaultUserValues: DefaultUserValues
constructor(openServer = true) { constructor(openServer = true) {
@ -89,7 +88,6 @@ class TestConfiguration {
} }
this.appId = null this.appId = null
this.allApps = [] this.allApps = []
this.tenantId = null
this.defaultUserValues = this.populateDefaultUserValues() this.defaultUserValues = this.populateDefaultUserValues()
} }
@ -154,19 +152,10 @@ class TestConfiguration {
// use a new id as the name to avoid name collisions // use a new id as the name to avoid name collisions
async init(appName = newid()) { async init(appName = newid()) {
this.defaultUserValues = this.populateDefaultUserValues()
if (context.isMultiTenant()) {
this.tenantId = structures.tenant.id()
}
if (!this.started) { if (!this.started) {
await startup() await startup()
} }
this.user = await this.globalUser() return this.newTenant(appName)
this.globalUserId = this.user._id
this.userMetadataId = generateUserMetadataID(this.globalUserId)
return this.createApp(appName)
} }
end() { end() {
@ -182,24 +171,22 @@ class TestConfiguration {
} }
// MODES // MODES
#setMultiTenancy = (value: boolean) => { setMultiTenancy = (value: boolean) => {
env._set("MULTI_TENANCY", value) env._set("MULTI_TENANCY", value)
coreEnv._set("MULTI_TENANCY", value) coreEnv._set("MULTI_TENANCY", value)
} }
#setSelfHosted = (value: boolean) => { setSelfHosted = (value: boolean) => {
env._set("SELF_HOSTED", value) env._set("SELF_HOSTED", value)
coreEnv._set("SELF_HOSTED", value) coreEnv._set("SELF_HOSTED", value)
} }
modeCloud = () => { modeCloud = () => {
this.#setSelfHosted(false) this.setSelfHosted(false)
this.#setMultiTenancy(true)
} }
modeSelf = () => { modeSelf = () => {
this.#setSelfHosted(true) this.setSelfHosted(true)
this.#setMultiTenancy(false)
} }
// UTILS // UTILS
@ -354,6 +341,8 @@ class TestConfiguration {
}) })
} }
// HEADERS
defaultHeaders(extras = {}) { defaultHeaders(extras = {}) {
const tenantId = this.getTenantId() const tenantId = this.getTenantId()
const authObj: AuthToken = { const authObj: AuthToken = {
@ -374,6 +363,7 @@ class TestConfiguration {
`${constants.Cookie.CurrentApp}=${appToken}`, `${constants.Cookie.CurrentApp}=${appToken}`,
], ],
[constants.Header.CSRF_TOKEN]: this.defaultUserValues.csrfToken, [constants.Header.CSRF_TOKEN]: this.defaultUserValues.csrfToken,
Host: this.tenantHost(),
...extras, ...extras,
} }
@ -383,10 +373,6 @@ class TestConfiguration {
return headers return headers
} }
getTenantId() {
return this.tenantId || TENANT_ID
}
publicHeaders({ prodApp = true } = {}) { publicHeaders({ prodApp = true } = {}) {
const appId = prodApp ? this.prodAppId : this.appId const appId = prodApp ? this.prodAppId : this.appId
@ -397,9 +383,7 @@ class TestConfiguration {
headers[constants.Header.APP_ID] = appId headers[constants.Header.APP_ID] = appId
} }
if (this.tenantId) { headers[constants.Header.TENANT_ID] = this.getTenantId()
headers[constants.Header.TENANT_ID] = this.tenantId
}
return headers return headers
} }
@ -413,6 +397,34 @@ class TestConfiguration {
return this.login({ email, roleId, builder, prodApp }) return this.login({ email, roleId, builder, prodApp })
} }
// TENANCY
tenantHost() {
const tenantId = this.getTenantId()
const platformHost = new URL(coreEnv.PLATFORM_URL).host.split(":")[0]
return `${tenantId}.${platformHost}`
}
getTenantId() {
if (!this.tenantId) {
throw new Error("no test tenant id - init has not been called")
}
return this.tenantId
}
async newTenant(appName = newid()): Promise<App> {
this.defaultUserValues = this.populateDefaultUserValues()
this.tenantId = structures.tenant.id()
this.user = await this.globalUser()
this.globalUserId = this.user._id
this.userMetadataId = generateUserMetadataID(this.globalUserId)
return this.createApp(appName)
}
doInTenant(task: any) {
return context.doInTenant(this.getTenantId(), task)
}
// API // API
async generateApiKey(userId = this.defaultUserValues.globalUserId) { async generateApiKey(userId = this.defaultUserValues.globalUserId) {
@ -432,7 +444,7 @@ class TestConfiguration {
} }
// APP // APP
async createApp(appName: string) { async createApp(appName: string): Promise<App> {
// create dev app // create dev app
// clear any old app // clear any old app
this.appId = null this.appId = null
@ -442,7 +454,7 @@ class TestConfiguration {
null, null,
controllers.app.create controllers.app.create
) )
this.appId = this.app.appId this.appId = this.app?.appId!
}) })
return await context.doInAppContext(this.appId, async () => { return await context.doInAppContext(this.appId, async () => {
// create production app // create production app

View File

@ -13,8 +13,6 @@ import {
const { v4: uuidv4 } = require("uuid") const { v4: uuidv4 } = require("uuid")
export const TENANT_ID = "default"
export function basicTable() { export function basicTable() {
return { return {
name: "TestTable", name: "TestTable",